亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

詳解如何編寫一個(gè)Vue3響應(yīng)式系統(tǒng)

 更新時(shí)間:2023年07月25日 15:24:51   作者:前端胖頭魚(yú)  
這篇文章主要為大家學(xué)習(xí)介紹了如何編寫一個(gè)Vue3響應(yīng)式系統(tǒng),文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,感興趣的小伙伴可以了解一下

前言

都說(shuō)今年是最慘工作年,大廠裁員,小廠跟風(fēng),簡(jiǎn)歷投了幾百封回信的寥寥無(wú)幾,金三銀四怕是成了銅三鐵四,冷冷清清,凄凄慘慘。

但是今天的主角,小帥同學(xué)卻在逆風(fēng)環(huán)境中給了面試官當(dāng)頭一喝,秀了他一身,優(yōu)秀如他,到底經(jīng)歷了一場(chǎng)怎樣的面試?

文中的例子和代碼都可以點(diǎn)擊這里查看

1.題目亮相

面試官: 我看你簡(jiǎn)歷寫的精通Vue3,并研究過(guò)其源碼? 小伙子很狂?。∧窃劬同F(xiàn)場(chǎng)秀一段如何?

說(shuō)罷,面試官現(xiàn)場(chǎng)給了一道題...

<div id="app"></div>
<script>
  const $app = document.querySelector('#app')
  let state = {
    text: 'hello fatfish'
  }
  function effect() {
    $app.innerText = state.text
  }
  effect()
  setTimeout(() => {
    // 1秒后希望app的內(nèi)容變成hello Vue3
    state.text = 'hello Vue3'
  }, 1000)
</script>

小帥竊喜: 這個(gè)簡(jiǎn)單,只要攔截state對(duì)象,在對(duì)text進(jìn)行取值時(shí),收集effect函數(shù)依賴,然后text設(shè)置值時(shí),把收集的effect函數(shù)執(zhí)行一波就可以。

面試官: 口嗨我也會(huì),別逼逼了,趕緊寫起來(lái)...

2 版本1:跑起來(lái)了,卻不通用,卒

2.1 源碼實(shí)現(xiàn)

小帥很快就寫出了第一版,核心只有兩步:

  • 第一步:收集依賴(effect函數(shù)),在讀取key時(shí),將effect函數(shù)存儲(chǔ)起來(lái)
  • 第二步:設(shè)置值時(shí),將依賴(effect函數(shù))執(zhí)行
const $app = document.querySelector('#app')
const bucket = new Set()
const state = new Proxy({ text: 'hello fatfish' }, {
  get (target, key) {
    const value = target[ key ]
    // 第一步:收集依賴,在讀取key時(shí),將effect函數(shù)存儲(chǔ)起來(lái)
    bucket.add(effect)
    console.log(`get ${key}: ${value}`)
    return value
  },
  set (target, key, newValue) {
    console.log(`set ${key}: ${newValue}`)
    target[ key ] = newValue
    // 第二步:設(shè)置值時(shí),將依賴執(zhí)行
    bucket.forEach((fn) => fn())
  }
})
function effect() {
  console.log('執(zhí)行了effect')
  $app.innerText = state.text
}
effect()
setTimeout(() => {
  state.text = 'hello Vue3'
}, 1000)

效果預(yù)覽

點(diǎn)擊預(yù)覽,噠噠噠,看起來(lái)很簡(jiǎn)單哦,瞬間就完成啦!

2.2 面試官點(diǎn)評(píng)

面試官: 功能是實(shí)現(xiàn)了,但是我不太滿意,你這里收集依賴是寫死的函數(shù)名字effect,只要稍微變化一下題目,就不行了。

<div id="container">
  <div id="app1"></div>
  <div id="app2"></div>
</div>
const $app1 = document.querySelector('#app1')
const $app2 = document.querySelector('#app2')
const state = { 
  text: 'hello fatfish', 
  text2: 'hello fatfish2' 
}
// 改變app1的值
function effect1() {
  console.log('執(zhí)行了effect')
  $app1.innerText = state.text
}
// 改變app2的值
function effect2() {
  console.log('執(zhí)行了effect2')
  $app2.innerText = state.text2
}
// 1秒鐘之后兩個(gè)div的值要分別改變
setTimeout(() => {
  state.text = 'hello Vue3'
  state.text2 = 'hello Vue3-2'
}, 1000)

3 版本2: 支持多屬性響應(yīng)式修改和主動(dòng)注冊(cè)

3.1 源碼實(shí)現(xiàn)

小帥心想: "大意了,我應(yīng)該把effect依賴函數(shù)通過(guò)某種機(jī)制,主動(dòng)注冊(cè)到桶中,這樣無(wú)論你是匿名函數(shù)亦或者是具名函數(shù)都一視同仁"

機(jī)靈的他馬上就想到了答案。

const bucket = new Set()
let activeEffect
// 變化點(diǎn):
// 通過(guò)effect函數(shù)來(lái)主動(dòng)收集依賴
const effect = function (fn) {
  // 每執(zhí)行一次,將當(dāng)前fn賦值給activeEffect,這樣在fn中觸發(fā)讀取操作時(shí),就可以被收集進(jìn)bucket中了
  activeEffect = fn
  // 主動(dòng)執(zhí)行一次很重要,必不可少
  fn()
}
const state = new Proxy({ text: 'hello fatfish', text2: 'hello fatfish2' }, {
  get (target, key) {
    const value = target[ key ]
    // 變化點(diǎn):由版本1的effect變成了activeEffect,從而不再依賴具體的函數(shù)名字
    bucket.add(activeEffect)
    console.log(`get ${key}: ${value}`)
    return value
  },
  set (target, key, newValue) {
    console.log(`set ${key}: ${newValue}`)
    target[ key ] = newValue
    bucket.forEach((fn) => fn())
  }
})
effect(function effect1 () {
  console.log('執(zhí)行了effect1')
  $app1.innerText = state.text
})
effect(function effect2() {
  console.log('執(zhí)行了effect2')
  $app2.innerText = state.text2
})
setTimeout(() => {
  state.text = 'hello Vue3'
  state.text2 = 'hello Vue3-2'
}, 1000)

效果預(yù)覽 可以看到,此時(shí)app1和app2在1秒后都變成了對(duì)應(yīng)值,目標(biāo)達(dá)成。

3.2 面試官點(diǎn)評(píng)

面試官:小伙子非常不錯(cuò),思路靈活,變通很快嘛!不過(guò)你有沒(méi)有想過(guò)一個(gè)問(wèn)題?

state上增加一個(gè)之前不存在的屬性,你的bucket卻會(huì)把收集的依賴執(zhí)行一次,是不是有點(diǎn)浪費(fèi)?

能否做到effect中依賴了state的什么值,其值改變了回調(diào)才會(huì)被執(zhí)行?

4 版本3:推倒重來(lái),再次設(shè)計(jì)"桶"數(shù)據(jù)結(jié)構(gòu)

4.1 重新設(shè)計(jì)數(shù)據(jù)結(jié)構(gòu)

小帥: 心里有點(diǎn)沒(méi)底了,簡(jiǎn)歷上寫精通Vue,深入研究過(guò)Vue源碼真TM巨坑?。?/p>

面試還得繼續(xù),苦思冥想之后終于明白了第二個(gè)版本的問(wèn)題所在:

沒(méi)有在effect函數(shù)與被操作的目標(biāo)字段之間建立明確的聯(lián)系:

const state = new Proxy({ text: 'hello fatfish' }, {
  get (target, key) {
    const value = target[ key ]
    // 無(wú)論`state`上啥屬性被讀取了,都會(huì)執(zhí)行`get`然后被收集進(jìn)`bucket`
    bucket.add(effect)
    return value
  },
  set (target, key, newValue) {
    target[ key ] = newValue
    // 無(wú)論`state`上啥值被修改了,都會(huì)觸發(fā)`set`,進(jìn)而收集的依賴被執(zhí)行。
    bucket.forEach((fn) => fn())
  }
})

1. 新的映射關(guān)系

該如何設(shè)計(jì)bucket中存儲(chǔ)的值呢?咱們先來(lái)看看關(guān)鍵代碼

effect(function effectFn () {
  $app.innerText = state.text
})

這段代碼中有幾個(gè)角色:

  • 被操作(讀取)的代理對(duì)象state
  • 被操作的(讀?。┑淖侄蚊鹴ext
  • 使用effect函數(shù)注冊(cè)的effectFn函數(shù)

那么他們之間的關(guān)系可以用一顆樹(shù)來(lái)表述

state
    |__key
       |__effectFn

2. 場(chǎng)景1:有兩個(gè)effectFn讀取同一個(gè)對(duì)象的屬性值

effect(function effectFn1 () {
  // 讀取text
  state.text
})
effect(function effectFn2 () {
  // 讀取text
  state.text
})

那么按照上面樹(shù)形結(jié)構(gòu),現(xiàn)在表示如下: text屬性應(yīng)該要和effectFn1、effectFn2建立聯(lián)系

state
    |__text
       |__effectFn1
       |__effectFn2

3. 場(chǎng)景2:effectFn中讀取了同一個(gè)對(duì)象的多個(gè)不同屬性

effect(function effectFn1 () {
  // 讀取text1和text2
  state.text
  state.text2
})

texttext2屬性應(yīng)該要和effectFn1建立聯(lián)系

state
    |__text
       |__effectFn1
    |__text2
       |__effectFn1 

4. 場(chǎng)景3:不同的effectFn中讀取了不同對(duì)象的不同屬性

 effect(function effectFn1 () {
   // 讀取text1
   state1.text1
 })
 effect(function effectFn2 () {
   // 讀取text2
   state2.text2
 })

對(duì)應(yīng)的關(guān)系表示如下:

state1
     |__text1
        |__effectFn1

state2
     |__text2
        |__effectFn2

看到這里,相信聰明的你一定明白了,當(dāng)我們改變了state2.text2的值時(shí),只有effectFn2函數(shù)會(huì)被重新執(zhí)行,而effectFn1卻不會(huì)。當(dāng)然了新增一個(gè)以往不存在的屬性時(shí),effectFn1和effectFn2都不會(huì)被執(zhí)行。

5. 畫一個(gè)數(shù)據(jù)結(jié)構(gòu)圖來(lái)理解一下存儲(chǔ)關(guān)系:

4.2 源碼實(shí)現(xiàn)

6: 新版源碼實(shí)現(xiàn)

const $app = document.querySelector('#app')
// 重新定義bucket數(shù)據(jù)類型為WeakMap
const bucket = new WeakMap()
let activeEffect
const effect = function (fn) {
  activeEffect = fn
  fn()
}
const state = new Proxy({ name: 'fatfish', age: 100 }, {
  get (target, key) {
    const value = target[ key ]
    // activeEffect無(wú)值意味著沒(méi)有執(zhí)行effect函數(shù),無(wú)法收集依賴,直接return掉
    if (!activeEffect) {
      return
    }
    // 每個(gè)target在bucket中都是一個(gè)Map類型: key => effects
    let depsMap = bucket.get(target)
    // 第一次攔截,depsMap不存在,先創(chuàng)建聯(lián)系
    if (!depsMap) {
      bucket.set(target, (depsMap = new Map()))
    }
    // 根據(jù)當(dāng)前讀取的key,嘗試讀取key的effects函數(shù)
    let deps = depsMap.get(key)
    if (!deps) {
      // deps本質(zhì)是個(gè)Set結(jié)構(gòu),即一個(gè)key可以存在多個(gè)effect函數(shù),被多個(gè)effect所依賴
      depsMap.set(key, (deps = new Set()))
    }
    // 將激活的effectFn存進(jìn)桶中
    deps.add(activeEffect)
    console.log(`get ${key}: ${value}`)
    return value
  },
  set (target, key, newValue) {
    console.log(`set ${key}: ${newValue}`)
    // 設(shè)置屬性值
    target[ key ] = newValue
    // 讀取depsMap 其結(jié)構(gòu)是 key => effects
    const depsMap = bucket.get(target)
    if (!depsMap) {
      return
    }
    // 真正讀取依賴當(dāng)前屬性值key的effects
    const effects = depsMap.get(key)
    // 挨個(gè)執(zhí)行即可
    effects && effects.forEach((fn) => fn())
  }
})
effect(() => {
  console.log('執(zhí)行了effect')
  $app.innerText = `hello ${ state.name }, are you ${state.age} years old?`
})
setTimeout(() => {
  state.name = 'Vue3'
  state.age = 18
}, 1000)

效果預(yù)覽

點(diǎn)擊查看

可以看到我們給state新增了一個(gè)屬性text但是effect并不會(huì)被執(zhí)行,修改了name屬性為juejin之后才被執(zhí)行了,而視圖層也更新了。

4.3 面試官點(diǎn)評(píng)

牛,差點(diǎn)給我整懵逼,小弟佩服!

不過(guò)能不能再進(jìn)一步,你這只能對(duì)state一個(gè)對(duì)象進(jìn)行響應(yīng)式處理,能不能再封裝一下,像Vue3里面使用reactive一樣使用?

5 版本4:reactive抽象,有點(diǎn)Vue3的味道了

5.1 源碼實(shí)現(xiàn)

小帥心想:你一定是不想讓我面試通過(guò),故意刁難我,不過(guò)你是面試官你最大。搞就搞。

前面我們已經(jīng)實(shí)現(xiàn)了基本的響應(yīng)式功能,不過(guò)為了通用化,我們可以進(jìn)一步封裝。

const bucket = new WeakMap()
// 重新定義bucket數(shù)據(jù)類型為WeakMap
let activeEffect
const effect = function (fn) {
  activeEffect = fn
  fn()
}
// track表示追蹤的意思
function track (target, key) {
  // activeEffect無(wú)值意味著沒(méi)有執(zhí)行effect函數(shù),無(wú)法收集依賴,直接return掉
  if (!activeEffect) {
    return
  }
  // 每個(gè)target在bucket中都是一個(gè)Map類型: key => effects
  let depsMap = bucket.get(target)
  // 第一次攔截,depsMap不存在,先創(chuàng)建聯(lián)系
  if (!depsMap) {
    bucket.set(target, (depsMap = new Map()))
  }
  // 根據(jù)當(dāng)前讀取的key,嘗試讀取key的effects函數(shù)  
  let deps = depsMap.get(key)
  if (!deps) {
    // deps本質(zhì)是個(gè)Set結(jié)構(gòu),即一個(gè)key可以存在多個(gè)effect函數(shù),被多個(gè)effect所依賴
    depsMap.set(key, (deps = new Set()))
  }
  // 將激活的effectFn存進(jìn)桶中
  deps.add(activeEffect)
}
// trigger執(zhí)行依賴
function trigger (target, key) {
  // 讀取depsMap 其結(jié)構(gòu)是 key => effects
  const depsMap = bucket.get(target)
  if (!depsMap) {
    return
  }
  // 真正讀取依賴當(dāng)前屬性值key的effects
  const effects = depsMap.get(key)
  // 挨個(gè)執(zhí)行即可
  effects && effects.forEach((fn) => fn())
}
// 統(tǒng)一對(duì)外暴露響應(yīng)式函數(shù)
function reactive (state) {
  return new Proxy(state, {
    get (target, key) {
      const value = target[ key ]
      track(target, key)
      console.log(`get ${key}: ${value}`)
      return value
    },
    set (target, key, newValue) {
      console.log(`set ${key}: ${newValue}`)
      // 設(shè)置屬性值
      target[ key ] = newValue
      trigger(target, key)
    }
  })
}

有了上面的封裝咱們使用起來(lái)就真的有點(diǎn)Vue3的感覺(jué)啦!

const $app = document.querySelector('#app')
const nameObj = reactive({
  name: 'fatfish'
})
const ageObj = reactive({
  age: 100
})
effect(() => {
  console.log('執(zhí)行了effect')
  $app.innerText = `hello ${ nameObj.name }, are you ${ageObj.age} years old?`
})
setTimeout(() => {
  nameObj.name = 'Vue3'
}, 1000)
setTimeout(() => {
  ageObj.age = 18
}, 2000)

效果預(yù)覽

點(diǎn)擊預(yù)覽

可以看到咱們通過(guò)reactive定義了兩個(gè)響應(yīng)式數(shù)據(jù),在1秒后修改了nameObj的值,視圖也馬上更新了,2秒后修改了ageObj的值,視圖也馬上更新了。這下夠通用了吧!完美

到此這篇關(guān)于詳解如何編寫一個(gè)Vue3響應(yīng)式系統(tǒng)的文章就介紹到這了,更多相關(guān)Vue3響應(yīng)式系統(tǒng)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • vuepress實(shí)現(xiàn)自定義首頁(yè)的樣式風(fēng)格

    vuepress實(shí)現(xiàn)自定義首頁(yè)的樣式風(fēng)格

    這篇文章主要介紹了vuepress實(shí)現(xiàn)自定義首頁(yè)的樣式風(fēng)格,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • Vue.js表單控件綁定示例盤點(diǎn)

    Vue.js表單控件綁定示例盤點(diǎn)

    這篇文章主要為大家介紹了一些Vue.js表單控件綁定示例盤點(diǎn),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-07-07
  • 一篇文章教會(huì)你部署vue項(xiàng)目到docker

    一篇文章教會(huì)你部署vue項(xiàng)目到docker

    在前端開(kāi)發(fā)中,部署項(xiàng)目是我們經(jīng)常發(fā)生的事情,下面這篇文章主要給大家介紹了關(guān)于部署vue項(xiàng)目到docker的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-04-04
  • vue之prop與$emit的用法說(shuō)明

    vue之prop與$emit的用法說(shuō)明

    這篇文章主要介紹了vue之prop與$emit的用法說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-04-04
  • Vue中使用jsencrypt進(jìn)行RSA非對(duì)稱加密的操作方法

    Vue中使用jsencrypt進(jìn)行RSA非對(duì)稱加密的操作方法

    這篇文章主要介紹了Vue中使用jsencrypt進(jìn)行RSA非對(duì)稱加密,在這里需要注意要加密的數(shù)據(jù)必須是字符串,對(duì)Vue?RSA非對(duì)稱加密相關(guān)知識(shí)感興趣的朋友一起看看吧
    2022-04-04
  • vue?iview?導(dǎo)航高亮動(dòng)態(tài)設(shè)置方式

    vue?iview?導(dǎo)航高亮動(dòng)態(tài)設(shè)置方式

    這篇文章主要介紹了vue?iview?導(dǎo)航高亮動(dòng)態(tài)設(shè)置方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-05-05
  • Vue3的路由傳參方法超全匯總

    Vue3的路由傳參方法超全匯總

    vue路由傳參的使用場(chǎng)景一般都是應(yīng)用在父路由跳轉(zhuǎn)到子路由時(shí),攜帶參數(shù)跳轉(zhuǎn),下面這篇文章主要給大家介紹了關(guān)于Vue3路由傳參方法的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-04-04
  • vue項(xiàng)目中Eslint校驗(yàn)代碼報(bào)錯(cuò)的解決方案

    vue項(xiàng)目中Eslint校驗(yàn)代碼報(bào)錯(cuò)的解決方案

    這篇文章主要介紹了vue項(xiàng)目中Eslint校驗(yàn)代碼報(bào)錯(cuò)的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-04-04
  • vue實(shí)現(xiàn)虛擬列表功能的代碼

    vue實(shí)現(xiàn)虛擬列表功能的代碼

    這篇文章主要介紹了vue實(shí)現(xiàn)虛擬列表,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-07-07
  • vue使用svg文件補(bǔ)充-svg放大縮小操作(使用d3.js)

    vue使用svg文件補(bǔ)充-svg放大縮小操作(使用d3.js)

    這篇文章主要介紹了vue使用svg文件補(bǔ)充-svg放大縮小操作(使用d3.js),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-09-09

最新評(píng)論