■ 1.請(qǐng)簡(jiǎn)述 Vue 首次渲染的過(guò)程。
首先進(jìn)行Vue的初始化,初始化Vue的實(shí)例成員以及靜態(tài)成員。
當(dāng)初始化結(jié)束之后,開(kāi)始調(diào)用構(gòu)造函數(shù),在構(gòu)造函數(shù)中調(diào)用this._init(),這個(gè)方法相當(dāng)于我們整個(gè)Vue的入口。
-
在_init()中調(diào)用this.$mount(),共有兩個(gè)this.$mount()。
①第一個(gè)this.$mount()是entry-runtime-with-compiler.js入口文件,這個(gè)$mount()的核心作用是幫我們把模板編譯成render函數(shù),但它首先會(huì)判斷一下當(dāng)前是否傳入了render選項(xiàng),如果沒(méi)有傳入的話,它會(huì)去獲取我們的template選項(xiàng),如果template選項(xiàng)也沒(méi)有的話,他會(huì)把el中的內(nèi)容作為我們的模板,然后把模板編譯成render函數(shù),它是通過(guò)compileToFunctions()函數(shù),幫我們把模板編譯成render函數(shù)的,當(dāng)把render函數(shù)編譯好之后,它會(huì)把render函數(shù)存在我們的options.render中。
=> src\platforms\web\entry-runtime-with-compiler.js
=> 如果沒(méi)有傳遞render,把模版編譯成render函數(shù)
=> compileToFunction()生成render()渲染函數(shù)
=> options.render=render②第二個(gè)this.$mount()是runtime/index.js中的this.$mount()方法,這個(gè)方法首先會(huì)重新獲取el,因?yàn)槿绻沁\(yùn)行時(shí)版本的話,是不會(huì)走entry-runtime-with-compiler.js這個(gè)入口中獲取el,所以如果是運(yùn)行時(shí)版本的話,我們會(huì)在runtime/index.js的$mount()中重新獲取el。
=> src\platforms\web\runtime\index.js
=> mountComponent() 接下來(lái)調(diào)用mountComponent(),mountComponent()是在src/core/instance/lifecycle.js中定義的,在mountComponent()中,首先會(huì)判斷render選項(xiàng),如果沒(méi)有render,但是傳入了模板,并且當(dāng)前是開(kāi)發(fā)環(huán)境的話會(huì)發(fā)送警告,警告運(yùn)行時(shí)版本不支持編譯器。接下來(lái)會(huì)觸發(fā)beforeMount這個(gè)生命周期中的鉤子函數(shù),也就是開(kāi)始掛載之前。
然后定義了updateComponent(),在這個(gè)方法中,定義了_render和_update,_render的作用是生成虛擬DOM,_update的作用是將虛擬DOM轉(zhuǎn)換成真實(shí)DOM,并且掛載到頁(yè)面上來(lái)。
再接下來(lái)就是創(chuàng)建Watcher對(duì)象,在創(chuàng)建Watcher時(shí),傳遞了updateComponent這個(gè)函數(shù),這個(gè)函數(shù)最終是在Watcher內(nèi)部調(diào)用的。在Watcher創(chuàng)建完之后還調(diào)用了get方法,在get方法中,會(huì)調(diào)用updateComponent()。
-
然后觸發(fā)了生命周期的鉤子函數(shù)mounted,掛載結(jié)束,最終返回Vue實(shí)例。
vue首次渲染
■ 2、請(qǐng)簡(jiǎn)述 Vue 響應(yīng)式原理。
Vue2.0+版本 的響應(yīng)式原理核心是通過(guò) ES5 的保護(hù)對(duì)象的 Object.defindeProperty 中的訪問(wèn)器屬性中的 get 和 set 方法,data 中聲明的屬性都被添加了訪問(wèn)器屬性,當(dāng)讀取 data 中的數(shù)據(jù)時(shí)自動(dòng)調(diào)用 get 方法,當(dāng)修改 data 中的數(shù)據(jù)時(shí),自動(dòng)調(diào)用 set 方法,檢測(cè)到數(shù)據(jù)的變化,會(huì)觸發(fā)dep的notify通知觀察者 Wacher,觀察者 Wacher調(diào)用update方法自動(dòng)觸發(fā)重新render 當(dāng)前組件(子組件不會(huì)重新渲染),生成新的虛擬 DOM 樹(shù),Vue 框架會(huì)遍歷并對(duì)比新舊虛擬 DOM 樹(shù)中每個(gè)節(jié)點(diǎn)的差別,并記錄下來(lái),最后把不同點(diǎn)渲染到真實(shí) DOM 樹(shù)上。因?yàn)橛玫腛bject.defindeProperty只能對(duì)屬性進(jìn)行監(jiān)聽(tīng), 所以要深度遍歷每個(gè)對(duì)象,對(duì)象的新增刪除也檢測(cè)不到,所以vue
3.0采用了proxy,可以對(duì)整個(gè)對(duì)象進(jìn)行劫持
■ 3、請(qǐng)簡(jiǎn)述虛擬 DOM 中 Key 的作用和好處。
v-for遍歷的時(shí)候,能夠追蹤每個(gè)節(jié)點(diǎn)的身份,在進(jìn)行新舊虛擬DOM節(jié)點(diǎn)比較的時(shí)候,會(huì)基于key的變化重新排列元素的順序,從而重用和重新排序現(xiàn)有的元素,并且移除key不存在的元素,方便在diff過(guò)程中找到對(duì)應(yīng)的節(jié)點(diǎn),然后復(fù)用,從而減少dom的操作。
■ 4、請(qǐng)簡(jiǎn)述 Vue 中模板編譯的過(guò)程。
/** 通用的 Vue 文件格式*/
<template>
<!-- 模版-->
</template>
<script>
// 生命周期函數(shù) 一些事件邏輯處理
</script>
<style lang="scss" scoped rel="stylesheet/scss">
<!-- css 樣式或者 scss / less 等樣式預(yù)處理語(yǔ)言-->
</style>
編譯時(shí)主要是對(duì) template 里面的內(nèi)容進(jìn)行編譯。script 可以直接使用,style 也是可以直接抽離出來(lái)使用或者進(jìn)行樣式預(yù)處理。
整體編譯過(guò)程:
模板編譯的入庫(kù)是compileToFunctions,先從緩存中加載編譯好的render函數(shù),如果沒(méi)有就去調(diào)用compile函數(shù)合并選項(xiàng),然后調(diào)用baseCompile(參數(shù):合并好的選項(xiàng))編譯模板。之后通過(guò)調(diào)用createFunction函數(shù),把baseCompile中生成的字符串形式JS代碼轉(zhuǎn)化為函數(shù)形式。當(dāng)render和staticRenderFns初始化完畢,掛載到Vue實(shí)例的options對(duì)應(yīng)的屬性上
baseCompile函數(shù)
第一步將 模板字符串 轉(zhuǎn)換成 element ASTs(parser 解析器)
<div>
<p>{{name}}</p>
</div>
把上面轉(zhuǎn)為ASTs
{
tag: "div"
type: 1,
// staticRoot: false,
// static: false,
plain: true,
parent: undefined,
attrsList: [],
attrsMap: {},
children: [
{
tag: "p"
type: 1,
staticRoot: false,
// static: false,
plain: true,
parent: {tag: "div", ...},
attrsList: [],
attrsMap: {},
children: [{
type: 2,
text: "{{name}}",
// static: false,
expression: "_s(name)"
}]
}
]
}
第二步是對(duì) AST 進(jìn)行靜態(tài)根節(jié)點(diǎn),靜態(tài)節(jié)點(diǎn)標(biāo)記,主要用來(lái)做虛擬 DOM 的渲染優(yōu)化(optimizer 優(yōu)化器)
主要是循環(huán)把上面的代碼的 staticRoot 及 static 設(shè)為 true / false,patch過(guò)程中會(huì)跳過(guò)靜態(tài)根節(jié)點(diǎn)
(只包含純文本的靜態(tài)節(jié)點(diǎn)不是靜態(tài)根節(jié)點(diǎn),因?yàn)榇藭r(shí)的優(yōu)化成本大于收益)
{
tag: "div"
type: 1,
staticRoot: false,
static: false,
plain: true,
parent: undefined,
attrsList: [],
attrsMap: {},
children: [
{
tag: "p"
type: 1,
staticRoot: false,
static: false,
plain: true,
parent: {tag: "div", ...},
attrsList: [],
attrsMap: {},
children: [{
type: 2,
text: "{{name}}",
static: false,
expression: "_s(name)"
}]
}
]
}
第三步是 使用 element ASTs 生成 render 函數(shù)代碼字符串(generate代碼生成器)
主要是把上面的 element ASTs 轉(zhuǎn)成下面的 render 函數(shù)代碼串, _c 是 createElement,執(zhí)行后也是創(chuàng)建 vnode 節(jié)點(diǎn)
with(this){
return _c(
'div',
[
_c(
'p',
[
_v(_s(name))
]
)
]
)
}