什么是Web Component
WebCompoent用官方的说法是一套不同的技术,允许您创建可重用的定制元素(它们的功能封装在您的代码之外)并且在您的web应用中使用它们。
也就是说,它是一套可以支持原生实现组件化的技术。这意味这Web Component是浏览器原生支持的组件方式,可以在任何的框架或原生环境下中使用!
从MDN的描述中可以看到,Web Components旨在解决代码复用、组件自定义、复用管理等问题,它们可以一起使用来创建封装功能的定制元素,可以在你喜欢的任何地方重用,不必担心代码冲突。
如何写Web Component
在Vue3.2以前,写原生的Web Component组件需要额外的学习Web Component的语法,生命周期,了解Custom elements,Shadow DOM,HTML templates等一些概念,需要一定的学习成本。
而Vue3.2的defineCustomElement API就可以很好的解决这个问题,将你的vue组件通过简单的方式封装成Web Component。
defineCustomElement API
现在 Vue 3.2 已经发布,借助 defineCustomElement
,从 Vue 组件创建原生自定义元素比以往任何时候都容易。
在这篇文章中,我们将学习如何使用这个新的API将Vue组件转换为Web组件,如何将其打包为库,以及如何在纯HTML中使用它。
create-custom-element脚手架
借助create-custom-element这个脚手架可以很方便的创建一个支持defineCustomElementAPI的vue项目
npm:
npm init custom-element
yarn:
yarn create custom-element
pnpm:
pnpm create custom-element
复制代码
这里使用脚手架创建了一个支持ts的custom-element项目
打开项目,使用pnpm i
安装好依赖后,我们通过学习这个脚手架的模板来学习如何使用defineCustomElement
目录结构
这里的目录结构与vite-Vue项目的目录结构类似
创建自定义元素
首先看src/CustomElement/index.ce.vue
第一步是创建一个我们想要用作自定义元素的 Vue 组件。在此示例中,我们将构建一个可以在网站上切换深色主题的按钮。
<script setup lang='ts'>
import { ref } from 'vue'
const isDark = ref(false)
</script>
<template>
<button @click="isDark = !isDark">
<span v-if="isDark">?</span>
<span v-else>?</span>
</button>
</template>
复制代码
这里没什么特别的,就是使用vue语法创建了一个vue组件
从Vue 组件到Web Component
再看到src/CustomElement/index.ts
import { defineCustomElement } from 'vue'
import VueDarkModeSwitch from './index.ce.vue'
// Vue generates a new HTML element class from the component definition.
export const DarkModeSwitch = defineCustomElement(VueDarkModeSwitch)
// Register the custom element so that it can be used as <dark-mode-switch>.
export function register (tagName: string = 'dark-mode-switch') {
customElements.define(tagName, DarkModeSwitch)
}
复制代码
这个文件是使Vue组件封装为WebComponet的关键:
-
首先从vue中引入defineCustomElement
import { defineCustomElement } from 'vue'
-
再引入之前写的vue组件并命名为VueDartModeSwitch(这里的名字可以根据需求自定义)
import VueDarkModeSwitch from './index.ce.vue'
-
使用defineCustomElement包装VueDartModeSwitch,并导出
export const DarkModeSwitch = defineCustomElement(VueDarkModeSwitch)
-
导出register方法,方便自定义组件标签名
export function register (tagName: string = 'dark-mode-switch') { customElements.define(tagName, DarkModeSwitch) } 复制代码
给组件添加样式
重新回到src/CustomElement/index.ce.vue文件
这里可能有人会奇怪,这里的vue文件,为什么会以 .ce.vue
结尾?
这里的ce是CustomElemtn的简写,vue在默认情况下,以.ce.vue
结尾的文件将以Web Component导入。
这样做的目的是为了让vue组件支持Shadow DOM,在以.ce.vue
结尾的文件中。写style标签不需要加上scoped属性也能使css属性隔离,互不影响。
了解原因后,现在为我们的组件加上样式:
<style>
:host {
--color: #fbbf24;
--bg-normal: #fAfAf9;
--bg-active: #f5f5f4;
--font-size: 24px;
}
:host([dark]) {
--color: #fef3c7;
--bg-normal: #262626;
--bg-active: #2d2d2d;
}
button {
background-color: var(--bg-normal);
border: none;
border-radius: .5rem;
color: var(--color);
cursor: pointer;
display: flex;
font-size: var(--font-size);
overflow: hidden;
padding: 0.4em;
transition: background-color 0.3s ease, color 0.3s cubic-bezier(0.64, 0, 0.78, 0);
}
button:hover,
button:focus {
background-color: var(--bg-active);
outline: none;
}
.slide-enter-active,
.slide-leave-active {
transition: transform 0.3s ease-out;
}
.slide-enter-from {
transform: translateY(-150%);
}
.slide-enter-to,
.slide-leave-from {
transform: translateY(0);
}
.slide-leave-to {
transform: translateY(150%);
}
</style>
复制代码
使用Web Component
现在再来到src/APP.vue文件
<script setup lang="ts">
import { register } from './CustomElement';
register('dart-element')
</script>
<template>
<dart-element></dart-element>
</template>
<style>
#app {
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
</style>
复制代码
- 首先从封装完成的
src/CustomElement/index.ts
中引入之前导出的register方法 - 执行
register()
方法,register方法接收一个字符串参数,为自定义的标签名称 - 在模板语法里使用
<dart-element></dart-element>
标签
在vue中使用
如果我们想在Vue中使用这些Web Component组件,我们需要到vite.config.ts
里进行配置plugins,让vue能够识别Web Component组件,并跳过vue的编译部分,直接执行。
plugins: [vue({
template: {
compilerOptions: {
isCustomElement: (tag: string[]) => tag.includes('-')
}
}
})]
复制代码
当然这些配置,都通过create-custom-element脚手架自动生成,你无需操心。
运行Web Component
是时候,将这个组件跑起来看看了
pnpm dev
出现这个小太阳就证明你已经成功跑起来了!!
从devtools的element节点可以看出这里确实是一个WebComponent,我们通过defineCustomElement API将一个vue组件封装得到WebComponent
可以在这个页面进行运行,调试你的组件
打包Web Component
使用vue写一个Web Component最主要的目的是打包成一个公共的包,可以在不同的框架下使用
Vite.js提供了一个打包模式,我们这里直接使用Vite.js进行打包。
build: {
target: 'esnext',
minify: 'terser',
lib: {
entry: 'src/CustomElement/index.ts',
formats: ['es', 'cjs', 'iife'],
name: 'CustomElement'
}
}
复制代码
这里将src/CustomElement/index.ts
作为入口文件,打包分别生成三种模块化规范的包。
使用pnpm build
进行打包
即可以在dist文件夹下找到打包的三个模块
在原生环境下使用Web Component
创建一个index.html文件引入打包的模块
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script type="module">
import { register } from './dist/custom-element.es.js'
register('custom-element')
</script>
</head>
<body>
<custom-element></custom-element>
</body>
</html>
复制代码
完美运行
可以看到我们之前在vue里写的组件,也在原生的HTML环境下成功运行
End??
Vue defineCustomElement
使开发Web Component变得前所未有的简单。
都看到这里了,点个赞吧?