最近在看Vue的回應式原理,記錄下自己的總結,方便后續查看,
如果這篇文章能給你一些思考,不要吝嗇的幫我點上👍 哦
要是寫的有啥錯誤 ? ,也拜托大佬們不吝賜教
Vue初始化
下面是vue的初始化模板 , 這段代碼很簡單,最后會在頁面輸出 hello world,下面我們從new Vue 開始來分析下他是如何實作的
<template>
<div>
{{ message }}
</div>
</template>
<script>
new Vue({
data() {
return {
message: "hello world",
};
},
});
</script>
執行流程
在 new Vue 之前,Vue初始化實體成員和靜態成員的地方我們就不過多贅述,直接從new Vue()開始`
除錯
在new Vue的時候打了個斷點,之后進入函式,去執行_init()方法
Vue.prototype._init
這個函式在/core/instance/init.js下面定義,里面初始化了一些vue的實體方法,其中關鍵的代碼塊為
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
vm.$mount
這個函式在/platforms/web/entry-runtime-with-compiler.js里面定義,這個函式的核心作用是把模板編譯成render函式 ,大致分為三個部分
- 首先判斷如果el是
body或者html標簽,在開發環境會報警告, - 之后去判斷用戶傳遞引數options.render函式,如果不傳,決議模板
return mount.call(this, el, hydrating)
下面重點看一下對于第二點的代碼塊,下面是對原始碼的簡要摘錄,主要是去理解首次渲染的流程
if (!options.render) {
let template = options.template
// 如果模板存在
if (template) {
if (typeof template === 'string') {
// 如果模板是id選擇器,例如我們傳遞的‘#app’
if (template.charAt(0) === '#') {
// 獲取對應DOM物件的innerHMTL
template = idToTemplate(template)
/* 在生產環境對于空模板告警 */
}
} else if (template.nodeType) {
// 如果模板是dom元素,回傳innerHTML
template = template.innerHTML
} else {
/* 兩種情況都不是,發出警告當前模板不合法,并且回傳當前實體 */
return this
}
} else if (el) {
// 如果沒有template,獲取el的outerHTML作為模板
template = getOuterHTML(el)
}
/**----------上面的一大段代碼,其實就是根據不同的情況決議模板,給template賦值,最后template的值 都是對應dom的innnerHTM---------------L */
if (template) {
// 將模板轉換成render函式
const { render, staticRenderFns } = compileToFunctions(template, {
outputSourceRange: process.env.NODE_ENV !== 'production',
shouldDecodeNewlines,
shouldDecodeNewlinesForHref,
delimiters: options.delimiters,
comments: options.comments
}, this)
// 將render函式存盤到options選項中去
options.render = render
options.staticRenderFns = staticRenderFns
}
}
繼續往下走會執行mount這個方法
// 呼叫mount方法渲染dom
return mount.call(this, el, hydrating)
mount
這個函式在/platforms/web/runtime/index.js里面定義,是對Vue.prototype.$mount的改寫,這個函式的核心作用是渲染dom
Vue.prototype.$mount = function (
el?: string | Element,
hydrating?: boolean
): Component {
el = el && inBrowser ? query(el) : undefined
return mountComponent(this, el, hydrating)
}
mountComponent
這個函式在core/instance/lifecycle.js下面定義,主要分為幾個步驟
- 首先判斷當前選項是不是存在
render函式,如果沒有在生產的時候會發出警告??( 這個判斷的目的是如果我們當前是 運行時 環境,并且我們通過選項傳入了模板,此時如果是開發環境會發出警告,提示當前使用的是運行時版本,編譯器是無效的,應該傳入render函式或者使用帶有編譯器的版本) - 觸發
beforeMount生命周期函式 - 給
updateComponent函式賦值 - 創建
Watcher物件的時候傳遞了updateComponent - 最后觸發了
mounted生命周期函式
這個函式的核心代碼塊是3,4步驟,下面我們根據原始碼分析下他具體做了什么
let updateComponent
// 如果是開發環境并啟動了性能監測
if(process.env.NODE_ENV !== 'production' && config.performance && mark){
...
}else{
updateComponent = () => {
// _update函式的作用是把虛擬dom轉成真實dom,更新到界面上,
// 注意此時是賦值并沒有執行呢
vm._update(vm._render(), hydrating)
}
}
// 創建Watcher物件的時候傳遞了updateComponent,所以updateComponent的執行是在Watcher中呼叫的
new Watcher(vm, updateComponent, noop, {
before () {
if (vm._isMounted && !vm._isDestroyed) {
callHook(vm, 'beforeUpdate')
}
}
}, true /* isRenderWatcher */)
接下來我們就看updateComponent 在 new Watcher里面什么時候呼叫的
new Watcher
這個函式在/core/observer/watcher.js中定義,這里提一下,我們在vue中的Watcher有三種,一種是渲染watcher,也就是我們當前創建的watcher,第二種是計算watcher,第三種監聽器的watcher,剩下的兩種watcher在以后的文章中進行分析
- 為
this.getter賦值
這個函式在開始創建了很多屬性,然后執行一個判斷,判斷第二個引數expOrFn是否為函式, 首次渲染的時候,我們傳遞的是updateComponent,所以是expOrFn是函式,我們直接把這個函式賦值給getter
if (typeof expOrFn === 'function') {
this.getter = expOrFn
} else {
...
}
- 為
this.value賦值
之后給this.value賦值,當前是渲染watcher,this.lazy = false,也就是不延遲執行,所以就是把get()方法賦值給了this.value,下面看get()做了什么
this.value = this.lazy
? undefined
: this.get()
- 定義
get()方法
get () {
// 把當前的watcher物件存入到堆疊中
pushTarget(this)
let value
const vm = this.vm
try {
// 這個地方呼叫了updateComponent,并改變函式內部指向為vue實體
// 也就是說,這個函式執行完,我們界面的資料就會完成更新
value = this.getter.call(vm, vm)
} catch (e) {
...
} finally {
...
}
return value
}
總結
- Vue初始化,實體成員,靜態成員
- new Vue()
- this._init()
- vm.$mount
- 這是
/platforms/web/entry-runtime-with-compiler.js的$mount, 這個函式的核心作用是幫我們把模板編譯成render函式 - 如果沒有傳遞
render,把模板編譯成render函式 compileToFunction()生成render()渲染函式options.render = render
- 這是
- vm.$mount
- 這個是
/platforms/web/runtime/ind ex.js中的$mount - 呼叫
mountComponent()
- 這個是
- mountComponent(this,el)
/core/instance/lifecycle.js中定義- 判斷是否有
render選項, 如果沒有但是傳入了模板,并且當前是開發環境會發出警告 - 觸發
beforeMount - 定義
updateComponentvm._update(vm.render(), ... )vm.render()渲染,渲染虛擬DOM
-vm._update()更新,將虛擬DOM轉換成真實DOM
- 創建
Watcher實體- 傳入了
updateComponent方法 - 呼叫
get()方法
- 傳入了
- 觸發
mounted return vm
- watcher.get()
- 創建完
watcher會呼叫一次get - 呼叫
updateComponent() - 呼叫
vm.render()創建VNode - 呼叫
vm._update(vnode, ...)
- 創建完
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/253498.html
標籤:其他
上一篇:字體圖示的三種參考方法(二)
