在我們的vue 專案中(特別是後台系統),總是會出現一些需要多業務線共同開發同一個專案的場景,如果各業務團隊向框架中提供一些私有的展示元件,但是這些元件並不能和框架一起打包,因為框架不能因為某個私有模組的頻繁變更而重複建置發布。在這種場景下我們需要一個載入遠端非同步程式碼的元件來完成將這些元件載入到框架中。
vue-cli 作為 Vue 官方推薦的項目構建腳手架,它提供了開發過程中常用的,熱重載,構建,調試,單元測試,代碼檢測等功能。我們本次的非同步遠端組件將基於 vue-cli 開發。
需求分析
如何載入遠端的程式碼?
如何註冊載入後的程式碼到框架中。
父元件如何和遠端引入的元件通訊。
遠端程式碼如何重複使用框架中已引入的函式庫。
避免遠端程式碼被類似 v-for 多次呼叫所導致的不必要請求。
載入遠端程式碼
遠端程式碼應該儲存在一個可存取的URL 上,這樣我們透過Axios 類似的HTTP client 請求這個連結拿到源碼。
import Axios from 'axios'; export default { name: 'SyncComponent', props: { // 父组件提供请求地址 url: { type: String, default: '' } }, data() { return { resData: '' }; }, async mounted() { if (!this.url) return; const res = await Axios.get(this.url); // 我们在组件挂载完成时,请求远端代码并存储结果。 this.resData = res.data; } };
以上是基礎程式碼 為了方便 一下範例中 我將省略重複的程式碼部分。
註冊程式碼到框架中
這部分有些繁瑣,涉及到多個問題:
瀏覽器不支援.vue 範本或ES.next 語法,模組需要編譯後才可以使用。
處理這部分比較簡單,我們自己定義一個webpack設定檔來打包這些模板。
// 在 build 目录下新建 webpack.sync-components.prod.conf.js 文件 const webpack = require('webpack'); const path = require('path'); const utils = require('./utils'); const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin') function resolve(dir) { return path.join(__dirname, '..', dir) } module.exports = { // 此处引入要打包的组件 entry: { componentA: resolve('/src/views/component-a.vue') }, // 输出到静态目录下 output: { path: resolve('/static/'), filename: '[name].js', }, resolve: { extensions: ['.js', '.vue', '.json'], alias: { 'vue$': 'vue/dist/vue.esm.js', '@': resolve('src'), } }, module: { rules: [ { test: /\.vue$/, loader: 'vue-loader', options: { esModule: false, // ****** vue-loader v13 更新 默认值为 true v12及之前版本为 false, 此项配置影响 vue 自身异步组件写法以及 webpack 打包结果 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"' }), // 压缩JS new webpack.optimize.UglifyJsPlugin({ compress: false, sourceMap: true }), // 压缩CSS 注意不做提取 new OptimizeCSSPlugin({ cssProcessorOptions: { safe: true } }) ] };
至此我們的模組已經被編譯成框架可以辨識的檔案。
1.如何將字串轉換成js物件。
new Function。 async mounted() { if (!this.url) return; const res = await Axios.get(this.url); let Fn = Function; this.mode = new Fn(`return ${res.data}`)(); }
1.轉換後的js物件並不能被vue辨識。
有兩種可能會導致這個問題:
// vue-loader v13 esModule 更新 默认值为 true, v12及之前版本为 false, 此项配置影响 vue 自身异步组件写法以及 webpack 打包结果 { test: /\.vue$/, loader: 'vue-loader', options: { esModule: false ... 以下省略千军万码 } } // UglifyJs 需要取消变量名替换配置,此配置并不会极大影响压缩率 new webpack.optimize.UglifyJsPlugin({ compress: false, sourceMap: true })
至此 遠端元件就被引進到框架中了。
父元件如何和遠端引入的元件通訊
這裡有一個問題,從view元件到遠端非同步載入元件再到實際業務元件通訊一共三層,中間層遠端非同步元件作為公共元件不可修改,需要view元件直接向實際業務元件通訊。 vuex 和 eventBus 方案都過於繁瑣,這裡我們採用 $attrs 和 $listeners(vue v2.4+), 來實現 “fallthrough”(vue元件跨層級通訊)。
// 修改 sync-component.vue 组件 // 新增 v-bind="$attrs" v-on="$listeners" <component :is="mode" v-bind="$attrs" v-on="$listeners"> </component> // inheritAttrs: true export default { name: 'SyncComponent', props: { // 父组件提供请求地址 url: { type: String, default: '' } }, inheritAttrs: true ... 以下省略千军万码 }
遠端程式碼如何復用框架中已引入的庫
我們不希望看到遠端元件和框架中存在較大庫或插件的重複的引入,這部分內容尚處在實踐階段,主要思路是把公共庫掛載到Vue原型鏈上實現組件公共複用Vue.prototype.$xxx。
// 全局添加 axios 对象 import axios from 'axios'; Vue.prototype.$http = axios;
引入的遠端元件可以存取到框架中的公用套件了,這時候還需要設定 webpack 讓遠端元件打包時不要包含公用套件的程式碼。
// webpack.sync-components.prod.conf.js 添加 externals: { vue: 'vue', 'element-ui': 'element-ui', axios: 'axios' }
避免因遠端程式碼被類似 v-for 多次呼叫所導致的不必要請求。
這部分我們直接用一個全域變數做字典,儲存 以 請求位址:資料 為子項的陣列。
async mounted() { if (!this.url) return; // Cache 缓存 根据 url 参数 if (!window.SyncComponentCache) { window.SyncComponentCache = {}; } let res; if (!window.SyncComponentCache[this.url]) { window.SyncComponentCache[this.url] = Axios.get(this.url); res = await window.SyncComponentCache[this.url]; } else { res = await window.SyncComponentCache[this.url]; } let Fn = Function; this.mode = new Fn(`return ${res.data}`)(); console.log(this.mode); }
至此,非同步遠端元件就可以載入並和框架進行通訊了。
本文中的原始碼請訪問 github 獲取,元件已經發佈到NPM 上,可以直接安裝。
相關推薦:
以上是載入 vue 遠端程式碼的元件分享的詳細內容。更多資訊請關注PHP中文網其他相關文章!