核心概念:注入、虚拟DOM树、挂载

205 阅读4分钟

注入

把配置项里的一些成员放到vue实例里,这个过程叫注入。

这个过程是在Vue()构造函数里完成的。

通过new Vue({})创建了一个vue实例对象,这个实例对象里本身自带了一写成员,是vue框架给我们加的一些成员。

vue实例里的成员分成三部分:

  1. $开头(一些实用方法和属性,便于开发者使用)
  2. 下划线_开头 (vue内部使用的成员,不建议开发者使用)
  3. 从配置项中注入的成员 image.png 为什么vue自带的成员要用$或者_开头? 防止与注入的成员重名。

vue模版可以直接使用vue实例中的成员。 为什么能直接使用{{title}},因为vue实例里有title。

image.png

数据响应式:数据改变,模版重新渲染。 数据响应的过程是在注入之后才执行的。

虚拟DOM树

虚拟DOM是一个普通的js对象。用于描述界面上应该有什么。(此时页面上还没有)

举例:

var vnode = {
    tag: "h1",
    children: [
        {
            tag: undefined,
            text: "这是第一个vue应用",
        },
    ],
};
// 该对象描述了:有一个标签名为h1的节点,里面有一个子节点,子节点是一个文本节点,文本内容为‘这是第一个vue应用’

image.png 在vue模版里,这些都不是真正的dom元素,vue会把它当成字符串,会把它当成虚拟DOM。

假如直接操作真实的dom元素,就会严重的影响效率,因为真实的dom元素会触发浏览器的重排、回流,会触发重新渲染。

所以,为了提高效率,vue使用了虚拟DOM(vnode)的方式来描述要渲染的内容。

vue模版并不是真实的DOM,它会被编译为虚拟DOM

举例说明把vue模版编译为虚拟DOM的样子:

<div id="app">
    <h1>第一个vue应用:{{title}}</h1>
    <p>作者:{{author}}</p>
</div>
var vnode = {
    tag:'div',
    children:[
        {
            tag:'h1',
            children:[
                {
                    text:'第一个vue应用:Hello World'
                }
            ]
        },
        {
            tag:'p',
            children:[
                {
                    text:'作者:袁'
                }
            ]
        }
    ]
}

虚拟DOM树最终会生成真实的DOM树。

image.png 先生成虚拟DOM,再生成真实的节点。

当数据变化时,重新渲染页面,这个过程是怎样的? 当数据变化后,重新生成新的vnode tree(虚拟DOM),vue会比较新旧两棵vnode tree(虚拟DOM树),找出差异,然后仅把差异应用到真实DOM tree中

举例说明: image.png 把author改成‘进’,然后重新生成虚拟DOM,比较发现只有text有差异,然后仅把text应用到真实dom中。

可见,在vue中,要得到最终界面,必须要生成vnode tree,vue通过以下逻辑生成vnode tree:

虚拟DOM如何来的?

  • 是运行render函数的返回结果render(){},这个函数返回的结果就是它的虚拟DOM树。 举例:
<div id="app"></div>
const vm = new Vue({
    el:'#app',
    data:{
        title:'Hello World',
        author:'袁'
    },
    render(h){
        return h('div',[
            h('h1',`第一个vue应用:${this.title}`),
            h('p',`作者:${this.author}`)
        ])
    }
})

但是,直接使用render函数生成节点很麻烦,所以,vue给我们提供了模版,即<div id="app">xxx</div>,模版的作用只有一个,就是根据模版,生成render函数。也就是说,假如不写render函数,只写模版,vue会把模版编译成render函数,最终还是render。

具体逻辑如下:

image.png

  1. 先看render,有render就执行render,不看其他的;
  2. 如果没有render,则看template,有template,则把它编译成render;
  3. 如果没有template,则看el对应的元素,把它作为模版。把el元素的outerHTML作为模版。

总之,一定得有render,因为只有render能生成虚拟DOM,虚拟DOM才能渲染出真实DOM。

注意:虚拟节点必须是单根的。

挂载

将生成的真实的DOM树,放置到某个元素位置,称之为挂载。

挂载的两种方式:

  1. 通过el:'css选择器'进行挂载
  2. 通过vue实例.$mount("css选择器")进行挂载

完整流程

image.png

  1. 创建vue实例
  2. 把成员注入到实例里(注入完成后,有了数据响应式)
  3. 编译生成虚拟dom树
    • 如何编译?
      1. 先看render
      2. 无render则编译template,把它生成redner,再运行render
      3. 无template则编译模版,把它生成redner,再运行render
  4. 根据虚拟DOM树生成真实DOM树,生成完之后进行挂载5. 此时首次渲染完成
  5. 数据变化,会重新渲染
    • 如何重新渲染?
      1. 重新生成新的虚拟DOM树,即重新调用render
      2. 对比新旧虚拟DOM树的差异
      3. 将差异应用到真实DOM树中
  6. 此时二次渲染完成