摘要:目前采用動(dòng)態(tài)加載異步組件的方式來實(shí)現(xiàn)小組件之間的通信。內(nèi)容使用過的都應(yīng)該知道的動(dòng)態(tài)加載組件通過來綁定需要加載的組件??偨Y(jié)本篇主要借助的動(dòng)態(tài)組件和打包單文件來實(shí)現(xiàn)動(dòng)態(tài)加載異步組件,通過的事件總線掛載在上來實(shí)現(xiàn)平級(jí)組件之間的通信。
背景:
目前我們項(xiàng)目都是按組件劃分的,然后各個(gè)組件之間封裝成產(chǎn)品。目前都是采用iframe直接嵌套頁面。項(xiàng)目中我們還是會(huì)碰到一些通用的組件跟業(yè)務(wù)之間有通信,這種情況下iframe并不是最好的選擇,iframe存在跨域的問題,當(dāng)然是postMessage還是可以通信的,但也并非是最好的。目前有這么一個(gè)場景:門戶需要制作通用的首頁和數(shù)據(jù)概覽頁面,首頁和數(shù)據(jù)概覽頁面通過小部件來自由拼接。業(yè)務(wù)組件在制作的時(shí)候只需要提供各個(gè)模塊小部件的url就可以了,可是如果小部件之間還存在聯(lián)系呢?那么iframe是不好的。目前采用Vue動(dòng)態(tài)加載異步組件的方式來實(shí)現(xiàn)小組件之間的通信。當(dāng)然門戶也要提供一個(gè)通信的基線:Vue事件總線(空的Vue實(shí)例對(duì)象)。
內(nèi)容:使用過vue的都應(yīng)該知道vue的動(dòng)態(tài)加載組件components:
Vue通過is來綁定需要加載的組件。那么我們現(xiàn)在需要的就是如何打包組件,如果通過復(fù)制業(yè)務(wù)組件內(nèi)部的代碼,那么這種就需要把依賴全部找齊,并復(fù)制過去(很多情況下會(huì)漏下某個(gè)圖片或css等),這種方式是比較low的,不方便維護(hù)。因此我們需要通過webpack來打包單個(gè)vue文件成js,這邊一個(gè)vue打包成一個(gè)js,不需壓代碼分割,css分離。因?yàn)閏omponent加載時(shí)只需要加載一個(gè)文件即可。打包文件配置如下:
首先在package.json加入打包命令:
"scripts": { ... "build-outCMP": "node build/build-out-components.js" },
Build-out-components.js文件:
"use strict" require("./check-versions")() process.env.NODE_ENV = "production" const ora = require("ora") const path = require("path") const chalk = require("chalk") const webpack = require("webpack") const webpackConfig = require("./webpack.out-components.prod.conf") const spinner = ora("building for sync-components...") spinner.start() webpack(webpackConfig, function (err, stats) { spinner.stop() if (err) throw err process.stdout.write(stats.toString({ colors: true, modules: false, children: false, chunks: false, chunkModules: false }) + " ") if (stats.hasErrors()) { console.log(chalk.red(" Build failed with errors. ")) process.exit(1) } console.log(chalk.cyan(" Build complete. ")) console.log(chalk.yellow( " Tip: built files are meant to be served over an HTTP server. " + " Opening index.html over file:// won"t work. " )) })
webpack.out-components.prod.conf.js文件配置如下
const webpack = require("webpack"); const path = require("path"); const utils = require("./utils"); const OptimizeCSSPlugin = require("optimize-css-assets-webpack-plugin") const {entry, mkdirsSync} = require("./out-components-tools") function resolve(dir) { return path.join(__dirname, "..", dir) } mkdirsSync(resolve("/static/outComponents")) module.exports = { entry: entry, output: { path: resolve("/static/outComponents"), filename: "[name].js", }, resolve: { extensions: [".js", ".vue", ".json"], alias: { "vue$": "vue/dist/vue.esm.js", "@": resolve("src"), } }, externals: { vue: "vue", axios: "axios" }, module: { rules: [ { test: /.vue$/, loader: "vue-loader", options: { esModule: false, // vue-loader v13 更新 默認(rèn)值為 true v12及之前版本為 false, 此項(xiàng)配置影響 vue 自身異步組件寫法以及 webpack 打包結(jié)果 loaders: utils.cssLoaders({ sourceMap: true, extract: false // css 不做提取 }), transformToRequire: { video: "src", source: "src", img: "src", image: "xlink:href" } } }, { test: /.js$/, loader: "babel-loader", include: [resolve("src"), resolve("test")] }, { test: /.(png|jpe?g|gif|svg)(?.*)?$/, loader: "url-loader", options: { limit: 10000, name: utils.assetsPath("img/[name].[hash:7].[ext]") } }, { test: /.(mp4|webm|ogg|mp3|wav|flac|aac)(?.*)?$/, loader: "url-loader", options: { limit: 10000, name: utils.assetsPath("media/[name].[hash:7].[ext]") } }, { test: /.(woff2?|eot|ttf|otf)(?.*)?$/, loader: "url-loader", options: { limit: 10000, name: utils.assetsPath("fonts/[name].[hash:7].[ext]") } } ] }, plugins: [ new webpack.DefinePlugin({ "process.env.NODE_ENV": ""production"" }), // UglifyJs do not support ES6+, you can also use babel-minify for better treeshaking: https://github.com/babel/minify new webpack.optimize.UglifyJsPlugin({ compress: false, sourceMap: true }), // Compress extracted CSS. We are using this plugin so that possible // duplicated CSS from different components can be deduped. new OptimizeCSSPlugin({ cssProcessorOptions: { safe: true } }) ] };
out-components-tools.js文件配置如下:
const glob = require("glob") const fs = require("fs"); const path = require("path"); // 遍歷要打包的組件 let entry = {} var moduleSrcArray = glob.sync("./src/out-components/*") for(var x in moduleSrcArray){ let fileName = (moduleSrcArray[x].split("/")[3]).slice(0, -4) entry[fileName] = moduleSrcArray[x] } // 清理文件 function mkdirsSync(dirname) { if (fs.existsSync(dirname)) { deleteall(dirname) return true; } else { if (mkdirsSync(path.dirname(dirname))) { fs.mkdirSync(dirname); return true; } } } // 刪除文件下的文件 function deleteall(path) { ??var files = []; ??if(fs.existsSync(path)) { ????files = fs.readdirSync(path); ????files.forEach(function(file, index) { ??????var curPath = path + "/" + file; ??????if(fs.statSync(curPath).isDirectory()) { // recurse ????????deleteall(curPath); ??????} else { // delete file ????????fs.unlinkSync(curPath); ??????} ????}); ??} }; exports.entry = entry exports.mkdirsSync = mkdirsSync
build-out-components是打包的入口文件,webpack.out-components.prod.conf.js是webpack打包的配置文件,out-components-tools.js是工具庫,這邊是打包的entry自動(dòng)獲取(默認(rèn)為src/out-components),還有自動(dòng)刪除之前打包的文件。
目前的文件目錄為
通過打包生產(chǎn)文件:
在static下outComponents文件夾內(nèi)的js文件。(最終打包需要打包到dist下面,這邊做測試先打包在static文件下,方便后續(xù)動(dòng)態(tài)組件ajax獲取組件使用)
門戶的小部件是通過配置url,和調(diào)整布局來生產(chǎn)的。因此業(yè)務(wù)組件至此已經(jīng)完成了。只需要提供對(duì)門戶暴露的url即可。 接下來就是門戶這邊加載動(dòng)態(tài)組件的實(shí)現(xiàn)了。門戶這邊就相對(duì)簡單了??慈缦聢D配置:
門戶通過component的動(dòng)態(tài)組件來實(shí)現(xiàn)加載異步組件,通過ajax請(qǐng)求剛才打包的url,然后實(shí)例化函數(shù)new Function來賦值給mode(new Function之所以分成2部,是因此效驗(yàn)規(guī)則的問題,可忽略)。如下圖演示
這樣就實(shí)現(xiàn)了動(dòng)態(tài)加載異步組件了。門戶和業(yè)務(wù)組件可以各個(gè)開發(fā),任何業(yè)務(wù)開發(fā)數(shù)據(jù)概覽,門戶都不需要改代碼,只需要界面上配置url即可。這個(gè)異步加載組件已經(jīng)結(jié)束了。這邊門戶需要封裝一封實(shí)現(xiàn)異步組件。父級(jí)只需要傳入url即可。這邊還有個(gè)可以優(yōu)化的是,可以把mode優(yōu)先緩存,那么不需要每次都去加載請(qǐng)求。如下:
我們可以看到在門戶的一個(gè)數(shù)據(jù)概覽頁面上加載了多個(gè)異步組件,那么異步組件之間也是可能存在通信的,這樣該如何做呢?因?yàn)楝F(xiàn)在已經(jīng)不是iframe嵌套了,可以通過監(jiān)聽一個(gè)組件,然調(diào)用另一個(gè)組件的方法,這樣確實(shí)可以實(shí)現(xiàn)平級(jí)組件間的通信,但這樣勢必不可取的,因?yàn)橐坏┻@樣做了門戶必須要根據(jù)業(yè)務(wù)來輔助,修改代碼來實(shí)現(xiàn)功能。因此這邊借用門戶來生成vue事件總線(空的vue實(shí)例)來實(shí)現(xiàn)。
門戶代碼如下: 在this.$root上掛在一個(gè)事件總線:
created () { if (!this.$root.eventBus) { this.$root.eventBus = new Vue() } }
然后業(yè)務(wù)組件之間就可以根據(jù)自己的業(yè)務(wù)實(shí)現(xiàn)通信:
組件一和組件二代碼如下:
這是一個(gè)外部組件a1
這也是外部組件哦這是a1傳來的{{a1}}
業(yè)務(wù)組件就可以根據(jù)this.$root.eventBus和vue上的事件傳遞($emit, $on)來實(shí)現(xiàn)相互的通信。
總結(jié):本篇主要借助vue的動(dòng)態(tài)組件和webpack打包單文件來實(shí)現(xiàn)動(dòng)態(tài)加載異步組件,通過vue的事件總線掛載在this.$root上來實(shí)現(xiàn)平級(jí)組件之間的通信。
拓展方向:這個(gè)方式不僅僅可以應(yīng)用在門戶單個(gè)頁面上的小部件上,同樣如果某個(gè)項(xiàng)目中的頁面文件需要復(fù)用時(shí),不想通過代碼的復(fù)制,同樣可以再那個(gè)文件配置打包單文件配置,打包出的文件在領(lǐng)一個(gè)項(xiàng)目中動(dòng)態(tài)加載出來即可。這種模式與通用組件的install模式是有點(diǎn)類似的,只是這個(gè)單文件vue不是通用的,但同樣可以達(dá)到打包復(fù)用頁面。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/108691.html
摘要:動(dòng)態(tài)組件如果我們打算在一個(gè)地方根據(jù)不同的狀態(tài)引用不同的組件的話,比如頁,那么給我們提供動(dòng)態(tài)組件。實(shí)現(xiàn)動(dòng)態(tài)組件的加載。的值可以是一個(gè)已經(jīng)注冊的組件的名字或者一個(gè)組件的選對(duì)象。 動(dòng)態(tài)組件 如果我們打算在一個(gè)地方根據(jù)不同的狀態(tài)引用不同的組件的話,比如tab頁,那么Vue給我們提供動(dòng)態(tài)組件。 基本使用 Parent.vue {{btn.name}} ...
摘要:發(fā)現(xiàn)了動(dòng)態(tài)組件異步組件這個(gè)東西簡直是救命啊動(dòng)態(tài)組件異步組件思路分析有了動(dòng)態(tài)組件這個(gè)東西之后,我們就可以根據(jù)綁定不同的值來渲染不同的組件。每個(gè)組件要傳給子組件的值和接收子組件的事件也可以動(dòng)態(tài)的綁定上去。 推動(dòng)我實(shí)現(xiàn)這個(gè)功能的業(yè)務(wù)背景 最近接到一個(gè)讓我很頭疼的需求:產(chǎn)品要求我們系統(tǒng)頁面上所有的模塊都支持順序的變動(dòng)。比如有 模塊A、B、C、D,可以無序的展示在頁面上,我剛聽到這個(gè)需求的時(shí)候我...
摘要:,常規(guī)組件,卒。小結(jié)總之呢,上面分析了在中編譯遠(yuǎn)程模板的可能性,最后得出了兩種方法異步組件,應(yīng)該是官方的推薦方法動(dòng)態(tài)組件,變通之法,論壇上發(fā)現(xiàn)的思路當(dāng)然如果有其他方法歡迎交流,本文如果有不嚴(yán)謹(jǐn)不正確的地方也歡迎指出本文發(fā)自我的,原文鏈接我的 說明 有些時(shí)候你可能需要從后臺(tái)獲取模板,并在前臺(tái)在自己編譯,這在用 AngularJS 1.x 的時(shí)候似乎很常見,可以直接用 ng-include...
摘要:,常規(guī)組件,卒。小結(jié)總之呢,上面分析了在中編譯遠(yuǎn)程模板的可能性,最后得出了兩種方法異步組件,應(yīng)該是官方的推薦方法動(dòng)態(tài)組件,變通之法,論壇上發(fā)現(xiàn)的思路當(dāng)然如果有其他方法歡迎交流,本文如果有不嚴(yán)謹(jǐn)不正確的地方也歡迎指出本文發(fā)自我的,原文鏈接我的 說明 有些時(shí)候你可能需要從后臺(tái)獲取模板,并在前臺(tái)在自己編譯,這在用 AngularJS 1.x 的時(shí)候似乎很常見,可以直接用 ng-include...
摘要:當(dāng)一個(gè)的項(xiàng)目體積變得十分龐大的時(shí)候,使用的代碼分離功能將,或的代碼進(jìn)行分離并按需加載,會(huì)極大的提高的首屏加載速度。如果我們使用函數(shù)在中返回模塊作為載荷,就實(shí)現(xiàn)了懶加載。 當(dāng)一個(gè)Vue的項(xiàng)目體積變得十分龐大的時(shí)候,使用Webpack的代碼分離功能將Vue Components,routes或Vuex的代碼進(jìn)行分離并按需加載,會(huì)極大的提高App的首屏加載速度。 showImg(https:...
閱讀 2308·2021-09-30 09:47
閱讀 2226·2021-09-26 09:55
閱讀 2957·2021-09-24 10:27
閱讀 1546·2019-08-27 10:54
閱讀 972·2019-08-26 13:40
閱讀 2502·2019-08-26 13:24
閱讀 2429·2019-08-26 13:22
閱讀 1737·2019-08-23 18:38