摘要:源碼是選用了作為,看的源碼時發(fā)現(xiàn)對應(yīng)了不同的構(gòu)建選項。這也對應(yīng)了最后打包構(gòu)建后產(chǎn)出的不同的包。第四種構(gòu)建方式對應(yīng)的構(gòu)建腳本為不同于前面種構(gòu)建方式這一構(gòu)建對應(yīng)于將關(guān)于模板編譯的成函數(shù)的多帶帶進(jìn)行打包輸出。
Vue源碼是選用了rollup作為bundler,看Vue的源碼時發(fā)現(xiàn):npm script對應(yīng)了不同的構(gòu)建選項。這也對應(yīng)了最后打包構(gòu)建后產(chǎn)出的不同的包。
不同于其他的library,Vue為什么要在最后的打包構(gòu)建環(huán)節(jié)輸出不同類型的包呢?接下來我們通過Vue的源碼以及對應(yīng)的構(gòu)建配置中簡單的去分析下。
由于Vue是基于rollup進(jìn)行構(gòu)建的,我們先來簡單了解下rollup這個bundler:rollup是默認(rèn)使用ES Module規(guī)范而非CommonJS,因此如果你在你的項目中使用rollup作為構(gòu)建工具的話,那么可以放心的使用ES Module規(guī)范,但是如果要引入只遵循了CommonJs規(guī)范的第三包的話,還需要使用相關(guān)的插件,插件會幫你將CommonJs規(guī)范的代碼轉(zhuǎn)為ES Module。得益于ES Module,rollup在構(gòu)建前進(jìn)行靜態(tài)分析,進(jìn)行tree-shaking。關(guān)于tree-shaking的描述請戳我。在構(gòu)建輸出環(huán)節(jié),rollup提供了多種文件輸出類型:
iife: 立即執(zhí)行函數(shù)
cjs: 遵循CommonJs Module規(guī)范的文件輸出
amd: 遵循AMD Module規(guī)范的文件輸出
umd: 支持外鏈/CommonJs Module/AMD Module規(guī)范的文件輸出
es: 將多個遵循ES6 Module的文件編譯成1個ES6 Module
接下來我們就看看Vue的使用rollup進(jìn)行構(gòu)建的幾個不同的版本(使用于browser的版本)。
npm run dev 對應(yīng) rollup -w -c build/config.js --environment TARGET:web-full-dev
rollup對應(yīng)的配置信息為:
// Runtime+compiler development build (Browser) "web-full-dev": { entry: resolve("web/runtime-with-compiler.js"), dest: resolve("dist/vue.js"), format: "umd", env: "development", alias: { he: "./entity-decoder" }, banner },
開發(fā)環(huán)境下輸出的umd格式的代碼,入口文件是runtime-with-compiler.js,這個入口文件中是將Vue的構(gòu)建時和運行時的代碼都統(tǒng)一進(jìn)行打包了,通過查看這個入口文件,我們注意到
... import { compileToFunctions } from "./compiler/index" ... const mount = Vue.prototype.$mount Vue.prototype.$mount = function () { }
我們發(fā)現(xiàn),這個文件當(dāng)中,首先將原來定義的Vue.prototype.$mount方法緩存起來,然后將這個方法進(jìn)行重寫,重寫后的方法當(dāng)中,首先判斷是否有自定義的render函數(shù),如果有自定義的render函數(shù)的話,Vue不會通過自帶的compiler對模板進(jìn)行編譯并生成render函數(shù)。但是如果沒有自定義的render函數(shù),那么會調(diào)用compiler對你定義的模板進(jìn)行編譯,并生成render函數(shù),所以通過這個rollup的配置構(gòu)建出來的代碼既支持自定義render函數(shù),又支持template模板編譯:
// 將模板編譯成render函數(shù),并掛載到vm實例的options屬性上 const { render, staticRenderFns } = compileToFunctions(template, { shouldDecodeNewlines, delimiters: options.delimiters }, this) options.render = render options.staticRenderFns = staticRenderFns ... // 調(diào)用之前緩存的mount函數(shù),TODO: 關(guān)于這個函數(shù)里面發(fā)生了什么請戳我 return mount.call(this, el, hydrating)
接下來看第二種構(gòu)建方式:
npm run dev:cjs 對應(yīng)的構(gòu)建腳本 rollup -w -c build/config.js --environment TARGET:web-runtime-cjs
rollup對應(yīng)的配置信息為:
// Runtime only (CommonJS). Used by bundlers e.g. Webpack & Browserify "web-runtime-cjs": { entry: resolve("web/runtime.js"), dest: resolve("dist/vue.runtime.common.js"), format: "cjs", banner }
最后編譯輸出的文件是遵循CommonJs Module同時只包含runtime部分的代碼,它能直接被webpack 1.x和Browserify直接load。它對應(yīng)的入口文件是runtime.js:
import Vue from "./runtime/index" export default Vue
這里沒有重寫Vue.prototye.$mount方法,因此在vm實例的生命周期中,進(jìn)行到beforeMount階段時:
// vm掛載的根元素 if (vm.$options.el) { vm.$mount(vm.$options.el) }
export function mountComponent ( vm: Component, el: ?Element, hydrating?: boolean ): Component { // vm.$el為真實的node vm.$el = el // 如果vm上沒有掛載render函數(shù) if (!vm.$options.render) { // 空節(jié)點 vm.$options.render = createEmptyVNode if (process.env.NODE_ENV !== "production") { /* istanbul ignore if */ if ((vm.$options.template && vm.$options.template.charAt(0) !== "#") || vm.$options.el || el) { warn( "You are using the runtime-only build of Vue where the template " + "compiler is not available. Either pre-compile the templates into " + "render functions, or use the compiler-included build.", vm ) } else { warn( "Failed to mount component: template or render function not defined.", vm ) } } } // 鉤子函數(shù) callHook(vm, "beforeMount") let updateComponent /* istanbul ignore if */ if (process.env.NODE_ENV !== "production" && config.performance && mark) { updateComponent = () => { const name = vm._name const id = vm._uid const startTag = `vue-perf-start:${id}` const endTag = `vue-perf-end:${id}` mark(startTag) const vnode = vm._render() mark(endTag) measure(`${name} render`, startTag, endTag) mark(startTag) vm._update(vnode, hydrating) mark(endTag) measure(`${name} patch`, startTag, endTag) } } else { // updateComponent為監(jiān)聽函數(shù), new Watcher(vm, updateComponent, noop) updateComponent = () => { // Vue.prototype._render 渲染函數(shù) // vm._render() 返回一個VNode // 更新dom // vm._render()調(diào)用render函數(shù),會返回一個VNode,在生成VNode的過程中,會動態(tài)計算getter,同時推入到dep里面 // 在非ssr情況下hydrating為false vm._update(vm._render(), hydrating) } } // 新建一個_watcher對象 // vm實例上掛載的_watcher主要是為了更新DOM // 在實例化watcher的過程中,就會執(zhí)行updateComponent,完成對依賴的變量的收集過程 // vm/expression/cb vm._watcher = new Watcher(vm, updateComponent, noop) hydrating = false // manually mounted instance, call mounted on self // mounted is called for render-created child components in its inserted hook if (vm.$vnode == null) { vm._isMounted = true callHook(vm, "mounted") } return vm }
首先判斷vm實例上是否定義了render函數(shù)。如果沒有,那么就會新建一個新的空vnode并掛載到render函數(shù)上。此外,如果頁面的渲染是通過傳入根節(jié)點的形式:
new Vue({ el: "#app" })
Vue便會打出log信息:
warn( "You are using the runtime-only build of Vue where the template " + "compiler is not available. Either pre-compile the templates into " + "render functions, or use the compiler-included build."
意思就是你當(dāng)前使用的是只包含runtime打包后的代碼,模板的編譯器(即構(gòu)建時)的代碼并不包含在里面。因此,你不能通過掛根節(jié)點或者是聲明式模板的方式去組織你的html內(nèi)容,而只能使用render函數(shù)去書寫模板內(nèi)容。不過報錯信息里面也給出了提示信息就是,你還可以選擇pre-compile預(yù)編譯工具去將template模板編譯成render函數(shù)(vue-loader就起到了這個作用)或者是使用包含了compiler的輸出包,也就是上面分析的即包含compiler,又包含runtime的包。
第三種構(gòu)建方式:
npm run dev:esm 對應(yīng)的構(gòu)建腳本為: rollup -w -c build/config.js --environment TARGET:web-runtime-esm
入口文件及最后構(gòu)建出來的代碼內(nèi)容和第二種一樣,只包含runtime部分的代碼,但是輸出代碼是遵循ES Module規(guī)范的??梢员恢С?b>ES Module的bundler直接加載,如webpack2和rollup。
第四種構(gòu)建方式:
npm run dev:compiler 對應(yīng)的構(gòu)建腳本為: rollup -w -c build/config.js --environment TARGET:web-compiler
不同于前面3種構(gòu)建方式:
// Web compiler (CommonJS). "web-compiler": { entry: resolve("web/compiler.js"), dest: resolve("packages/vue-template-compiler/build.js"), format: "cjs", external: Object.keys(require("../packages/vue-template-compiler/package.json").dependencies) },
這一構(gòu)建對應(yīng)于將關(guān)于Vue模板編譯的成render函數(shù)的compiler.js多帶帶進(jìn)行打包輸出。最后輸出的packages/vue-template-compiler/build.js文件會多帶帶作為一個node_modules進(jìn)行發(fā)布,在你的開發(fā)過程中,如果使用了webpack作為構(gòu)建工具,以及vue-loader,在開發(fā)構(gòu)建環(huán)節(jié),vue-loader便會通過web compiler去處理你的*.vue文件中的模板當(dāng)中的內(nèi)容,將這些模板字符串編譯為render函數(shù)。
文檔請戳我
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/87271.html
摘要:在講解之前先回顧一下筆者在項目開發(fā)中的工作流變化時代此時工作流大致為結(jié)合插件處理視圖處理樣式等庫此時由于依賴少手動引入各種標(biāo)簽結(jié)合調(diào)試界面時代利用指令服務(wù)控制器將邏輯拆分為多個文件利用編譯會將分為全局樣式和組件樣式下載各種依賴此時任需要手動 在講解 webpack 之前先回顧一下筆者在項目開發(fā)中的工作流變化. jquery 時代 此時工作流大致為 jquery 結(jié)合插件處理視圖 bo...
摘要:前端面試題總結(jié)持續(xù)更新中是哪個組件的屬性模塊的組件。都提供合理的鉤子函數(shù),可以讓開發(fā)者定制化地去處理需求。 前端面試題總結(jié)——VUE(持續(xù)更新中) 1.active-class是哪個組件的屬性? vue-router模塊的router-link組件。 2.嵌套路由怎么定義? 在 VueRouter 的參數(shù)中使用 children 配置,這樣就可以很好的實現(xiàn)路由嵌套。 //引入兩個組件 ...
摘要:以為例,編寫來幫助我們完成重復(fù)的工作編譯壓縮我只要執(zhí)行一下就可以檢測到文件的變化,然后為你執(zhí)行一系列的自動化操作,同樣的操作也發(fā)生在這些的預(yù)處理器上。的使用是針對第三方類庫使用各種模塊化寫法以及語法。 showImg(https://segmentfault.com/img/bVbtZYK); 一:前端工程化的發(fā)展 很久以前,互聯(lián)網(wǎng)行業(yè)有個職位叫做 軟件開發(fā)工程師 在那個時代,大家可能...
摘要:第一步新建一個名字自己定義值得關(guān)注的是數(shù)組資源依賴包,提前編譯這里根據(jù)自己項目的具體引用情況自行添加資源依賴包,提前編譯定義文件生成的位置的部分由的名字替換輸出到那個全局變量上和一樣即可。 原因 在使用vue開發(fā)單頁面應(yīng)用時,隨著項目頁面的增多,你會發(fā)現(xiàn)生產(chǎn)環(huán)境的build速度會很慢,同時頁面初始的js大小越來越大。當(dāng)你無法忍受的時候就該優(yōu)化了。 思路 這方面的優(yōu)化無非就是異步加載、提...
摘要:第一步新建一個名字自己定義值得關(guān)注的是數(shù)組資源依賴包,提前編譯這里根據(jù)自己項目的具體引用情況自行添加資源依賴包,提前編譯定義文件生成的位置的部分由的名字替換輸出到那個全局變量上和一樣即可。 原因 在使用vue開發(fā)單頁面應(yīng)用時,隨著項目頁面的增多,你會發(fā)現(xiàn)生產(chǎn)環(huán)境的build速度會很慢,同時頁面初始的js大小越來越大。當(dāng)你無法忍受的時候就該優(yōu)化了。 思路 這方面的優(yōu)化無非就是異步加載、提...
閱讀 2305·2021-09-30 09:47
閱讀 2223·2021-09-26 09:55
閱讀 2954·2021-09-24 10:27
閱讀 1543·2019-08-27 10:54
閱讀 971·2019-08-26 13:40
閱讀 2499·2019-08-26 13:24
閱讀 2423·2019-08-26 13:22
閱讀 1735·2019-08-23 18:38