Home > Article > Web Front-end > Component sharing that loads vue remote code
In our vue project (especially the backend system), there will always be some scenarios that require multiple business lines to jointly develop the same project. If each business team provides some private display components to the framework, but these components are not It cannot be packaged together with the framework, because the framework cannot be repeatedly built and released due to frequent changes in a private module. In this scenario we need a component that loads remote asynchronous code to complete loading these components into the framework.
vue-cli is Vue's officially recommended project construction scaffolding. It provides functions commonly used in the development process, such as hot reloading, building, debugging, unit testing, code detection and other functions. Our asynchronous remote component this time will be developed based on vue-cli.
Requirements Analysis
How to load remote code?
How to register the loaded code into the framework.
How the parent component communicates with the remotely imported component.
How the remote code reuses the libraries that have been introduced in the framework.
Avoid unnecessary requests caused by remote code being called multiple times like v-for.
Load remote code
The remote code should be stored on an accessible URL, so that we can request this link through an HTTP client similar to Axios to the source code.
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; } };
The above is the basic code. For convenience, I will omit the repeated code parts in the following examples.
Registering code into the framework
This part is a bit cumbersome and involves multiple issues:
The browser does not support .vue template or ES.next syntax, and the module requires It can be used after compilation.
Dealing with this part is relatively simple. We define a webpack configuration file ourselves to package these templates.
// 在 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 } }) ] };
At this point our module has been compiled into a file that the framework can recognize.
1. How to convert string into js object.
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. The converted js object cannot be recognized by vue.
There are two possibilities that may cause this problem:
// 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 })
At this point, the remote component has been introduced into the framework.
How the parent component communicates with the remotely introduced component
There is a problem here. There are three layers of communication from the view component to the remote asynchronous loading component to the actual business component. The middle layer As a public component, the remote asynchronous component cannot be modified and requires the view component to communicate directly with the actual business component. Both vuex and eventBus solutions are too cumbersome. Here we use $attrs and $listeners (vue v2.4+) to achieve "fallthrough" (cross-level communication of vue components).
// 修改 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 ... 以下省略千军万码 }
How remote code reuses libraries that have been introduced in the framework
We don’t want to see the repeated introduction of larger libraries or plug-ins in remote components and frameworks. This part It is still in the practical stage. The main idea is to mount the public library to the Vue prototype chain to realize the public reuse of components Vue.prototype.$xxx.
// 全局添加 axios 对象 import axios from 'axios'; Vue.prototype.$http = axios;
The introduced remote component can access the public package in the framework. At this time, webpack needs to be configured so that the code of the public package is not included when packaging the remote component.
// webpack.sync-components.prod.conf.js 添加 externals: { vue: 'vue', 'element-ui': 'element-ui', axios: 'axios' }
Avoid unnecessary requests caused by remote code being called multiple times like v-for.
In this part, we directly use a global variable as a dictionary to store an array with request address: data as the sub-item.
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); }
At this point, the asynchronous remote component can be loaded and communicated with the framework.
Please visit github to obtain the source code in this article. The components have been released to NPM and can be installed directly.
Related recommendations:
Detailed explanation of how to read or grab remote code instances in php
ajax achieves remote control Communication
Example sharing of PHP judging the existence of remote files
The above is the detailed content of Component sharing that loads vue remote code. For more information, please follow other related articles on the PHP Chinese website!