vue 多项目公共组件处理方案
vue 多项目公共组件处理方案
背景
一个产品线中会有多个项目,每个项目都存在一些公共组件。这些组件是需要共享的
解决方案
我们一开始最先想到的是使用npm包
进行共享的。但是最大的问题就是npm包
的更新问题。由于在开发初期,共享组件的改动非常频繁,一天下来可能发布三四个版本。这样子下来就会导致每个项目都需要重新安装依赖,然后重新打包,显得非常麻烦。
无独有偶,我在无意间看见一篇文章:滴滴 webapp 5.0 Vue 2.0 重构经验分享,里面提及到了异步加载的业务线组件,如何动态注册?
,使用到了 Vue 提供了动态注册组件的 api,通过Vue.component('async-example',function(resolve){ //... })
的方式可以去加载远程的组件。基于这个解决方案,我做了一个简单的 demo 来尝试一下,发现是可行的。
公共组件处理
我们以一个自定义demo-button
组件为例:
编写组件
新建button/demo-button.vue
文件
<template>
<button class="demo-button" :class="`demo-button-${type}`">按钮</button>
</template>
<script>
export default {
props: {
type: {
type: String,
default: "primary",
},
},
};
</script>
<style scope>
.demo-button {
appearance: none;
box-sizing: border-box;
display: inline-block;
margin: 0;
padding: 12px 20px;
user-select: none;
background: #fff;
border: 1px solid #dcdfe6;
border-radius: 4px;
outline: none;
transition: 0.1s;
}
.demo-button-primary {
color: #fff;
background-color: #409eff;
border-color: #409eff;
}
.demo-button-success {
color: #fff;
background-color: #67c23a;
border-color: #67c23a;
}
</style>
新建button/index.js
文件导出组件
import DemoButton from "./demo-button.vue";
export default DemoButton;
打包组件
关于组件的打包,我们其实并不需要自己搭建一个环境出来,可以直接借助vue-cli-service
,vue-cli-service
不仅可以打包项目,还可以单独打包组件,详情可以点击这里。我们在package.json
中添加如下script
脚本:
{
"scripts": {
"build-component": "vue-cli-service build --target lib --name demo-button ./src/component/button/index.js"
}
}
--target lib
:打包出来的文件格式。指定lib
格式会打包umd
和commonjs
格式的文件格式。我们这里需要的是umd
格式的文件
--name demo-button
:两个作用。第一个作用是声明打包出来的文件名。第二个作用就是umd
格式文件暴露在全局的变量名,即可以通过windown['demo-button']
获取该自定义组件
./src/component/button/index.js
:组件的入口,入口文件也可以直接指向.vue
文件
打包出来的文件包括demo-button.umd.js
和demo-button.css
,我们只需要把这 2 个文件丢到服务器上面即可
加载公共组件
以往加载异步组件的方式是:
Vue.component(
"demo-button",
// 这个动态导入会返回一个 `Promise` 对象。
() => import("xxx/xxx.vue")
);
这种方式是结合了webpack
的功能,并且xxx/xxx.vue
文件是在项目中的,但是我们此时的公共组件是在服务器上面的,需要跑到服务器上面加载,因此上面这种方式是不行的。
非常庆幸的是Vue.component
第二个参数是一个工厂函数,并且需要返回一个promise
实例。因此我们需要自己封装一个函数,并把组件通过resolve
的方式返回出去。
加载服务器文件
function httpRequest(src) {
return new Promise((resolve) => {
var xhr = new XMLHttpRequest();
xhr.open("GET", src, true);
xhr.setRequestHeader("Content-Type", "application;charset=utf-8");
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
resolve(xhr.responseText);
}
};
xhr.send();
});
}
执行文件代码并获取组件
/**
*
* @param {*} src 文件路径
* @param {*} componentName 跟打包时传入的`--name demo-button`有关
* @returns
*/
function resolveComponent(src, componentName) {
return httpRequest(src).then((res) => {
// 使用eval或者new Function执行代码
// eval(res);
new Function(res)();
// 代码执行完毕之后会把组件挂载到windown下面,这个跟打包的格式有关
// 为了防止全局环境变量的污染,打包的时候可以使用命名空间
const component = window[componentName].default;
return component;
});
}
注意点:
1、获取得到的文件内容是字符串,需要使用eval
或者new Function
执行代码,这里强烈推荐使用new Function
这种方式使用
2、字符串代码执行完毕之后,windown对象上面会存在一个demo-button
的全局对象,此时一定要通过.default
获取,因为我们是通过export default
导出的
3、打包的文件格式一定要用umd
格式,因为可以在全局对象windown
中找到,其他格式的找不到在哪里
4、为了防止全局变量的污染,打包的时候可以使用命名空间,获取组件的时候也需要把命名空间添加上
注册组件
Vue.component(
'demo-button',
// 这个动态导入会返回一个 `Promise` 对象。
()=>resolveComponent('http://127.0.0.1:8081/demo-button.umd.js','demo-button')
)
到这里为止,我们就可以使用demo-button
这个组件了
关于样式的处理方式
因为我们是使用vue-cli-service
进行打包,所以css
文件和js
文件是分离的。主要自行引入服务端的样式文件。可以在html
文件中使用style
标签引入样式,也可以在css
文件中通过原生css
的@import
进行引入
当然,如果是自己搭建的环境,可以考虑把样式也打包进js
文件中,这样子就可以不用考虑样式引入的问题了
总结
到此,我们就已经可以加载远程服务器的文件,并使用组件了,这样子做的最大好处就是组件的频繁更新不用重新发包,项目的开发人员也不用重新安装依赖。组件修改完成之后,只需要重新打包放到远程服务上即可。
更多推荐
所有评论(0)