zoukankan      html  css  js  c++  java
  • Vue 虚拟Dom 及 部分生命周期初探

    踏入前端,步入玄学

        17年底至18年初附带做了vue的一些框架搭建,中途断断续续用了部分vue,时隔几个月后的工作又拾起vue,对于一些原理性的知识淡忘了,正值这段时间使用中遇到了一些坑,又拨了部分代码出来温习温习。

          正式进入大前端,新同事称谓的玄学...

    读在最前面:

      1、本文根据问题,讲述大致 Vue虚拟Dom Diff 思路、数据响应式机制相关,源码版本 Vue.js v2.5.17-beta.0

           2、知识点:vue virtual dom diff、 observe 、 watch、render ,各知识点暂不深入剖析

      3、阅读本文,读者应了解Vue有一定的前端基础,知道一些名词概念如:render、vnode、virtual dom 

      4、本文先引出问题及原理,然后单用2个实现给出详情解析

      4、v-if案例解析(element-ui  form-item表单无法验证问题剖析 )(问1)

      5、watch案例解析(element-ui el-select 无法选中问题剖析)(问2)

    直接甩疑问案例,Fire!

    问1:当v-if为true时,会重新渲染相关dom节点吗?

    问题来源(element-ui  form-item表单无法验证问题剖析)

    <child v-if="true"><child>

    答:会!(什么??? 难道v-if 为true不应该一直坚强到最后吗,是什么让她如此虚弱)

    涉及 知识点 vue virtual dom diff 

    问题原理:

    前言:vue2 引入了虚拟dom,即如果使用.vue的模板方式书写,会首先编译为render函数,生成vnode,每次数据变更后,会在nexttick中进行新老vnode对比进行patch操作

    1、对比前后 vnode对象相同颜色的节点进行同层级对比 ,如下图

     

    2、我们挑选黄色节点说明:0、1、2标识节点序号 , 头、尾 分别代表节点的头部和节点的尾部

       对比序号从0、0(分别对应老节点序号0,新节点序号0)开始 ,每一次循环对比一个节点数据(使用sameVNode方法判断,见下面代码示例,源码5858行),新老对比范围为 ,老(0-2)新(0-2)

    (1)头头,即老0->新0 如果一致,则新老开始节点 序号 +1 同时,跳出循环 ,开始下一个循环对比 新老对比范围为 ,老(1-2)新(1-2)

    (2)尾尾,即老2->新2 如果一致,则新老结束节点 序号 -1 同时,跳出循环 ,开始下一个循环对比 新老对比范围为 ,老(0-1)新(0-1)

    (3)头尾,即老0->新2 如果一致,则老开始节点 序号 +1,新结束节点序号-1  同时,跳出循环 ,开始下一个循环对比 新老对比范围为 ,老(1-2)新(0-1)

    (4)尾头,即老2->新0 如果一致,则老结束节点 序号 -1 ,新开始节点序号+1  同时,跳出循环 ,开始下一个循环对比 新老对比范围为 ,老(0-1)新(1-2)

    (5)用新节点去未遍历过的的老节点中查找

    (6)老节点全部遍历完或新节点全部遍历完,则退出大循环(对未遍历到的老节点执行删除操作,对未遍历到的新节点执行新增操作

    function sameVnode (a, b) {
      return (
        a.key === b.key && (
          (
            a.tag === b.tag &&
            a.isComment === b.isComment &&
            isDef(a.data) === isDef(b.data) &&
            sameInputType(a, b)
          ) || (
            isTrue(a.isAsyncPlaceholder) &&
            a.asyncFactory === b.asyncFactory &&
            isUndef(b.asyncFactory.error)
          )
        )
      )
    }  

    大致如图:

     问2:子组件中明明有watch value,为什么this.$emit('input', 888);没有触发watch回调,反而在父组件data数据变化后触发回调?

    问题来源(element-ui el-select 无法选中问题剖析)

        <child v-model="b"></child> // b在父组件中没有初始化,即没有执行父组件的observe
        var child = Vue.extend({
          template: '<p>数据响应及render相关-案例说明</p>',
          props: {
            value: {
              required: true
            }
          },
          mounted() {
            this.$emit('input', 888);
          },
          watch: {
            value(val, oldVal) {
              console.log(val, oldVal, 'child')
            }
          }
        });

     涉及 知识点 observe、render、watch

    问题原理:

    1、vue实例在生成时,会经历比较漫长的初始化过程如:初始化事件、渲染器、注入器、数据代理劫持等等

    2、v-model指令为被默认构造 :value="b" @input="b = xxx" (此处b为上面代码中v-model="b"),执行this.$emit('input', 888),会更新b的值

    3、每次data数据变化的时候,会收集watcher进入一个队列,队列中收集了所有此时刻周期中有相关变化形成queueWatcher,在下一个时间周期中进行数据响应更新 flushSchedulerQueue,并执行render 、patch

    4、在render中会进行新老vnode patch,在更新过程中会执行node的prepatch(源码5938行)操作,其中会执行updateChildComponent -> validateProp 从而更新了 value的值,从而进入了watch回调

    大致如图:

        

     by:海豚湾-丰

       

  • 相关阅读:
    IE hasLayout详解
    seajs引入jquery
    jquery实现轮播插件
    CSS视觉格式化模型
    js事件冒泡和事件捕获详解
    你尽力了么===BY cloudsky
    前向否定界定符 python正则表达式不匹配某个字符串 以及无捕获组和命名组(转)
    php safe mode bypass all <转>
    WAF指纹探测及识别技术<freebuf>
    linux集群管理<转>
  • 原文地址:https://www.cnblogs.com/teamblog/p/9565233.html
Copyright © 2011-2022 走看看