vue源码解读(一) -- Vue的基本概念

281 阅读4分钟

Vue中 options 和 instance 的理解

var vm = new Vue(options)

在Vue实例化的过程中,传入的值称为 options, 生成的对象 vm 称为 instance,在Vue的官网上有 options 与 instance 的介绍,点击跳转,Vue实例代理了对 options 对象的访问,比如:

  • vm.$data 代理了对 options 中 data 属性的访问
  • vm.$props 代理了对 options 中 props 属性的访问

Vue中 $ 开头的属性和 _(下划线)开头的属性

Vue中有很多以 $ 开头和以 _ (下划线)的属性,其中以 $ 开头的属性让开发者使用,以_下划线开头的为内置属性,不建议开发者使用

  • vm.$ 等价于 vm
  • vm.$data.name 等价于 vm.name
let vm = new Vue({ 
    el: "#app", 
    data:{ name: 'XiongHe' }, 
}) 
vm.$data.name 
vm.name
vm._data.name
// XiongHe 读取实例属性 vm.name 
// XiongHe 读取 data 数据 vm._data.name
// XiongHe

Vue选项options

var vm = new Vue({
      // 数据
      data: "Vue实例的数据对象",
      props: "用于接收来自父组件的数据,props 可以是数组或对象",
      propsData: "创建实例时传递 props,主要作用是方便测试",
      computed: "计算属性,结果会被缓存",
      methods: "方法,可以通过 VM 实例访问",
      watch: "一个对象,键是需要观察的表达式,值是对应回调函数,Vue 实例将会在实例化时"
              "调用$watch(),遍历 watch 对象的每一个 property"
      // DOM
      el: "提供一个在页面上已存在的 DOM 元素作为 Vue 实例的挂载目标",
      template: "可以替换挂载元素的字符串模板",
      render: "渲染函数,字符串模板的替代方案",
      renderError: "仅用于开发环境,在render()出现错误时,提供另外的渲染输出"
      // 生命周期钩子
      beforeCreate: "发生在Vue实例初始化之后,data、observer、event/watcher事件被配置之前执行",
      created: "发生在Vue实例初始化以及data、observer、event/watcher事件被配置之后",
      beforeMount: "在挂载开始之前被调用:相关的 render 函数首次被调用",
      mounted: "实例被挂载后调用,这时 el 被新创建的 vm.$el 替换了",
      beforeUpdate: "在数据发生改变后,DOM 被更新之前被调用",
      updated: "在数据更改导致的虚拟 DOM 重新渲染和更新完毕之后被调用",
      activated: "被 keep-alive 缓存的组件激活时调用",
      deactivated: "被 keep-alive 缓存的组件失活时调用",
      beforeDestory: "实例销毁之前调用。在这一步,实例仍然完全可用",
      destoryed: "实例销毁后调用"
      errorCaptured: "在捕获一个来自后代组件的错误时被调用"
      // 资源
      components: "包含 Vue 实例可用组件的哈希表"
      directives: "包含 Vue 实例可用指令的哈希表",
      filters: "包含 Vue 实例可用过滤器的哈希表"
      // 组合
      mixins: "将属性混入Vue实例对象,并在Vue自身实例对象的属性被调用之前得到执行",
      parent: "指定已创建的实例之父实例,在两者之间建立父子关系"
              "子实例可以用 this.$parent 访问父实例,"
              "子实例被推入父实例的 $children 数组中"
      extends: "允许声明扩展另一个组件",
      provide/inject: "这对选项需要一起使用,用来向所有子组件注入依赖"
      // 其他
  }})

Vue实例 Instance

Vue 实例自带的原生属性与方法,带前缀 $,以便与用户定义的属性区分开来,以下介绍几个源码中常见的属性:

Vue Instance 属性

1、vm.$el

vm 挂载的 DOM 节点,写法有三种:

el: '#app' 
el: 'div' 
el: document.getElementById('app') 
// el 实例的value值和HTML模板建立关联,实现事件、属性和HTML模板的绑定 
<div id="app"></div>
2、vm.$options
<div id="app">
    <button @click="myClick()"></button>
</div>
let vm = new Vue({ 
    el: "#app", 
    customOption: 'foo', 
    methods: { 
        myClick() { console.log(vm.$options) }, 
        myClick2() { console.log('this is myClick2') } 
    } 
})

Vue 实例的创建通过 new Vue(options) 来实现,options 是创建 Vue 实例传递的参数,它是一个对象,包含 Vue 实例的所有属性(包含原生属性)和方法(包含原生方法),console.log(vm.$options)打印结果如下:

image.png

3、vm.parentvm.parent、vm.children、vm.$root
<根组件>
    <子组件>
        <孙组件>
            .....
        <孙组件>
    <子组件>
<根组件>

根实例的访问:$root

父组件的访问:$parent $parent.$parent

子组件的访问:$children

4、vm.$Props

当前组件接收到的 props 对象,Vue 实例代理了对其 props 对象 property 的访问

5、vm.$data

Vue 实例观察的数据对象,Vue 实例代理了对其 data 对象 property 的访问

6、vm.$slot

当前组件接受的 props 对象

7、vm.$refs

注册过 ref 的 DOM 元素或者组件实例

Vue Instance 方法

1、vm.$watch

vm.$watch 功能上和 Vue 实例上的 watch 方法相同

<div id="app"> {{ count }} </div> 
const vm = new Vue({ 
    el: "#app", 
    data: { count: 0 }, 
    watch: { 
        count(newValue,oldValue){ 
        console.log(`${oldValue} : ${newValue}`); 
         } 
     }, 
 }) 
 setInterval(() => { 
     vm.$data.count += 1; 
 }, 1000) 
//count : 监听的属性值 
//newValue: 变化之后count的值 
//oldValue: 变化之前count的值 
vm.$watch('count', (newValue,oldValue) => { 
    console.log(`${oldValue} : ${newValue}`); 
}) 
//上述vm.$watch实现方法和 vm实例中watch中实现的功能相同

不同点:

vm 中 watch 方法中对 count 的监听在 vm 实例销毁的时候自动移除。

通过 vm.$watch 添加的监听,需要主动移除监听。

2、vm.$set

vm.$set 是给响应式的对象(基本都是 Vue 实例中的 data 对象)添加一个新的属性,并且这个新的属性也是响应式的

//obj是响应式对象,obj.count每次改变,都会重新渲染 
const vm = new Vue({ 
    el: "#app", 
    template: `<div>{{ obj.count }}</div>`, 
    data: { obj:{ count: 0 } }
}) 
let num = 1; 
setInterval(() => { 
    num++; 
    vm.$set(vm.obj, 'count', num); 
}, 1000)
3、vm.$delete

删除对象属性。响应式对象删除的时候会更新视图

4、vm.emitvm.emit、vm.on、vm.$off

vm.$emit 触发一个自定义事件

vm.$on 监听当前实例上的自定义事件

vm.$off 移除自定义事件监听器

vm.$on('test', function (msg) { 
    console.log(msg)
}) 
vm.$emit('test', 'hi')

Vue Instance 生命周期

1、vm.$mount

手动地挂载一个未挂载的实例到元素上

new Vue({ 
    data: { 
        msg: 'hello vue' 
    } 
}).$mount('#app')
2、vm.$forceUpdate

迫使 Vue 实例重新渲染

3、vm.$nextTick

将回调延迟到下次 DOM 更新循环之后执行

4、vm.destory

完全销毁一个实例

Vue实例和Vue的实例

Vue 实例包括 Vue 实例和 Vue 的实例(Vue 组件实例)

Vue实例

在 Vue 的项目中,只有一个 Vue 实例在 main.js 文件中定义

new Vue({ 
    el:'#app', 
    data:{ 
        msg:'hello world' 
    } 
})

Vue的实例

Vue 是由一个个实例构建而成,每一个 Vue 的组件都是 Vue 的实例,任何一个 Vue 项目都是由很多个 Vue 实例组成,上文提到的vm.$parent、vm.$children、vm.$root,都是Vue 的实例

Vue中的数据代理

定义: 通过vm对象来代理 data 对象中属性的操作(读/写)

优势: 更加方便的操作 data 中的数据

原理:

  • 通过 Object.defineProperty() 把 data 对象中所有属性添加到 vm 上
  • 为每一个添加到 vm 上的属性,都指定一个 getter/setter
  • getter/setter 内部去操作(读/写)data 中对应的属性

Object.defineProperty(object, propertyname, descriptor)

  • object:必须,要在其上添加或修改属性的对象
  • propertyname:必需。 一个包含属性名称的字符串
  • descriptor: 属性描述符。 它可以针对数据属性或访问器属性

descriptor:有以下参数值

  • value: 该属性的值,默认为 undefined
  • writable:  该属性是否可写,默认为 false
  • configurable: 控制了该属性是否可被改变、是否可删除,默认为 false
  • enumerable: 该属性是否可枚举,默认为 false
  • get: 给属性提供 getter 的方法,若是没有 getter 则为 undefined
  • set: 给属性提供 setter 的方法,若是没有 setter 则为 undefined
let obj = { x: 100 } 
let obj2 = { y: 200 } 
Object.defineProperty(obj2, 'z', { 
    get() { return obj.x }, 
    set(value) { obj.x = value + 1001 },
}) 
obj2.z = 100 
obj 
// { x: 1101 }

以上是一个简单的数据代理实现,通过 obj2 可以对 obj1 的属性值进行操作

let vm = new Vue({ 
    el: "#app", 
    customOption: 'foo', 
    data:{ 
        name: 'XiongHe', 
        gender: '男', 
        msg: '程序员' 
    }, 
    methods: { 
        myClick() { 
            console.log(vm) 
        }, 
        myClick2() { 
            console.log('') 
        } 
    } 
})

在控制台打印 vm.$data 对象,可以看到 data 对象中的属性都做了代理,也就是通过getter/setter方式访问,通过Object.defineProperty()方法为每个属性添加了getter/setter方法,在读取 data 中的属性时,也是通过getter/setter方法访问_data,通过 _data 对象操作 data 对象

image.png

在 vm 实例中,不仅 data 对象被代理到 vm 实例,还有很多其他对象也被代理到 vm 实例上,如下图:

image.png