详解function Vue的代码结构及逻辑

写了很多年的vue2,于是决定从源码的角度对vue2的理解写一下,即是为后面学习vue3打下坚实的基础,也为彻底告别vue2转向vue3做个纪念。

本文是该系列文章的第一篇,欢迎阅读。

vue结构

vue项目中,入口文件都会有一个实例化vue的逻辑:

var app = new Vue({ 
    el: '#app'
})

那么在实例化的过程中会发生什么呢?我们来看看Vue的构造函数:

import { initMixin } from './init'
import { stateMixin } from './state'
import { renderMixin } from './render'
import { eventsMixin } from './events'
import { lifecycleMixin } from './lifecycle'
import { warn } from '../util/index'

function Vue (options) {
  this._init(options)
}

initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)

export default Vue

它实际上就是一个用 Function 实现的类,我们只能通过 new Vue 去实例化它。

在引入import Vue from 'vue'时,它会执行如下函数:

initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)

这些函数的参数是vue的构造函数,它们的作用是往构造函数的原型上挂载一系列的方法和属性,下面对这些函数做个简要的介绍,留个印象为后面的文章打下基础。

初始化函数

  • initMixin
function initMixin (Vue) {
  Vue.prototype._init = function (options) {
    ...
  };
}

initMixinvue构造函数的原型上挂载了_init方法。因此,在vue构造函数内部可以执行this._init

  • stateMixin
function stateMixin (Vue) {
  ...
  var dataDef = {};
  dataDef.get = function () { return this._data };
  var propsDef = {};
  propsDef.get = function () { return this._props };
  
  Object.defineProperty(Vue.prototype, '$data', dataDef);
  Object.defineProperty(Vue.prototype, '$props', propsDef);

  Vue.prototype.$set = set;
  Vue.prototype.$delete = del;

  Vue.prototype.$watch = function () {
    ...
  };
}

stateMixinvue原型上挂载了$data$props属性,当访问this.$data的时候其实是在访问this._data,另外,this._data其实就是指向了我们定义的data() { return {} }数据。

同时,它还挂载了$set, $delete, $watch等方法。

在我们使用vue进行开发的过程中,可能会遇到一种情况:当生成vue实例后,当再次给数据赋值时,有时候并不会自动更新到视图上去。

data () {
  return {
    student: {
      name: '',
      sex: ''
    }
  }
}
mounted () { 
  this.student.age = 24  // 此时对age的修改,页面并不会响应。
}

这个时候就可以使用this.$set(this.student, 'age' , 24)

同样的道理,除了在组件的options中写watch监听函数外,我们还可以直接调用this.$watch进行监听。

  • eventsMixin
function eventsMixin (Vue) {
  Vue.prototype.$on = function (event, fn) {
     ...
  };
  Vue.prototype.$once = function (event, fn) {
     ...
  };
  Vue.prototype.$off = function (event, fn) {
      ...
  };
  Vue.prototype.$emit = function (event) {
      ...
  };
}

eventsMixin在原型上挂载了$on,$once,$off,$emit等方法用于事件监听,至于具体的逻辑后面会专门在讲事件的时候介绍,你只需要知道每个vue实例上都有这些方法。

在开发过程中,当你遇到要在页面间传递参数时,可以使用一个vue实例当做eventbus来传递参数。

  • lifecycleMixin
function lifecycleMixin (Vue) {
  Vue.prototype._update = function (vnode, hydrating) {
    ...
  };

  Vue.prototype.$forceUpdate = function () {
    ...
  };

  Vue.prototype.$destroy = function () {
    ...
  };
}

lifecycleMixin在原型上挂载了_update,$forceUpdate,$destroy三个方法,这三个方法是跟页面渲染相关的,后面会一一介绍。

  • renderMixin
function renderMixin (Vue) {
  Vue.prototype.$nextTick = function (fn) {
    return nextTick(fn, this)
  };

  Vue.prototype._render = function () {
    ...
  };
}

renderMixin在原型上挂载了$nextTick, _render方法,_render方法执行后会生成vnode$nextTick函数中的参数能保证在页面渲染之后才会执行,这里面涉及到异步渲染机制,后面也会讲。

_划线开头的函数,表示这个方法是给vue本身调用的,以$开头的函数表示开发者可以使用。

为何 Vue 不用 ES6 的 Class 去实现呢?

上面我看到有很多 xxxMixin 的函数调用,并把 Vue 当参数传入,它们的功能都是给 Vue 的 prototype 上扩展一些方法,Vue 按功能把这些扩展分散到多个模块中去实现,而不是在一个模块里实现所有,这种方式是用 Class 难以实现的。

这么做的好处是非常方便代码的维护和管理,这种编程技巧也非常值得我们去学习。

总结

Vue本身就是一个构造函数,因此只能通过new Vue的方式生成一个实例,这个实例就继承了Vue原型prototype上的方法和属性,这也是你可以使用this.$watch, this.$set, this.$nextTick等方法的原因,this就是指向这个vue实例。

有了这个vue实例后,页面上的dom节点都会通过这个实例来生成。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

哎,好难

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值