Vue渲染远端组件的方法
在项目中,我们有时候需要按不同条件,渲染不同的组件,把这些组件都打包到项目中,会影响包的大小,修改组件后也需要上线整个项目,对开发、上线、维护都不利。
我们利用Vue的is属性,去加载一个动态组件
首先我们需要把组件打包,放到服务器
/** 打包remote components */
const webpack = require('webpack');
const path = require('path');
const utils = require('./utils');
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
let componentNames = [] //远端组件名 array
let param = process.argv[2] // npm run buildRmotes remote-xfnj | param = remote-xfnj
let prePath = '/src/insurances/'
const componentsObj = {}
console.log(process.argv)
// let arr = require('../src/structure/'+param+'.json')
// console.log(arr)
if (param) {
componentNames = require('../src/asyncBuildConfig/' + param + '.json')
} else {
componentNames = require('../src/asyncBuildConfig/index.js')
}
console.log(componentNames)
function resolve (dir) {
return path.join(__dirname, '..', dir)
}
if (componentNames.length <= 0) {
return
}
componentNames.map((val) => {
componentsObj[val.name] = resolve(prePath + val.path + '.vue')
})
module.exports = {
entry: {
// componentA: resolve('/src/views/component-a.vue'),
...componentsObj
// componentB: resolve('/src/views/component.vue')
},
output: {
path: resolve('/main/'),
filename: '[name].min.js',
},
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': resolve('src'),
}
},
externals: [ //相关依赖注明,防止重复打包
'vue',
{
// lodash: {
// commonjs: 'lodash',
// commonjs2: 'lodash',
// amd: 'lodash',
// root: '_', // 指向全局变量
// }
lodash: '_'
},
// './jTool': 'jTool',
// '@/util/jTool': 'jTool',
function (context, request, callback) {
// if (/common(\.js)?$/.test(request)) {
// console.log(context, '===context====');
// console.log(request, '===request===');
// }
if (/eventBus(\.js)?$/.test(request)) {
return callback(null, 'eventBus');
}
if (/jTool(\.js)?$/.test(request)) {
console.log(request, '===========request=======')
return callback(null, 'jTool');
}
callback();
}
],
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
esModule: false, // vue-loader v13 更新 默认值为 true v12及之前版本为 false, 此项配置影响 vue 自身异步组件写法以及 webpack 打包结果
loaders: utils.cssLoaders({
sourceMap: false,
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]')
}
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}
]
},
plugins: [
// new webpack.DefinePlugin({
// 'process.env.NODE_ENV': '"production"'
// }),
new webpack.LoaderOptionsPlugin({
vue: {
postcss: [require('postcss-pxtorem')({ rootValue: 40, propWhiteList: [] })]
}
}),
// 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: false
}),
// Compress extracted CSS. We are using this plugin so that possible
// duplicated CSS from different components can be deduped.
new OptimizeCSSPlugin({
cssProcessorOptions: {
safe: true
}
}),
new BundleAnalyzerPlugin()
]
};
用 Axios 获取到组件,new Function的方式转换成Vue组件,通过 is 属性渲染。
<template>
<component
:is="mode"
v-bind="$attrs"
v-on="$listeners">
</component>
</template>
<script>
import Axios from 'axios';
export default {
name: 'AsyncComponent',
props: {
// 父组件提供请求地址
url: {
type: String,
default: ''
}
},
inheritAttrs: true,
data() {
return {
resData: '',
mode: ''
};
},
watch: {
url: {
immediate: true,
async handler(val, oldVal) {
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}`)();
}
}
}
};
</script>
也可以添加一些钩子函数,用于资源加载完后的回调。
需要注意的是,远端组件不要去加载公共的代码,用 externals 排除掉。