Immutable.js 以及在 react+redux 項目中的實踐

来源:https://www.cnblogs.com/bai1218/archive/2018/12/01/10048378.html
-Advertisement-
Play Games

前言   本文主要介紹facebook推出的一個類庫immutable.js,以及如何將immutable.js集成到我們團隊現有的react+redux架構的移動端項目中。 本文較長(5000字左右),建議閱讀時間: 20 min 通過閱讀本文,你可以學習到: 什麼是immut ...


來自一位美團大牛的分享,相信可以幫助到你。
原文鏈接:https://juejin.im/post/5948985ea0bb9f006bed7472?utm_source=tuicool&utm_medium=referral

前言

  本文主要介紹facebook推出的一個類庫immutable.js,以及如何將immutable.js集成到我們團隊現有的react+redux架構的移動端項目中。

本文較長(5000字左右),建議閱讀時間: 20 min

通過閱讀本文,你可以學習到:

  • 什麼是immutable.js,它的出現能解決什麼問題
  • immutable.js的特性以及使用api
  • 在一個redux+react的項目中,引入immutable.js能帶來什麼提升
  • 如何集成immutable.js到react+redux中
  • 集成前後的數據對比
  • immutabe.js使用過程中的一些註意點

目錄

  • 一. immutable.js
    • 1.1 原生js引用類型的坑
    • 1.2 immutable.js介紹
      • 1.2.1 Persistent data structure (持久化數據結構)
      • 1.2.2 structural sharing (結構共用)
      • 1.2.3 support lazy operation (惰性操作)
    • 1.3 常用api介紹
    • 1.4 immutable.js的優缺點
  • 二. 在react+redux中集成immutable.js實踐
    • 2.1 點餐H5項目引入immutable.js前的現狀
    • 2.2 如何將immutableJS集成到一個react+redux項目中
      • 2.2.1 明確集成方案,邊界界定
      • 2.2.2 具體集成代碼實現方法
    • 2.3 點餐H5項目優化前後對比
  • 三. immutable.js使用過程中的一些註意點
  • 四. 總結

一. immutable.js

1.1 原生js引用類型的坑

先考慮如下兩個場景:

// 場景一
var obj = {a:1, b:{c:2}};
func(obj);
console.log(obj)  //輸出什麼??

// 場景二
var obj = ={a:1};
var obj2 = obj;
obj2.a = 2;
console.log(obj.a);  // 2
console.log(obj2.a);  // 2複製代碼

  上面兩個場景相信大家平日里開發過程中非常常見,具體原因相信大家也都知道了,這邊不展開細說了,通常這類問題的解決方案是通過淺拷貝或者深拷貝複製一個新對象,從而使得新對象與舊對象引用地址不同。
  在js中,引用類型的數據,優點在於頻繁的操作數據都是在原對象的基礎上修改,不會創建新對象,從而可以有效的利用記憶體,不會浪費記憶體,這種特性稱為mutable(可變),但恰恰它的優點也是它的缺點,太過於靈活多變在複雜數據的場景下也造成了它的不可控性,假設一個對象在多處用到,在某一處不小心修改了數據,其他地方很難預見到數據是如何改變的,針對這種問題的解決方法,一般就像剛纔的例子,會想複製一個新對象,再在新對象上做修改,這無疑會造成更多的性能問題以及記憶體浪費。
  為瞭解決這種問題,出現了immutable對象,每次修改immutable對象都會創建一個新的不可變對象,而老的對象不會改變。

1.2 immutable.js介紹

  現今,實現了immutable數據結構的js類庫有好多,immutable.js就是其中比較主流的類庫之一。

Immutable.js出自Facebook,是最流行的不可變數據結構的實現之一。它從頭開始實現了完全的持久化數據結構,通過使用像tries這樣的先進技術來實現結構共用。所有的更新操作都會返回新的值,但是在內部結構是共用的,來減少記憶體占用(和垃圾回收的失效)。

immutable.js主要有三大特性:

  • Persistent data structure (持久化數據結構)
  • structural sharing (結構共用)
  • support lazy operation (惰性操作)

下麵我們來一一具體介紹下這三個特性:

1.2.1 Persistent data structure (持久化數據結構)

  一般聽到持久化,在編程中第一反應應該是,數據存在某個地方,需要用到的時候就能從這個地方拿出來直接使用
  但這裡說的持久化是另一個意思,用來描述一種數據結構,一般函數式編程中非常常見,指一個數據,在被修改時,仍然能夠保持修改前的狀態,從本質來說,這種數據類型就是不可變類型,也就是immutable
  immutable.js提供了十餘種不可變的類型(List,Map,Set,Seq,Collection,Range等)
  到這,有些同學可能會覺得,這和之前講的拷貝有什麼區別,也是每次都創建一個新對象,開銷一樣很大。ok,那接下來第二個特性會為你揭開疑惑。

1.2.2 structural sharing (結構共用)

(圖片來自網路)

immutable使用先進的tries(字典樹)技術實現結構共用來解決性能問題,當我們對一個Immutable對象進行操作的時候,ImmutableJS會只clone該節點以及它的祖先節點,其他保持不變,這樣可以共用相同的部分,大大提高性能。

這邊岔開介紹一下tries(字典樹),我們來看一個例子




(圖片來自網路)
  圖1就是一個字典樹結構object對象,頂端是root節點,每個子節點都有一個唯一標示(在immutable.js中就是hashcode)
  假設我們現在取data.in的值,根據標記i和n的路徑.可以找到包含5的節點.,可知data.in=5, 完全不需要遍歷整個對象
  那麼,現在我們要把data.tea從3修改成14,怎麼做呢?
  可以看到圖2綠色部分,不需要去遍歷整棵樹,只要從root開始找就行
  實際使用時,可以創建一個新的引用,如圖3,data.tea建一個新的節點,其他節點和老的對象共用,而老的對象還是保持不變
  由於這個特性,比較兩個對象時,只要他們的hashcode是相同的,他們的值就是一樣的,這樣可以避免深度遍歷

1.2.3 support lazy operation (惰性操作)

  • 惰性操作 Seq
  • 特征1:Immutable (不可變)
  • 特征2:lazy(惰性,延遲)

這個特性非常的有趣,這裡的lazy指的是什麼?很難用語言來描述,我們看一個demo,看完你就明白了


  這段代碼的意思就是,數組先取奇數,然後再對基數進行平方操作,然後在console.log第2個數,同樣的代碼,用immutable的seq對象來實現,filter只執行了3次,但原生執行了8次。
  其實原理就是,用seq創建的對象,其實代碼塊沒有被執行,只是被聲明瞭,代碼在get(1)的時候才會實際被執行,取到index=1的數之後,後面的就不會再執行了,所以在filter時,第三次就取到了要的數,從4-8都不會再執行
  想想,如果在實際業務中,數據量非常大,如在我們點餐業務中,商戶的菜單列表可能有幾百道菜,一個array的長度是幾百,要操作這樣一個array,如果應用惰性操作的特性,會節省非常多的性能

1.3 常用api介紹

//Map()  原生object轉Map對象 (只會轉換第一層,註意和fromJS區別)
immutable.Map({name:'danny', age:18})

//List()  原生array轉List對象 (只會轉換第一層,註意和fromJS區別)
immutable.List([1,2,3,4,5])

//fromJS()   原生js轉immutable對象  (深度轉換,會將內部嵌套的對象和數組全部轉成immutable)
immutable.fromJS([1,2,3,4,5])    //將原生array  --> List
immutable.fromJS({name:'danny', age:18})   //將原生object  --> Map

//toJS()  immutable對象轉原生js  (深度轉換,會將內部嵌套的Map和List全部轉換成原生js)
immutableData.toJS();

//查看List或者map大小  
immutableData.size  或者 immutableData.count()

// is()   判斷兩個immutable對象是否相等
immutable.is(imA, imB);

//merge()  對象合併
var imA = immutable.fromJS({a:1,b:2});
var imA = immutable.fromJS({c:3});
var imC = imA.merge(imB);
console.log(imC.toJS())  //{a:1,b:2,c:3}

//增刪改查(所有操作都會返回新的值,不會修改原來值)
var immutableData = immutable.fromJS({
    a:1,
    b:2,
    c:{
        d:3
    }
});
var data1 = immutableData.get('a') //  data1 = 1  
var data2 = immutableData.getIn(['c', 'd']) // data2 = 3   getIn用於深層結構訪問
var data3 = immutableData.set('a' , 2);   // data3中的 a = 2
var data4 = immutableData.setIn(['c', 'd'], 4);   //data4中的 d = 4
var data5 = immutableData.update('a',function(x){return x+4})   //data5中的 a = 5
var data6 = immutableData.updateIn(['c', 'd'],function(x){return x+4})   //data6中的 d = 7
var data7 = immutableData.delete('a')   //data7中的 a 不存在
var data8 = immutableData.deleteIn(['c', 'd'])   //data8中的 d 不存在複製代碼

上面只列舉了部分常用方法,具體查閱官網api:facebook.github.io/immutable-j…
immutablejs還有很多類似underscore語法糖,使用immutable.js之後完全可以在項目中去除lodash或者underscore之類的工具庫。

1.4 immutable.js的優缺點

優點:

  • 降低mutable帶來的複雜度
  • 節省記憶體
  • 歷史追溯性(時間旅行):時間旅行指的是,每時每刻的值都被保留了,想回退到哪一步只要簡單的將數據取出就行,想一下如果現在頁面有個撤銷的操作,撤銷前的數據被保留了,只需要取出就行,這個特性在redux或者flux中特別有用
  • 擁抱函數式編程:immutable本來就是函數式編程的概念,純函數式編程的特點就是,只要輸入一致,輸出必然一致,相比於面向對象,這樣開發組件和調試更方便

缺點:

  • 需要重新學習api
  • 資源包大小增加(源碼5000行左右)
  • 容易與原生對象混淆:由於api與原生不同,混用的話容易出錯。

二. 在react+redux中集成immutable.js實踐

  前面介紹了這麼多,其實是想引出這塊重點,這章節會結合點評點餐團隊在實際項目中的實踐,給出使用immutable.js前後對react+redux項目的性能提升

2.1 點餐H5項目引入immutable.js前的現狀

  目前項目使用react+redux,由於項目的不斷迭代以及需求複雜度的提高,redux中維護的state結構日漸龐大,已經不是一個簡單的平鋪數據了,如菜單頁state已經會出現三四層的object以及array嵌套,我們知道,JS中的object與array是引用類型,在不斷的操作過程中,state經過多次的action改變之後, 原本複雜state已經變得不可控,結果就是導致了一次state變化牽動了許多自身狀態沒有發生改動的component去re-render。如下圖


  這裡推薦一下react的性能指標工具react-addons-perf
  如果你沒有使用這個工具看之前,別人問你,圖中這個簡單的堂食/外帶的button的變化會引起哪些component去re-render,你可能會回答只有就餐方式這個component。
  但當你真正使用react-addons-perf去查看之後你會發現,WTF??!一次操作竟然導致了這麼多沒任何關係的component重新渲染了??
  什麼原因??

shouldComponentUpdate

shouldComponentUpdate (nextProps, nextState) {
   return nextProps.id !== this.props.id;
};複製代碼

  相信接觸過react開發的同學都知道,react有個重要的性能優化的點就是shouldComponentUpdate,shouldComponentUpdate返回true代碼該組件要re-render,false則不重新渲染
  那簡單的場景可以直接使用==去判斷this.props和nextProps是否相等,但當props是一個複雜的結構時,==肯定是沒用的
  網上隨便查一下就會發現shallowCompare這個東西,我們來試一下
使用shallowCompare的例子:


可以看到,其實2個對象的count是不相等的,但shallowCompare返回的還是true
原因:
  shallowCompare只是進行了對象的頂層節點比較,也就是淺比較,上圖中的props由於結構比較複雜,在深層的對象中有count不一樣,所以這種情況無法通過shallowCompare處理。
shallowEqual源碼:

function shallowEqual(objA, objB) {
  if (is(objA, objB)) {
    return true;
  }

  if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) {
    return false;
  }

  var keysA = Object.keys(objA);
  var keysB = Object.keys(objB);

  if (keysA.length !== keysB.length) {
    return false;
  }
//這裡只比較了對象A和B第一層是否相等,當對象過深時,無法返回正確結果
  // Test for A's keys different from B.
  for (var i = 0; i < keysA.length; i++) {
    if (!hasOwnProperty.call(objB, keysA[i]) || !is(objA[keysA[i]], objB[keysA[i]])) {
      return false;
    }
  }

  return true;
}複製代碼

  這裡,我們肯定不可能每次比較都是用深比較,去遍歷所有的結構,這樣帶來的性能代價是巨大的,剛纔我們說到immutable.js有個特性是引用比較(hashcode),這個特性就完美契合這邊的場景

2.2 如何將immutableJS集成到一個react+redux項目中

2.2.1 明確集成方案,邊界界定

  首先,我們有必要來劃分一下邊界,哪些數據需要使用不可變數據,哪些數據要使用原生js數據結構,哪些地方需要做互相轉換

  • 在redux中,全局state必須是immutable的,這點毋庸置疑是我們使用immutable來優化redux的核心
  • 組件props是通過redux的connect從state中獲得的,並且引入immutableJS的另一個目的是減少組件shouldComponentUpdate中不必要渲染,shouldComponentUpdate中比對的是props,如果props是原生JS就失去了優化的意義
  • 組件內部state如果需要提交到store的,必須是immutable,否則不強制
  • view提交到action中的數據必須是immutable
  • Action提交到reducer中的數據必須是immutable
  • reducer中最終處理state必須是以immutable的形式處理並返回
  • 與服務端ajax交互中返回的callback統一封裝,第一時間轉換成immutable數據

  從上面這些點可以看出,幾乎整個項目都是必須使用immutable的,只有在少數與外部依賴有交互的地方使用了原生js。
  這麼做的目的其實就是為了防止在大型項目中,原生js與immutable混用,導致coder自己都不清楚一個變數中存儲的到底是什麼類型的數據。
  那有人可能會覺得說,在一個全新項目中這樣是可行的,但在一個已有的成熟項目中,要將所有的變數全部改成immutablejs,代碼的改動量與侵入性非常大,風險也高。那他們會想到,將reducer中的state用fromJS()改成immutable進行state操作,然後再通過toJS()轉成原生js返回出來,這樣不就可以即讓state變得可追溯,又不用去修改reducer以外的代碼,代價非常的小。

export default function indexReducer(state, action) {
    switch (action.type) {
    case RECEIVE_MENU:
        state = immutable.fromJS(state);   //轉成immutable
        state = state.merge({a:1});
        return state.toJS()    //轉回原生js
    }  
}複製代碼

兩點問題:

  1. fromJS() 和 toJS() 是深層的互轉immutable對象和原生對象,性能開銷大,儘量不要使用(見下一章節做了具體的對比)
  2. 組件中props和state還是原生js,shouldComponentUpdate仍然無法做利用immutablejs的優勢做深度比較

2.2.2 具體集成代碼實現方法

redux-immutable

  redux中,第一步肯定利用combineReducers來合併reducer並初始化state,redux自帶的combineReducers只支持state是原生js形式的,所以這裡我們需要使用redux-immutable提供的combineReducers來替換原來的方法

import {combineReducers} from 'redux-immutable';
import dish from './dish';
import menu from './menu';
import cart from './cart';

const rootReducer = combineReducers({
    dish,
    menu,
    cart,
});

export default rootReducer;複製代碼

  reducer中的initialState肯定也需要初始化成immutable類型

const initialState = Immutable.Map({});
export default function menu(state = initialState, action) {
    switch (action.type) {
    case SET_ERROR:
        return state.set('isError', true);
    }
}複製代碼

  state成為了immutable類型,那相應的頁面其他文件都需要做相應的寫法改變

//connect
function mapStateToProps(state) {
    return {
        menuList: state.getIn(['dish', 'list']),  //使用get或者getIn來獲取state中的變數
        CartList: state.getIn(['dish', 'cartList'])
    }
}複製代碼

  頁面中原來的原生js變數需要改造成immutable類型,不一一列舉了

服務端交互ajax封裝

  前端代碼使用了immutable,但服務端下發的數據還是json,所以需要統一在ajax處做封裝並且將服務端返回數據轉成immutable

//偽代碼
$.ajax({
    type: 'get',
    url: 'XXX',
    dataType: 'json',
    success(res){
        res = immutable.fromJS(res || {});
        callback && callback(res);
    },
    error(e) {
        e = immutable.fromJS(e || {});
        callback && callback(e);
    },
});複製代碼

這樣的話,頁面中統一將ajax返回當做immutable類型來處理,不用擔心混淆

shouldComponentUpdate

  重中之重!之前已經介紹了很多為什麼要用immutable來改造shouldComponentUpdate,這裡就不多說了,直接看怎麼改造
shouldComponentUpdate具體怎麼封裝有很多種辦法,我們這裡選擇了封裝一層component的基類,在基類中去統一處理shouldComponentUpdate,組件中直接繼承基類的方式

//baseComponent.js   component的基類方法

import React from 'react';
import {is} from 'immutable';

class BaseComponent extends React.Component {
    constructor(props, context, updater) {
        super(props, context, updater);
    }

    shouldComponentUpdate(nextProps, nextState) {
        const thisProps = this.props || {};
        const thisState = this.state || {};
        nextState = nextState || {};
        nextProps = nextProps || {};

        if (Object.keys(thisProps).length !== Object.keys(nextProps).length ||
            Object.keys(thisState).length !== Object.keys(nextState).length) {
            return true;
        }

        for (const key in nextProps) {
            if (!is(thisProps[key], nextProps[key])) {
                return true;
            }
        }

        for (const key in nextState) {
            if (!is(thisState[key], nextState[key])) {
                return true;
            }
        }
        return false;
    }
}

export default BaseComponent;複製代碼

  組件中如果需要使用統一封裝的shouldComponentUpdate,則直接繼承基類

import BaseComponent from './BaseComponent';
class Menu extends BaseComponent {
    constructor() {
        super();
    }
    …………
}複製代碼

  當然如果組件不想使用封裝的方法,那直接在該組件中重寫shouldComponentUpdate就行了

2.3 點餐H5項目優化前後對比

這邊只是截了幾張圖舉例
優化前搜索頁:


優化後:

優化前購物車頁:

優化後:

三. immutable.js使用過程中的一些註意點

1.fromJS和toJS會深度轉換數據,隨之帶來的開銷較大,儘可能避免使用,單層數據轉換使用Map()和List()

(做了個簡單的fromJS和Map性能對比,同等條件下,分別用兩種方法處理1000000條數據,可以看到fromJS開銷是Map的4倍)

2.js是弱類型,但Map類型的key必須是string!(看下圖官網說明)

3.所有針對immutable變數的增刪改必須左邊有賦值,因為所有操作都不會改變原來的值,只是生成一個新的變數

//javascript
var arr = [1,2,3,4];
arr.push(5);
console.log(arr) //[1,2,3,4,5]

//immutable
var arr = immutable.fromJS([1,2,3,4])
//錯誤用法
arr.push(5);
console.log(arr) //[1,2,3,4]
//正確用法
arr = arr.push(5);
console.log(arr) //[1,2,3,4,5]複製代碼

4.引入immutablejs後,不應該再出現對象數組拷貝的代碼(如下舉例)

//es6對象複製
var state = Object.assign({}, state, {
    key: value
});

//array複製
var newArr = [].concat([1,2,3])複製代碼

5. 獲取深層深套對象的值時不需要做每一層級的判空

//javascript
var obj = {a:1}
var res = obj.a.b.c   //error

//immutable
var immutableData=immutable.fromJS({a:1})
var res = immutableData.getIn(['a', 'b', 'c'])  //undefined複製代碼

6.immutable對象直接可以轉JSON.stringify(),不需要顯式手動調用toJS()轉原生

7. 判斷對象是否是空可以直接用size

8.調試過程中要看一個immutable變數中真實的值,可以chrome中加斷點,在console中使用.toJS()方法來查看

四. 總結

  總的來說immutable.js的出現解決了許多原生js的痛點,並且自身對性能方面做了許多的優化處理,而且immuable.js作為和react同期推出的一個產品,完美的契合了react+redux的state流處理,redux的宗旨就是單一數據流,可追溯,這兩點恰恰是immutable.js的優勢,自然水到渠成,何樂而不為。
  當然也不是所有使用react+redux的場景都需要使用immutable.js,建議滿足項目足夠大,state結構足夠複雜的原則,小項目可以手動處理shouldComponentUpdate,不建議使用,得不償失。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 使用百度語音介面,實現文字轉化成語音播放 ...
  • 什麼是屬性選擇器 指對帶有指定屬性的HTML元素設置樣式,有以下幾種: 1.element[attribute] 2.element[attribute = "value"] 3.element[attribute ~= "value"] 4.element[attribute ^= "value" ...
  • 一般用於公告的滾動效果 ...
  • chalk 是一個可以給終端輸出加上色彩的工具包,有了它,你的終端將變得五顏六色,花哨的一匹
  • Class constructor() 實例對象 表達式 提升 私有方法和私有屬性 this name屬性 取值函數和存值函數 Generator方法 靜態方法 靜態屬性和實例屬性 new.target屬性 1.constructor是構造方法。 2.this關鍵字則代表實例對象。 3.定義“類”的 ...
  • 1. dom>documentElement>body>tagname 2.我們常用的節點標簽。 元素節點(標簽) 文本節點 屬性節點(標簽里的屬性) 3.document有個屬性nodeType返回的是數字 1.代表元素節點 2.代表屬性節點 3.代表文本節點 4.節點的獲取 元素節點獲取有很多方 ...
  • 話不多說,直接開魯 1. startsWith() 作用: 檢測字元串以什麼開頭 實例: 2. endsWith() 作用: 檢測字元串以什麼結尾 實例: 3. includes() 作用: 檢測字字元串中是否包含某個字元 實例: 4. repeat(n) 作用: 重覆 n 次 實例 5. padS ...
  • 接下去一系列關於 JavaScript 語法的文章中,將會出現很多在 Java 里出現或者沒出現過的專業術語,所以第一篇就先來羅列一下,每個術語先給一定的解釋。 有的理解不了沒關係,等這系列文章看完,再回過頭來看這一張基本也都能理解了。先羅列出來,至少有個印象,不至於到時看到一臉懵逼。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...