Vue.js是一套構建用戶界面的漸進式框架。與其餘重量級框架不一樣的是,Vue 採用自底向上增量開發的設計。Vue 的核心庫只關注視圖層,它不只易於上手,還便於與第三方庫或既有項目整合。另外一方面,當與單文件組件和 Vue 生態系統支持的庫結合使用時,Vue 也徹底可以爲複雜的單頁應用程序提供驅動。 因爲上述優勢,Vue的使用在業界愈來愈普遍,截至本文寫做時Vue在github上的star已經高達113322。javascript
一個Vue實例具備許多階段,例如observing data、initializing events、compiling template 及render,在這些階段咱們能夠註冊相應的鉤子函數。vue
/*初始化生命週期*/
initLifecycle(vm)
/*初始化事件*/
initEvents(vm)
/*初始化render*/
initRender(vm)
/*調用beforeCreate鉤子函數而且觸發beforeCreate鉤子事件*/
callHook(vm, 'beforeCreate')
initInjections(vm) // resolve injections before data/props
/*初始化props、methods、data、computed與watch*/
initState(vm)
initProvide(vm) // resolve provide after data/props
/*調用created鉤子函數而且觸發created鉤子事件*/
callHook(vm, 'created')
複製代碼
之因此稱爲響應式,是由於Vue.js實現了數據和視圖的雙向綁定,即數據發生變化後視圖也隨之變化。 其中Vue實現響應式的核心函數是Object.defineProperty,並且由於Object.defineProperty最低支持IE8,因此致使Vue只兼容到IE8。Vue使用Object.defineProperty來實現響應式的具體原理會在接下來的內容中進行詳細講解。java
虛擬DOM是真實DOM的抽象,Vue的主要操做先是在虛擬DOM上進行的,最後再根據不一樣的平臺來映射到真實DOM上,其中的核心是diff算法.node
本項目使用npm做爲包管理器,所以構建myVue項目的第一件事,即是在空文件夾下使用npm init
命令初始化項目。 項目初始化以後,在正式編寫項目前,咱們需還要下載和配置第三方工具,其中包括模塊構建工具和測試工具。git
在該項目中咱們使用Rollup做爲打包工具。Rollup做爲一款javascript打包工具,其支持ES2015語法,同時它也是Vue的打包工具。github
使用Rollup前,首先是使用npm install rollup rollup-watch --save-dev
命令安裝Rollup相關包,安裝完成後在根目錄下建立rollup.config.js
文件並進行一些基礎配置。關於Rollup的進一步使用,請自行前往官網。web
export default {
input: 'src/instance/index.js',
output: {
name: 'Vue',
file: 'dist/vue.js',
format: 'iife'
},
};
複製代碼
本項目採用karma做爲自動化測試工具,jasmine做爲單元測試工具,一樣要想使用這兩個工具,使用前需下載相關包並進行配置。算法
npm install karma jasmine karma-jasmine karma-chrome-launcher buble --save-dev
npm install karma-rollup-plugin karma-rollup-preprocessor rollup-plugin-buble --save-dev
複製代碼
下載相關包後在根目錄下建立karma.conf.js
並配置。chrome
module.exports = function(config) {
config.set({
files: [{ pattern: 'test/**/*.spec.js', watched: false }],
frameworks: ['jasmine'],
browsers: ['Chrome'],
preprocessors: {
'./test/**/*.js': ['rollup']
},
rollupPreprocessor: {
plugins: [
require('rollup-plugin-buble')(),
],
output: {
format: 'iife',
name: 'Vue',
sourcemap: 'inline'
}
}
})
}
複製代碼
項目搭建好的結構以下。npm
- package.json
- rollup.config.js
- node_modules
- dist
- test
- src
- observer
- instance
- util
- vdom
複製代碼
項目搭建好後,要想啓動項目,咱們能夠在npm的配置文件中進行配置。 package.json
"scripts": {
"build": "rollup -c",
"watch": "rollup -c -w",
"test": "karma start"
}
複製代碼
要想編寫出高質量的代碼,正式編程前咱們最好養成編寫代碼前先編寫測試用例進行單元測試的好習慣,下面咱們就開始編寫測試用例吧。
test/options/options.spec.js
import Vue from "../src/instance/index";
describe('Proxy test', function() {
it('should proxy vm._data.a = vm.a', function() {
var vm = new Vue({
data:{
a:2
}
})
expect(vm.a).toEqual(2);
});
});
複製代碼
編寫測試用例後,咱們的準備工做就所有完成了,咱們就能夠開始編寫真正的代碼了,此時是否是感到很激動。(畢竟特煩配置什麼的了,特別是javaweb項目,想到當年被javaweb三大框架配置支配的恐懼就不由內牛滿面啊...)
要想實現Vue,天然是先編寫Vue的構造函數,而後再一步一步的實現其餘功能。
src/instance/index.js
import { initMixin } from './init'
function Vue (options) {
this._init(options)
}
initMixin(Vue)
export default Vue
複製代碼
上面只是Vue的構造器調用了this._init
方法來初始化一個Vue的實例,而initMixin
的做用即是給Vue定義原型方法,具體代碼以下。
src/instance/init.js
import { initState } from './state'
export function initMixin (Vue) {
Vue.prototype._init = function (options) {
var vm = this
vm.$options = options
initState(vm)
}
}
複製代碼
initMixin方法只是在Vue的原型上定義了_init
方法,而_init
方法則是調用了initState
方法來初始化data。
src/instance/state.js
export function initState(vm) {
initData(vm)
}
function initData(vm) {
var data = vm.$options.data
vm._data = data
// proxy data on instance
var keys = Object.keys(data)
var i = keys.length
while (i--) {
proxy(vm, keys[i])
}
}
function proxy(vm, key) {
Object.defineProperty(vm, key, {
configurable: true,
enumerable: true,
get: function proxyGetter() {
return vm._data[key]
},
set: function proxySetter(val) {
vm._data[key] = val
}
})
}
複製代碼
如上initState
方法只是調用了initData
方法,而initData
方法則是初始化了data並使用proxy
方法將vm._data.a
代理成vm.a
。
proxy
在vm
上使用一樣的key定義了一個屬性,並經過 get/set 從vm._data
上存取data。
最後使用npm run test
命令啓動項目。