vue源码学习

146 阅读5分钟

****vue源码学习路径**** vue-js.com/learn-vue/s…

1、什么是MVVM框架?它适用于哪些场景?

(1)mvvm框架是什么?

MVVM是Model-View-ViewModel的简写
Model:模型
View:视图
ViewModel:视图模型,连接view和model的桥梁

通常要实现一个observer观察者,当数据发生变化,ViewModel能够监听到数据的这种变化,然后通知到对应的视图做自动更新,而当用户操作视图,ViewModel 也能监听到视图的变化,然后通知数据做改动,这实际上就实现了数据的双向绑定。

(2)它和其它框架(jquery)的区别是什么?

概念上:vue:前端js库,是一个精简的MVVM,它专注于MVVM模型的viewModel层,通过双向数据绑定把view和model层连接起来,通过对数据的操作就可以完成对页面视图的渲染; jquery:轻量级的js库,

在操作思想上: vue是使用数据驱动的方式,通过vue对象将数据和view完全分离开,对数据操 作,不在引用相应的DOM对象,通过vue对象,将数据和相应的DOM对象相互绑定起 来;主要是操作数据基于一种MVVM模式,jQuery是使用选择器($)选取DOM对象,并对其进行赋值、取值、事件绑定等 操作,主要是操作DOM

(3)哪些场景适合?

应用场景的区别:

 vue适用的场景:复杂数据操作的后台页面,表单填写页面;

jquery适用的场景:比如说一些html5的动画页面,一些需要js来操作页面样式的页面。

二者也是可以结合起来一起使用的,vue侧重数据绑定,jquery侧重样式操作, 动画效果等,则会更加高效率的完成业务

2、Vue的响应式原理(MVVM)深入解析

segmentfault.com/a/119000000…

3、vue 源码解读。(非常重要)

blog.csdn.net/qq\_3796989…

4、Vue源码解读之Dep,Observer和Watcher

www.cnblogs.com/datiangou/p…

www.jb51.net/article/107…

5、vue原理、vnode-源码版

1、VNode是什么及作用?

VNode 表示 虚拟节点 Virtual DOM,为什么叫虚拟节点呢,因为不是真的 DOM 节点。

他只是用 javascript 对象来描述真实 DOM,这么描述,把DOM标签,属性,内容都变成 对象的属性

就像用 JavaScript 对象描述一个人一样

{sex:'男', name:'神仙朱', salary:5000,children:null}

过程就是,把你的 template 模板 描述成 VNode,然后一系列操作之后通过 VNode 形成真实DOM进行挂载

a、VNode是什么

JavaScript 对象

b、VNode有什么作用?

1、兼容性强,不受执行环境的影响。VNode 因为是 JS 对象,不管 Node 还是 浏览器,都可以统一操作, 从而获得了服务端渲染、原生渲染、手写渲染函数等能力

2、减少操作 DOM。任何页面的变化,都只使用 VNode 进行操作对比,只需要在最后一步挂载更新DOM,不需要频繁操作DOM,从而提高页面性能

6、VNode怎么生成

在 Vue 源码中,vnode 是通过一个构造函数生成的,构造函数看起来挺简单的

本来以为很多内容,带着沉重的心情去探索,然后看到之后就放松了下来,看了一会,心情再次沉重了起来

其中涉及的内容还是挺多的....不然哪里来开篇的那么多问题

行了,看下 VNode 的构造函数

function VNode(
    tag, data, children, 
    text, elm, context, 
    componentOptions

) {    

    this.tag = tag; // 标签名

    this.data = data;    

    this.children = children; // 子元素

    this.text = text; // 文本内容

    this.elm = elm; // Dom 节点



    this.context = context;    

    this.componentOptions = componentOptions;    

    this.componentInstance = undefined;    

    this.parent = undefined;    

    this.isStatic = false; // 是否静态节点

    this.isComment = false; // 是否是注释节点

    this.isCloned = false; // 是否克隆节点

};

看完上面,先不要纠结都是什么东西,先来走一遍

比如我们使用 vnode 去描述这样一个template

<div class="parent" style="height:0" href="2222">
    111111

</div>

使用 VNode 构造函数就可以生成下面的 VNode

{    

    tag: 'div',    

    data: {        

        attrs:{href:"2222"}

        staticClass: "parent",        

        staticStyle: {            

            height: "0"

        }
    },    

    children: [{        

        tag: undefined,        

        text: "111111"

    }]
}

这个 JS 对象,就已经囊括了整个模板的所有信息,完全可以根据这个对象来构造真实DOM了

至于其中都是什么意思,请看下个问题

7、VNode存放什么信息

新建一个 vnode 的时候,包含了非常多的属性,每个属性都是节点的描述的一部分

我们只捡一些属性来探索一下,了解主体即可

普通属性

1、data

1、存储节点的属性,class,style 等

2、存储绑定的事件

3、....其他

2、elm

真实DOM 节点

生成VNode 的时候,并不存在真实 DOM

elm 会在需要创建DOM 时完成赋值,具体函数在 createElm 中

赋值语句就是一句(简化了源码)

vnode.elm = document.createElement(tag)

3、context

渲染这个模板的上下文对象

意思就是,template 里面的动态数据要从这个 context 中获取,而 context 就是 Vue 实例

如果是页面,那么context 就是本页面的实例,如果是组件,context则是组件的实例

4 isStatic

是否是静态节点

当一个节点被标记为静态节点的时候,说明这个节点可以不用去更新它了,当数据变化的时候,可以忽略去比对他,以提高比对效率

组件相关属性

1、parent

这个parent 表示是组件的外壳节点

额,什么是外壳节点,举个栗子先吧

1、存在这样一个组件 test

2、页面中使用这个组件

诶,到这里就有意思了,组件其实应有两种 VNode

这两种VNode 名义上都是对的,都有理,谁是正牌不好说

最后尤大判定第一个 VNode 是 第二个 VNode 的爸爸,也就是外壳节点

外壳节点通常是 父组件和 子组件的 关联,用于保存一些父组件传给子组件的数据 等

2 componentInstance

这个顾名思义,就是组件生成的实例,保存在这里

上面 test 组件的外壳节点中的 componentInstance

3 componentOptions

这个就存储一些 父子组件 PY 交易的证据

比如 props,事件,slot 什么的,打印看下

其中 children 保存的就是 slot,listeners 保存 事件,propsData 保存 props

8、VNode怎么生成

在初始化完选项,解析完模板之后,就需要挂载 DOM了。此时就需要生成 VNode,才能根据 VNode 生成 DOM 然后挂载

挂载 DOM 第一步,就是先执行渲染函数,得到整个模板的 VNode

比如有以下渲染函数,执行会返回 VNode,就是 _c 返回的VNode

function (){ 
    with(this){  
        return _c('div',{attrs:{"href":"xxxx"}},["1111"]).
    } 
}

渲染函数会绑定上下文对象,加上 with 的作用,_c 其实就是 vm._c

现在就来看 vm._c 是什么东西

vm._c = function(a, b, c, d) {    

    return createElement(vm, a, b, c, d, false);

};

function createElement(

    context, tag, data, 

    children, normalizationType

) {    

    var vnode;    

    if (tag是正常html标签) {

        vnode = new VNode(

            tag, data, children, undefined, 

            undefined, context

        );
    } 
    else if (tag 是组件) {
        vnode = createComponent(

            Ctor, data, context, 

            children, tag

        );

    }    

    return vnode

}

我们可以看到,正常标签 和 组件会走不同流程

1 、正常标签

比如有这样一个正常标签模板

<div href="xxxxx">1111</div>

解析成渲染函数如下

function (){    

    with(this){  

        return _c('div',{

            attrs:{"href":"xxxx"}},

            ["1111"]

        )

    }
}

看上面_c 源码,可以知道经过 _c 把参数传导,这样去构建 VNode

new VNode(tag, data, children, undefined, undefined, context);

这样就保存了 tag,data,children 和 context

2、组件

比如页面使用了test组件

<div>    <test :name="name">1111</test>  </div>

解析成渲染函数如下

with(this){  
    return _c('div',[
        _c('test',
            {attrs:{"name":name}},
            ["1111"]
        )
    ],1)
}

看上面 _c 代码知道 ,_c 会先调用 createComponent

createComponent(Ctor, data, context, children, tag);}

createComponent 中也会调用 VNode 构造函数,生成VNode 并返回

function createComponent(

    Ctor, data, context, 

    children, tag

) {    

    // extractPropsFromVNodeData 作用是把传入data的 attr 中属于 props的筛选出来
    var propsData = extractPropsFromVNodeData(data, Ctor, tag);    


    var vnode = new VNode(

        ("vue-component-" + (Ctor.cid) + tag),
        data, undefined, undefined, undefined,

        context, {            

            Ctor: Ctor, 

            // 父组件给子组件绑定的props
            propsData: propsData, 

            // 父组件给子组件绑定的事件
            listeners: listeners, 

            tag: tag,            

            children: children

        });    

    return vnode

}

9、VNode存放在哪里

主要是三个位置存了 vnode,分别是

parent ,_vnode ,$vnode

parent 上面已经说过,就先不提了,剩下两个全部是挂在 Vue 实例一级属性上的

打印一下组件的实例,可以很清楚看到这两个属性

下面来说说这两个东西

1、_vnode

_vnode 存放表示当前节点的 VNode

什么叫当前,也就是可以通过这个VNode 直接映射成 当前真实DOM

他的作用是什么呢?

用来比对更新,比如你的数据变化了,此时会生成一个新的 VNode,然后再拿到保存的_vnode 对比,就可以得到最小区域,从而只用更新这部分

所以, _vnode 存放的可以说是当前节点,也可以说是旧节点

另外,_vnode 中保存有一个 parent,这个parent 就是外壳节点,上面说 vnode 的时候已经说过了

在哪里赋值?

我们来完整地走一遍流程,涉及源码很多,但是我已经非常精简了,大概了解个流程

function Vue() {
    ...初始化组件选项等
    mountComponent()

}

function mountComponent() {

    ....解析模板,生成渲染函数
   
    // 用于生成VNode,生成DOM,挂载DOM
    updateComponent = function() {
        vm._update(vm._render());
    };    

    // 新建 watcher,保存updateComponent为更新函数,新建的时候会立即执行一遍
    new Watcher(vm, updateComponent)
}

function Watcher(vm, expOrFn) {    

    this.getter = expOrFn ;    

    this.getter()

}

// 执行前面解析得到的渲染函数,返回生成的 VNode
Vue.prototype._render = () {}

// 根据vnode,生成DOM 挂载
Vue.prototype._update = function(vnode) {    

    var prevVnode = vm._vnode;

    vm._vnode = vnode;    

    if (不存在旧节点) { ...使用vnode创建DOM并直接挂载 }    

    else { ...存在旧节点,开始比对旧节点和新节点,然后创建DOM并挂载 }

}

2、$vnode

vnode只有组件实例才有,因为vnode 只有组件实例才有,因为 vnode 存放的是外壳节点,页面实例中是不存在 $vnode 的

本来也想走下流程的,无奈兜兜转转太多,涉及源码更多

在哪里进行赋值?

我就放最后一步 updateChildComponent

updateChildComponent 会在上个 _vnode 提到的 vm._update 执行流程中调用

function updateChildComponent(
    vm, parentVnode

) {

    vm.$options._parentVnode = parentVnode;
    vm.$vnode = parentVnode; 
    if (vm._vnode) {
        vm._vnode.parent = parentVnode;
    }
}

10、vue源码-nextTick

segmentfault.com/a/119000002…

www.cnblogs.com/tugenhua070…

a、什么是vue.nextTick();

官方文档解释为:在下次DOM更新循环结束之后执行的延迟回调。在修改数据之后立即使用该方法,获取更新后的DOM。

我们也可以简单的理解为:当页面中的数据发生改变了,就会把该任务放到一个异步队列中,只有在当前任务空闲时才会进行DOM渲染,当DOM渲染完成以后,该函数就会自动执行。

b、2.1 更改数据后,进行节点DOM操作。

比如修改数据、修改节点样式、等操作。比如说我修改data中的一个属性数据后,如果我这个时候直接获取该html内容的话,它还是老数据的,那么此时此刻,我们可以使用 Vue.nextTick(), 在该函数内部获取该数据即可: 如下代码:

<!DOCTYPE html>
<html>
<head>
  <title>vue.nextTick()方法的使用</title>
  <meta charset="utf-8">
  <script type="text/javascript" src="https://cn.vuejs.org/js/vue.js"></script>
</head>
<body>
  <div id="app">
    <template>
      <div ref="list">{{name}}</div>
    </template>
  </div>
  <script type="text/javascript">
    new Vue({
      el: '#app',
      data: {
        name: 'kongzhi111'
      },
      mounted() {
        this.updateData();
      },
      methods: {
        updateData() {
          this.name = 'kongzhi222';
          console.log(this.$refs.list.textContent); // 打印 kongzhi111
          this.$nextTick(() => {
            console.log('-------');
            console.log(this.$refs.list.textContent); // 打印 kongzhi222
          });
        }
      }
    })
  </script>
</body>
</html>

如上代码,页面初始化时候,页面显示的是 "kongzhi111"; 当页面中的所有的DOM更新完成后,我在mounted()生命周期中调用 updateData()方法,然后在该方法内部修改 this.name 这个数据,再打印 this.𝑟𝑒𝑓𝑠.𝑙𝑖𝑠𝑡.𝑡𝑒𝑥𝑡𝐶𝑜𝑛𝑡𝑒𝑛𝑡,可以看到打印的数据还是′𝑘𝑜𝑛𝑔𝑧ℎ𝑖111′;为什么会是这样呢?那是因为修改𝑛𝑎𝑚𝑒数据后,我们的𝐷𝑂𝑀还没有被渲染完成,所以我们这个时候获取的值还是之前的值,但是我们放在𝑛𝑒𝑥𝑡𝑇𝑖𝑐𝑘函数里面的时候,代码会在𝐷𝑂𝑀更新完成后会自动执行𝑛𝑒𝑥𝑡𝑇𝑖𝑐𝑘()函数,因此这个时候我们再去使用𝑡ℎ𝑖𝑠.refs.list.textContent 获取该值的时候,就可以获取到最新值了。

c、2.2 在created生命周期中进行DOM操作。

在Vue生命周期中,只有在mounted生命周期中我们的HTML才渲染完成,因此在该生命周期中,我们就可以获取到页面中的html DOM节点,但是如果我们在 created生命周期中是访问不到DOM节点的。
在该生命周期中我们想要获取DOM节点的话,我们需要使用 this.$nextTick() 函数。

比如如下代码进行演示:

<!DOCTYPE html>
<html>
<head>
  <title>vue.nextTick()方法的使用</title>
  <meta charset="utf-8">
  <script type="text/javascript" src="https://cn.vuejs.org/js/vue.js"></script>
</head>
<body>
  <div id="app">
    <template>
      <div ref="list">{{name}}</div>
    </template>
  </div>
  <script type="text/javascript">
    new Vue({
      el: '#app',
      data: {
        name: 'kongzhi111'
      },
      created() {
        console.log(this.$refs.list); // 打印undefined
        this.$nextTick(() => {
          console.log(this.$refs.list); // 打印出 "<div>kongzhi111</div>"
        });
      },
      methods: {
        
      }
    })
  </script>
</body>
</html>

如上代码,在created生命周期内,我们打印 this.𝑟𝑒𝑓𝑠.𝑙𝑖𝑠𝑡值为𝑢𝑛𝑑𝑒𝑓𝑖𝑛𝑒𝑑,那是因为在𝑐𝑟𝑒𝑎𝑡𝑒𝑑生命周期内页面的ℎ𝑡𝑚𝑙没有被渲染完成,因此打印出为𝑢𝑛𝑑𝑒𝑓𝑖𝑛𝑒𝑑;但是我们把它放入𝑡ℎ𝑖𝑠.nextTick函数内即可 打印出值出来,这也印证了 nextTick 是在下次DOM更新循环结束之后执行的延迟回调。因此只有DOM渲染完成后才会自动执行的延迟回调函数。

Vue的特点之一就是能实现响应式,但数据更新时,DOM不会立即更新,而是放入一个异步队列中,因此如果在我们的业务场景中,需要在DOM更新之后执行一段代码时,这个时候我们可以使用 this.$nextTick() 函数来实现。

d、 Vue.nextTick的调用方式如下:

Vue.nextTick([callback, context]) 和 vm.$nextTick([callback]);

Vue.nextTick([callback, context]); 该方法是全局方法,该方法可接收2个参数,分别为回调函数 和 执行回调函数的上下文环境。

vm.$nextTick([callback]): 该方法是实列方法,执行时自动绑定this到当前的实列上。

e、vm.$nextTick 与 setTimeout 的区别是什么?

<!DOCTYPE html>
<html>
<head>
  <title>vue.nextTick()方法的使用</title>
  <meta charset="utf-8">
  <script type="text/javascript" src="https://cn.vuejs.org/js/vue.js"></script>
</head>
<body>
  <div id="app">
    <template>
      <div ref="list">{{name}}</div>
    </template>
  </div>
  <script type="text/javascript">
    new Vue({
      el: '#app',
      data: {
        name: 'kongzhi111'
      },
      created() {
        console.log(this.$refs.list); // 打印undefined
        setTimeout(() => {
          console.log(this.$refs.list); // 打印出 "<div>kongzhi111</div>"
        }, 0);
      }
    })
  </script>
</body>
</html>

如上代码,我们不使用 nextTick, 我们使用setTimeout延迟也一样可以获取页面中的HTML元素的,那么他们俩之间到底有什么区别呢?

通过看vue源码我们知道,nextTick 源码在 src/core/util/next-tick.js 里面。在vue中使用了三种情况来延迟调用该函数,首先我们会判断我们的设备是否支持Promise对象,如果支持的话,会使用 Promise.then 来做延迟调用函数。如果设备不支持Promise对象,再判断是否支持 MutationObserver 对象,如果支持该对象,就使用MutationObserver来做延迟,最后如果上面两种都不支持的话,我们会使用 setTimeout(() => {}, 0); setTimeout 来做延迟操作。

在比较 nextTick 与 setTimeout 的区别,其实我们可以比较 promise 或 MutationObserver 对象 与 setTimeout的区别的了,因为nextTick会先判断设备是否支持promise及MutationObserver 对象的,只要我们弄懂 promise 和 setTimeout的区别,也就弄明白 nextTick 与 setTimeout的区别了。

在比较promise与setTimeout之前,我们先来看如下demo。

<!DOCTYPE html>
<html>
<head>
  <title></title>
  <meta charset="utf-8">
</head>
<body>
  <script type="text/javascript">
    console.log(1);
    setTimeout(function(){
      console.log(2);
    }, 0);
    new Promise(function(resolve) {
      console.log(3);
      for (var i = 0; i < 100; i++) {
        i === 99 && resolve();
      }
      console.log(4);
    }).then(function() {
      console.log(5);
    });
    console.log(6);
  </script>
</body>
</html>

如上代码输出的结果是:1, 3, 4, 6, 5, 2; 首先打印1,这个我们能理解的,其实为什么打印3,在promise内部也属于同步的,只有在then内是异步的,因此打印 1, 3, 4 , 然后执行then函数是异步的,因此打印6. 那么结果为什么是 1, 3, 4, 6, 5, 2 呢? 为什么不是 1, 3, 4, 6, 2, 5呢?

我们都知道 Promise.then 和 setTimeout 都是异步的,那么在事件队列中Promise.then的事件应该是在setTimeout的后面的,那么为什么Promise.then比setTimeout函数先执行呢?

理解Event Loop 的概念

我们都明白,javascript是单线程的,所有的任务都会在主线程中执行的,当主线程中的任务都执行完成之后,系统会 "依次" 读取任务队列里面的事件,因此对应的异步任务进入主线程,开始执行。

但是异步任务队列又分为: macrotasks(宏任务) 和 microtasks(微任务)。 他们两者分别有如下API:

macrotasks(宏任务): setTimeout、setInterval、setImmediate、I/O、UI rendering 等。

microtasks(微任务): Promise、process.nextTick、MutationObserver 等。

如上我们的promise的then方法的函数会被推入到 microtasks(微任务) 队列中,而setTimeout函数会被推入到 macrotasks(宏任务) 任务队列中,在每一次事件循环中 macrotasks(宏任务) 只会提取一个执行,而 microtasks(微任务) 会一直提取,直到 microtasks(微任务)队列为空为止。

也就是说,如果某个 microtasks(微任务) 被推入到执行中,那么当主线程任务执行完成后,会循环调用该队列任务中的下一个任务来执行,直到该任务队列到最后一个任务为止。而事件循环每次只会入栈一个 macrotasks(宏任务), 主线程执行完成该任务后又会循环检查 microtasks(微任务) 队列是否还有未执行的,直到所有的执行完成后,再执行 macrotasks(宏任务)。 依次循环,直到所有的异步任务完成为止。

有了上面 macrotasks(宏任务) 和 microtasks(微任务) 概念后,我们再来理解上面的代码,上面所有的代码都写在script标签中,那么读取script标签中的所有代码,它就是第一个宏任务,因此我们就开始执行第一个宏任务。因此首先打印 1, 然后代码往下读取,我们遇到setTimeout, 它就是第二个宏任务,会将它推入到 macrotasks(宏任务) 事件队列里面排队。
下面我们继续往下读取,
遇到Promise对象,在Promise内部执行它是同步的,因此会打印3, 4。 然后继续遇到 Promise.then 回调函数,他是一个 microtasks(微任务)的,因此将他 推入到 microtasks(微任务) 事件队列中,最后代码执行 console.log(6); 因此打印6. 第一个macrotasks(宏任务)执行完成后,然后我们会依次循环执行 microtasks(微任务), 直到最后一个为止,因此我们就执行 promise.then() 异步回调中的代码,因此打印5,那么此时此刻第一个 macrotasks(宏任务) 执行完毕,会执行下一个 macrotasks(宏任务)任务。因此就执行到 setTimeout函数了,最后就打印2。到此,所有的任务都执行完毕。因此我们最后的结果为:1, 3, 4, 6, 5, 2;

我们可以继续多添加几个setTimeout函数和多加几个Promise对象来验证下,如下代码:

<script type="text/javascript">
  console.log(1);
  setTimeout(function(){
    console.log(2);
  }, 10);
  new Promise(function(resolve) {
    console.log(3);
    for (var i = 0; i < 10000; i++) {
      i === 9999 && resolve();
    }
    console.log(4);
  }).then(function() {
    console.log(5);
  });
  setTimeout(function(){
    console.log(7);
  },1);
  new Promise(function(resolve) {
    console.log(8);
    resolve();
  }).then(function(){
    console.log(9);
  });
  console.log(6);
</script>

如上打印的结果为: 1, 3, 4, 8, 6, 5, 9, 7, 2;

首先打印1,这是没有任何争议的哦,promise内部也是同步代码,因此打印 3, 4, 然后就是第二个promise内部代码,因此打印8,再打印外面的代码,就是6。因此主线程执行完成后,打印的结果分别为:

1, 3, 4, 8, 6。 然后再执行 promise.then() 回调的 microtasks(微任务)。因此打印 5, 9。因此microtasks(微任务)执行完成后,就执行第二个宏任务setTimeout,由于第一个setTimeout是10毫秒后执行,第二个setTimeout是1毫秒后执行,因此1毫秒的优先级大于10毫秒的优先级,因此最后分别打印 7, 2 了。因此打印的结果是: 1, 3, 4, 8, 6, 5, 9, 7, 2;
总结: 如上我们也看到 microtasks(微任务) 包括 Promise 和 MutationObserver, 因此 我们可以知道在Vue中的nextTick 的执行速度上是快于setTimeout的。

我们从如下demo也可以得到验证:

<!DOCTYPE html>
<html>
<head>
  <title>vue.nextTick()方法的使用</title>
  <meta charset="utf-8">
  <script type="text/javascript" src="https://cn.vuejs.org/js/vue.js"></script>
</head>
<body>
  <div id="app">
    <template>
      <div ref="list">{{name}}</div>
    </template>
  </div>
  <script type="text/javascript">
    new Vue({
      el: '#app',
      data: {
        name: 'kongzhi111'
      },
      created() {
        console.log(this.$refs.list); // 打印undefined
        setTimeout(() => {
          console.log(this.$refs.list); // 打印出 "<div>kongzhi111</div>"
        }, 0);
        this.$nextTick(function(){
          console.log('nextTick比setTimeout先执行');
        });
      }
    })
  </script>
</body>
</html>

如上代码,先打印的是 undefiend, 其次是打印 "nextTick比setTimeout先执行" 信息, 最后打印出 "

kongzhi111
" 信息。

f、理解 MutationObserver

www.cnblogs.com/tugenhua070…

g、nextTick源码分析

7、详解vue的diff算法

juejin.cn/post/684490…

8、

Vue 的核心思想:组件化。 这一部分是关于构建组件树,形成虚拟 dom ,以及非常重要的 patch 方法。 

 再来亿遍:

a、 原因:当修改某条数据的时候,这时候js会将整个DOM Tree进行替换,这种操作是相当消耗性能的。所以在Vue中引入了Vnode的概念:Vnode是对真实DOM节点的模拟,可以对Vnode Tree进行增加节点、删除节点和修改节点操作。这些过程都只需要操作VNode Tree,不需要操作真实的DOM,大大的提升了性能。修改之后使用diff算法计算出修改的最小单位,在将这些小单位的视图进行更新。

 b、原理:data中定义了一个变量a,并且模板中也使用了它,那么这里生成的Watcher就会加入到a的订阅者列表中。当a发生改变时,对应的订阅者收到变动信息,这时候就会触发Watcher的update方法,实际update最后调用的就是在这里声明的updateComponent。 当数据发生改变时会触发回调函数updateComponent,updateComponent是对patch过程的封装。patch的本质是将新旧vnode进行比较,创建、删除或者更新DOM节点/组件实例。

9、Vue 源码patch过程详解

juejin.cn/post/684490…

10、Q: vue自带的内置组件有什么?A: Vue中内置的组件有以下几种:

a、component

 component组件:有两个属性—is inline-template 渲染一个‘元组件’为动态组件,按照’is’特性的值来渲染成那个组件 

b、 transition

 transition组件:为组件的载入和切换提供动画效果,具有非常强的可定制性,支持16个属性和12个事件

c、 transition-group

 transition-group:作为多个元素/组件的过渡效果 

d、 keep-alive

keep-alive:包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们

e、slot

slot:作为组件模板之中的内容分发插槽,slot元素自身将被替换

10、vue的编译过程

www.shangdixinxi.com/detail-1105…