vueJs 源碼解析 (三) 具體代碼 在之前的文章中提到了 vuejs 源碼中的 架構部分,以及 談論到了 vue 源碼三要素 vm、compiler、watcher 這三要素,那麼今天我們就從這三要素逐步瞭解清楚 好了,話不多說, let's do it 在這之前,我們需要 對上文中講到的 vu ...
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)
第五步: 比較核心的 渲染功能。
今天先到這裡... 先下個班,回家繼續整理