vue原理Compile從新建實(shí)例到結(jié)束流程源碼
引言
Compile 的內(nèi)容十分之多,今天先來個熱身,先不研究 compile 內(nèi)部編譯細(xì)節(jié),而是記錄一下
從新建實(shí)例開始,到結(jié)束 compile ,其中的大致外部流程,不涉及 compile 的內(nèi)部流程
或者說,我們要研究 compile 這個函數(shù)是怎么生成的
注意,如果你沒有準(zhǔn)備好,請不要閱讀這篇文章
注意哦,會很繞,別暈了
好的,正文開始
首先,當(dāng)我們通過 Vue 新建一個實(shí)例的時候會調(diào)用Vue
所以從 Vue 函數(shù)入手
function Vue(){ // ..... vm.$mount(vm.$options.el); }
然后內(nèi)部的其他處理都可以忽視,直接定位到 vm.$mount,就是從這里開始去編譯的
繼續(xù)去查找這個函數(shù)
Vue.prototype.$mount = function(el) { var options = this.$options; if (!options.render) { var tpl= options.template; // 獲取模板字符串 if (tpl) { // 根據(jù)傳入的選擇器找到元素,然后拿到該元素內(nèi)的模板 // 本來有很多種獲取方式,但是為了簡單,我們簡化為一種,知道意思就可以了 tpl = document.querySelector(tpl).innerHTML; } if (tpl) { // 生成 render 函數(shù)保存 var ref = compileToFunctions(tpl, {},this); // 每一個組件,都有自己的 render options.render = ref.render options.staticRenderFns =ref.staticRenderFns; } } // 執(zhí)行上面生成的 render,生成DOM,掛載DOM,這里忽略不討論 return mount.call(this, el) };
compile 的主要作用就是,根據(jù) template 模板,生成 render 函數(shù)
那么到這里,整個流程就走完了,因?yàn)?render 已經(jīng)在這里生成了
我們觀察到
在上面這個函數(shù)中,主要就做了三件事
1 獲取 template 模板
根據(jù)你傳入的參數(shù),來各種獲取 template 模板
這里應(yīng)該都看得懂了,根據(jù)DOM,或者根據(jù)選擇器
2 生成 render
通過 compileToFunctions ,傳入 template
就可以生成 render 和 staticRenderFns
看著是挺簡單哦,就一個 compileToFunctions,但是我告訴你,這個函數(shù)的誕生可不是這么容易的,兜兜轉(zhuǎn)轉(zhuǎn),十分曲折,相當(dāng)?shù)们蹚?fù)雜,沒錯,這就是我們下面研究的重點(diǎn)
但是這流程其實(shí)好像也沒有什么幫助?但是如果你閱讀源碼的話,或許可以對你理清源碼有些許幫助吧
再一次佩服 尤大的腦回路
3 保存 render
保存在 vm.$options 上,用于在后面調(diào)用
下面就來說 compileToFunctions 的誕生史
注意,很繞很繞,做好心理準(zhǔn)備
首先我定位到 compileToFunctions,看到下面這段代碼
var ref$1 = createCompiler(); // compileToFunctions 會返回 render 函數(shù) 以及 staticRenderFns var compileToFunctions = ref$1.compileToFunctions;
于是我知道
compileToFunctions 是 createCompiler 執(zhí)行返回的??!
那么繼續(xù)定位 createCompiler
createCompiler
var createCompiler = createCompilerCreator( function baseCompile(template, options) { var ast = parse(template.trim(), options); if (options.optimize !== false) { optimize(ast, options); } var code = generate(ast, options); return { ast: ast, render: code.render, staticRenderFns: code.staticRenderFns } } );
臥槽,又來一個函數(shù),別暈啊兄弟
不過,注意注意,這里是重點(diǎn),非常重
首先明確兩點(diǎn)
1、createCompiler 是 createCompilerCreator 生成的
2、給 createCompilerCreator 傳了一個函數(shù) baseCompile
baseCompile
這個 baseCompile 就是 生成 render 的大佬
看到里面包含了 渲染三巨頭,【parse,optimize,generate】
但是今天不是講這個的,這三個東西,每個內(nèi)容都十分巨大
這里先跳過,反正 baseCompile 很重要,會在后面被調(diào)用到,得先記著
然后,沒錯,我們又遇到了一個 函數(shù) createCompilerCreator ,定位它!
createCompilerCreator
function createCompilerCreator(baseCompile) { return function () { // 作用是合并選項,并且調(diào)用 baseCompile function compile(template) { // baseCompile 就是 上一步傳入的,這里執(zhí)行得到 {ast,render,statickRenderFn} var compiled = baseCompile(template); return compiled } return { // compile 執(zhí)行會返回 baseCompile 返回的 字符串 render compile: compile, // 為了創(chuàng)建一層 緩存閉包,并且閉包保存 compile // 得到一個函數(shù),這個函數(shù)是 把 render 字符串包在 函數(shù) 中 compileToFunctions: createCompileToFunctionFn(compile) } } }
這個函數(shù)執(zhí)行過后,會返回一個函數(shù)
很明顯,返回的函數(shù)就 直接賦值 給了上面講的的 createCompiler
我們看下這個返回給 createCompiler 的函數(shù)里面都干了什么?
生成一個函數(shù) compile
內(nèi)部存在一個函數(shù) compile,這個函數(shù)主要作用是
調(diào)用 baseCompile,把 baseCompile 執(zhí)行結(jié)果 return 出去
baseCompile 之前我們強(qiáng)調(diào)過的,就是那個生成 render 的大佬
忘記的,可以回頭看看,執(zhí)行完畢會返回
{ render,staticRenderFns }
返回 compileToFunctions 和 compile
其實(shí) 返回的這兩個函數(shù)的作用大致都是一樣的
都是為了執(zhí)行上面那個 內(nèi)部 compile
但是為什么分出一個 compileToFunctions 呢?
還記得開篇我們的 compileToFunctions 嗎
就是那個在 vm.$mount 里我們要探索的東西啊
就是他這個吊毛,生成的 render 和 staticRenderFns
再看看那個 內(nèi)部 compile,可以看到他執(zhí)行完就是返回
{ render, staticRenderFns }
你看,內(nèi)部 compile 就是 【vm.$mount 執(zhí)行的 compileToFunctions】 啊
為什么 compileToFunctions 沒有直接賦值為 compile 呢?。?/p>
因?yàn)橐瞿0寰彺妫。?/h3>
可以看到,沒有直接讓 compileToFunctions = 內(nèi)部compile
而是把 內(nèi)部 compile 傳給了 createCompileToFunctionFn
沒錯 createCompileToFunctionFn 就是做緩存的
為了避免每個實(shí)例都被編譯很多次,所以做緩存,編譯一次之后就直接取緩存
createCompileToFunctionFn
來看看內(nèi)部的源碼,緩存的代碼已經(jīng)標(biāo)紅
function createCompileToFunctionFn(compile) { // 作為緩存,防止每次都重新編譯 // template 字符串 為 key , 值是 render 和 staticRenderFns var cache = Object.create(null); return function compileToFunctions(template, options, vm) { var key = template; // 有緩存的時候直接取出緩存中的結(jié)果即可 if (cache[key]) return cache[key] // compile 是 createCompileCreator 傳入的compile var compiled = compile(template, options); var res = { // compiled.render 是字符串,需要轉(zhuǎn)成函數(shù) render : new Function(compiled.render) staticRenderFns : compiled.staticRenderFns.map(function(code) { return new Function(code, fnGenErrors) }); }; return (cache[key] = res) } }
額外:render 字符串變成可執(zhí)行函數(shù)
var res = { render: new Function(compiled.render) , staticRenderFns: compiled.staticRenderFns.map(function(code) { return new Function(code, fnGenErrors) }); };
這段代碼把 render 字符串可執(zhí)行函數(shù),因?yàn)閞ender生成的形態(tài)是一個字符串,如果后期要調(diào)用運(yùn)行,比如轉(zhuǎn)成函數(shù)
所以這里使用了 new Function() 轉(zhuǎn)化成函數(shù)
同理,staticRenderFns 也一樣,只不過他是數(shù)組,需要遍歷,逐個轉(zhuǎn)化成函數(shù)
他的緩存是怎么做的
使用一個 cache 閉包變量
template 為 key
生成的 render 作為 value
當(dāng)實(shí)例第一次渲染解析,就會被存到 cache 中
當(dāng)實(shí)例第二次渲染解析,那么就會從 cache 中直接獲取
什么時候?qū)嵗龝馕龅诙危?/p>
比如 頁面A到頁面B,頁面B又轉(zhuǎn)到頁面A。
頁面A 這個實(shí)例,按理就需要解析兩次,但是有緩存之后就不會
理清思路
也就是說,compileToFunctions 其實(shí)內(nèi)核就是 baseCompile!
不過 compileToFunctions 是經(jīng)過了 兩波包裝的 baseCompile
第一波包裝在 createCompilerCreator 中的 內(nèi)部 compile 函數(shù)中
內(nèi)部函數(shù)的作用是
合并公共options和 自定義options ,但是相關(guān)代碼已經(jīng)省略,
另一個就是執(zhí)行 baseCompile
第二波包裝在 createCompileToFunctions 中,目的是進(jìn)行 緩存
以上就是vue原理Compile從新建實(shí)例到結(jié)束流程源碼的詳細(xì)內(nèi)容,更多關(guān)于vue原理Compile新建實(shí)例的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Vue進(jìn)階之CodeMirror的應(yīng)用小結(jié)
CodeMirror支持在線編輯代碼,風(fēng)格包括js, java, php, c++等等100多種語言,下面這篇文章主要來和大家講講CodeMirror的應(yīng)用,感興趣的可以了解一下2023-06-06vue中實(shí)時監(jiān)聽div元素盒子的寬高方法
這篇文章主要給大家介紹了關(guān)于vue中如何實(shí)時監(jiān)聽div元素盒子的寬高的相關(guān)資料,在Vue中你可以使用Vue的計算屬性和偵聽器來動態(tài)監(jiān)測元素的高度,文中給出了簡單代碼示例,需要的朋友可以參考下2023-09-09vue2.0 與 bootstrap datetimepicker的結(jié)合使用實(shí)例
本篇文章主要介紹了vue2.0 與 bootstrap datetimepicker的結(jié)合使用實(shí)例,非常具有實(shí)用價值,需要的朋友可以參考下2017-05-05利用Vue的v-for和v-bind實(shí)現(xiàn)列表顏色切換
這篇文章主要介紹了利用Vue的v-for和v-bind實(shí)現(xiàn)列表顏色切換,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-07-07vue-video-player?播放m3u8視頻流的實(shí)現(xiàn)
本文主要介紹了vue-video-player?播放m3u8視頻流的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-04-04vue 解決addRoutes多次添加路由重復(fù)的操作
這篇文章主要介紹了vue 解決addRoutes多次添加路由重復(fù)的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-08-08vant組件中 dialog的確認(rèn)按鈕的回調(diào)事件操作
這篇文章主要介紹了vant組件中 dialog的確認(rèn)按鈕的回調(diào)事件操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-11-11Vue實(shí)現(xiàn)側(cè)邊菜單欄手風(fēng)琴效果實(shí)例代碼
本文通過一段簡單的代碼給大家介紹了基于純vue實(shí)現(xiàn)側(cè)邊菜單欄手風(fēng)琴效果,代碼很簡單,感興趣的朋友跟隨腳本之家小編一起看看吧2018-05-05