摘要:源碼解析起因最近在搞框架的熱加載方案,自然是少不了向成熟的框架學(xué)習(xí)偷窺。這將銷毀并重建整個(gè)組件包括子組件。通過使用說明可以看出,暴露的接口還是很清晰的,下面來看下具體源碼實(shí)現(xiàn)。
Vue-hot-reload-api 源碼解析 起因
最近在搞san框架的熱加載方案,自然是少不了向成熟的框架學(xué)習(xí)(偷窺ing)。熱加載方案基本也只是主流框架在做,且做的比較成熟,大部分應(yīng)用開發(fā)者并不會(huì)接觸到這部分東西,所以相應(yīng)的資料比較少。google了一下這個(gè)庫,發(fā)現(xiàn)木有人做相應(yīng)的解析,順手記錄下好了。
什么是Vue-hot-reload-api?眾所周知,*.vue文件為廣大開發(fā)者提供了良好的開發(fā)體驗(yàn),vue-loader的原理不多贅述,在vue的腳手架中,webpack通過vue-loader來解析*.vue文件,把template、js和style文件分離并讓相應(yīng)的loader去處理。
在這個(gè)過程中,vue-loader還會(huì)做些其他事情,比如向client端注入hot-reload相應(yīng)的代碼,構(gòu)建時(shí)編譯等等。
webpack的hmr原理也不多說了,vue的熱加載就是通過注入的代碼來實(shí)現(xiàn)組件的熱更新,下面來看下使用時(shí)的文檔和源碼。
用法先來看下官方文檔。
你僅會(huì)在開發(fā)一個(gè)基于 Vue components 構(gòu)建工具的時(shí)候用到這個(gè)。對(duì)于普通的應(yīng)用,使用 vue-loader 或者 vueify 就可以了。
文檔中明確說明了,一般使用不需要用到這個(gè),只有在開發(fā)相應(yīng)的構(gòu)建工具時(shí)才會(huì)用到。
// 定義一個(gè)組件作為選項(xiàng)對(duì)象 // 在vue-loader中,這個(gè)對(duì)象是Component.options const myComponentOptions = { data () { ... }, created () { ... }, render () { ... } } // 檢測(cè) Webpack 的 HMR API // https://doc.webpack-china.org/guides/hot-module-replacement/ if (module.hot) { const api = require("vue-hot-reload-api") const Vue = require("vue") // 將 API 安裝到 Vue,并且檢查版本的兼容性 api.install(Vue) // 在安裝之后使用 api.compatible 來檢查兼容性 if (!api.compatible) { throw new Error("vue-hot-reload-api與當(dāng)前Vue的版本不兼容") } // 此模塊接受熱重載 // 在這兒多說一句,webpack關(guān)于hmr的文檔實(shí)在是太。。。 // 各大框架的loader中關(guān)于hmr的實(shí)現(xiàn)都是基于自身模塊接受更新來實(shí)現(xiàn) module.hot.accept() if (!module.hot.data) { // 為了將每一個(gè)組件中的選項(xiàng)變得可以熱加載, // 你需要用一個(gè)不重復(fù)的id創(chuàng)建一次記錄, // 只需要在啟動(dòng)的時(shí)候做一次。 api.createRecord("very-unique-id", myComponentOptions) } else { // 如果一個(gè)組件只是修改了模板或是 render 函數(shù), // 只要把所有相關(guān)的實(shí)例重新渲染一遍就可以了,而不需要銷毀重建他們。 // 這樣就可以完整的保持應(yīng)用的當(dāng)前狀態(tài)。 api.rerender("very-unique-id", myComponentOptions) // --- 或者 --- // 如果一個(gè)組件更改了除 template 或 render 之外的選項(xiàng), // 就需要整個(gè)重新加載。 // 這將銷毀并重建整個(gè)組件(包括子組件)。 api.reload("very-unique-id", myComponentOptions) } }
通過使用說明可以看出,vue-hot-reload-api暴露的接口還是很清晰的,下面來看下具體源碼實(shí)現(xiàn)。
var Vue // late bind var version // 全局對(duì)象__VUE_HOT_MAP__來保存所有的構(gòu)造器和實(shí)例 var map = window.__VUE_HOT_MAP__ = Object.create(null) var installed = false // 這個(gè)參數(shù)來判斷是vue-loader還是vueify在調(diào)用 var isBrowserify = false // 2.0.0-alpha.7版本前的初始化鉤子名是init,這個(gè)參數(shù)來作區(qū)分 var initHookName = "beforeCreate" exports.install = function (vue, browserify) { if (installed) return installed = true // 判斷打包的是esodule還是普通的js函數(shù) Vue = vue.__esModule ? vue.default : vue version = Vue.version.split(".").map(Number) isBrowserify = browserify // compat with < 2.0.0-alpha.7 if (Vue.config._lifecycleHooks.indexOf("init") > -1) { initHookName = "init" } exports.compatible = version[0] >= 2 // 兼容性,1.x和2.x的框架實(shí)現(xiàn)和loader實(shí)現(xiàn)都有很大差異 if (!exports.compatible) { console.warn( "[HMR] You are using a version of vue-hot-reload-api that is " + "only compatible with Vue.js core ^2.0.0." ) return } } /** * Create a record for a hot module, which keeps track of its constructor * and instances * * @param {String} id * @param {Object} options */ exports.createRecord = function (id, options) { var Ctor = null // 判斷傳入的options是對(duì)象還是函數(shù) if (typeof options === "function") { Ctor = options options = Ctor.options } // 燥起來,這個(gè)函數(shù)會(huì)在組件初始化和結(jié)束時(shí)的生命周期注入hook函數(shù) // 當(dāng)實(shí)例化以后,hook函數(shù)調(diào)用會(huì)把實(shí)例記錄到map中 // destroy后會(huì)從map中刪除實(shí)例自身 makeOptionsHot(id, options) map[id] = { Ctor: Vue.extend(options), instances: [] } } /** * Make a Component options object hot. * * @param {String} id * @param {Object} options */ function makeOptionsHot (id, options) { // 注入hook函數(shù),到達(dá)相應(yīng)聲明周期后執(zhí)行 injectHook(options, initHookName, function () { map[id].instances.push(this) }) injectHook(options, "beforeDestroy", function () { var instances = map[id].instances instances.splice(instances.indexOf(this), 1) }) } /** * Inject a hook to a hot reloadable component so that * we can keep track of it. * * @param {Object} options * @param {String} name * @param {Function} hook */ function injectHook (options, name, hook) { // 判斷未注入時(shí),生命周期init/beforeDestroy是否已經(jīng)有了函數(shù) // 不存在的話,直接把生命周期函數(shù)置為[hook] // 存在的話,判斷是否為Array,從而把已存在的函數(shù)和hook連接起來 var existing = options[name] options[name] = existing ? Array.isArray(existing) ? existing.concat(hook) : [existing, hook] : [hook] } // 不得不說,這個(gè)一開始確實(shí)沒搞懂是為啥要包一層 // 自己實(shí)現(xiàn)的時(shí)候才知道,當(dāng)有error彈出時(shí) // 如果不手動(dòng)這樣接住error,webpack會(huì)接到然后立即location.reload() // 根本來不及看reload之前給出的提示 // 所以要手動(dòng)處理下error function tryWrap (fn) { return function (id, arg) { try { fn(id, arg) } catch (e) { console.error(e) console.warn("Something went wrong during Vue component hot-reload. Full reload required.") } } } exports.rerender = tryWrap(function (id, options) { var record = map[id] // 邊界處理 // 如果沒有傳options或者已經(jīng)為空 // 會(huì)把這個(gè)構(gòu)造函數(shù)生成的所有實(shí)例強(qiáng)制刷新并返回 if (!options) { record.instances.slice().forEach(function (instance) { instance.$forceUpdate() }) return } // 判斷是否是構(gòu)造函數(shù)還是proto if (typeof options === "function") { options = options.options } // 修改map對(duì)象中的Ctor以便記錄 record.Ctor.options.render = options.render record.Ctor.options.staticRenderFns = options.staticRenderFns // .slice方法保證了instances的length是有效的 record.instances.slice().forEach(function (instance) { // 把更新過的模塊render函數(shù)和靜態(tài)方法指到舊的實(shí)例上 // reset static trees // 然后重刷新 instance.$options.render = options.render instance.$options.staticRenderFns = options.staticRenderFns instance._staticTrees = [] // reset static trees instance.$forceUpdate() }) }) exports.reload = tryWrap(function (id, options) { var record = map[id] if (options) { if (typeof options === "function") { options = options.options } makeOptionsHot(id, options) if (version[1] < 2) { // preserve pre 2.2 behavior for global mixin handling record.Ctor.extendOptions = options } // 其實(shí)最開始的commit中,并未繼承Ctor的父類,是直接Vue.extend(options) // 對(duì)vue了解不深,不知道為啥改成這樣 // 有興趣的同學(xué)可以思考下 var newCtor = record.Ctor.super.extend(options) record.Ctor.options = newCtor.options record.Ctor.cid = newCtor.cid record.Ctor.prototype = newCtor.prototype // 2.0早期版本兼容 if (newCtor.release) { // temporary global mixin strategy used in < 2.0.0-alpha.6 newCtor.release() } } record.instances.slice().forEach(function (instance) { // 判斷vNode和上下文是否存在 // 不存在的需要手動(dòng)刷新 if (instance.$vnode && instance.$vnode.context) { instance.$vnode.context.$forceUpdate() } else { console.warn("Root or manually mounted instance modified. Full reload required.") } }) })
短短的100多行代碼,從這個(gè)庫支持2.x的第一個(gè)commit讀起,慢慢由簡(jiǎn)單實(shí)現(xiàn)到覆蓋大部分邊界及兼容性考慮,再到vue-loader的調(diào)用,webpack的hmr各種坑和debug,這個(gè)過程很受啟發(fā)。
更多的前端、健身內(nèi)容,請(qǐng)點(diǎn)擊 將就的博客查看
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/87350.html
摘要:筆者系貢獻(xiàn)者之一官方說明簡(jiǎn)單來說就是將文件變成,然后放入瀏覽器運(yùn)行。部分首先分析部分從做右到左,也就是被先后被和處理過了。源碼解析之二源碼解析之三寫作中源碼解析之四寫作中作者博客作者微博 筆者系 vue-loader 貢獻(xiàn)者(#16)之一 官方說明 vue-loader is a loader for Webpack that can transform Vue components ...
摘要:包中導(dǎo)出的默認(rèn)是運(yùn)行時(shí)構(gòu)建。當(dāng)然,我們期待的是只修改代碼,不用重新運(yùn)行命令,甚至不需要刷新瀏覽器即看到代碼的改動(dòng)效果,這時(shí)候需要新的插件來配置實(shí)現(xiàn)的熱重載。 首先已經(jīng)全局安裝了node/vue/webpack; 新建文件夾demo4并初始化 cd demo4 npm init -y 這是頁面會(huì)生成一個(gè)package.json文件。 安裝webpack及相關(guān)插件 npm install ...
摘要:參考令人困惑的地方項(xiàng)目名稱項(xiàng)目名稱版本描述作者開源協(xié)議主文件指定了運(yùn)行腳本命令的命令行縮寫,比如這是的指定了運(yùn)行時(shí),所要執(zhí)行的命令。要解析并且完成相應(yīng)的功能,這些基本都是必須的。 參考 Webpack——令人困惑的地方 package.json { name: 項(xiàng)目名稱, //項(xiàng)目名稱 version: 1.0.0, //版本 description: vue+...
摘要:但我們今天學(xué)的是,原因我還不會(huì)而且新手還是學(xué)習(xí)為主吧。原因中文文檔全,學(xué)習(xí)曲線簡(jiǎn)單,很容易上手。后續(xù)總結(jié)在學(xué)習(xí)打包工具過程中由于出現(xiàn)的問題各種蛋疼,讓很多人都半途而廢。大家互相學(xué)習(xí)共同進(jìn)步本節(jié)講的都是很基礎(chǔ)的東西,自己可以延展一下。 寫這篇文章的時(shí)候先說一下原因:webpack:現(xiàn)在很流行的打包工具;推薦原因:學(xué)習(xí)成本比gulp,fis3等簡(jiǎn)單,就是這么直接!vue:國(guó)人開發(fā)的MVVM...
摘要:線上另加入了排行榜功能,如需查看源碼的,請(qǐng)切換到分支整個(gè)項(xiàng)目結(jié)構(gòu)清晰,尤其單文件組件的表現(xiàn)力尤為突出,使得每個(gè)組件的邏輯都沒有過于復(fù)雜,而且在的統(tǒng)籌下,的單向數(shù)據(jù)流模式使得所有的變化都在可控制可預(yù)期的范圍內(nèi)。 2016注定不是個(gè)平凡年,無論是中秋節(jié)問世的angular2,還是全面走向穩(wěn)定的React,都免不了面對(duì)另一個(gè)競(jìng)爭(zhēng)對(duì)手vue2。喜歡vue在設(shè)計(jì)思路上的先進(jìn)性(原諒我用了這么一個(gè)...
閱讀 3069·2021-09-28 09:43
閱讀 902·2021-09-08 09:35
閱讀 1441·2019-08-30 15:56
閱讀 1183·2019-08-30 13:00
閱讀 2732·2019-08-29 18:35
閱讀 1829·2019-08-29 14:07
閱讀 3432·2019-08-29 13:13
閱讀 1333·2019-08-29 12:40