最近在搞BI仪表板重构项目,有个特殊的功能,要让用户可以上传自己开发的的组件,即远程组件。
远程组件的前提是要定好组件规范,数据,变量映射,全局配置(主题色,字体之类)的传入,以及暴露组件属性,表单配置项(有一套自动生成配置板面的form输入项),组件内动作触发时,抛出动作,定义事件数据规范,用于统一的交互处理。
1.vue组件install
js
import comp from './index.vue';
const components = [comp];
//注册组件,两种形式的组件名都注册aaa-bc,AaaBc
function install(Vue) {
components.forEach((component) => {
let item = component.name;
if (component.name.indexOf('-') == -1) {
let name = item.replace(/([A-Z])/g, (str, match) => {
return '-' + match.toLowerCase();
});
name = name.substring(1);
name = name.substring(0, 1).toLowerCase() + name.substring(1);
Vue.component(name, component);
} else {
let name = item.replace(/-([a-z])/g, (str, match) => {
return match.toUpperCase();
});
name = name.substring(0, 1).toUpperCase() + name.substring(1);
Vue.component(name, component);
}
Vue.component(component.name, component);
});
}
export default { install };
2.rollup打包组件
js
import json from '@rollup/plugin-json';
import vue from 'rollup-plugin-vue';
import scss from 'rollup-plugin-scss';
// import nodePolyfills from 'rollup-plugin-polyfill-node';
import { nodeResolve } from '@rollup/plugin-node-resolve';
// import filesize from 'rollup-plugin-filesize';
import postcss from 'rollup-plugin-postcss';
import postcssImport from 'postcss-import';
import commonjs from '@rollup/plugin-commonjs';
import babel from '@rollup/plugin-babel';
import terser from '@rollup/plugin-terser';
import strip from '@rollup/plugin-strip';
import replace from 'rollup-plugin-replace';
const env = process.env.NODE_ENV;
export default {
//外部引入vue
external: ['vue'],
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify(env)
}),
json(),
// nodePolyfills(),
nodeResolve(),
scss(),
babel({
exclude: 'node_modules/**' // 只转译我们的源代码
}),
vue({
css: true, // Dynamically inject css as a <style> tag
compileTemplate: true // Explicitly convert template to render function
}),
postcss({
extensions: ['.css', '.scss'],
extract: true,
plugins: [postcssImport()]
}),
commonjs({
include: ['node_modules/**', 'node_modules/**/*']
}),
// 压缩代码
terser(),
// 剔除debugger、assert.equal和 console.log 类似的函数
strip({
labels: ['unittest']
})
]
};
打包入口与输出
js
// rollup.config.js
import baseConfig from './rollup.base.config.js';
let compName = 'MyWorld';
export default {
input: `./src/${compName}/index.js`,
output: [
{
format: 'es',
dir: 'dist/build/es'
},
{
format: 'umd',
dir: 'dist/build/umd',
//vue用全局变量
globals: {
vue: 'vue'
},
name: compName
}
],
...baseConfig
};
3.远程加载组件
vite有个奇怪的引入限制,vite会强制转换import的路径,导致引入错误,因此用import的文件必须全部在项目目录下。
js
import(customUrl).then((comp) => {
app.use(comp.default);
});


当然有人会说用import.meta.glob
动态路由,表示远程组件后面会放在服务器上,动态路由还是相对于当前项目目录的,还是走不通!
最终我放弃了用esmodule的import引入方式,改用了umd
很简单的创建一个script
脚本元素,把src设为远程组件的地址,然后就ok了
js
//name组件名,customUrl远程组件地址
export function registerComp(name: string, customUrl: string) {
return new Promise((resolve, reject) => {
if (customUrl && !compMapping[name]) {
try {
let js = document.createElement('script');
js.id = name;
js.src = customUrl;
js.onload = () => {
compMapping[name] = 1;
console.log(window[name]);
app.use(window[name]);
resolve(null);
};
js.onerror = (error) => {
console.log(error);
reject(null);
};
document.body.appendChild(js);
} catch (error) {
console.log(error);
resolve(null);
}
} else {
resolve(null);
}
});
}
然后成功将组件下载下来了,但新的问题来了!
4.打包的组件竟然子组件不解析
这是个二维码组件,引用了qrcode.vue
组件,但是不知道为什么这个子组件不解析
我怀疑这个子组件没打包进去,然后自己写了个小组件引入


结果还是这样没解析
然后,临下班前,我又突发奇想弄一个没有子组件的组件,看看能不能渲染!

然后,我怀疑这个打包的组件是不是有问题,直接引入项目使用。
果不其然,也是没解析,说明这个组件本身就是有问题的。
啥情况?为什么别没这个问题,我就有这个问题?
然后我看了一下打包后的代码,我好像把vue打包进去了。
因为运行项目的vue跟组件里面的vue不是同一个vue,所以不解析子组件
然后问了搜索引擎,有人的博客说加了个external,globals,然后esmodule直接项目引入正常,但是远程umd失败了
然后看报错,说vue没有,然后加了个全局挂载的vue
js
import * as Vue from 'vue';
window.vue = Vue;
还是不行?我人品问题吗?
问了我们组的大佬,大佬也没遇到过。
然后,大佬推荐问了AI,AI的回答没什么用
然后我找了好久,搞不定,大佬发话先不管,最终彻底摆烂,赶紧下班!

第二天早上脑袋清醒,回想了一下昨天找问题的全过程,仔细看了一下rollup配置文档,发现了华点。
我把globals的位置写错了,我直接放在了外面,这个东东应该放在output里面,然后没问题了~
js
//外部引入vue
external: ['vue'],
output:[
{
format: 'umd',
dir: 'dist/build/umd',
//vue用全局变量
globals: {
vue: 'vue'
},
name: compName
}
]
...
}
谨记!
别的博客不一定写得很细致,该看官网还得看官网!
细节决定效率!唉~浪费了一天!