国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

20230628----重返學(xué)習(xí)-自定義指令的玩法和作用-對(duì)象新增屬性不能響應(yīng)的問題-Vue組件中的data屬性-Vue生命周

這篇具有很好參考價(jià)值的文章主要介紹了20230628----重返學(xué)習(xí)-自定義指令的玩法和作用-對(duì)象新增屬性不能響應(yīng)的問題-Vue組件中的data屬性-Vue生命周。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

day-101-one-hundred-and-one-20230628-自定義指令的玩法和作用-對(duì)象新增屬性不能響應(yīng)的問題-Vue組件中的data屬性-Vue生命周期-$nextTick-computed和watch

常見面試題

  • 面試題:自定義指令的玩法和作用
  • 面試題:Vue怎么用 vm.$set() 解決對(duì)象新增屬性不能響應(yīng)的問題 ?
  • 面試題:Vue 組件中的 data 為什么必須是函數(shù)?
  • 面試題:談?wù)勀銓?duì) Vue2 生命周期的理解?
  • 面試題:簡(jiǎn)單說一下 $nextTick 的作用及實(shí)現(xiàn)原理?
  • 面試題:computed 和 watch 的區(qū)別和運(yùn)用的場(chǎng)景?

自定義指令的玩法和作用

  • 面試題:自定義指令的玩法和作用
    • 在我之前的項(xiàng)目中,有些需求我是基于Vue.directive()創(chuàng)建自定義指令完成的。我覺得這也算是一種封裝技巧,把一些要實(shí)現(xiàn)的功能,封裝成為自定義指令,以后基于v-xxx進(jìn)行調(diào)用,用起來也很方便!
    • 比如我之前封裝過:
      • v-power 實(shí)現(xiàn)權(quán)限的校驗(yàn)。
        • 在自定義指令內(nèi)部,獲取登錄者具備的權(quán)限標(biāo)識(shí),根據(jù)傳遞進(jìn)來需要判斷的標(biāo)識(shí),驗(yàn)證當(dāng)前登錄者是否具備相應(yīng)的權(quán)限,從而控制元素的渲染和銷毀。
      • v-debounce/throttle 實(shí)現(xiàn)函數(shù)的防抖和節(jié)流。
      • 在ElementUI組件庫中,也提供了兩個(gè)自定義指令:v-InfiniteScroll處理無限滾動(dòng)、v-loading處理加載效果。
    • 在Vue2和Vue3中,自定義指令的玩法是有一些區(qū)別的:
      • 在vue2中,基于Vue.directive()創(chuàng)建自定義指令。
        • 在其配置項(xiàng)中,包含這樣幾個(gè)鉤子函數(shù):

          // 創(chuàng)建自定義指令。一般在全局環(huán)境中配置。
          // v-jinyu
          Vue.directive("jinyu", {
            bind() {},
            inserted() {},
            update() {},
            componentUpdated() {},
            unbind() {},
          });
          
          • bind:把自定義指令綁定給某個(gè)元素后就會(huì)觸發(fā)。一般是組件第一次渲染的時(shí)候。
          • inserted:被綁定元素插入父節(jié)點(diǎn)時(shí)調(diào)用 (僅保證父節(jié)點(diǎn)存在,但不一定已被插入文檔中)。
            1. 就保證父節(jié)點(diǎn)已經(jīng)存在了,但自身或子組件不一定已經(jīng)在頁面中有DOM結(jié)構(gòu)了。
          • update/componentUpdated:組件更新后觸發(fā)(建議使用componentUpdated)。
          • unbind:指令與元素解綁時(shí)調(diào)用(一般是組件銷毀的時(shí)候)。
        • 而在這些鉤子函數(shù)中,都可以接收到四個(gè)參數(shù):

          • el:指令所綁定的元素,可以用來直接操作DOM。
          • binding:一個(gè)對(duì)象,包含很多信息。
            • name:指令名字(沒有v-)。
            • value:指令綁定的值。
            • oldValue:指令綁定的原來的值。在componentUpdated鉤子函數(shù)中使用。
            • expression:表達(dá)式。
            • arg:參數(shù)。
            • modifiers:修飾符。
          • vnode/oldVnode:編譯的虛擬DOM對(duì)象。
        • 我們會(huì)在指定的鉤子函數(shù)中,基于接收到的信息,完成項(xiàng)目中的需求!

      • 但是在Vue3中,自定義指令的語法有些許的變化:
        • 因?yàn)椴辉倬邆銿ue這個(gè)類,所以基于app.directive創(chuàng)建全局自定義指令。
        • 相應(yīng)的鉤子函數(shù),調(diào)整為和組件鉤子函數(shù)相似的名字,讓語義化更明顯。
          • created 代替了 bind
          • beforeMount / mounted 代替了 inserted
          • beforeUpdate / updated 代替了 componentUpdated
          • beforeUnmount / unmounted 代替了 unbind
        • 還有binging接收的信息中,新增了一個(gè)instance,當(dāng)把指令綁定給組件,可以獲取組件的實(shí)例。
    • 總之,我覺得自定義指令還是很常用的,尤其是針對(duì)于視圖中的每個(gè)元素/組件,需要做什么特殊的處理,用自定義指令來進(jìn)行封裝處理,在開發(fā)和使用上,會(huì)更加便捷一些。
      1. 自定義指令一般都是全局指令,以便全局都可以使用。
        • fang/f20230628/day0628/src/main.js

          import Vue from 'vue'
          import './global'
          import App from './App.vue'
          
          new Vue({
            render: h => h(App)
          }).$mount('#app')
          
        • fang/f20230628/day0628/src/global.js

          import Vue from "vue";
          import { Button, Tag, Loading, Message } from "element-ui";
          import "element-ui/lib/theme-chalk/index.css";
          Vue.use(Button).use(Tag).use(Loading.directive);
          Vue.prototype.$message = Message;
          
          Vue.config.productionTip = false;
          
          /* // 創(chuàng)建自定義指令。一般在全局環(huán)境中配置。
          // v-jinyu
          Vue.directive("jinyu", {
            bind() {},
            inserted() {},
            update() {},
            componentUpdated() {},
            unbind() {},
          }); */
          
          // 創(chuàng)建自定義指令。一般在全局環(huán)境中配置。
          // v-jinyu
          Vue.directive("jinyu", {
            bind(el, binding, vnode) {
              console.log(`el-->`, el);
              console.log(`binding-->`, binding);
              console.log(`vnode-->`, vnode);
            },
          });
          
        • fang/f20230628/day0628/src/App.vue

          <template>
            <div id="app">
              <!-- <Demo /> -->
              <Demo1 />
              <!-- <Demo2 />
              <Demo2 />
              <Demo2 /> -->
              <!-- <Demo3 /> -->
              <!-- <Demo4 /> -->
              <!-- <Demo5 /> -->
              <!-- <Demo6 /> -->
              <!-- <Demo7 /> -->
            </div>
          </template>
          
          <script>
          // import Demo from './views/Demo1.vue'
          import Demo1 from './views/Demo1.vue'
          import Demo2 from './views/Demo2.vue'
          import Demo3 from './views/Demo3.vue'
          import Demo4 from './views/Demo4.vue'
          import Demo5 from './views/Demo5.vue'
          import Demo6 from './views/Demo6.vue'
          import Demo7 from './views/Demo7.vue'
          export default {
            name: 'App',
            components: {
              Demo1,
              Demo2,
              Demo3,
              Demo4,
              Demo5,
              Demo6,
              Demo7,
            }
          }
          </script>
          
          <style lang="less">
          @import './assets/reset.min.css';
          
          html,
          body,
          #app {
            height: 100%;
            font-size: 14px;
            overflow: hidden;
          }
          </style>
          
        • fang/f20230628/day0628/src/views/Demo1.vue

          <template>
              <div class="demo-box">
                  <button v-jinyu:aa.stop.trim="1 + 1">自定義指令</button>
              </div>
          </template>
          
          <script>
          export default {
          
          }
          </script>
          
          <style lang="less" scoped>
          .demo-box {
              box-sizing: border-box;
          }
          </style>
          

對(duì)象新增屬性不能響應(yīng)的問題

  • 面試題:Vue怎么用vm.$set()解決對(duì)象新增屬性不能響應(yīng)的問題 ?
    • 在Vue既定的響應(yīng)式策略中,當(dāng)new Vue()第一次渲染組件的時(shí)候,只會(huì)把初始化data()時(shí)現(xiàn)有的狀態(tài)進(jìn)行數(shù)據(jù)劫持,對(duì)于后期新增進(jìn)來的成員,默認(rèn)將不會(huì)進(jìn)行數(shù)據(jù)劫持了!

      • 所以我之前開發(fā)的時(shí)候,都會(huì)把需要的狀態(tài)信息提前寫入到data中,讓其變?yōu)轫憫?yīng)式的。
      • 只是新增的成員不會(huì)再做響應(yīng)式處理
        • 但是對(duì)于已有成員新增或者修改的值,Vue2內(nèi)部依然會(huì)調(diào)用observe()方法進(jìn)行數(shù)據(jù)劫持。
    • 所以不瞞你說,this.$set()我雖然知道是什么回事,但是項(xiàng)目中基本上很少使用。

    • 但是我之前研究Vue2響應(yīng)式原理的時(shí)候,看過$set()的源碼,它最主要的目的是:給對(duì)象新增的成員做響應(yīng)式的數(shù)據(jù)劫持,并且通知視圖更新!

      1. /node_modules/vue/dist/vue.js中的function set(target, key, val);
      • 但是其內(nèi)部有很多特殊處理:

        Vue.prototype.$set = set
        實(shí)例.$set(target, key, val)
        
        1. $set()在Vue.prototype上。
        • 要求傳遞進(jìn)來的target是一個(gè)對(duì)象類型值,而且不能是只讀的(比如:props),也不能是Vue的實(shí)例對(duì)象。
        • 如果target是一個(gè)數(shù)組,key是其一個(gè)索引項(xiàng)。
          • target是響應(yīng)式的:基于重寫的splice方法新增/修改索引項(xiàng)的信息,完成后通知視圖更新,對(duì)新修改/新增的值,基于ovserve進(jìn)行響應(yīng)式處理。
          • 如果其不是響應(yīng)式的:則直接基于內(nèi)置的splice實(shí)現(xiàn)此索引項(xiàng)的新增/修改即可。
        • 如果target是對(duì)象,核心在于target對(duì)象是否是經(jīng)過響應(yīng)式處理的:
          • 如果沒有經(jīng)過處理:則基于$set的所有操作,都僅僅是在新增值或修改值,不會(huì)做任何其余的處理。例如:讓視圖更新,或者對(duì)新增的值進(jìn)行響應(yīng)式處理等。

          • 如果是經(jīng)過處理的:

            • key:如果是target現(xiàn)有的成員(本身也是響應(yīng)式的),那么在修改值的時(shí)候,會(huì)觸發(fā)key本身的setter劫持函數(shù),實(shí)現(xiàn)數(shù)據(jù)更改、新數(shù)據(jù)的響應(yīng)式處理、通知視圖更新等。

            • key如果在target中不存在,則給新增的成員,基于defineReactive()進(jìn)行數(shù)據(jù)劫持、并且讓視圖更新。

            • 具體的操作:

              • 如果key是target中現(xiàn)在的一個(gè)成員,則直接修改成員值:
                • 如果target是響應(yīng)式的(key也是響應(yīng)式):則基于target[key]=value操作,針對(duì)觸發(fā)key的set劫持函數(shù),讓視圖進(jìn)行更新。
                • 如果target不是響應(yīng)式的:則僅是修改成員值,但是后續(xù)不會(huì)做任何的操作。比如:讓視圖更新,或者新值的響應(yīng)式處理等,都不會(huì)做。
              • 如果操作的target對(duì)象本身不是響應(yīng)式的,則也是直接修改/新增值,后續(xù)不會(huì)做任何的操作。
              • 只有target對(duì)象本身是響應(yīng)式的,key這個(gè)成員,之前在對(duì)象中并不存在,此時(shí)才會(huì)給新增的對(duì)象進(jìn)行數(shù)據(jù)劫持,并且讓視圖更新!
            function set(target, key, val) {
                //要求傳遞進(jìn)來的target是一個(gè)對(duì)象類型值。
                if ((isUndef(target) || isPrimitive(target))) {
                    warn$2("Cannot set reactive property on undefined, null, or primitive value: ".concat(target));
                }
                //要求傳遞進(jìn)來的對(duì)象不能是只讀的!
                if (isReadonly(target)) {
                    warn$2("Set operation on key \"".concat(key, "\" failed: target is readonly."));
                    return;
                }
            
                //如果target是一個(gè)數(shù)組,key是其一個(gè)索引項(xiàng)。
                var ob = target.__ob__;
                if (isArray(target) && isValidArrayIndex(key)) {
                    target.length = Math.max(target.length, key);
                    target.splice(key, 1, val);
                    // when mocking for SSR, array methods are not hijacked
                    if (ob && !ob.shallow && ob.mock) {
                        observe(val, false, true);
                    }
                    return val;
                }
                //如果key是target中現(xiàn)在的一個(gè)成員,則直接修改成員值,但是后續(xù)不會(huì)做任何的操作。
                if (key in target && !(key in Object.prototype)) {
                    target[key] = val;
                    return val;
                }
                //要求傳遞進(jìn)來的target是不能是Vue的實(shí)例對(duì)象。
                if (target._isVue || (ob && ob.vmCount)) {
                    warn$2('Avoid adding reactive properties to a Vue instance or its root $data ' +
                            'at runtime - declare it upfront in the data option.');
                    return val;
                }
                //如果操作的target對(duì)象本身不是響應(yīng)式的,則也是直接修改/新增值,后續(xù)不會(huì)做任何的操作。
                if (!ob) {
                    target[key] = val;
                    return val;
                }
            
                //只有target對(duì)象本身是響應(yīng)式的,key這個(gè)成員,之前在對(duì)象中并不存在,此時(shí)才會(huì)給新增的對(duì)象進(jìn)行數(shù)據(jù)劫持,并且讓視圖更新。
                defineReactive(ob.value, key, val, undefined, ob.shallow, ob.mock);
                {
                    ob.dep.notify({
                        type: "add" /* TriggerOpTypes.ADD */,
                        target: target,
                        key: key,
                        newValue: val,
                        oldValue: undefined
                    });
                }
                return val;
            }
            
    • 其實(shí)vue.prototype上也還有和 s e t 類似的方法: set類似的方法: set類似的方法:delete,主要目的是:刪除data中數(shù)組/對(duì)象的某個(gè)成員,可以讓視圖更新!

      function del(target, key) {
          if ((isUndef(target) || isPrimitive(target))) {
              warn$2("Cannot delete reactive property on undefined, null, or primitive value: ".concat(target));
          }
          if (isArray(target) && isValidArrayIndex(key)) {
              target.splice(key, 1);
              return;
          }
          var ob = target.__ob__;
          if (target._isVue || (ob && ob.vmCount)) {
              warn$2('Avoid deleting properties on a Vue instance or its root $data ' +
                      '- just set it to null.');
              return;
          }
          if (isReadonly(target)) {
              warn$2("Delete operation on key \"".concat(key, "\" failed: target is readonly."));
              return;
          }
          if (!hasOwn(target, key)) {
              return;
          }
          delete target[key];
          if (!ob) {
              return;
          }
          {
              ob.dep.notify({
                  type: "delete" /* TriggerOpTypes.DELETE */,
                  target: target,
                  key: key
              });
          }
      }
      
    • 總結(jié):真實(shí)項(xiàng)目中, s e t / set/ set/delete雖然有用,但是我一般很少用,個(gè)人覺得不如把需要的狀態(tài)都事先寫在data中,這樣方便開發(fā)調(diào)試!

Vue組件中的data屬性

  • 面試題:Vue 組件中的 data 為什么必須是函數(shù)?

    • 例子:
      • 如果是函數(shù):

        <script>
        export default {
          data() {
            return {
              num: 0,
            };
          },
        };
        </script>
        
        <template>
          <div class="demo-box">
            <div class="text">{{num}}</div>
            <el-button type="primary" size="small" @click="handle">按鈕</el-button>
          </div>
        </template>
        
        <script>
        export default {
          data() {
            return {
              num: 0,
            };
          },
          methods: {
            handle() {
              this.num += 10;
            },
          },
        };
        </script>
        
        <style lang="less" scoped>
        .demo-box {
          box-sizing: border-box;
          margin: 10px auto;
          padding: 10px;
          width: 200px;
          background: lightblue;
        }
        </style>
        
      • 如果是對(duì)象:

        <script>
        export default {
          data: {
            num: 0,
          },
        };
        </script>
        
        <template>
          <div class="demo-box">
            <div class="text">{{ num }}</div>
            <el-button type="primary" size="small" @click="handle">按鈕</el-button>
          </div>
        </template>
        
        <script>
        export default {
          data: {
            num: 0,
          },
          methods: {
            handle() {
              this.num += 10;
            },
          },
        };
        </script>
        
        <style lang="less" scoped>
        .demo-box {
          box-sizing: border-box;
          margin: 10px auto;
          padding: 10px;
          width: 200px;
          background: lightblue;
        }
        </style>
        
  • 如果給單文件組件中的data寫成對(duì)象格式-而非函數(shù),直接會(huì)報(bào)錯(cuò)。[Vue warn]: The "data" option should be a function that returns a per-instance value in component definitions.

  • 為什么Vue內(nèi)部,要求組件中的data必須是函數(shù),而不能是對(duì)象呢?

    • 因?yàn)槿绻麑懗蓪?duì)象,那么對(duì)象中的狀態(tài)數(shù)據(jù)是共享的,此時(shí)如果一個(gè)組件被調(diào)用多次,即便創(chuàng)建多個(gè)實(shí)例,但是這樣狀態(tài)是共享的,在某個(gè)被調(diào)用的組件中修改狀態(tài),也會(huì)影響其它被調(diào)用組件中的信息!

    • 而把data寫成一個(gè)函數(shù),是因?yàn)楹瘮?shù)執(zhí)行會(huì)產(chǎn)生閉包,讓狀態(tài)是組件內(nèi)部私有的,而不是共享的,以此來避免沖突!

    • 源碼中是在initState()–>initData(vm);

      function initState(vm) {
          var opts = vm.$options;
          if (opts.props)
              initProps$1(vm, opts.props);
          // Composition API
          initSetup(vm);
          if (opts.methods)
              initMethods(vm, opts.methods);
          if (opts.data) {
              initData(vm);
          }
          else {
              var ob = observe((vm._data = {}));
              ob && ob.vmCount++;
          }
          if (opts.computed)
              initComputed$1(vm, opts.computed);
          if (opts.watch && opts.watch !== nativeWatch) {
              initWatch(vm, opts.watch);
          }
      }
      
      function initData(vm) {
          var data = vm.$options.data;
          data = vm._data = isFunction(data) ? getData(data, vm) : data || {};
          if (!isPlainObject(data)) {
              data = {};
              warn$2('data functions should return an object:\n' +
                      'https://v2.vuejs.org/v2/guide/components.html#data-Must-Be-a-Function', vm);
          }
          // proxy data on instance
          var keys = Object.keys(data);
          var props = vm.$options.props;
          var methods = vm.$options.methods;
          var i = keys.length;
          while (i--) {
              var key = keys[i];
              {
                  if (methods && hasOwn(methods, key)) {
                      warn$2("Method \"".concat(key, "\" has already been defined as a data property."), vm);
                  }
              }
              if (props && hasOwn(props, key)) {
                  warn$2("The data property \"".concat(key, "\" is already declared as a prop. ") +
                          "Use prop default value instead.", vm);
              }
              else if (!isReserved(key)) {
                  proxy(vm, "_data", key);
              }
          }
          // observe data
          var ob = observe(data);
          ob && ob.vmCount++;
      }
      

Vue生命周期

  • 面試題:談?wù)勀銓?duì) Vue 生命周期的理解?
    • Vue2和Vue3的鉤子函數(shù)還是有一些區(qū)別的。
      • 在Vue2中,提供了好幾個(gè)鉤子函數(shù),但是在我之前的項(xiàng)目開發(fā)中,常用的就那幾個(gè):
        1. created:當(dāng)把要初始化的信息都處理好(例如屬性、狀態(tài)、監(jiān)聽器、計(jì)算屬性、還有一些內(nèi)置的私有屬性等),當(dāng)應(yīng)該掛載到實(shí)例上的信息都掛載完畢了,此時(shí)Vue內(nèi)部會(huì)基于callHook函數(shù),觸發(fā)created鉤子函數(shù)執(zhí)行!
          • 我一般會(huì)在這里向服務(wù)器發(fā)送異步數(shù)據(jù)請(qǐng)求,這樣在組件第一次渲染期間,就在請(qǐng)求數(shù)據(jù),等待第一次渲染完畢,可能數(shù)據(jù)已經(jīng)獲取到了,這樣可以盡快地把真實(shí)數(shù)據(jù)渲染到視圖中!
          • 當(dāng)然也可以放在beforeMount鉤子函數(shù)中處理,和created是一樣的!
        2. mounted:會(huì)在組件第一次渲染完畢后觸發(fā),此時(shí)在這個(gè)鉤子中,就可以基于ref獲取真實(shí)的DOM(或者子組件的實(shí)例),這樣可以干很多事情,例如:
          • 手動(dòng)基于addEventListener給真實(shí)DOM進(jìn)行事件綁定(一般是某些第三方插件中,需要我們自己手動(dòng)綁定-如元素拖拽插件)。
          • 還可以自己創(chuàng)建定時(shí)器或者監(jiān)聽器去實(shí)現(xiàn)一些需求,例如:
            • 我之前實(shí)現(xiàn)一個(gè)觸底加載更多數(shù)據(jù)的功能,就是基于創(chuàng)建IntersectionObserver()監(jiān)聽器實(shí)現(xiàn)的
            • 之前也設(shè)置過定時(shí)器,在第一次渲染完畢后,延遲一段時(shí)間去處理一些事情
          • 用的一些第三方插件(比如:echarts/swiper/IScroll等),也需要在視圖第一次渲染完畢有對(duì)應(yīng)的DOM元素后,進(jìn)行初始化處理,實(shí)現(xiàn)相關(guān)的效果!
          • 總之mounted鉤子函數(shù)還是很常用的!
        3. updated:在組件每一次更新完畢后觸發(fā),如果想在更新完畢后做什么事,可以寫在這個(gè)鉤子函數(shù)中。
          • 只不過,不論那個(gè)狀態(tài)改變,只要組件更新了,updated都會(huì)被觸發(fā)。如果需求就是:不論誰改變,都要做這個(gè)事情,寫在updated中沒有任何問題!
          • 但如果是改變不同狀態(tài)要做不同的事情,我往往會(huì)基于 n e x t T i c k 來代替 u p d a t e d , nextTick來代替updated, nextTick來代替updated,nextTick也是在組件更新完畢后觸發(fā),而且是晚于updated的。
        4. beforeDestory/destoryed:組件銷毀之前/之后觸發(fā)的鉤子函數(shù)。
          • 我一般會(huì)在這里,手動(dòng)清除一些內(nèi)容,比如:設(shè)置的定時(shí)器、監(jiān)聽器、手動(dòng)基于addEventListener做的事件綁定等等,這些東西不會(huì)隨著組件銷毀而釋放,需要手動(dòng)清除,來優(yōu)化內(nèi)存空間。
          • 之前搞管理系統(tǒng)的項(xiàng)目,有的表單頁表單內(nèi)容特別多,用戶可能一次性寫不完,我做了個(gè)信息草稿箱的功能:在組件銷毀之前,把用戶已經(jīng)填寫(但是還沒有填完、沒有提交)的信息,存儲(chǔ)到本地 localStorage 中,當(dāng)用戶關(guān)掉頁面,下一次再重新進(jìn)來的時(shí)候,把之前存儲(chǔ)的信息直接放在對(duì)應(yīng)的表單中,這樣可以讓用戶不再重新編寫!
        • 還有一些其它的鉤子函數(shù),比如:beforeCreate、beforeMount、beforeUpdate等,只不過我在項(xiàng)目中沒有用過!
          • 項(xiàng)目中配合<keep-alive>組件進(jìn)行緩存,讓組件還具備了activated、deactivated兩個(gè)鉤子!
          • 當(dāng)我們使用 vue-router 的時(shí)候,在組件中有一些組件內(nèi)的,導(dǎo)航守衛(wèi)鉤子函數(shù),例如:beforeRouteEnter、beforeRouteLeave、beforeRouteUpdate等,只不過我也很少用!
      • 在Vue3中,全面使用函數(shù)式編程,在vue中提供了相應(yīng)的鉤子函數(shù),需要處理的事情和Vue2依然差不多,只不過鉤子函數(shù)的名字改了!
        • 沒有 created/beforeCreate 了,被 setup 聚合函數(shù)代替,所以我會(huì)把發(fā)送數(shù)據(jù)請(qǐng)求的事情,放在 onBeforeMount 鉤子函數(shù)中!
        • onMounted代替了mounted
        • onUpdated代替了updated,nextTick函數(shù)代替了$nextTick
        • 這些變化還不大,主要是組件銷毀:
          • onBeforeUnmount 代替了 beforeDestory
          • onUnmounted 代替了 destoryed
    • 以上就是我常用的鉤子函數(shù)和處理的一些事情,當(dāng)然還有很多之前做過的需求,這里就不一一列舉了!
生命周期的細(xì)節(jié)
  • 在Vue中修改狀態(tài)值,會(huì)觸發(fā)對(duì)應(yīng)setter劫持函數(shù),在此函數(shù)中:
    • 同步修改狀態(tài)值-并且立即基于observe對(duì)新的值進(jìn)行響應(yīng)式處理。
    • 異步通知視圖更新-每一次更改狀態(tài),并沒有立即進(jìn)行視圖更新,而是把更新的操作,放在更新隊(duì)列中,等待同步操作處理完畢,再把更新隊(duì)列中的任務(wù)統(tǒng)一批處理一次,這樣可以有效減少視圖更新編譯的次數(shù),節(jié)約性能,讓視圖渲染速度更快。

$nextTick

  • 面試題:簡(jiǎn)單說一下 $nextTick 的作用及實(shí)現(xiàn)原理?
    • 我之前研究過Vue視圖更新的原理:

      • 在Vue中,修改狀態(tài)讓視圖更新,其中修改狀態(tài)值是同步的,但是讓視圖更新的操作是異步的。
        • 其實(shí)就是Vue內(nèi)部實(shí)現(xiàn)了一個(gè)更新隊(duì)列,每次讓視圖更新,都不會(huì)立即更新,而是把其放在更新隊(duì)列中;
        • 而讓更新隊(duì)列執(zhí)行的操作是一個(gè)異步任務(wù)。支持Promise的瀏覽器,它是一個(gè)異步微任務(wù),因?yàn)槭腔赑romise處理的。不支持的瀏覽器,它是一個(gè)異步宏任務(wù),因?yàn)槭腔诙〞r(shí)器處理的。
        • 當(dāng)同步代碼執(zhí)行完畢后,會(huì)通知異步任務(wù)執(zhí)行,讓所有的更新操作統(tǒng)一批處理,這樣視圖只需要更新一次。
        • 這樣設(shè)計(jì)的目的是:
          1. 連續(xù)修改多個(gè)狀態(tài)值,視圖只需要更新一次,節(jié)省性能,加快視圖的渲染。
          2. 也可以讓代碼執(zhí)行的順序更加清晰。
          3. 還可以避免無效的更新操作。每一次加入更新隊(duì)列的時(shí)候,內(nèi)部都會(huì)去重。
    • 但是這樣也導(dǎo)致一些問題:如果我們想在狀態(tài)更改、視圖更新完畢后,做一些事情,只能寫在updated鉤子函數(shù)中,但是不論進(jìn)行何種操作,只要讓視圖更新,updated都會(huì)執(zhí)行;如果只想這幾個(gè)狀態(tài)更改,單獨(dú)做一些事情,此時(shí)就需要使用Vue中提供的$nextTick函數(shù)了!

    • $nextTick之所以有這樣的效果,其內(nèi)部是利用EventLoop事件循環(huán)機(jī)制:

      1. 當(dāng)執(zhí)行$nextTick(callback)的時(shí)候,會(huì)把callback放在一個(gè)callbacks集合中。
      2. 而異步微任務(wù)(IE是異步宏任務(wù))中有一個(gè)任務(wù),就是用來通知callbacks集合中的函數(shù)執(zhí)行的。
      3. 等待同步操作完畢(或者上一個(gè)異步任務(wù)執(zhí)行完畢-例如:視圖更新),會(huì)把callbacks中的函數(shù)都按照順序執(zhí)行。
    • 在我之前的項(xiàng)目開發(fā)中,我經(jīng)常使用$nextTick代替updated;

    • $nextTick在沒有傳遞callback函數(shù)的時(shí)候,會(huì)返回一個(gè)promise實(shí)例,我們可以基于await等待實(shí)例成功后,再執(zhí)行我們要干的事情,效果和傳遞callback一樣,只不過我用的最多的還是callback方式。

computed和watch

  • 面試題:computed 和 watch 的區(qū)別和運(yùn)用的場(chǎng)景?
    • 在我之前的項(xiàng)目開發(fā)中,computed和watch都很常用。
      • computed:計(jì)算屬性。
        • 用法:

          computed:{
              ratio(){}
          }
          
          computed:{
              ratio:{
                get(){},  //等價(jià)于上面的函數(shù)
                set(){}  //手動(dòng)修改計(jì)算屬性值的時(shí)候,觸發(fā)這個(gè)函數(shù)
              }
          }
          
        • 依賴于其它狀態(tài)/屬性值,計(jì)算出一個(gè)新值:

          • 創(chuàng)建的計(jì)算屬性會(huì)被掛載到實(shí)例上,所以不能和data/methods/props中的字段沖突。
          • 而且也進(jìn)行了數(shù)據(jù)劫持。
            • set劫持函數(shù):在我們沒有設(shè)置set函數(shù)的時(shí)候,手動(dòng)修改計(jì)算屬性的值,會(huì)報(bào)錯(cuò)。如果我們自己設(shè)置了set函數(shù),則手動(dòng)修改值的時(shí)候,執(zhí)行我們自己設(shè)置的set函數(shù)!
            • get劫持函數(shù):獲取計(jì)算屬性值,只不過這個(gè)值可能用的是緩存的舊值,也可能是重新執(zhí)行函數(shù)算出來的新值。
        • 計(jì)算屬性具備一個(gè)特點(diǎn):計(jì)算緩存。

          • 第一次獲取計(jì)算屬性,會(huì)把對(duì)應(yīng)的函數(shù)執(zhí)行,計(jì)算出結(jié)果。
            • 把此結(jié)果緩存起來。
            • 把函數(shù)中用到的狀態(tài)與屬性,建立起依賴追蹤:所謂依賴追蹤,就是在函數(shù)執(zhí)行的時(shí)候,用到那些狀態(tài)/屬性,都會(huì)觸發(fā)其對(duì)應(yīng)的getter函數(shù),在getter函數(shù)中,設(shè)立監(jiān)聽機(jī)制(目的是監(jiān)聽其是否變化)!
          • 第二次及以后再獲取計(jì)算屬性值的時(shí)候,首先看依賴的狀態(tài)是否發(fā)生過改變。
            • 如果沒有變:直接獲取之前緩存的結(jié)果來使用。
            • 如果其中至少有一個(gè)依賴的狀態(tài)/屬性有變化,都需要把函數(shù)重新執(zhí)行,重新計(jì)算新的值出來…
        • 想比較于methods中的方法來講,computed計(jì)算屬性,因?yàn)槠溆?jì)算緩存/依賴追蹤的機(jī)制,可以避免函數(shù)每一次都重新執(zhí)行,減少非必要的消耗,提高頁面渲染的速度!

      • watch: 監(jiān)聽器
        1. 從initWatch(vm, watch)中開始。
        • 監(jiān)聽器的語法:

          watch: {
            x(newVal, oldVal) {
              console.log(`x發(fā)生改變`, newVal, oldVal);
              // ...
            },
            y: {
              handler(newVal, oldVal) {
                console.log(`y發(fā)生改變`, newVal, oldVal);
                //...
              },
              immediate: true, //讓組件第一次渲染就立即執(zhí)行一次。
              deep: true, //開啟深度監(jiān)聽:如果監(jiān)聽的狀態(tài)是個(gè)對(duì)象,修改對(duì)象中的內(nèi)容,也可以觸發(fā)監(jiān)聽器。
            },
            z: [
              // 當(dāng)z發(fā)生改變,數(shù)組中的每一項(xiàng)規(guī)則都會(huì)執(zhí)行。
              function () {},
              {
                handler(newVal, oldVal) {
                  console.log(`y發(fā)生改變`, newVal, oldVal);
                  //...
                },
                immediate: true, //讓組件第一次渲染就立即執(zhí)行一次。
                deep: true, //開啟深度監(jiān)聽:如果監(jiān)聽的狀態(tài)是個(gè)對(duì)象,修改對(duì)象中的內(nèi)容,也可以觸發(fā)監(jiān)聽器。
              },
            ],
            h:'init',//當(dāng)h改變的時(shí)候,去實(shí)例上找init方法執(zhí)行。
          },
          
        • 監(jiān)聽現(xiàn)有的狀態(tài)/現(xiàn)有的屬性,當(dāng)被監(jiān)聽的狀態(tài)/被監(jiān)聽的屬性發(fā)生改變,會(huì)把指定的handler方法執(zhí)行。在Vue內(nèi)部,就是基于創(chuàng)建Watcher類的實(shí)例來完成的。文章來源地址http://www.zghlxwxcb.cn/news/detail-507160.html

    • 無論是computed還是watch,其實(shí)它們的底層都是建立了監(jiān)聽機(jī)制。都是當(dāng)依賴或監(jiān)聽的狀態(tài)發(fā)生改變,對(duì)應(yīng)的函數(shù)才會(huì)執(zhí)行,從性能來講其實(shí)是差不多的!
      • 只不過,computed會(huì)自動(dòng)對(duì)函數(shù)中用到的所有狀態(tài),都自動(dòng)建立起依賴追蹤,而watch需要我們一個(gè)個(gè)地去進(jìn)行監(jiān)聽。從便捷度來講,如果需要監(jiān)聽多個(gè)狀態(tài),做相同的事情,用computed會(huì)更方便一些。
      • 如果是依賴別的狀態(tài)算出新的值,computed更符合語義化一些。而watch的語義,就是監(jiān)聽某個(gè)狀態(tài)變化,做點(diǎn)啥事,但是不一定需要算出新的值!從這一點(diǎn)出發(fā),到底用computed還是watch,可以看是否需要算新的值出來!
      • watch有一個(gè)computed做不到的能力:在watch設(shè)定的監(jiān)聽函數(shù)中,是可以發(fā)送異步數(shù)據(jù)請(qǐng)求的,而computed中是不搞異步操作的!
    • 在項(xiàng)目中,我就是根據(jù)computed和watch的多方面特征,來選擇用誰更合適的,如果兩個(gè)都合適,我個(gè)人習(xí)慣于用computed計(jì)算屬性!
    • 接下來還可以舉一些具體應(yīng)用的例子…

進(jìn)階參考

到了這里,關(guān)于20230628----重返學(xué)習(xí)-自定義指令的玩法和作用-對(duì)象新增屬性不能響應(yīng)的問題-Vue組件中的data屬性-Vue生命周的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 【C++】類和對(duì)象①(什么是面向?qū)ο?| 類的定義 | 類的訪問限定符及封裝 | 類的作用域和實(shí)例化 | 類對(duì)象的存儲(chǔ)方式 | this指針)

    【C++】類和對(duì)象①(什么是面向?qū)ο?| 類的定義 | 類的訪問限定符及封裝 | 類的作用域和實(shí)例化 | 類對(duì)象的存儲(chǔ)方式 | this指針)

    ?? 個(gè)人主頁 : Forcible Bug Maker ?? 專欄 : C++ 目錄 前言 什么是面向?qū)ο螅?類的定義 類的訪問限定符及封裝 訪問限定符 封裝 類的作用域 類的實(shí)例化 類對(duì)象的存儲(chǔ)方式 this指針 結(jié)語 最早的C++版本(C with classes)中,最先加上的就是類的機(jī)制,它構(gòu)成了面向?qū)ο缶幊蹋∣OP)的

    2024年04月14日
    瀏覽(48)
  • 【Vue2.0源碼學(xué)習(xí)】指令篇-Vue自定義指令

    在 Vue 中,除了 Vue 本身為我們提供的一些內(nèi)置指令之外, Vue 還支持用戶自定義指令。并且用戶有兩種定義指令的方式:一種是使用全局API—— Vue.directive 來定義全局指令,這種方式定義的指令會(huì)被存放在 Vue.options[\\\'directives\\\'] 中;另一種是在組件內(nèi)的 directive 選項(xiàng)中定義專為

    2024年02月09日
    瀏覽(29)
  • 【學(xué)習(xí)記錄24】vue3自定義指令

    【學(xué)習(xí)記錄24】vue3自定義指令

    1、html部分 ?2、js部分 3、實(shí)現(xiàn)效果 1、html部分 2、js部分 在components下創(chuàng)建loading文件夾,在loading文件夾里創(chuàng)建directive.js? 在main.js中全局注冊(cè)指令 1、在components下創(chuàng)建loading文件夾,在loading文件夾里創(chuàng)建directive.js 2、在loading文件夾里創(chuàng)建loading.vue 3、在loading文件夾里放入一張G

    2024年01月19日
    瀏覽(23)
  • [Angular 基礎(chǔ)] - 自定義指令,深入學(xué)習(xí) directive

    [Angular 基礎(chǔ)] - 自定義指令,深入學(xué)習(xí) directive

    這篇筆記的前置筆記為 [Angular 基礎(chǔ)] - 數(shù)據(jù)綁定(databinding),對(duì) Angular 的 directives 不是很了解的可以先過一下這篇筆記 后面也會(huì)拓展一下項(xiàng)目,所以感興趣的也可以補(bǔ)一下文后對(duì)應(yīng)的項(xiàng)目: 第一個(gè) Angular 項(xiàng)目 - 靜態(tài)頁面 第一個(gè) Angular 項(xiàng)目 - 動(dòng)態(tài)頁面 directive 的創(chuàng)建方式和 com

    2024年02月21日
    瀏覽(15)
  • 20230428----重返學(xué)習(xí)-vue項(xiàng)目

    配置開發(fā)服務(wù)器 如果沒有后端接口,可以直接通過瀏覽器控制臺(tái)看到別人網(wǎng)站的接口。 Shift+Control+I打開瀏覽器控制臺(tái),點(diǎn)擊Network查看網(wǎng)絡(luò)請(qǐng)求,點(diǎn)擊Fetch/XHR,查看當(dāng)前頁面發(fā)送的請(qǐng)求。 觸發(fā)請(qǐng)求,可以看到瀏覽器向別人的后端發(fā)送的請(qǐng)求及請(qǐng)求信息。 復(fù)制一下請(qǐng)求路徑和

    2024年02月01日
    瀏覽(21)
  • 20230602----重返學(xué)習(xí)-React路由

    新版的是react-router-dom是6版本,而舊版的是5版本。 基于前端路由實(shí)現(xiàn)SPA單頁面應(yīng)用,其核心在于:構(gòu)建路由表(構(gòu)建路由匹配機(jī)制)。 每一次 路由切換 (包含 剛開始第一次渲染頁面 ),都會(huì)拿 當(dāng)前的地址 (或者 哈希值 )去路由表中進(jìn)行查找匹配,找到相匹配的組件。 包含

    2024年02月07日
    瀏覽(29)
  • 20230712----重返學(xué)習(xí)-權(quán)限校驗(yàn)

    無權(quán)限直接移除需權(quán)限校驗(yàn)的視圖v-if版 登錄時(shí)拿到用戶所有的權(quán)限標(biāo)識(shí)列表并保存到vuex中。 v-if中判斷vuex中權(quán)限標(biāo)識(shí)列表是否包含有當(dāng)前v-if對(duì)應(yīng)按鈕或視圖的權(quán)限標(biāo)識(shí),沒有就直接移除。 無權(quán)限直接移除需權(quán)限校驗(yàn)的視圖-自定義指令版 登錄時(shí)拿到用戶所有的權(quán)限標(biāo)識(shí)列表

    2024年02月15日
    瀏覽(45)
  • 20230406----重返學(xué)習(xí)-AJAX

    AJAX全稱async javascript and xml。 以前的數(shù)據(jù)一般是xml,現(xiàn)在大多是json。 AJAX不是一種新的技術(shù),而一個(gè)與后端通信的方式。 特色: 異步獲取數(shù)據(jù),局部更新頁面。 數(shù)據(jù)渲染 服務(wù)器渲染 客戶端渲染----(局部更新頁面) 服務(wù)器渲染 [外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖

    2023年04月10日
    瀏覽(23)
  • Unity Shader 學(xué)習(xí)筆記(4)URP渲染管線帶陰影PBR-Shader模板 -- 新增可自定義陰影顏色

    Unity Shader 學(xué)習(xí)筆記(4)URP渲染管線帶陰影PBR-Shader模板 -- 新增可自定義陰影顏色

    材質(zhì)面板截圖 功能實(shí)現(xiàn)(URP渲染管線下): 1、進(jìn)一步優(yōu)化Shader結(jié)構(gòu)和算法; 2、包含PBR材質(zhì); 3、投射和接收陰影,并升級(jí) 支持自定義陰影顏色 ; 4、支持點(diǎn)光源照射(但不支持點(diǎn)光源陰影)。 通用渲染截圖 自定義陰影顏色截圖 完整代碼: 寫在最后: 1、在我的上一篇文

    2024年02月12日
    瀏覽(27)
  • 20230620----重返學(xué)習(xí)-移動(dòng)端事件處理-響應(yīng)式

    移動(dòng)端的事件處理 移動(dòng)端事件處理 PC端主要以: 鼠標(biāo)事件 、 鍵盤事件 、 資源加載事件 、 動(dòng)畫事件 等事件為主。 其中 click 在 PC端 是 點(diǎn)擊事件 ! 移動(dòng)端主要以: 手指事件 ( 單手指 和 多手指 )、 資源加載事件 、 動(dòng)畫事件 等為主。 其中, click 在 移動(dòng)端 是 單擊事件

    2024年02月09日
    瀏覽(20)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包