有时候想写一个无关框架组件,又不想用原生或者 Jquery 那套去写,而且还要避免样式冲突,用 Web Components 去做刚觉就挺合适的。但是现在 Web Components 使用起来还是不够灵活,很多地方还是不太方便的,如果能和 MVVM 搭配使用就好了。早在之前 Angular 就支持将组件构建成 Web Components,Vue3 3.2+ 开始终于支持将组建构建成 Web Components 了。正好最近想重构下评论插件,于是上手试了试。

构建 Web Components

vue 提供了一个 defineCustomElement 方法,用来将 vue 组件转换成一个扩展至HTMLElement的自定义函数构造函数,使用方式和 defineComponent 参数api基本保持一致。

import { defineCustomElement } from 'vue' 

const MyVueElement = defineCustomElement({
// 在此提供正常的 Vue 组件选项
props: {},
emits: {},
template: `...`, // defineCustomElement 独有特性: CSS 会被注入到隐式根 (shadow root) 中
styles: [`/* inlined css */`]
}) // 注册 Web Components
customElements.define('my-vue-element', MyVueElement)

如果需要使用单文件,需要 @vitejs/plugin-vue@^1.4.0vue-loader@^16.5.0 或更高版本工具。如果只是部分文件需要使用,可以将后缀改为 .ce.vue 。若果需要将所有文件都构建 Web Components 可以将 @vitejs/plugin-vue@^1.4.0vue-loader@^16.5.0customElement 配置项开启。这样不需要再使用 .ce.vue 后缀名了。

属性

vue 会把所有的的 props 自定义元素的对象的 property 上,也会将自定义元素标签上的 attribute 做一个映射。

<com-demo type="a"></com-demo>

props:{
type:String
}

因为 HTML 的 attribute 的只能是字符串,除了基础类型(Boolean、Number) Vue 在映射时会帮忙做类型转换,其他复杂类型则需要设置到 DOM property 上。

事件

在自定义元素中,通过 this.$emit 或在 setup 中的 emit 发出的事件会被调度为原生 CustomEvents。附加的事件参数 (payload) 会作为数组暴露在 CustomEvent 对象的 details property 上。

插槽

编写组件时,可以想 vue 一样,但是使用时只能原生的插槽语法,所以也不在支持作用域插槽。

子组件样式问题

使用子组件嵌套的时,有个坑的地方就是默认不会将子组件里的样式抽离出来。

父组件

<template>
<div class="title">{{ title }}</div>
<Childer />
</template>
<script>
import Childer from "./childer.vue"
export default {
components: { Childer },
data() {
return {
title: "父组件"
}
},
}
</script>
<style lang="less" scoped>
.title {
padding: 10px;
background-color: #eee;
font-weight: bold;
}
</style>

子组件

<template>
<div class="childer">{{ title }}</div>
</template>
<script>
export default {
data() {
return {
title: "子组件"
}
},
}
</script>
<style lang="less" scoped>
.childer {
padding: 10px;
background-color: #222;
color: #fff;
font-weight: bold;
}
</style>

可以看到子组件的样式没有插入进去,但是样式隔离的标识是有生成的 data-v-5e87e937。不知道vue官方后续会不会修复这个bug



查看组件是可以看到,子组件的样式是有被抽离出来的,这样就只需要自己注入进去了。

将子组件样式抽离插入到父组件里,参考这个的实现

import ComDemo from '~/demo/index.vue'

const deepStylesOf = ({ styles = [], components = {} }) => {
const unique = array => [...new Set(array)];
return unique([...styles, ...Object.values(components).flatMap(deepStylesOf)]);
}
// 将子组件样式插入到父组件里
ComDemo.styles = deepStylesOf(ComDemo) !customElements.get('com-demo') && customElements.define('com-demo', defineCustomElement(ComDemo))

完美解决子组件样式问题

方法

defineCustomElement 构建的组件默认是不会将方法挂到 customElement 上的,看 Vue 源码中,只有 _def(构造函数),_instance(组件实例))。如果想调用组件内的方法,dom._instance.proxy.fun(),感觉实在不太优雅。



我们当然希望我们组件暴露的方法能像普通dom那样直接 dom.fun() 去掉用,我们对 defineCustomElement 稍作扩展。

import { VueElement, defineComponent } from 'vue'

const defineCustomElement = (options, hydate) => {
const Comp = defineComponent(options);
class VueCustomElement extends VueElement {
constructor(initialProps) {
super(Comp, initialProps, hydate);
if (Comp.methods) {
Object.keys(Comp.methods).forEach(key => {
// 将所有非下划线开头方法 绑定到 元素上
if(!/^_/.test(key)){
this[key] = function (...res) {
if (this._instance) {
// 将方法thi改为 组件实例的proxy
return Comp.methods[key].call(this._instance.proxy, ...res)
} else {
throw new Error('未找到组件实例')
}
}
}
})
}
}
}
VueCustomElement.def = Comp;
return VueCustomElement;
}

总结

总体来说坑还是有不少的,如果仅仅需要构建一些比较简单跨框架插件,使用这种方式来构建 Web Components 也是一种不错的方案。

使用 Vue3 构建 Web Components的更多相关文章

  1. Web Components

    Web Components是不是Web的未来   今天 ,Web 组件已经从本质上改变了HTML.初次接触时,它看起来像一个全新的技术.Web组件最初的目的是使开发人员拥有扩展浏览器标签的能力,可以 ...

  2. 腾讯发布新版前端组件框架 Omi,全面拥抱 Web Components

    Omi - 合一 下一代 Web 框架,去万物糟粕,合精华为一 → https://github.com/Tencent/omi 特性 4KB 的代码尺寸,比小更小 顺势而为,顺从浏览器的发展和 AP ...

  3. 从HTML Components的衰落看Web Components的危机 HTML Components的一些特性 JavaScript什么叫端到端组件 自己对Polymer的意见

    http://blog.jobbole.com/77837/ 原文出处: 徐飞(@民工精髓V) 搞前端时间比较长的同学都会知道一个东西,那就是HTC(HTML Components),这个东西名字很现 ...

  4. Lightning Web Components 开发指南(二)

    Lightning Web Components 是自定义元素使用html 以及现代javascript进行构建. Lightning Web Components UI 框架使用web compon ...

  5. 使用CLI 3 创建发布Web Components

    本文翻译自:codementor 翻译不当之处,欢迎指正交流 Web Components是web平台的未来吗?关于这一问题支持和反对的观点有很多.事实上浏览器对Web Components的支持正在 ...

  6. 使用纯粹的JS构建 Web Component

    原文链接:https://ayushgp.github.io/htm...译者:阿里云 - 也树 Web Component 出现有一阵子了. Google 费了很大力气去推动它更广泛的应用,但是除 ...

  7. 前端未来趋势之原生API:Web Components

    声明:未经允许,不得转载. Web Components 现世很久了,所以你可能听说过,甚至学习过,非常了解了.但是没关系,可以再重温一下,温故知新. 浏览器原生能力越来越强. js 曾经的 JQue ...

  8. Svelte入门——Web Components实现跨框架组件复用

    Svelte 是构建 Web 应用程序的一种新方法,推出后一直不温不火,没有继Angular.React和VUE成为第四大框架,但也没有失去热度,无人问津.造成这种情况很重要的一个原因是,Svelte ...

  9. NodeJs+http+fs+request+cheerio 采集,保存数据,并在网页上展示(构建web服务器)

    目的: 数据采集 写入本地文件备份 构建web服务器 将文件读取到网页中进行展示 目录结构: package.json文件中的内容与上一篇一样:NodeJs+Request+Cheerio 采集数据 ...

随机推荐

  1. 给你准备好了——50道Python面试题集锦(附答案)

    Python是目前编程领域最受欢迎的语言.在本文中,我将总结Python面试中最常见的50个问题.每道题都提供参考答案,希望能够帮助你在2019年求职面试中脱颖而出,找到一份高薪工作.这些面试题涉及P ...

  2. C#.NET笔试题-高级

    1.说说什么是架构模式. 1,分层. 2,分割. 分层是对网站进行横向的切分,那么分割就是对网站进行纵向的切分.将网站按照不同业务分割成小应用,可以有效控制网站的复杂程度. 3,分布式. 在大型网站中 ...

  3. 强化学习-学习笔记7 | Sarsa算法原理与推导

    Sarsa算法 是 TD算法的一种,之前没有严谨推导过 TD 算法,这一篇就来从数学的角度推导一下 Sarsa 算法.注意,这部分属于 TD算法的延申. 7. Sarsa算法 7.1 推导 TD ta ...

  4. WannaRen来袭:螣龙安科带你盘点那些年的勒索病毒

    2020年4月7日,360CERT监测发现网络上出现一款新型勒索病毒wannaRen,该勒索病毒会加密windows系统中几乎所有的文件,并且以.WannaRen作为后缀.360CERT该事件评定:危 ...

  5. Assembly.GetManifestResourceStream为null

    想把某个项目的某个文件夹里面的ini文件生成的时候顺便生成为网站和服务文件夹项目 string _path = Path.Combine(AppDomain.CurrentDomain.BaseDir ...

  6. day01 Java_JVM,JCR,JDK

    精华笔记: java开发环境: 编译运行过程: 编译期:.java源文件,经过编译,生成.class字节码文件 运行期:JVM加载.class并运行.class(0和1) 特点:跨平台.一次编程到处使 ...

  7. 01 开发App真机调试问题

    逍遥安卓模拟器 :https://juejin.cn/post/7062922018710093831 HBuilderX真机调试插上手机却提示"未检测到手机或浏览器"的问题:ht ...

  8. 常用的函数式接口_Predicate接口_默认方法and和Predicate接口练习_集合接口筛选

    默认方法:and 既然是条件判断,就会存在与.或.非三种常见的逻辑关系.其中将两个Preadicate条件使用"与"逻辑连接起来实现"并且"的效果时,可以使用d ...

  9. Kubernetes v1.24 基于containerd部署

      k8s每个节点安装containerd.   containerd安装参考<containerd安装博文>:https://www.cnblogs.com/punchlinux/p/1 ...

  10. 国产钡铼分布式IO模块如何与西门子PLC Profinet通讯

    1.准备IO模块:耦合器BL200PN.数字量输出模块M2082.数字量输入M1081.模拟量输入模块M3401.模拟量输出M4043. 编辑搜图 请点击输入图片描述(最多18字) ​2.BL200P ...