Vue2的 Vue Components 打包成 Web Components 实践&踩坑全纪录
什么是Web Components?
Web Components 是HTML5 提供的一种原生组件封装方案,我们可以通过Web Components相关原生API封装,生产一些组件,然后像普通标签那样,写到HTML当中,并渲染出来。
Web Components 对于我们来说,可能会相对陌生,因为我们平时开发项目时,我们可能本来就是依赖框架的,如依赖 Vue、React、Angular 来开发我们的页面,而一些第三方的组件库,往往都是依赖对应框架的,比如一些第三方库就专门为框架而生,如elementUI vue-awesome-swiper、vue-fullpage等。使用这些第三方插件、组件时、需要依赖框架。
Web Components 正好就是不依赖框架的存在,能够脱离框架,或者是在不同框架中,都能够正常使用。
Vue Components 打包成 Web Components。
上面说,Web Components 都是使用原生API来进行编写的,那为什么这里提出了用Vue Components 打包成Web Components 呢?
这里,主要是因为我们的团队技术栈主要是围绕Vue2/Vue3进行业务开发的。 然后 通过一次调研的发现,Vue-cli 是支持将Vue组件,打包成 Web Components的。
例子:
vue-cli-service build --target wc --name my-element [entry]
通过vue-cli-service 我们就可以通过上面那个命令把我们的Vue组件打包成 Web Components啦。
不依赖Vue-cli 来打包
那上面既然Vue-cli 有实现如何打包Web Components了,那我们也可以尝试自己摸索,如何通过Webpack来构建 Web Components,通过阅读 Vue-cli的文档,我们可以发现,Vue-cli底层用的是 @vue/web-component-wrapper库,通过这个库,对我们的Vue组件进行包括,这个包裹器会自动处理我们的 data,methods,slot 等等。
// web-component-wrapper库的使用
import Vue from 'vue'
import wrap from '@vue/web-component-wrapper'
const Component = {
// 任何组件选项
}
const CustomElement = wrap(Vue, Component)
window.customElements.define('my-element', CustomElement)
坑点1: 引入时组件要带上?shadow
如:
import ComponentA from './index.vue?shadow';
import wrap from '@vue/web-component-wrapper';
if (!window.Vue) {
console.error('you need import vue2.runtime.js');
}
const CustomElement = wrap(window.Vue, ComponentA);
window.customElements.define('component-a', CustomElement);
备注:这里利用的是全局挂载的Vue.runtime.js 这样是为了避免多组件同时把Vue加载进来,同时我们可以在window.Vue的原型链上做一些项目级别的扩展,如(utils,api,等等)。
坑点2: Webpack配置需要注意;
在使用 vue-loader / vue-style-loader /css-loader 的使用,记得要配置options值, 不然打包出来的组件样式会丢失,要结合坑点1; 如:
{
test: /\.css$/,
include: [
path.resolve(process.cwd(), 'src/components'), // 指定哪些路径下的文件需要经过 loader 处理
],
use: [
{
loader: '@alancnet/vue-style-loader',
options: {
shadowMode: true,
},
},
{
loader: 'css-loader',
options: {
sourceMap: true,
},
},
{
loader: 'postcss-loader',
},
].filter(Boolean),
},
坑点3:Vue-style-loader的热更新问题;
最后引用 一个外国作者的 @alancnet/vue-style-loader 版本为 "^4.1.4"
css-loader的版本为 "^3.2.0"
因为发现通过web-components形式进行本地调试时,无法进行样式的热更新。这就很影响本地的开发效率。
坑点4: Web Components 传参 由于 使用 @vue/web-component-wrapper 的原因,我们无法往web Components 中传入 对象。
<!-- 如下,config将会被解析成字符串,因为 @vue/web-component-wrapper没帮我们处理。-->
<get-gift-3 config="{a:33232323}"></get-gift-3>
官方也有相关issue,但是就是有野生方法,能够将传入的数组和对象转化成响应的值 github.com/vuejs/vue-w…
export function convertAttributeValue (value, name, { type } = {}) {
if (isBoolean(type)) {
if (value === 'true' || value === 'false') {
return value === 'true'
}
if (value === '' || value === name) {
return true
}
return value != null
} else if (isNumber(type)) {
const parsed = parseFloat(value, 10)
return isNaN(parsed) ? value : parsed
} else if (type.name == 'Array') {
try {
var parsed = JSON.parse(value);
return parsed && Array.isArray(parsed) ? parsed : [];
} catch (err) {
return [];
}
} else if (type.name == 'Object') {
try {
var parsed = JSON.parse(value);
return parsed && typeof parsed === 'object' ? parsed : {};
} catch (err) {
return {};
}
} else {
return value
}
}
主要就是 添加了 Array 和Object 的判断。组件的props能够接收成功, 但是由于HTML不推荐使用这种形式传参,
关于如何使用 打包出来的Web Component
<html lang="en"
><head
>
<script src="https://unpkg.com/vue@2"></script>
<script src="../dist/common/index.js"></script>
</head>
<body>
<!-- <header-1></header-1> -->
<get-gift-3 cccc="{a:33232323}"></get-gift-3>
<script src="../dist/get-gift/get-gift-3/get-gift-3.js"></script>
</body>
<script></script>
</html>
大家可能会看到,我们的使用很简单,直接引入一个 Vue2的 runtime.js 引入一个common.js(主要是一些工具类,挂载到了Vue原型链上,其实这里应该将Vue2打到common.js中的,后续考虑下)。
总结
今日为大家带来了 Web Components 的实践和踩坑全记录,希望对大家有用,并对于有疑问或者错误的地方,评论并指出。