Interviewer: Können Sie Vue3 responsive schreiben (Implementierung eines responsiven Systems zur Vue3-Prinzipanalyse)

Dieser Artikel hat an der Veranstaltung „Newcomer Creation Ceremony“ teilgenommen, um gemeinsam den Weg der Goldschöpfung zu beginnen.

vorne schreiben

Mit der allmählichen Reife von Vue3 wurden die Vorteile von Vue3 gegenüber Vue2 immer offensichtlicher.Obwohl es in der Community immer noch viele Streitigkeiten darüber gibt, ob Vue3 besser als Vue2 ist und ob ein Upgrade erforderlich ist, ist es unbestreitbar, dass Vue3 hat sich allmählich positiv entwickelt. , fing an, die offizielle Standard-Vue-Version zu werden , dann im Front-End-Kreis des Stehens, Sitzens und Liegens ist das Erlernen von Vue3 bereits eine unausweichliche Hürde, und wir alle wissen mehr oder weniger, dass Vue3 der Grund ist Warum es geboren wurde, ist eigentlich, "die von Vue hinterlassenen technischen Schulden zu begleichen" , also hat Vue3 viele Anpassungen in Bezug auf das architektonische Design und die Optimierung des Schreibens vorgenommen , also auch wenn wir bereits mit den Prinzipien von Vue2 vertraut sind, aber Es ist immer noch notwendig, ein gutes Verständnis der Designprinzipien und Designideen von Vue3 zu haben. Ein Vergleich von Vue2 und Vue3 von der Prinzipebene aus kann uns ein klareres Verständnis vermitteln. Jeder Schritt der Entwicklung von Vue2 zu Vue3 besteht darin, das Problem und warum zu lösen Es ist geschafft, diese Optimierung. Beim Lernen dieser Designideen und Optimierungsmethoden geht es nicht nur darum, Vue3 zu verstehen oder zu lernen. Der größere Gewinn des Lernens kann es sein, unser Denk- und Designniveau zu verbessern. Im anschließenden Codierungsprozess kann es uns einige Basisübungen liefern , lassen Sie uns entwerfen eine vernünftigere und effizientere Architektur

Implementierung des Reaktionssystems

Primitive API-Proxy

Als MVVM-Framework sollte das Wichtigste in Vue die Implementierung des responsiven Systems sein. Die native API, die in der Vue2-Implementierung verwendet wird, ist Object.defineProperty. Ich habe in früheren Artikeln eine detaillierte Prinzipanalyse durchgeführt. Interessiert können Sie den Interviewer : Können Sie Vue responsive von Hand schreiben? (Vue Responsiveness Principle [Vollversion]) und in Vue3 hat Youda Object.defineProperty aufgegeben, nachdem er das Leistungsproblem gemessen hatte , und stattdessen Proxy als zugrunde liegende API verwendet, um die Funktion zum Überwachen von Datenänderungen im Antwortsystem zu implementieren das Antwortsystem, lassen Sie uns zuerst verstehen, was Proxy ist? Wie verwendet man Proxy zur Implementierung der Datenüberwachung?

Schauen Sie sich zuerst die API-bezogenen Dokumente MDN-Proxy an.Durch die Einführung können wir grob verstehen, dass Proxy und Object.defineProperty eigentlich ähnlich sind , beide empfangen ein Objekt und überwachen dann das Objekt, aber es gibt zwei Unterschiede:

Vollständige Überwachung des Objektschlüsselwerts

Object.defineProperty kann nur die Änderung des Werts überwachen, der dem angegebenen Schlüssel entspricht, während Proxy alle Schlüssel des Objekts direkt überwachen kann.Mit anderen Worten, Proxy muss die zu überwachenden Objekte nicht manuell durchlaufen, jeden Schlüssel überwachen und Dann schauen Sie sich ein einfaches Beispiel an (Object.defineProperty-bezogene Beispiele lesen Sie direkt frühere Artikel Interviewer: Können Sie Vue-Reaktionsfähigkeit von Hand schreiben? (Vue-Reaktionsfähigkeitsprinzip [Vollversion]) )

// 模拟数据更新
function update(){
  console.log('update view')
}

// proxy 
function observe_proxy(data){
  // 代理 data,并将生成的代理对象作为返回值返回 
  const res = new Proxy(data, {
    // 代理对象的getter
    get(target, key, receiver){
      // Reflect.get/set 为官方标准写法,直接将 get、set 的参数赋值即可
      // 不需要手动写 return target[key]
      return Reflect.get(target, key, receiver)
    },
    // 代理对象的setter
    set(target, key, value, receiver){
      // 模拟触发更新 
      update()
      return Reflect.set(target, key, value, receiver)
    }
  })
  return res
}

// 测试数据
const data = {
  name: 'yimwu',
  tel: '134xxx54567'
}
// 代理返回的对象
const dataProxy = observe_proxy(data)
console.log(dataProxy.name) // yimwu
dataProxy.name = 'YI' // 更新成功 --> 输出 update view
复制代码

可以看到当 observe 函数的实现方式很简单,直接通过 new Proxy(), 将对象传入即可代理对象的所有 key,但是细心的朋友可能注意到了,示例代码触发更新的方式和Object.defineProperty 的并不一样,这也是接下来要说的第二个不同点

触发更新

Object.defineProperty 触发更新的方式很简单,直接修改原对象,因为 Object.defineProperty 直接对原对象进行监听,而 Proxy 的实现思路不同,它通过代理的方式,将自己作为中间人的角色,所以对原对象所有的操作需要通过 Proxy 进行,才能够得到响应式的效果,例子如下,我们 通过修改 observe 返回的代理对象 dataProxy 触发更新,而不是修改原对象 data

const dataProxy = observe_proxy(data)
console.log(dataProxy.name) // yimwu
dataProxy.name = 'YI' // 更新成功 --> 输出 update view
复制代码

嵌套代理

使用 Object.defineProperty 实现嵌套监听的方式其实很简单粗暴,直接递归调用,通过给所有层级的所有key建立对于的 getter、setter,完成所有嵌套的监听,而使用 Proxy 略微有些差别,Proxy 对于嵌套赋值,会触发 get,例如 obj.a.b = 2, 会触发 obj.a 的 get,利用这个特性,可以在 get 中做判断,若获取的是 object 则返回 Proxy,而不是返回该值,通过这种方式能够对对象,以及对象内嵌套的子对象进行代理,例子如下

// 模拟数据更新
function update(){
  console.log('update view')
}
// proxy
function observe_proxy(data){
  const res = new Proxy(data, {
    get(target, key, receiver){
      // 若修改的为嵌套对象,如 obj.a.b = 2,则 obj.a 触发 get,
      // 返回嵌套代理对象,完成嵌套对象代理
      if(typeof target[key] === 'object'){
        return observe_proxy(target[key])
      }
      return Reflect.get(target, key, receiver)
    },
    set(target, key, value, receiver){
      update()
      return Reflect.set(target, key, value, receiver)
    }
  })
  return res
}
// 测试数据
const data = {
  name: 'yimwu',
  tel: '134xxx54567',
  address: {
    firstAdd: 'guangzhou',
    second: 'shenzhen'
  },
}
// 
const dataProxy = observe_proxy(data)
dataProxy.address.firstAdd = 'jieyang' // 更新成功 --> 输出 update view
复制代码

可以看到,在开始监听对象的时候,并没有和采用和 Object.defineProperty 的实现方式一样,在监听开始时就递归完成所有 getter、setter 绑定,而是等到触发了某个嵌套对象,才进行监听,所以,从这一步上来说也是采用 Proxy 这种实现方式的一个性能优化的点。

依赖收集

前面介绍了 Vue3 中采用的 Proxy API,通过该 API,就能够实现对数据的变化进行特定的响应,但是,这在 Vue 的响应系统中,只是最基础的一部分,更加复杂的应该是与数据监听相配合的依赖收集,举个例子,在 Vue 中我们经常使用的计算属性 computed ,能够在依赖更新时更新,所谓的依赖,就是该 computed 值由什么值决定,const myComputed = computed(() => { return a+b }),则myComputed依赖a、b,即当 a、b变化时,myComputed 将随着变化

理解了什么是依赖之后,副作用函数就很清晰了,顾名思义,就是有副作用的函数,指的是 执行了该函数,会影响某些外部的变量,而副作用函数的绑定,恰恰就是为了 在所依赖的值变化时,重新执行副作用函数,重新对该变量进行影响

0.0 版本(基础实现)

当对数据进行监听时,我们为 Proxy 提供了 getter和setter 函数,那么,副作用函数自然也能够融合到这两个函数里面来,形成副作用函数绑定,如下图,我们在 getter 中将 effectFn 函数绑定到对应的对象上,然后在 setter 中执行该函数,也就能够实现简单的副作用函数绑定了

Bild.png

// 数据监听函数
function observe_proxy(data){
  // 监听对象
  const res = new Proxy(data, {
    get(target, key, receiver){
      if(typeof target[key] === 'object' && target[key] !== null){
        return observe_proxy(target[key])
      }
      // 提前到 fnList.add 前执行,避免监听行为被 fnList.add 干扰
      const res = Reflect.get(target, key, receiver)
      // =============== 新增 ===============
      // 将 effectFn 添加到 fnList 中
      fnList.add(activeEffect)
      // =============== 新增 ===============
      return res
    },
    set(target, key, value, receiver){
      // 提前到 fnList.forEach 前执行,避免 set 未生效而执行 effectFn
      let res = Reflect.set(target, key, value, receiver) 
      // =============== 新增 ===============
      // 执行 fnList 中所有副作用函数
      fnList.forEach(fn => fn())
      // =============== 新增 ===============
      return res
    }
  })
  return res
}

// 存储所有副作用函数的数组
// 这里采用 Set 替换 Array 实现自动去重
// const fnList = new Array()
const fnList = new Set()

// 存放需要添加到 funList 中的函数
let activeEffect

// 触发副作用函数 effectFn 绑定行为
function effect(effectFn){
  activeEffect = effectFn
  // 执行effectFn,触发 get
  effectFn()
}

// 测试数据
const data = {
  name: 'yimwu',
  tel: '134xxx54567',
  address: {
    firstAdd: 'guangzhou',
    second: 'shenzhen'
  },
}

// 第一步:先启动数据监听
const dataProxy = observe_proxy(data)
// 第二步:定义副作用函数
const effectFn = () => {
  console.log('My tel-num is: ', dataProxy.tel)
}
// 第三步:触发副作用函数绑定
effect(effectFn) // My tel-num is: 134xxx54567 ==> 触发 get, 副作用函数绑定成功

// 测试效果
setTimeout(() => {
  dataProxy.tel = '159000xxx54'  // My tel-num is: 159000xxx54 ==> 副作用函数成功执行
}, 2000);
复制代码

1.0版本(key 单独绑定副作用函数)

在基础版中,只是让我们能够初步体会到依赖绑定的行为,但是还存在很多问题,比如依赖函数并没有针对 data 的每个 key 单独绑定,下面引入一个数据结构解决这个问题,如图

Bild.png fnList 为一个 WeakMap,key 值为一个个的数据对象 data ,value 则是一个 Map,该 Map 中用 data 的 key值 作为 key,存储副作用函数的 Set 作为 value,这样就形成了针对每个数据对象的每个key的单独的副作用函数的 Set ,接下来对代码进行改造,支持新的数据结构

// 存储所有副作用函数的对象
const fnList = new WeakMap()
// 收集副作用函数
function track(target, key){
  if(!activeEffect) return
  // 依次获取data对应的Map以及Map中key所对应的Set,
  // 有则将 activeEffect 加入,没有则新建后加入
  let dataMap = fnList.get(target)
  if(!dataMap){
    dataMap = new Map()
    fnList.set(target, dataMap)
  }
  let fnSet = dataMap.get(key)
  if(!fnSet){
    fnSet = new Set()
    dataMap.set(key,fnSet)
  }
  fnSet.add(activeEffect)
}
// 执行副作用函数
function trigger(target, key){
  // 获取副作用函数集并执行
  const dataMap = fnList.get(target)
  if(!dataMap) return
  const effectFns = dataMap.get(key)
  effectFns && effectFns.forEach(effectFn=>effectFn())
}
function observe_proxy(data){
  const res = new Proxy(data, {
    get(target, key, receiver){
      if(typeof target[key] === 'object' && target[key] !== null){
        return observe_proxy(target[key])
      }
      // 提前到 fnList.add 前执行,避免监听行为被 fnList.add 干扰
      const res = Reflect.get(target, key, receiver)
      // 将 effectFn 添加到 fnList 中
      // fnList.add(activeEffect)
      track(data, key)  // 更新写法
      return res
    },
    set(target, key, value, receiver){
      // 提前到 fnList.forEach 前执行,避免 set 未生效而执行 effectFn
      let res = Reflect.set(target, key, value, receiver) 
      // 执行 fnList 中所有副作用函数
      // fnList.forEach(fn => fn())
      trigger(target,key)  // 更新写法
      return res
    }
  })
  return res
}

// 存放需要添加到 funList 中的函数
let activeEffect = null

// 触发 effectFn添加行为
function effect(effectFn){
  activeEffect = effectFn
  // 执行effectFn,触发 get
  effectFn()
  // 由于通过 set 触发副作用函数时也会导致 get 被触发,
  // 将 activeEffect 设为 null 避免重复添加副作用函数
  activeEffect = null
}

// 测试数据
const data = {
  name: 'yimwu',
  tel: '134xxx54567',
  address: {
    firstAdd: 'guangzhou',
    second: 'shenzhen'
  },
}

// 第一步:先启动数据监听
const dataProxy = observe_proxy(data)
// 第二步:定义副作用函数
const effectFn = () => {
  console.log('My tel-num is: ', dataProxy.tel)
}
// 第三步:触发副作用函数绑定
effect(effectFn)  // My tel-num is: 134xxx54567 ==> 首次触发,监听成功

// 测试效果
setTimeout(() => {
  dataProxy.tel = '159000xxx54' // My tel-num is: 159000xxx54 ==> 监听成功触发
}, 2000);

setTimeout(() => {
  dataProxy.name = 'YI' // 没监听,不输出
}, 4000);
复制代码

2.0 版本(副作用函数重置)

通过 1.0 版本的改造,加入了新的数据结构之后,实现了副作用函数针对key的单独绑定,而 1.0 版本依然存在问题。在许多使用场景中,一个副作用函数往往关联的 key 不止一个,也就是说首次触发副作用函数后,多个 key 会同时绑定一个副作用函数,当任何一个 key 被修改时,都将触发该副作用函数重新执行,例如以下场景,通过两个变量决定电灯是否打开,一个是电源开关 powerOn,一个是电灯开关 turnOn

// 测试数据
const data = {
  powerOn: true,
  turnOn : true
}

// 第一步:先启动数据监听
const dataProxy = observe_proxy(data)
// 第二步:定义副作用函数
const effectFn = () => {
  console.log('是否开启电源',  dataProxy.powerOn)
  console.log('是否开启灯光',  dataProxy.turnOn)
}
// 第三步:触发副作用函数绑定
effect(effectFn)
复制代码

执行后,powerOn、turnOn 同时绑定了 effectFn ,当任何一个状态发生变化时,effectFn 都将被触发,看似符合规则,没有任何问题,但是这并不符合场景设定,场景需求是两个变量共同决定电灯状态,并且如果电源关闭,无论电灯开关是否开启,电灯都为关闭状态,对上面的副作用进行一下修改,如下

// 测试数据
const data = {
  powerOn: true,
  turnOn : true
}
// 第一步:先启动数据监听
const dataProxy = observe_proxy(data)
// 第二步:定义副作用函数
// 表示若电源(powerOn)开启,则灯光状态由(turnOn)决定,
// 若电源(powerOn)关闭,无论灯光(turnOn)是否开起,状态都为关闭(false)
const effectFn = () => {
  console.log('是否开启灯光',  dataProxy.powerOn ? dataProxy.turnOn : false)
}
// 第三步:触发副作用函数绑定
effect(effectFn)

// 测试效果
setTimeout(() => {
  dataProxy.turnOn = false
}, 2000);

setTimeout(() => {
  dataProxy.turnOn = true
}, 3000);

setTimeout(() => {
  dataProxy.powerOn = false 
}, 4000);

// 将powerOn 置为 false 后,turnOn 的变化依然触发 副作用函数
setTimeout(() => {
  dataProxy.turnOn = false
}, 5000);

setTimeout(() => {
  dataProxy.turnOn = true
}, 6000);
复制代码

初始化时,powerOn 为 true,执行表达式时同时触发 powerOn、turnOn 的 get 并绑定副作用函数,而当我们将 powerOn 置为 false 后,无论 turnOn 的值如何变化,表达式恒等于 false,也就是说当我们将 powerOn 置为 false 后,再修改 turnOn 就不需要再触发副作用函数 effectFn了,这也是一个性能优化的手段,那该如何实现呢

最直接的方法是 在每次触发副作用函数时将该副作用函数从 所有 key 的依赖集合中清除掉,通过副作用函数触发的 get 重新形成绑定,这样一来,当 powerOn 为 false 时,将不再触发 turnOn 的 get ,因此 turnOn 的变化将不再触发 副作用函数。具体改动如下,在副作用函数上定义一个数组,用于触发绑定了该副作用函数的依赖集(Set),当触发副作用函数时调用 reset 函数,从所有依赖集中将副作用函数清除掉

Bild.png

// 触发 effectFn 添加行为
function effect(effectFn){
  const innerFn = () => {
    // 清除依赖
    reset(innerFn)
    activeEffect = innerFn
    effectFn()
  }
  // 存放依赖集的数组
  innerFn.deps = []
  // 间接执行effectFn,触发 get
  innerFn()
}
// 用于清除依赖的函数
function reset(effectFn){
  effectFn.deps && effectFn.deps.forEach(dep => {
    dep.delete(effectFn)
  })
  effectFn.deps = []
}

// 修改 track 函数
function track(target, key){
  if(!activeEffect) return
  // 依次获取data对应的Map以及Map中key所对应的Set,有则将 activeEffect 加入,没有则新建后加入
  let dataMap = fnList.get(target)
  if(!dataMap){
    dataMap = new Map()
    fnList.set(target, dataMap)
  }
  let fnSet = dataMap.get(key)
  if(!fnSet){
    fnSet = new Set()
    dataMap.set(key,fnSet)
  }
  fnSet.add(activeEffect)
  // ================ 新增 ================
  activeEffect.deps.push(fnSet)
  // ================ 新增 ================
}

// 测试数据
const data = {
  powerOn: true,
  turnOn : true
}
// 第一步:先启动数据监听
const dataProxy = observe_proxy(data)
// 第二步:定义副作用函数
// 表示若电源(powerOn)开启,则灯光状态由(turnOn)决定,
// 若电源(powerOn)关闭,无论灯光(turnOn)是否开起,状态都为关闭(false)
const effectFn = () => {
  console.log('是否开启灯光',  dataProxy.powerOn ? dataProxy.turnOn : false)
}
// 第三步:触发副作用函数绑定
effect(effectFn)

setTimeout(() => {
  dataProxy.turnOn = false
}, 2000);

setTimeout(() => {
  dataProxy.turnOn = true
}, 3000);

setTimeout(() => {
  dataProxy.powerOn = false
}, 4000);

// powerOn = false turnOn 变化不再触发副作用函数
setTimeout(() => {
  dataProxy.turnOn = false
}, 5000);

setTimeout(() => {
  dataProxy.turnOn = true
}, 6000);
复制代码

当运行上面版本的代码的时候会发现陷入无限循环,其实问题主要出现在 trigger 函数,原因如图所示,reset 函数将 Set 中的 effectFn 删除后,执行track时,track 又重新将 effectFn 加入到 Set 中,导致 ForEach 无限循环,所以最直接的办法是 forEach 一个新的 Set,这样执行过程中对原来的 Set 进行修改就不会影响循环的执行,修改如下

Bild.png

// 修改
function trigger(target, key){
  // 获取副作用函数集并执行
  const dataMap = fnList.get(target)
  if(!dataMap) return
  const effectFns = dataMap.get(key)
  const effectFnsTmp = new Set(effectFns)
  // =============== 修改 ===============
  effectFnsTmp.forEach(fn => fn())
  // effectFns && effectFns.forEach(effectFn=>effectFn())
  // =============== 修改 ===============
}
复制代码

3.0 版本(栈消除嵌套副作用函数bug)

2.0 版本已经基本上修复了大部分的坑了,而还有一个平时写程序经常会遇到的一个,那就是嵌套,我们先拿 2.0 版本的代码测试一下是否支持嵌套

// 测试数据
const data = {
  name: 'Yimwu',
  age: 18
}
// 第一步:先启动数据监听
const dataProxy = observe_proxy(data)
// 第二步:嵌套监听
effect(function effectFn1(){
  console.log('第一层开始')
  effect(function effectFn2(){
    // 触发副作用函数绑定
    console.log('第二层', dataProxy.name)
  })
  // 在第二层执行完成后,触发副作用函数绑定,验证是否能够正常绑定
  console.log('第一层结束', dataProxy.age)
})

// 测试效果
setTimeout(() => {
      dataProxy.age = 30 
      // 第二层 Yimwu ==> 输出错误,age 绑定了第二层的函数,
      // 而不是预想的第一层函数
}, 2000);
复制代码

很显然,2.0 版本并不支持嵌套,一旦发生嵌套,就会出现 activeEffect不匹配的问题,那么这个问题该如何解决呢

这里顺便说一下日常编程的一个小 Tip ,在平时我们遇到的问题中,有两个法宝可以使用,一个是队列,一个是栈,而顾名思义,队列,就是针对顺序进行的事情所定义的数据结构,它能够保证我们的程序按照我们所设想的顺序执行,而栈我们相对陌生,由于栈的特殊读取、输入规则(先进后出),栈最常被用于处理嵌套逻辑

从嵌套执行的 effect 可以看得出副作用函数错误绑定的原因主要是 全局 activeEffect 在进入第二层后,被第二层覆盖,当第二层结束后,activeEffect 没有恢复到第一层对应的 activeEffect,因此,我们使用栈结构,对每一层的 activeEffect 进行保存,通过出栈的方式保证 activeEffect 的对应关系是正确的,修改如下

Bild.png

// 修改 effect 函数
function effect(effectFn){
  const innerFn = () => {
    // 清除依赖
    reset(innerFn)
    activeEffect = innerFn
    // ==================== 新增 ====================
    // 入栈
    activeStack.push(innerFn)
    // ==================== 新增 ====================
    effectFn()

    // ==================== 新增 ====================
    // 出栈
    activeStack.pop()
    // 执行外层 activeEffect
    activeEffect = activeStack[activeStack.length-1]
    // ==================== 新增 ====================
  }
  // 存放依赖集的数组
  innerFn.deps = []
  // 间接执行effectFn,触发 get
  innerFn()
}
// 测试数据
const data = {
  name: 'Yimwu',
  age: 18
}
// 第一步:先启动数据监听
const dataProxy = observe_proxy(data)
// 第二步:嵌套监听
effect(function effectFn1(){
  console.log('第一层开始')
  effect(function effectFn2(){
    // 触发副作用函数绑定
    console.log('第二层', dataProxy.name)
  })
  // 在第二层执行完成后,触发副作用函数绑定,验证是否能够正常绑定
  console.log('第一层结束', dataProxy.age)
})

// 测试效果
setTimeout(() => {
  dataProxy.age = 30 
}, 2000);
// 输出:
// 第一层开始
// 第二层 Yimwu
// 第一层结束 30
复制代码

最后解决一个特殊情况下会出现的 Bug,当副作用函数中出现 自增、自减 操作时,副作用函数会重复调用,导致栈溢出,例子如下

// 测试数据
const data = {
  name: 'Yimwu',
  age: 18
}
// 定义副作用函数
const effectFn = () => {
  dataProxy.age++
}
// 触发副作用函数绑定
effect(effectFn)
复制代码

Bild.png

分析如下:

Bild.png

解决这个问题很简单,我们只需要在 trigger 中判断从 Set 中取出的 effectFn 与目前正在执行的 activeEffect 是否相同,相同则不执行,这样就能够避免无线循环,修改如下

// 修改副作用函数
function trigger(target, key){
  // 获取副作用函数集并执行
  const dataMap = fnList.get(target)
  if(!dataMap) return
  const effectFns = dataMap.get(key)

  // =================== 新增 ===================
  // 创建空 Set,筛选不等于 activeEffect 的副作用函数
  const effectFnsTmp = new Set()
  effectFns.forEach(fn => {
    if(fn !== activeEffect) effectFnsTmp.add(fn)
  })
  effectFnsTmp.forEach(fn => fn())
  // =================== 新增 ===================
  
  // const effectFnsTmp = new Set(effectFns)
  // effectFnsTmp.forEach(fn => fn())
  // effectFns && effectFns.forEach(effectFn=>effectFn())
}
复制代码

总结

Nach vielen "Versionsiterationen" wurde das Implementierungsprinzip des Vue-Antwortsystems umfassend analysiert. Durch diese Analyse und Recherche des Antwortsystems ist es nicht nur möglich zu klären, wie das Vue-Antwortsystem implementiert ist, sondern auch wichtiger Der Zweck ist es, uns mit dem Vue-Framework vertraut zu machen und den Grund für seine Verwendung zu kennen , anstatt blind die API der api anzupassen Gleichzeitig die im Implementierungsprozess verwendete architektonische Entwurfsmethode und die eingeführte Datenstruktur, B. das Lösen des Stapels von Verschachtelungsproblemen und der Speicherdatenstruktur WeakMap => Map => Set => Funktion zum Lösen des Speicherproblems, stellen ebenfalls sehr große Gewinne beim Erlernen des Vue-Antwortsystems dar. Durch das Erlernen der zugrunde liegenden Prinzipien und das Kontaktieren komplexerer Designs Bei Szenarios geht es nicht nur um das Verständnis. Framework ist für uns der beste Weg, um Programmieren zu lernen .

am Ende schreiben

Der Blogger wird auch in Zukunft gute Artikel aktualisieren, bitte achten Sie auf den Blogger! !
Wenn der Artikel hilfreich für dich ist, like , sammle + folge und wachse mit dem Blogger! ! ❤❤❤

Referenzen (Danksagungen)

„Vue-Design und -Implementierung“ – Huo Chunyang (HcySunYang)

Supongo que te gusta

Origin juejin.im/post/7084915514434306078
Recomendado
Clasificación