- 1.1 Vue的引入
- 1.1.1 基础使用
- 1.1.2 Vue构造器
- 1.1.3 定义原型属性方法
- 1.1.4 定义静态属性方法
1.1 Vue的引入
Vue的使用按照官方的说法支持CDN和NPM两种方式,CDN的方式是以script的方式将打包好的vue.js引入页面脚本中,而NPM的方式是和诸如 webpack 或 Browserify 模块打包器配置使用,以npm install vue的方式引入,这也是我们开发应用的主要形式。而从单纯分析源码思路和实现细节的角度来讲,打包后的vue.js在分析和提炼源码方面会更加方便,所以这个系列的源码分析,使用的是打包后的vue脚本,版本号是v2.6.8
1.1.1 基础使用
分析的开始当然是vue的基础使用,我们引入了vue.js并且new了一个Vue实例,并将它挂载到#app上,这是最基础的用法。
<div id="app"></div><script src="https://cdn.jsdelivr.net/npm/vue@2.6.8/dist/vue.js"></script><script>var vm = new Vue({el: '#app',data: {message: '选项合并'},})</script>
虽然这一节的重点是阐述Vue的选项配置,从选项配置入手也是我们从零开始品读源码最容易开始的思路,但是为了分析的完整性,避免后续出现未知的概念,有必要先大致了解一下vue在脚本引入之后分别做了什么。
1.1.2 Vue构造器
打包后的源码是遵从UMD规范的,它是commonjs和amd的整合。而Vue的本质是一个构造器,并且它保证了只能通过new实例的形式去调用,而不能直接通过函数的形式使用。
(function (global, factory) {// 遵循UMD规范typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :typeof define === 'function' && define.amd ? define(factory) :(global = global || self, global.Vue = factory());}(this, function () { 'use strict';···// Vue 构造函数function Vue (options) {// 保证了无法直接通过Vue()去调用,只能通过new的方式去创建实例if (!(this instanceof Vue)) {warn('Vue is a constructor and should be called with the `new` keyword');}this._init(options);}return Vue})
1.1.3 定义原型属性方法
Vue之所以能适应基础的开发场景,除了经常提到的支持组件化开发,以及完善的响应式系统等外,还有重要的一点是它提供了丰富的api方法,不管是静态还是原型方法,它们都丰富到足以满足我们日常基础的开发需求。所以熟练阅读vue-api文档并精准使用api方法是迈向熟练开发的前提。接下来我们看看这些方法属性是在哪里定义的,注意,该小节会忽略大部分属性方法具体的实现,这些详细的细节会贯穿在后续系列的分析中。
首先是原型上的属性方法,在构造函数的定义之后,有这样五个函数,他们分别针对不同场景定义了Vue原型上的属性和方法。
// 定义Vue原型上的init方法(内部方法)initMixin(Vue);// 定义原型上跟数据相关的属性方法stateMixin(Vue);//定义原型上跟事件相关的属性方法eventsMixin(Vue);// 定义原型上跟生命周期相关的方法lifecycleMixin(Vue);// 定义渲染相关的函数renderMixin(Vue);
我们一个个看,首先initMixin定义了内部在实例化Vue时会执行的初始化代码,它是一个内部使用的方法。
function initMixin (Vue) {Vue.prototype._init = function (options) {}}
stateMixin方法会定义跟数据相关的属性方法,例如代理数据的访问,我们可以在实例上通过this.$data和this.$props访问到data,props的值,并且也定义了使用频率较高的this.$set,this.$delte等方法。
function stateMixin (Vue) {var dataDef = {};dataDef.get = function () { return this._data };var propsDef = {};propsDef.get = function () { return this._props };{dataDef.set = function () {warn('Avoid replacing instance root $data. ' +'Use nested data properties instead.',this);};propsDef.set = function () {warn("$props is readonly.", this);};}// 代理了_data,_props的访问Object.defineProperty(Vue.prototype, '$data', dataDef);Object.defineProperty(Vue.prototype, '$props', propsDef);// $set, $delVue.prototype.$set = set;Vue.prototype.$delete = del;// $watchVue.prototype.$watch = function (expOrFn,cb,options) {};}
eventsMixin会对原型上的事件相关方法做定义,文档中提到的vm.$on,vm.$once,vm.$off,vm.$emit也就是在这里定义的。
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, fn) {}
lifecycleMixin,renderMixin两个都可以算是对生命周期渲染方法的定义,例如$forceUpdate触发实例的强制刷新,$nextTick将回调延迟到下次 DOM 更新循环之后执行等。
// 定义跟生命周期相关的方法function lifecycleMixin (Vue) {Vue.prototype._update = function (vnode, hydrating) {};Vue.prototype.$forceUpdate = function () {};Vue.prototype.$destroy = function () {}}// 定义原型上跟渲染相关的方法function renderMixin (Vue) {Vue.prototype.$nextTick = function (fn) {};// _render函数,后面会着重讲Vue.prototype._render = function () {};}
1.1.4 定义静态属性方法
除了原型方法外,Vue还提供了丰富的全局api方法,这些都是在initGlobalAPI中定义的。
/* 初始化构造器的api */function initGlobalAPI (Vue) {// configvar configDef = {};configDef.get = function () { return config; };{configDef.set = function () {warn('Do not replace the Vue.config object, set individual fields instead.');};}// 通过Vue.config拿到配置信息Object.defineProperty(Vue, 'config', configDef);// 工具类不作为公共暴露的API使用Vue.util = {warn: warn,extend: extend,mergeOptions: mergeOptions,defineReactive: defineReactive###1};// Vue.set = Vue.prototype.$setVue.set = set;// Vue.delete = Vue.prototype.$deleteVue.delete = del;// Vue.nextTick = Vue.prototype.$nextTickVue.nextTick = nextTick;// 2.6 explicit observable APIVue.observable = function (obj) {observe(obj);return obj};// 构造函数的默认选项默认为components,directive,filter, _baseVue.options = Object.create(null);ASSET_TYPES.forEach(function (type) {Vue.options[type + 's'] = Object.create(null);});// options里的_base属性存储Vue构造器Vue.options._base = Vue;extend(Vue.options.components, builtInComponents);// Vue.use()initUse(Vue);// Vue.mixin()initMixin$1(Vue);// 定义extend扩展子类构造器的方法// Vue.extend()initExtend(Vue);// Vue.components, Vue.directive, Vue.filterinitAssetRegisters(Vue);}
看着源码对静态方法的定义做一个汇总。
- 为源码里的
config配置做一层代理,可以通过Vue.config拿到默认的配置,并且可以修改它的属性值,具体哪些可以配置修改,可以先参照官方文档。 - 定义内部使用的工具方法,例如警告提示,对象合并等。
- 定义
set,delet,nextTick方法,本质上原型上也有这些方法的定义。 - 对
Vue.components,Vue.directive,Vue.filter的定义,这些是默认的资源选项,后续会重点分析。 - 定义
Vue.use()方法 - 定义
Vue.mixin()方法 - 定义
Vue.extend()方法
现在我相信你已经对引入Vue的阶段有了一个大致的认识,在源码分析的初期阶段,我们不需要死磕每个方法,思路的实现细节,只需要对大致的结构有基本的认识。有了这些基础,我们开始进入这个章节的主线。
