<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          什么,lodash 的防抖失效了?

          共 2457字,需瀏覽 5分鐘

           ·

          2020-11-25 23:13

          作者:yeyan1996

          https://juejin.im/post/6892577964458770445

          應某人的要求被迫營業(yè),望各位看官不要吝嗇手中的贊-。-

          背景

          在使用 uni-app 開發(fā)小程序時,有個填寫表單的需求,包含兩個輸入框,看起來像這樣

          image-20201107143814796

          兩個在普通不過的輸入框

          因為需要復用一些樣式和邏輯,所以將輸入框抽象成了組件,代碼簡化后如下




          子組件代碼






          由于在父組件中需要依賴輸入的值請求接口,為避免接口頻繁調(diào)用,這邊引入 lodash?debounce?用于防抖

          這個需求在 yeyan1996 眼中沒有任何難度,但在幾天后卻收到了部分用戶反饋,說在兩個輸入框分別填寫了值,但最終只有一個輸入框有效

          這時才回頭想起代碼中的 debounce ....

          問題原因

          收到用戶反饋后,yeyan1996 嘗試多次點擊輸入框,發(fā)現(xiàn)問題并不是必現(xiàn),最終總結(jié)出了規(guī)律

          填寫第一個輸入框后,快速對第二個輸入框進行輸入,才會造成最終的表單數(shù)據(jù)中只有一個值的問題

          通過下圖子組件的 log 可以看到,雖然 ui 界面顯示兩個輸入框都有值,但實際只觸發(fā)了第二個輸入框的 log

          Kapture 2020-11-07 at 14.58.57

          之所以 ui 界面顯示兩個輸入框都有值,是因為是用戶直接和 textarea 控件交互,實際并沒有更新控件綁定的 value 值

          最終結(jié)論:第一個輸入框中被 debounce 包裹的函數(shù)并沒有執(zhí)行

          是防抖問題么?

          嘗試將 debounce 去掉后,果然 bug 解決了

          Kapture 2020-11-07 at 15.06.41

          那么,是防抖的問題么?

          不妨先思考下 Vue 組件的實現(xiàn)原理,我在?[Vue.js進階]從源碼角度剖析Vue的生命周期?中提到過,每個 .vue 文件可以理解為一個構(gòu)造函數(shù),或者一個 Class,而在父組件中引用組件就等于對其的實例化

            



          上述代碼即創(chuàng)建了 2 個 CustomTextarea 組件的實例

          熟悉面向?qū)ο蟮耐瑢W應該知道,構(gòu)造函數(shù)實例化時,同時會創(chuàng)建實例的屬性和方法,一般每個實例的屬性都不相同,而方法因為是函數(shù),所以會復用,已達到節(jié)省內(nèi)存的效果

          class?Person?{
          ??constructor(name)?{
          ????this.name?=?name
          ??}
          ??eat()?{}
          }

          const?person1?=?new?Person('張三')
          const?person2?=?new?Person('李四')

          console.log(person1.name?===?person2.name)?//?false
          console.log(person1.eat?===?person2.eat)?//?true

          Vue2 ?的組件借鑒了面向?qū)ο蟮脑恚m然內(nèi)部的實現(xiàn)方式不同,但最終的行為一致,即組件的每個實例都擁有不同的 data,但會復用相同的 methods

          源碼地址:https://github.com/vuejs/vue/blob/dev/src/core/instance/state.js#L286

          image-20201107155528333

          286 行中 methods 對象是每個組件實例共用的,每實例化一個組件,會創(chuàng)建相同的引用,指向 methods 中的函數(shù)

          未命名

          上圖案例中, 所有 custom-textarea 中的 handleInput 都指向同一個函數(shù),而作為 props 的 value 字段是通過父組件傳入的,并不會共享(分別為 text1/text2)

          解決方案

          經(jīng)過上述的分析,答案顯而易見,兩個組件實例都指向了同一個被 debounce 包裹的 handleInput 函數(shù)

          所以在輸入第一個值后, 1000 毫秒內(nèi)快速切換到第二個輸入框進行輸入,此時由于防抖效果仍存在,導致第一次的輸入并沒有計算在內(nèi)

          而第二次輸入完畢后,經(jīng)過 1000 毫秒,最終只會執(zhí)行第二個 custom-textarea 的 handleInput

          只要使得每個組件實例的 handleInput 互相獨立,即可解決問題






          將 handleInput 從 methods 放到 data 中,每次初始化時創(chuàng)建防抖函數(shù),此時每個組件實例的 handleInput 就不會互相干擾

          Kapture 2020-11-07 at 16.44.41

          大功告成???

          題外話

          Vue 組件中通過將 data 定義為一個函數(shù),函數(shù)的返回值作為組件的數(shù)據(jù)來源,使得每個組件實例的數(shù)據(jù)都不相同

          而 Vue 組件中 methods 是所有實例共用的,那么對于 watch/computed/生命周期,它們是否會共用的呢?




          和 methods 對象相同,computed 對象的屬性名是一個響應式變量,而值是一個函數(shù),所以所有實例也會指向同一個函數(shù),但由于這個函數(shù)需要有返回值,所以不會用防抖函數(shù)進行包裹,很少遇到函數(shù)公用導致的問題

          而 watch 也和 methods 對象相同,所有組件實例共用,所以也會存在防抖的問題

          至于生命周期本身就是一個函數(shù),如果對生命周期設置了防抖,多個組件實例同時初始化時也會造成只執(zhí)行一次的情況

          參考資料

          [Vue.js進階]從源碼角度剖析Vue的生命周期

          【Vue原理】Methods - 源碼版](https://segmentfault.com/a/1190000019605909)


          瀏覽 29
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  精品久久ai | 91乱子伦国产乱子伦无码 | 青草视频亚洲 | 操逼网址进入 | 欧美艹壁|