转载

Vue.js - 基础原理

前言

本文将从官网的例子讲起,一步步对Vue.js的实现做讲解说明。

请注意下列事项:

  • 本文适合于使用了Vue.js一段时间,想进一步深入和对其实现原理有兴趣的人。

  • 本文基于 1.0.13 版本。

  • 本文较长,包含了部分vue.js源代码,删除了所有的警告信息(对本文来说没有任何的作用,而且影响阅读)和部分注释。

  • 文中对代码的注释做了部分翻译,以供阅读。

  • 作者是不写冒号主义者,可能会引起部分人的蛋疼。

  • 需要有ES6的知识作为基础。

  • 需要理解原型。

  • 文是我边看边写的。

  • 水平有限,如有错误请指出。

例子

下面是官网的例子,可以在上面直接看到执行的情况。接下来我们将会以这段代码做开头。

var demo = new Vue({   el: '#demo',   data: {     message: 'Hello Vue.js!'   } })
<div id="demo">   <p>{{message}}</p>   <input v-model="message"> </div>

首先

我们可以从上面的代码得到一些信息。从js来看,我们是new了一个Vue实例,提供了一个el和一个data。el是为了和html做映射,data则是本身涵盖的数据。再看看html,id用于映射,{{message}}是数据的显示,input的值作为message的model。

这样html和js根据一个id做出了映射关系,并将data和html做了双向的关联,这就是典型的MVVM模式。即Modle、View和ViewModel。

现在

我们需要看看这之中的执行过程到底发生了什么。

对此,我们需要查看源代码。因为项目的组织是基于ES6的模块方式组织的,所以寻找和阅读并不是很困难。让我们先找到这个入口。

vue/src 文件夹里我们可以很容易的找到 index.js 文件,看起来这个就是入口。

import Vue from './instance/vue' import directives from './directives/public/index' import elementDirectives from './directives/element/index' import filters from './filters/index' import { inBrowser } from './util/index'  Vue.version = '1.0.13'  /**  * Vue and every constructor that extends Vue has an  * associated options object, which can be accessed during  * compilation steps as `this.constructor.options`.  *  * 每一个Vue实例都会有下列options  */  Vue.options = {   directives,   elementDirectives,   filters,   transitions: {},   components: {},   partials: {},   replace: true }  export default Vue

好吧,似乎 vue/src/instance/vue.js 才是真正的本体。

// 构造函数 function Vue (options) {   this._init(options) }  // install internals initMixin(Vue) stateMixin(Vue) eventsMixin(Vue) lifecycleMixin(Vue) miscMixin(Vue)  // install APIs globalAPI(Vue) dataAPI(Vue) domAPI(Vue) eventsAPI(Vue) lifecycleAPI(Vue)  export default Vue

我把import部分和部分注释删掉了,影响阅读。额,这个Vue构造函数里似乎只是执行了一个_init函数用来处理options,所以我们要接着找。

这个我们简单的全局搜一下就可以了,然后定位到 vue/src/instance/internal/init.js 。一看这个代码,我们就知道重点来了。

这个文件export了一个函数,看看 vue/src/instance/vue.js 我们就能知道其实就是initMixin这个函数。所以init就是在这个函数里被赋值的,我们直接看代码,这样会比较直观。

 // mergeOptions这个函数,看名字是用来做options合并的 import { mergeOptions } from '../../util/index'  // uid?我们先不探讨 let uid = 0  // 被作为initMixin调用 export default function (Vue) {    // 这就是我们要找的东西 GJ   Vue.prototype._init = function (options) {      // 检查一下options是不是为空     options = options || {}      // 各种options,这里是各个默认值     this.$el = null     this.$parent = options.parent     this.$root = this.$parent       ? this.$parent.$root       : this     this.$children = []     this.$refs = {}       // child vm references     this.$els = {}        // element references     this._watchers = []   // all watchers as an array     this._directives = [] // all directives      // 哦,是Vue的实例个数     this._uid = uid++      // a flag to avoid this being observed     this._isVue = true      // events bookkeeping     this._events = {}            // registered callbacks     this._eventsCount = {}       // for $broadcast optimization      // fragment instance properties     this._isFragment = false     this._fragment =         // @type {DocumentFragment}     this._fragmentStart =    // @type {Text|Comment}     this._fragmentEnd = null // @type {Text|Comment}      // lifecycle state     this._isCompiled =     this._isDestroyed =     this._isReady =     this._isAttached =     this._isBeingDestroyed = false     this._unlinkFn = null      // context:     // if this is a transcluded component, context     // will be the common parent vm of this instance     // and its host.     this._context = options._context || this.$parent      // scope:     // if this is inside an inline v-for, the scope     // will be the intermediate scope created for this     // repeat fragment. this is used for linking props     // and container directives.     this._scope = options._scope      // fragment:     // if this instance is compiled inside a Fragment, it     // needs to reigster itself as a child of that fragment     // for attach/detach to work properly.     this._frag = options._frag     if (this._frag) {       this._frag.children.push(this)     }      // push self into parent / transclusion host     if (this.$parent) {       this.$parent.$children.push(this)     }      // merge options.     options = this.$options = mergeOptions(       this.constructor.options,       options,       this     )      // set ref     this._updateRef()      // initialize data as empty object.     // it will be filled up in _initScope().     this._data = {}      // call init hook     this._callHook('init')      // initialize data observation and scope inheritance.     this._initState()      // setup event system and option events.     this._initEvents()      // call created hook     this._callHook('created')      // if `el` option is passed, start compilation.     if (options.el) {       this.$mount(options.el)     }   } }

PS

  • 我希望SF支持ES6代码渲染。

  • 请纠错。

原文  http://segmentfault.com/a/1190000004221918
正文到此结束
Loading...