欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Vue3響應式對象Reactive和Ref的用法解讀

 更新時間:2022年09月07日 10:49:43   作者:SunsetFeng  
這篇文章主要介紹了Vue3響應式對象Reactive和Ref的用法,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

一、內(nèi)容簡介

本篇文章著重結合源碼版本V3.2.20介紹Reactive和Ref。前置技能需要了解Proxy對象的工作機制,以下貼出的源碼均在關鍵位置備注了詳細注釋。

備注:本篇幅只講到收集依賴和觸發(fā)依賴更新的時機,并未講到如何收集依賴和如何觸發(fā)依賴。響應式原理快捷通道。

二、Reactive

1. 關鍵源碼

/*源碼位置:/packages/reactivity/src/reactive.ts*/
/**
?* 創(chuàng)建響應式代理對象
?* @param target 被代理對象
?* @param isReadonly 是否只讀
?* @param baseHandlers 普通對象的攔截操作
?* @param collectionHandlers 集合對象的攔截操作
?* @param proxyMap 代理Map
?* @returns?
?*/
function createReactiveObject(
? target: Target,
? isReadonly: boolean,
? baseHandlers: ProxyHandler<any>,
? collectionHandlers: ProxyHandler<any>,
? proxyMap: WeakMap<Target, any>
) {
? //如果不是對象,則警告,Proxy代理只支持對象
? if (!isObject(target)) {
? ? if (__DEV__) {
? ? ? console.warn(`value cannot be made reactive: ${String(target)}`)
? ? }
? ? return target
? }
? //如果被代理對象已經(jīng)是一個proxy對象且是響應式的并且此次創(chuàng)建的新代理對象不是只讀的,則直接返回被代理對象
? //這兒存在一種情況需要重新創(chuàng)建,即被代理對象已經(jīng)是一個代理對象了,且可讀可寫。但新創(chuàng)建的代理對象是只讀的
? //那么,本次生成的那個代理對象最終是只讀的。響應式必須可讀可寫,只讀的代理對象是非響應式的。
? if (
? ? target[ReactiveFlags.RAW] &&
? ? !(isReadonly && target[ReactiveFlags.IS_REACTIVE])
? ) {
? ? return target
? }
? //從map中找,如果對象已經(jīng)被代理過,則直接從map中返回,否則生成代理
? const existingProxy = proxyMap.get(target)
? if (existingProxy) {
? ? return existingProxy
? }
? // 獲取代理類型,即采用集合類型的代理還是普通對象類型的代理
? const targetType = getTargetType(target)
? if (targetType === TargetType.INVALID) {
? ? return target
? }
? // 生成代理對象并存入map中
? const proxy = new Proxy(
? ? target,
? ? targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
? )
? proxyMap.set(target, proxy)
? return proxy
}

2. 源碼流程分析

Vue中創(chuàng)建響應式代理對象都是通過createReactiveObject方法創(chuàng)建。這個方法里面的主要邏輯很簡單,就是生成一個目標對象的代理對象,代理對象最為核心的操作攔截則由外部根據(jù)是否只讀和是否淺響應傳入,然后將這個代理對象存起來以備下次快捷獲取。

三、代理攔截操作

1. 數(shù)組操作

(1).關鍵源碼

//源碼位置: /packages/reactivity/src/baseHandlers.ts
function createArrayInstrumentations() {
? const instrumentations: Record<string, Function> = {}
? // instrument identity-sensitive Array methods to account for possible reactive
? // values
? ;(['includes', 'indexOf', 'lastIndexOf'] as const).forEach(key => {
? ? instrumentations[key] = function (this: unknown[], ...args: unknown[]) {
? ? ? //獲取原始數(shù)組
? ? ? const arr = toRaw(this) as any
? ? ? for (let i = 0, l = this.length; i < l; i++) {
? ? ? ? //收集依賴 鍵值為索引 i
? ? ? ? track(arr, TrackOpTypes.GET, i + '')
? ? ? }
? ? ? // 調(diào)用數(shù)組的原始方法
? ? ? const res = arr[key](...args)
? ? ? if (res === -1 || res === false) {
? ? ? ? // 如果不存在,則將參數(shù)參數(shù)轉(zhuǎn)換為原始數(shù)據(jù)在試一次(這兒可能是防止傳入的是代理對象導致獲取失敗)
? ? ? ? return arr[key](...args.map(toRaw))
? ? ? } else {
? ? ? ? return res
? ? ? }
? ? }
? })
? // instrument length-altering mutation methods to avoid length being tracked
? // which leads to infinite loops in some cases (#2137)
? ;(['push', 'pop', 'shift', 'unshift', 'splice'] as const).forEach(key => {
? ? instrumentations[key] = function (this: unknown[], ...args: unknown[]) {
? ? ? //由于上面的方法會改變數(shù)組長度,因此暫停收集依賴,不然會導致無限遞歸
? ? ? pauseTracking()
? ? ? //調(diào)用原始方法
? ? ? const res = (toRaw(this) as any)[key].apply(this, args)
? ? ? //復原依賴收集
? ? ? resetTracking()
? ? ? return res
? ? }
? })
? return instrumentations
}

(2).源碼流程分析

上述源碼其實就是重寫了對于數(shù)組方法的操作,在通過數(shù)組的代理對象訪問以上數(shù)組方法時,就會執(zhí)行重寫后的數(shù)組方法。

內(nèi)部邏輯很簡單,對于改變了數(shù)組長度的方法,先暫停依賴收集,調(diào)用原始數(shù)組方法,然后復原依賴收集。

對于判斷元素是否存在的數(shù)組方法,執(zhí)行依賴收集并調(diào)用數(shù)組原始方法。

總結來說最終都是調(diào)用了數(shù)組的原始方法,只不過在調(diào)用前后添加了關于依賴收集相關的行為。

2.Get操作

(1).關鍵源碼

//源碼位置: /packages/reactivity/src/baseHandlers.ts
/**
?* 創(chuàng)建并且返回一個Get方法
?* @param isReadonly 是否只讀
?* @param shallow 是否淺響應
?* @returns?
?*/
function createGetter(isReadonly = false, shallow = false) {
? return function get(target: Target, key: string | symbol, receiver: object) {
? ? //這兒不重要,其實就是通過代理對象訪問這幾個特殊屬性時,返回相應的值,和響應式無關
? ? if (key === ReactiveFlags.IS_REACTIVE) {
? ? ? return !isReadonly
? ? } else if (key === ReactiveFlags.IS_READONLY) {
? ? ? return isReadonly
? ? } else if (
? ? ? key === ReactiveFlags.RAW &&
? ? ? receiver ===
? ? ? ? (isReadonly
? ? ? ? ? ? shallow
? ? ? ? ? ? ? shallowReadonlyMap
? ? ? ? ? ? : readonlyMap
? ? ? ? ? : shallow
? ? ? ? ? ? shallowReactiveMap
? ? ? ? ? : reactiveMap
? ? ? ? ).get(target)
? ? ) {
? ? ? return target
? ? }
??
? ? const targetIsArray = isArray(target)
? ? //如果是調(diào)用的數(shù)組方法,則調(diào)用重寫后的數(shù)組方法,前提不是只讀的
? ? if (!isReadonly && targetIsArray && hasOwn(arrayInstrumentations, key)) {
? ? ? return Reflect.get(arrayInstrumentations, key, receiver)
? ? }
? ? //調(diào)用原始行為獲取值
? ? const res = Reflect.get(target, key, receiver)
? ? //訪問Symbol對象上的屬性和__proto__,__v_isRef,__isVue這3個屬性,直接返回結果值
? ? if (isSymbol(key) ? builtInSymbols.has(key) : isNonTrackableKeys(key)) {
? ? ? return res
? ? }
? ? if (!isReadonly) {
? ? ? //不是只讀,則收集依賴
? ? ? track(target, TrackOpTypes.GET, key)
? ? }
? ? if (shallow) {
? ? ? //如果對象是淺響應的 則返回結果
? ? ? return res
? ? }
? ? if (isRef(res)) {
? ? ? //如果值是Ref對象且是通過數(shù)組代理對象的下標訪問的,則不做解包裝操作,否則返回解包裝后的值
? ? ? // ref unwrapping - does not apply for Array + integer key.
? ? ? const shouldUnwrap = !targetIsArray || !isIntegerKey(key)
? ? ? return shouldUnwrap ? res.value : res
? ? }
? ? if (isObject(res)) {
? ? ? //走到這兒需要滿足非淺響應。如果結果是一個對象,則將改對象轉(zhuǎn)換為只讀代理對象或者響應式代理對象返回
? ? ? //e.g.?
? ? ? // test:{
? ? ? // ? a:{
? ? ? // ? ? c:10
? ? ? // ? }
? ? ? // }
? ? ? //以上測試對象當訪問屬性a時,此時res是一個普通對象,如果不轉(zhuǎn)換為代理對象,則對a.c的操作不會被攔截處理,導致無法響應式處理
? ? ? // Convert returned value into a proxy as well. we do the isObject check
? ? ? // here to avoid invalid value warning. Also need to lazy access readonly
? ? ? // and reactive here to avoid circular dependency.
? ? ? return isReadonly ? readonly(res) : reactive(res)
? ? }
? ??
? ? return res
? }
}

(2).源碼流程分析

上述Get方法是在通過代理對象獲取某一個值時觸發(fā)的。流程很簡單,就是對幾個特殊屬性做了特殊返回。

如果是數(shù)組方法,則調(diào)用重寫后的數(shù)組方法,不是則調(diào)用原始行為獲取值。

如果不是只讀,則收集依賴,對返回結果進行判斷特殊處理。其中最關鍵的地方在于收集依賴和將獲取到的嵌套對象轉(zhuǎn)換為響應式對象。

3. Set操作

(1).關鍵源碼

//源碼位置: /packages/reactivity/src/baseHandlers.ts
/**
?* 創(chuàng)建并返回一個Set方法
?* @param shallow 是否淺響應
?* @returns?
?*/
function createSetter(shallow = false) {
? return function set(
? ? target: object,
? ? key: string | symbol,
? ? value: unknown,
? ? receiver: object
? ): boolean {
? ? //獲取改變之前的值
? ? let oldValue = (target as any)[key]
? ? if (!shallow) {
? ? ? value = toRaw(value)
? ? ? oldValue = toRaw(oldValue)
? ? ? //對Ref類型值的特殊處理
? ? ? //比較2個值,如果舊值是Ref對象,新值不是,則直接變Ref對象的value屬性
? ? ? if (!isArray(target) && isRef(oldValue) && !isRef(value)) {
? ? ? ? //這兒看似沒有觸發(fā)依賴更新,其實Ref對象的value進行賦值會觸發(fā)Ref對象的寫操作,在那個操作里面會觸發(fā)依賴更新
? ? ? ? oldValue.value = value
? ? ? ? return true
? ? ? }
? ? } else {
? ? ? // in shallow mode, objects are set as-is regardless of reactive or not
? ? }
? ? const hadKey =
? ? ? isArray(target) && isIntegerKey(key)
? ? ? ? ? Number(key) < target.length
? ? ? ? : hasOwn(target, key)
? ? const result = Reflect.set(target, key, value, receiver)
? ? // don't trigger if target is something up in the prototype chain of original
? ? // 這個判斷其實是處理一個代理對象的原型也是代理對象的情況,以下是測試代碼
? ? // let hiddenValue: any
? ? // const obj = reactive<{ prop?: number }>({})
? ? // const parent = reactive({
? ? // ? set prop(value) {
? ? // ? ? hiddenValue = value
? ? // ? },
? ? // ? get prop() {
? ? // ? ? return hiddenValue
? ? // ? }
? ? // })
? ? // Object.setPrototypeOf(obj, parent)
? ? // obj.prop = 4
? ? // 當存在上述情形,第一次設置值時,由于子代理沒有prop屬性方法,會觸發(fā)父代理的set方法。父代理的這個判斷此時是false,算是一個優(yōu)化,避免2個觸發(fā)更新
? ? if (target === toRaw(receiver)) {
? ? ? if (!hadKey) {
? ? ? ? //觸發(fā)add類型依賴更新
? ? ? ? trigger(target, TriggerOpTypes.ADD, key, value)
? ? ? } else if (hasChanged(value, oldValue)) {
? ? ? ? //觸發(fā)set類型依賴更新
? ? ? ? trigger(target, TriggerOpTypes.SET, key, value, oldValue)
? ? ? }
? ? }
? ? return result
? }
}

(2).源碼流程分析

當設置時,首先對舊值是Ref類型對象做了個特殊處理,如果滿足條件,則走Ref對象的set方法邏輯觸發(fā)依賴更新。

否則根據(jù)是否存在key值,判斷是新增屬性,還是修改屬性,觸發(fā)不同類型的依賴更新。

之所以要區(qū)分依賴類型,是因為某些屬性會連帶別的屬性更改,比如數(shù)組直接設置下標,會導致length的更改,這個時候需要收集length為鍵值的依賴,以便連帶更新依賴的length屬性的地方。

4. 其余行為攔截操作

(1).關鍵源碼

//源碼位置: /packages/reactivity/src/baseHandlers.ts
/**
?* delete操作符時觸發(fā)
?* @param target 目標對象
?* @param key 鍵值
?* @returns?
?*/
function deleteProperty(target: object, key: string | symbol): boolean {
? const hadKey = hasOwn(target, key)
? const oldValue = (target as any)[key]
? const result = Reflect.deleteProperty(target, key)
? if (result && hadKey) {
? ? //觸發(fā)依賴更新
? ? trigger(target, TriggerOpTypes.DELETE, key, undefined, oldValue)
? }
? return result
}
/**
?* in 操作符時觸發(fā)
?* @param target 目標對象
?* @param key 鍵值
?* @returns?
?*/
function has(target: object, key: string | symbol): boolean {
? const result = Reflect.has(target, key)
? if (!isSymbol(key) || !builtInSymbols.has(key)) {
? ?? ?//收集依賴
? ? track(target, TrackOpTypes.HAS, key)
? }
? return result
}
/**
?* Object.keys()等類似方法時調(diào)用
?* @param target 目標對象
?* @returns?
?*/
function ownKeys(target: object): (string | symbol)[] {
? //收集依賴
? track(target, TrackOpTypes.ITERATE, isArray(target) ? 'length' : ITERATE_KEY)
? return Reflect.ownKeys(target)
}

(2).源碼流程分析

上述源碼其實就是在對一些特殊操作符或者特定API時的特殊處理,本質(zhì)還是收集依賴和觸發(fā)依賴更新,沒什么好講的。

四、Ref對象

1. 思考一個問題

為什么存在了Reactive代理對象后,已經(jīng)可以進行依賴收集和依賴更新了,還要設計一個Ref類型。

測試一:針對以下測試代碼,Ref對象值的改變正確觸發(fā)了更新

?? ?//源碼位置 /packages/reactivity/__test__/ref.spec.ts
? ? const a = ref(1)
? ? let dummy
? ? let calls = 0
? ? effect(() => {
? ? ? calls++
? ? ? dummy = a.value;
? ? })
? ? a.value = 2;
? ? //此時dummy = 2,a對象值的改變觸發(fā)了依賴更新

測試二:修改以上代碼,更新失敗

? ? const a = 1
? ? let dummy
? ? let calls = 0
? ? effect(() => {
? ? ? calls++
? ? ? dummy = a;
? ? })
? ? a= 2;
? ? //此時dummy = 1,a的改變沒有觸發(fā)依賴更新

上述2個示例很明顯的表明出了,對于非響應式對象的改變,不會觸發(fā)依賴更新。Reactive是通過代理實現(xiàn)的,代理只支持對象,不支持非對象的基礎類型。所以需要設計一個Ref類型來包裝這些類型數(shù)據(jù),以便擁有響應式狀態(tài)

2. 簡要說明

既然設計了Ref來支持非對象屬性,那么也一定需要兼容對象屬性。內(nèi)部其實很簡單,如果是對象,則直接轉(zhuǎn)為Reactive代理對象。

3. 關鍵源碼

class RefImpl<T> {
? private _value: T
? private _rawValue: T
? public dep?: Dep = undefined
? public readonly __v_isRef = true
? constructor(value: T, public readonly _shallow: boolean) {
? ? //原始數(shù)據(jù)
? ? this._rawValue = _shallow ? value : toRaw(value)
? ? //外部訪問到的數(shù)據(jù),轉(zhuǎn)換為響應式
? ? this._value = _shallow ? value : toReactive(value)
? }
? get value() {
? ? //跟蹤依賴
? ? trackRefValue(this)
? ? return this._value
? }
? set value(newVal) {
? ? newVal = this._shallow ? newVal : toRaw(newVal)
? ? if (hasChanged(newVal, this._rawValue)) {
? ? ? //如果原始數(shù)據(jù)之間的比較不一樣,則賦值
? ? ? this._rawValue = newVal
? ? ? //把新值轉(zhuǎn)換為響應式對象
? ? ? this._value = this._shallow ? newVal : toReactive(newVal)
? ? ? //觸發(fā)依賴
? ? ? triggerRefValue(this, newVal)
? ? }
? }
}
//轉(zhuǎn)換響應式對象方法
export const toReactive = <T extends unknown>(value: T): T =>
? isObject(value) ? reactive(value) : value
type CustomRefFactory<T> = (
? track: () => void,
? trigger: () => void
) => {
? get: () => T
? set: (value: T) => void
}
//收集依賴
export function trackRefValue(ref: RefBase<any>) {
? //是否可以收集
? if (isTracking()) {
? ? //獲取原始數(shù)據(jù)
? ? ref = toRaw(ref)
? ? if (!ref.dep) {
? ? ? //如果不存在依賴,就創(chuàng)建一個依賴對象
? ? ? ref.dep = createDep()
? ? }
? ? //收集依賴
? ? if (__DEV__) {
? ? ? trackEffects(ref.dep, {
? ? ? ? target: ref,
? ? ? ? type: TrackOpTypes.GET,
? ? ? ? key: 'value'
? ? ? })
? ? } else {
? ? ? trackEffects(ref.dep)
? ? }
? }
}
//觸發(fā)依賴更新
export function triggerRefValue(ref: RefBase<any>, newVal?: any) {
? ref = toRaw(ref)
? if (ref.dep) {
? ? if (__DEV__) {
? ? ? triggerEffects(ref.dep, {
? ? ? ? target: ref,
? ? ? ? type: TriggerOpTypes.SET,
? ? ? ? key: 'value',
? ? ? ? newValue: newVal
? ? ? })
? ? } else {
? ? ? triggerEffects(ref.dep)
? ? }
? }
}
/**
?* 自定義響應式對象
?*/
class CustomRefImpl<T> {
? public dep?: Dep = undefined
? private readonly _get: ReturnType<CustomRefFactory<T>>['get']
? private readonly _set: ReturnType<CustomRefFactory<T>>['set']
? public readonly __v_isRef = true
? constructor(factory: CustomRefFactory<T>) {
? ? const { get, set } = factory(
? ? ? () => trackRefValue(this),
? ? ? () => triggerRefValue(this)
? ? )
? ? this._get = get
? ? this._set = set
? }
? get value() {
? ? return this._get()
? }
? set value(newVal) {
? ? this._set(newVal)
? }
}

四. 源碼解析

Ref對象實際是代理的簡化版,針對value設置了一個getter,setter讀取器。

這個讀取器可以對讀寫操作進行攔截,因此可以進行依賴的收集和更新。

同時又巧妙了對reactive做了一層封裝,假如傳入的是一個多層嵌套的復雜對象,最終是類似ref.value.a其實操作的已經(jīng)是reactive代理對象上的屬性,已經(jīng)和ref無關了。對于CustomRefImpl類型,其實核心和RefImpl是一樣的,更加精簡,只不過將Get方法和Set方法交給程序員自己去實現(xiàn)了。

只需要在這個Get方法里面調(diào)用track方法進行依賴收集和在Set方法里面調(diào)用依賴更新即可。

示例代碼如下:

? ? let value = 1
? ? const custom = customRef((track, trigger) => ({
? ? ? get() {
? ? ? ? track()
? ? ? ? return value
? ? ? },
? ? ? set(newValue: number) {
? ? ? ? value = newValue
? ? ? ? trigger()
? ? ? }
? ? }))
? ? let dummy
? ? effect(() => {
? ? ? dummy = custom.value?
? ? })
? ? custom.value = 2
? ? //此時dummy = 2;

五、總結

1. 收集依賴和觸發(fā)依賴的本質(zhì)

export const enum TrackOpTypes {
? GET = 'get',
? HAS = 'has',
? ITERATE = 'iterate'
}
export const enum TriggerOpTypes {
? SET = 'set',
? ADD = 'add',
? DELETE = 'delete',
? CLEAR = 'clear'
}

以上時源碼中定義的收集依賴的和觸發(fā)依賴的類型。其實也就是當涉及讀操作時收集依賴,當設計寫操作時觸發(fā)依賴更新。

2. 響應式對象本質(zhì)是對數(shù)據(jù)進行了包裝,攔截了讀寫操作。

3. 上述篇幅并未講到集合類型代理的處理,原理其實一樣,有興趣的可以自行翻閱源碼。

4. 本篇幅只講到收集依賴和觸發(fā)依賴的時機,并未講到如何收集和如何觸發(fā)。

這些僅為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關文章

  • vue?跳轉(zhuǎn)頁面$router.resolve和$router.push案例詳解

    vue?跳轉(zhuǎn)頁面$router.resolve和$router.push案例詳解

    這篇文章主要介紹了vue?跳轉(zhuǎn)頁面$router.resolve和$router.push案例詳解,這樣實現(xiàn)了既跳轉(zhuǎn)了新頁面,又不會讓后端檢測到頁面鏈接不安全之類的,需要的朋友可以參考下
    2023-10-10
  • vue.js實現(xiàn)的經(jīng)典計算器/科學計算器功能示例

    vue.js實現(xiàn)的經(jīng)典計算器/科學計算器功能示例

    這篇文章主要介紹了vue.js實現(xiàn)的經(jīng)典計算器/科學計算器功能,具有基本四則運算計算器以及科學計算器的功能,可實現(xiàn)開方、乘方、三角函數(shù)以及公式運算等功能,需要的朋友可以參考下
    2018-07-07
  • 用Cordova打包Vue項目的方法步驟

    用Cordova打包Vue項目的方法步驟

    這篇文章主要介紹了用Cordova打包Vue項目的方法步驟,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2019-02-02
  • Vue移動端實現(xiàn)pdf/excel/圖片在線預覽

    Vue移動端實現(xiàn)pdf/excel/圖片在線預覽

    這篇文章主要為大家詳細介紹了Vue移動端實現(xiàn)pdf/excel/圖片在線預覽功能的相關方法,文中的示例代碼講解詳細,有需要的小伙伴可以參考下
    2024-04-04
  • VUE使用axios調(diào)用后臺API接口的方法

    VUE使用axios調(diào)用后臺API接口的方法

    這篇文章主要介紹了VUE使用axios調(diào)用后臺API接口的方法,文中講解非常細致,代碼幫助大家更好的理解和學習,感興趣的朋友可以了解下
    2020-08-08
  • 如何修改Vue打包后文件的接口地址配置的方法

    如何修改Vue打包后文件的接口地址配置的方法

    這篇文章主要介紹了如何修改Vue打包后文件的接口地址配置的方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-04-04
  • vue devserver及其配置方法

    vue devserver及其配置方法

    這篇文章主要介紹了vue devserver及其配置方法,本文結合示例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-12-12
  • vue2.X組件學習心得(新手必看篇)

    vue2.X組件學習心得(新手必看篇)

    下面小編就為大家?guī)硪黄獀ue2.X組件學習心得(新手必看篇)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-07-07
  • vant時間控件使用方法詳解

    vant時間控件使用方法詳解

    這篇文章主要為大家詳細介紹了vant時間控件的使用方法,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-12-12
  • webpack中的optimization配置示例詳解

    webpack中的optimization配置示例詳解

    這篇文章主要介紹了webpack中的optimization配置詳解,主要就是根據(jù)不同的策略來分割打包出來的bundle,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-02-02

最新評論