vueJs 源碼解析 (三) 具體代碼
在之前的文章中提到了 vuejs 源碼中的 架構部分,以及 談論到了 vue 源碼三要素 vm、compiler、watcher 這三要素,那么今天我們就從這三要素逐步了解清楚
好了,話不多說, let's do it
在這之前,我們需要 對上文中講到的 vuejs 的源碼是 flow 寫法的問題進行一個簡化。 畢竟還有有工具是可以解決的。
可以用babel-plugin-transform-flow-strip-types去轉化下即可。
1、 npm install --save-dev babel-plugin-transform-flow-strip-types
2、 .babelrc 文件中
{
"plugins": ["transform-flow-strip-types"]
}
具體轉換方法見 github地址
一、 instance 實例化入口 核心代碼
/src/core/instance/index.js
import { initMixin } from './init' // 實例化 混合
import { stateMixin } from './state' // 各類數據應用 混合
import { renderMixin } from './render' // render 函數等 混合
import { eventsMixin } from './events' // 例如 父子組件的 emit on 事件
import { lifecycleMixin } from './lifecycle' // 這個暫時比較模糊,后面的文章更新
import { warn } from '../util/index' // warn 報錯工具 在控制台經常會看到的 vue 的warn
function Vue (options) {
if (process.env.NODE_ENV !== 'production' &&
!(this instanceof Vue) // 這里就是判斷當前 this 的 prototype 是否是 Vue
) {
warn('Vue is a constructor and should be called with the `new` keyword')
}
this._init(options)
}
initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)
export default Vue
一、 instance 實例化入口 核心代碼 之 init.js
核心代碼區塊一:
if (options && options._isComponent) {
// optimize internal component instantiation
// since dynamic options merging is pretty slow, and none of the
// internal component options needs special treatment.
initInternalComponent(vm, options);
} else {
vm.$options = mergeOptions(resolveConstructorOptions(vm.constructor),
options || {}, vm);
}
解析:
判斷傳入的值當中是否有組件,如果有則先實例化組件。
核心代碼區塊二:
vm._self = vm;
initLifecycle(vm);
initEvents(vm);
initRender(vm);
callHook(vm, 'beforeCreate');
initInjections(vm); // resolve injections before data/props
initState(vm);
initProvide(vm); // resolve provide after data/props
callHook(vm, 'created');
解析:
看字面上的意思是分別一次 實例化了 生命周期、事件、渲染函數 、回調鈎子 ‘beforeCreate’、
依賴注入、狀態、 提供 、回調鈎子 ‘created’、
對,看到這里我們還是很奇怪這些東西是干嘛的?
那么下面我們繼續依次往下看:
一、實例化生命周期 initLifecycle
vm.$parent = parent;
vm.$root = parent ? parent.$root : vm;
vm.$children = [];
vm.$refs = {};
vm._watcher = null;
vm._inactive = null;
vm._directInactive = false;
vm._isMounted = false;
vm._isDestroyed = false;
vm._isBeingDestroyed = false;
看上去是增加了一系列的 屬性。但是還是不知道這個有什么用。不着急,我們繼續往下看。
二、實例化生命周期 initEvents
export function initEvents(vm) {
vm._events = Object.create(null);
vm._hasHookEvent = false;
// init parent attached events
const listeners = vm.$options._parentListeners;
if (listeners) {
updateComponentListeners(vm, listeners);
}
}
再走到這步的時候就會發現, vm.$options 這個對象 頻頻出現在我們的視野中,如果每次都不能理解的話。就會遇到 理解障礙。 那么 我們就需要 做一個 最簡單的測試用例。 (實際的把 vue 跑起來)
三、new Vue({ options }) Vue 最簡單的測試用例
我們在created 鈎子下打印 this 對象 部分結果如下
console.log(this)
// console.log(this)
_events: {}
_hasHookEvent :false
_inactive :null
_isBeingDestroyed :false
_isDestroyed :false
_isMounted: true
_isVue: true
這里我們就能夠看到比較清晰的一些屬性了 在 init.js 中的第一步 initLifecycle.js 中定義的
// console.log(this)
_events: {} // 事件對象集合
_hasHookEvent :false // 是否有鈎子事件
_inactive :null // 未知
_isBeingDestroyed :false // 是否要銷毀
_isDestroyed :false // 是否已經銷毀
_isMounted: true // 是否已掛載
_isVue: true // 是否是 Vue 對象
這里我們就再回到 initLifecycle.js 源碼 中去看
const options = vm.$options;
let parent = options.parent;
if (parent && !options.abstract) {
while (parent.$options.abstract && parent.$parent) {
parent = parent.$parent;
}
parent.$children.push(vm);
}
vm.$parent = parent;
vm.$root = parent ? parent.$root : vm;
vm.$children = []; // 這里定義的 children 數字對象未知是什么意思
vm.$refs = {}; // 這里定義的 refs 對象依然未知
走到這一步 大概對 vm.$options 這個對象 有個初步的了解,但是還有部分依然是未知。好了,我們繼續往下走。
四、 我回到 第二步 實例化生命周期 initEvents
export function initEvents(vm) {
vm._events = Object.create(null);
vm._hasHookEvent = false;
// init parent attached events
const listeners = vm.$options._parentListeners;
if (listeners) {
updateComponentListeners(vm, listeners);
}
}
給 vm 對象 新增了一個 _events 對象 , 並且會去判斷 vm.$options 是否有父級的事件監聽。
propsData : undefined
_componentTag : "App"
_parentElm : null
_parentListeners : undefined
_parentVnode : VNode {tag: "vue-component-1-Apps-test", data: {…}, children: undefined, text: undefined, elm: div.test11, …}
_refElm : null
_renderChildren : undefined
_parentListeners 值為空。 這里我們做一個大膽的假設: 是否有組件的引用的時候這個值就會發生改變。 那么下面我們簡單的測試下。新增一個基礎組件.
// 驗證失敗:這個值依然還是為 空
// 但是弄清楚了一個問題:
this.$root // 為根對象
this.$root.$parent // 根對象的 父屬性一定是為空
this.$root.$children // 根對象的 子代屬性一定是 一個數組。
// 如果你引用的 組件
this.$root.$children[0] // 為第一個組件
this.$root.$children[1] // 為第二個組件
...
// 以此類推
好,我們接着往下看
五、 initRender(vm)
第五步: 比較核心的 渲染功能。
今天先到這里... 先下個班,回家繼續整理
