前言:最近在掘金逛的時候,無意中看到前滴滴前端架構黃軼大佬,看到了大佬分享的一篇博客 滴滴 webapp 5.0 Vue 2.0 重構經驗分享 ,對於其中第5個問題(異步加載的業務線組件,如何動態注冊?)的解決辦法深為震撼。
滴滴的首屏展示的同步業務線組件,對於一些業務線(比如順風車,出租車,快車),這些業務線其實都可以成為單獨spa應用,由於種種原因(我感覺組件化應該是很大的一個原因,構建包很小只打核心和初始化給用戶顯示的,剩下的業務線通過Vue異步組件注冊引入)。Vue2提供的有異步注冊組件
我們注意到,官方提供的demo是異步引入本工程下的組件,對於滴滴(每個業務線是一個工程,在不同的git倉庫存儲)。滴滴的做法是將不同的業務線單獨構建,構建的每個業務線是一個Vue實例(不知道這塊是否理解有錯,歡迎指正),然后將其傳遞給resolve函數(我感覺滴滴在這塊的處理特別秒:比如全局的Vue,Vuex,公共業務組件的共享,異步業務組件的全局掛載,動態路由注冊很是妙哉,希望大家去原地址查看,我這里就不贅述了)。下面是我自己的一個嘗試:
首先,在一個目錄下運行 vue init webpack-simple async-component , 新建一個工程,用來開發需要異步注冊的組件,在src下新建component目錄,並創建index.js和async-component.vue文件,完整目錄結構如下
// async-component.vue <template> <div class="async-component"> <h1 class="name">{{name}}</h1> <div class="age">{{age}}</div> </div> </template> <script> export default { name: 'async-component', data () { return { } }, props: { name: { type: String, default: '張三' }, age: { type: Number, default: 18 } } } </script> <style lang="scss" scoped> .async-component { .name { color: red; } .age { color: green; } } </style>
// index.js /** * Created by hs on 2019/7/15 */ import AsyncComponent from './async-component' if (typeof window !== 'undefined' && window.Vue) { // 跟滴滴一樣,將業務線異步組件存到window.modules下 window.modules = { 'async-component': AsyncComponent } }
// webpack.config.js 修改入口和出口文件 entry: process.NODE_ENV === 'development' ? './src/main.js' : './src/component/index.js', output: { path: path.resolve(__dirname, './dist'), publicPath: '/dist/', filename: 'async-component.js' },
在 async-component 項目根目錄運行 npm run build,然后dist生成了 async-component.js 文件,然后我們進入到dist文件夾下,運行 puer (一個web服務器,如果提示沒有這個命令),用npm或者yarn全局安裝下,puer啟動后默認地址為 http://localhost:8000 , 在瀏覽器測試下http://localhost:8000/async-component.js , 能成功訪問即可。然后啟動下我的Vue測試項目,地址為 http://localhost:8080 ,Vue測試項目代碼如下:
// App.vue <template> <div id="app"> <el-button type="primary" @click="changeComponent">加載異步組件</el-button> <component :is="componentName" name="小明" :age="50"></component> </div> </template> <script> export default { name: 'App', data() { return {
name: 'async-component' }; }, methods: { changeComponent () { this.componentName = this.name }, loadAsyncComponent () { Vue.component(this.name, (resolve, reject) => { this.loadScript(`//localhost:8000/${this.name}.js`) .then(() => { resolve(window.modules[this.name]) }).catch((e) => { reject(e) }) }) }, loadScript (url){ return new Promise((resolve, reject) => { var script = document.createElement ("script") script.type = "text/javascript"; if (script.readyState){ //IE script.onreadystatechange = function(){ if (script.readyState == "loaded" || script.readyState == "complete"){ script.onreadystatechange = null; resolve() } }; } else { //Others script.onload = function(){ console.log('complete') resolve() }; script.onerror = function (e) { reject(e) } } script.src = url; document.getElementsByTagName("body")[0].appendChild(script); }) } }, mounted() { this.loadAsyncComponent() } } </script> <style lang="scss"> * { margin: 0; padding: 0; font-family: Microsoft YaHei; } #app { background: #f1f1f5; } </style>
瀏覽訪問 http://localhost:8080,如下
點擊按鈕后如下
個人感覺滴滴這個解決方法真的很精妙,但是細細想了一下,在公司的項目中好像用不到...這個方案是滴滴結合自身業務想出來的,技術還是要結合實際項目最好。最后引用下大佬對此相關的回復:滴滴這個場景是不適合用 webpack.ensure 的,因為是動態加載其它業務線的代碼,壓根代碼就不在一個倉庫下,只能通過 loadscript 方式加載,所以也有動態注冊路由的需求。技術重構往往伴隨着產品重構,單純的技術重構不太現實,除非特別閑。。所以慢慢來吧,新項目可以用 vue2 了~