Vue2的 Vue Components 打包成 Web Components 实践&踩坑全纪录

2,787 阅读4分钟

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的。

cli.vuejs.org/zh/guide/bu…

例子:

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>

github.com/vuejs/vue-w…

官方也有相关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 的实践和踩坑全记录,希望对大家有用,并对于有疑问或者错误的地方,评论并指出。