Web Components 学习 01 认识 Web Components、一些组件库使用

Web Components 学习 01 认识 Web Components、一些组件库使用什么是 Web Components 开发项目的时候为什么不手写原生 JS 而是要用现如今非常流行的前端框架 原因有很多 例如 良好的生态 数据驱动试图 模块化 组件化 等 Web Components 就是为了解决 组件化 而诞生的 它是浏览器原生支持的组件化 不依赖任何库

大家好,我是讯享网,很高兴认识大家。

什么是 Web Components

开发项目的时候为什么不手写原生 JS,而是要用现如今非常流行的前端框架,原因有很多,例如:

  • 良好的生态
  • 数据驱动试图
  • 模块化
  • 组件化

Web Components 就是为了解决“组件化”而诞生的,它是浏览器原生支持的组件化,不依赖任何库、依赖和打包工具就可以在浏览器中运行。

Vue、React 的组件化并不是真正的组件化,虽然写代码时写的是确实的组件化代码,但是编译后就不再是组件化了。

例如用 Vue + ElementUI 开发的应用,ElementUI 的组件都是 el 开头的,如 <el-button>,但编译后显示在页面上的就不再是 <el-button> 标签了。

这有点类似于 CSS 预处理器(如 Sass、Less),那些在开发阶段编译的变量(如 $color: red;)其实并不是真正的变量,而是伪变量。在编译过后就没有变量的概念了,所以很难和 JS 通信。

例如有一个需求,在页面上给用户提供一个输入框,用户输入什么颜色(如 red#ff00000),网站就会变成相应颜色的主题色,可是在我们获取到用户输入后,却没有变法将它们赋值给 Sass 变量上去。因为 Sass 代码在编译后已经变成了 CSS 代码,没有 Sass 变量了,例如 color: $color; 编译为 color: red;

所以此时就需要一个浏览器原生就支持的,不需要编译就能够运行的变量,于是 CSS 变量就出现了(--color: red;color: var(--color)),可以非常方便地与 JS 进行通信,因为它是浏览器级别地原生变量。

同理,框架的组件化也不是真正的标准,每家都用自己的组件化标准,这就导致了生态的分裂,而且这些框架的组件化也都是靠编译才能实现的,并且非常依赖于这个框架,是一种共生的关系,就像使用 Vue 时,后缀以 .vue 结尾的文件,根本没有办法在浏览器中运行,必须下载 Node、Webpack、vue-loader 等工具进行打包,但还是无法在脱离 Vue 这个框架的安装包的情况下进行运行。

通常来说,浏览器厂商会吸收一些前端非常流行框架之中的可取之处,然后推动其成为标准,并在浏览器中原生实现这些功能,最经典的莫过于 jQuery 的 $() 选择器。

“都 21 世纪了,还提 jQuery?”

尽管这几年风生水起的 Vue 和 React 加剧了 jQuery 的没落,但全世界仍有超过 6600 万个网站在使用 jQuery,同时 jQuery 也给业界留下了产生深远影响的遗产,W3C 就仿照 $() 函数实现了 querySelector()querySelectorAll() 方法。

而讽刺的是,也正是这两个原生方法的出现,大大加快了 jQuery 的没落,因为它们取代了 jQuery 最常用的功能之一:快捷的选择 DOM 元素。

那么浏览器原生支持的组件化会取代现在所流行的库或框架么?

还记得当 document.querySelector 最开始被广泛的被浏览器支持并且结束了无处不在的JQuery。这最终给我们提供了一个原生的方法,虽然JQuery已经提供了很久。我觉得这同样将会发生在像Angular和React这的前端框架身上。

这些框架可以帮助我们去做一些做不到的事情,比如创建可以复用的前端组件,但是这样需要付出复杂度、专属语法、性能消耗的代价。 但是这些将会得到改变。

现代浏览器的API已经更新到你不需要使用一个框架就可以去创建一个可服用的组件。Custom Element和Shadow DOM都可以让你去创造可复用的组件。

最早在2011年,Web Components就已经是一个只需要使用HTML、CSS、JavaScript就可以创建可复用的组件被介绍给大家。这也意味着你可以不使用类似React和Angular的框架就可以创造组件。甚至,这些组件可以无缝的接入到这些框架中。

—— 《Web Component可以取代你的前端框架吗?[译文]》

那么事实真的是这样么,其实不然。

Web Components 与如今非常流行的 MVVM 框架是一种共存的关系,而不是一种互斥的关系,就像 Sass 变量和 CSS 变量,两者可以非常完美的互补,而不是说用了 CSS 变量就不能用 Sass 变量。

再者来说,我们用那些 MVVM 框架也并不仅仅只是为了它们的组件化功能,虽然组件化是其中非常重要的一项功能,但是还有页面路由、数据绑定、模块化、CSS 预处理器、虚拟 DOM、Diff 算法,以及各种庞大的生态等功能。

Web Components 要解决的仅仅只是组件化的这么一项功能。

React 和 Web Components 为了解决不同的问题而生。Web Components 为可复用组件提供了强大的封装,而 React 则提供了声明式的解决方案,使 DOM 与数据保持同步。两者旨在互补。作为开发人员,可以自由选择在 Web Components 中使用 React,或者在 React 中使用 Web Components,或者两者共存。

—— 《Web Components – React》

我们认为 Vue 和 Web Components 主要是互补的技术。Vue 为使用和创建定制元素提供了出色的支持。无论你是将自定义元素集成到现有的 Vue 应用程序中,还是使用 Vue 来构建和分发自定义元素都很方便。

—— 《Vue 与 Web Components | Vue.js》

从 Vue 和 React 官网可以看到它们都对 Web Components 有很好的支持。


讯享网

可以在 Custom Elements Everywhere 查看“自定义元素”(Web Components 的功能)在各个框架中的互操作性得分。

Making sure frameworks and custom elements can be BFFs

翻译:确保框架和自定义元素可以成为永远的最好的朋友(Best Friend Forever)。

—— Custom Elements Everywhere

既然 WebComponents 不会取代前端框架,那为什么还要学习?

一方面,浏览器原生支持的组件化我们肯定是需要了解的,这是为了跟上时代的步伐。

另一方面,那些流行框架想要顺应时代的趋势,不被大浪淘沙的淘汰掉,也必须要符合浏览器的标准,才能够在前端领域成为常青树。

而且我们无法保证现在流行的框架在几年后依然流行,当初用 jQuery 的大部分人也觉得有生之年它不会被替代。

Web Components 最重要的一点,就是组件化的概念其实是相通的,并不是一个全新的很复杂的概念,所以如果你用过 MVVM 框架,Web Components 其实是非常易于理解的,并不像学习一门全新框架那么耗费精力。而且如果你用过 Vue,那么 Web Components 简直就是手到擒来,因为尤雨溪在创建 Vue 的时候就大量参考了 Web Components 的语法,导致 Web Components 现在的写法有一部分和 Vue 长得几乎一摸一样。

Web Components 还有一个难能可贵的一点,就是它并不是一门单一的技术,它总共有三种规范:

  • HTML Template:HTML 模板
  • Custom Elements:自定义元素
  • Shadow DOM:影子 DOM

第四个规范 HTML Imports 已被弃用,原因是该规范从未在任何浏览器中实现,已经被 ES Modules 取代。

参考:

  • HTML Imports - Chrome Platform Status
  • HTML Imports | MDN

其中有 Custom Elements 和 Shadow DOM 都是可以在不依赖 Web Components 的情况下用在其他地方上去的,也就是说只要你的想象力足够丰富,脑洞够大,就可以利用它们把 DOM 玩出花来。

下面挑选了市面上既好玩,颜值又高的组件库来体验以下 Web Components:

  • css-doodle:直译 - css 涂鸦
  • fancy-components:直译 - 花式组件库,可惜没有官方文档

css-doodle

官方网站:css-doodle.com

<!DOCTYPE html> <html lang="en"> <head> <title>css-doodle 静态效果</title> <script src="https://unpkg.com/css-doodle"></script> <style> html, body { 
      height: 100%; margin: 0; overflow: hidden; } </style> </head> <body> <css-doodle> :doodle { @grid: 20 / 100vmax; background: #12152f; } ::after { content: '\@hex(@rand(0x2500, 0x257f))'; font-size: 5vmax; color: hsla(@rand(360), 70%, 70%, @rand(.9)); } </css-doodle> </body> </html> 

讯享网
讯享网<!DOCTYPE html> <html lang="en"> <head> <title>css-doodle 动态效果</title> <script src="https://unpkg.com/css-doodle"></script> <style> html, body { 
      height: 100%; margin: 0; overflow: hidden; background: #011627; display: grid; place-items: center; } </style> </head> <body> <css-doodle> :doodle { @grid: 1x1x100 / 100vmax; animation: r 23s linear infinite; } @size: 100% 50%; position: absolute; top: 25%; transform: rotate(@r(360deg)); perspective: @r(100px, 200px); ::after { content: ''; position: absolute; @size: @r(.5vmin, 5vmin); color: @p(#fdffc2, #2ec4b6, #e71d36, #ff9f1c); background: currentColor; box-shadow: @m2(0 0 1.2vmin currentColor); animation: cycle @r(2s) linear infinite; --trans: scaleX(@r(1, 5)) translateZ(@r(10vmin, 20vmin)); transform: rotateY(0) @var(--trans); } :empty::after { display: none; } @keyframes cycle { to { transform: rotateY(@p(-1turn, 1turn)) @var(--trans) } } @keyframes r { to { transform: rotate(1turn) } } </css-doodle> </body> </html> 
<!DOCTYPE html> <html lang="en"> <head> <title>css-doodle 水波效果</title> <script src="https://unpkg.com/css-doodle"></script> <style> html, body { 
      height: 100%; margin: 0; overflow: hidden; background: radial-gradient(#459dc1, #070729); display: grid; place-items: center; } </style> </head> <body> <!-- 这个效果比较复杂,只能运行在 Google 内核的浏览器,Firefox 或 Safari 看不到效果 --> <css-doodle> :doodle { @grid: 80x1 / 100vw 100vh; @min-size: 100px; filter: url(#filter); animation: r 23s linear infinite; } @size: 100% 50%; position: absolute; top: 25%; transform: rotate(@r(360deg)); perspective: 130px; ::after { content: ''; position: absolute; @size: @r(10px); background: #fff; box-shadow: @m3(0 0 calc(.5vmin + 5px) #fff); animation: cycle @r(2s, 8s) linear infinite; animation-delay: -@r(100s); --trans: scaleX(@r(.1, 5)) translateZ(105px); transform: rotateY(0) @var(--trans); } @keyframes cycle { to { transform: rotateY(@p(-1turn, 1turn)) @var(--trans) } } @keyframes r { to { transform: rotate(@p(-1turn, 1turn)) } } </css-doodle> <svg style="width: 0;height: 0"> <filter id="filter"> <feGaussianBlur in="SourceGraphic" stdDeviation="5" result="blur"></feGaussianBlur> <feColorMatrix in="blur" mode="matrix" values=" 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 18 -7 " result="goo" ></feColorMatrix> <feBlend in="SourceGraphic" in2="goo"></feBlend> </filter> </svg> </body> </html> 

css-doodle 是用 Web Components 技术做出来的优秀案例,不过由于在 css-doodle 中写的代码并不是 Web Components 的语法,而是 css-doodle 通过 Web Components 技术拿到代码内容,然后用正则表达式解析出来的,属于它的自创语法。开发者可以自行去官网学习这些与 Web Components 无关的语法。

css-doodle 适合做背景,虽然 css-doodle 很炫,但它依然需要开发者自己写样式,而且与我们平时开发时所需要的组件(按钮、轮播图、文本输入框等)相差较大。

fancy-components 就是一款可以提供这些常用组件,可以用于日常开发的组件库。

fancy-components

官方网站:https://fancy-components.gitee.io/home-page/

讯享网<!DOCTYPE html> <html lang="en"> <head> <title>fancy-components</title> <style> * { 
      padding: 0; margin: 0; } html, body { 
      height: 100%; } body { 
      display: grid; place-items: center; background: #3f2766; } fc-3d-btn { 
      --color: #6e50a6; --shadow-color: rgba(255, 255, 255, .4); --inset-shadow-color: #315; --inset-shadow-color-active: rgba(49, 23, 7, .9); --cover-color: rgba(0, 0, 0, .4); } </style> </head> <body> <div> <!-- html 只支持小写标签,不支持驼峰命名法 --> <fc-input white placeholder="Username"></fc-input> <br /> <fc-input white disabled value="fancy components" placeholder="Username"></fc-input> <br /> <fc-btn>fancy-components</fc-btn> <br /> <fc-warp-btn></fc-warp-btn> <br /> <fc-3d-btn></fc-3d-btn> <br /> <fc-underline-btn></fc-underline-btn> <br /> <fc-pixel-btn></fc-pixel-btn> <br /> <fc-parentheses-btn></fc-parentheses-btn> <br /> <fc-round-btn></fc-round-btn> <br /> <fc-arrow-btn></fc-arrow-btn> <br /> <fc-bubbles click> <fc-parentheses-btn>撒花</fc-parentheses-btn> </fc-bubbles> </div> <script type="module"> import { 
      FcTypingInput } from 'http://unpkg.com/fancy-components' import { 
      FcDblWarpBtn } from 'http://unpkg.com/fancy-components' import { 
      FcWarpBtn } from 'http://unpkg.com/fancy-components' import { 
      Fc3DBtn } from 'http://unpkg.com/fancy-components' import { 
      FcUnderlineBtn } from 'http://unpkg.com/fancy-components' import { 
      FcPixelBtn } from 'http://unpkg.com/fancy-components' import { 
      FcParenthesesBtn } from 'http://unpkg.com/fancy-components' import { 
      FcRoundBtn } from 'http://unpkg.com/fancy-components' import { 
      FcArrowBtn } from 'http://unpkg.com/fancy-components' import { 
      FcBubbles } from 'http://unpkg.com/fancy-components' // 注册组件 // 可以传递一个重命名组件名的字符串,必须是小写,且用 `-` 连接 // 不传参数默认组件名就是 fc-typing-input new FcTypingInput('fc-input') new FcDblWarpBtn('fc-btn') new FcWarpBtn() new Fc3DBtn() new FcUnderlineBtn() new FcPixelBtn() new FcParenthesesBtn() new FcRoundBtn() new FcArrowBtn() new FcBubbles() </script> </body> </html> 

在脚手架中使用 Web Components 组件库

Vue 2 中使用

npm i -g @vue/cli vue create vue2-app cd vue2-app npm i fancy-components npm run serve 
讯享网// src\main.js import Vue from 'vue' import App from './App.vue' import { 
    FcBubbles } from 'fancy-components' // 禁用 no-new 校验规则 /* eslint-disable no-new */ new FcBubbles() Vue.config.productionTip = false new Vue({ 
    render: h => h(App) }).$mount('#app') 
<!-- src\App.vue --> <template> <div id="app"> <fc-bubbles click><img alt="Vue logo" src="./assets/logo.png"></fc-bubbles> <!-- <FcBubbles click><img alt="Vue logo" src="./assets/logo.png"></FcBubbles> --> </div> </template> ... 

Web Components 原生组件的地位和 HTML 标签的地位是相同的,大写的驼峰命名组件会被当做 Vue 组件,原生组件要和 HTML 标签一样,不要写成驼峰命名。React 框架中也一样。

Vue CLI 旧版本中使用 Web Components 控制台可能会发出警告,原因是 Vue 将 原生组件当作 Vue 组件去判断,警告组件没有注册,解决办法就是配置 ignoredElements 让 Vue 忽略原生组件:

讯享网Vue.config.ignoredElements = [ // 正则匹配 /^fc-/, // 或字符串 'css-coodle' ] 

Vue 3 中使用

vue create vue3-app cd vue3-app npm i fancy-components npm run serve 
讯享网// src\main.js import { 
    createApp } from 'vue' import App from './App.vue' import { 
    FcBubbles } from 'fancy-components' /* eslint-disable no-new */ new FcBubbles() createApp(App).mount('#app') 
<!-- src\App.vue --> <template> <fc-bubbles click><img alt="Vue logo" src="./assets/logo.png"></fc-bubbles> <HelloWorld msg="Welcome to Your Vue.js App"/> </template> 

使用上如 Vue2 一样,但实际上会报错:

讯享网[Vue warn]: Failed to resolve component: fc-bubbles If this is a native custom element, make sure to exclude it from component resolution via compilerOptions.isCustomElement. 

原因与 Vue CLI 旧版本创建的 Vue2 应用一样,解决办法依然是配置忽略原生组件(自定义元素),参考:Vue 与 Web Components

// vue.config.js module.exports = { 
    chainWebpack: config => { 
    config.module .rule('vue') .use('vue-loader') .tap(options => ({ 
    ...options, compilerOptions: { 
    // 将所有 fc- 开头的标签名都视为自定义元素 isCustomElement: tag => tag.startsWith('fc-') } })) } } 

重启应用,警告已经不见了,但是点击仍然没有生效,打开 Element 面板发现组件的 click 属性并没有添加上,而其他属性如 click1 可以添加,这可能是因为 Vue3 认为 click 是一个不能直接添加的关键字,测试发现只需将 click 改成大写 Click 即可添加上。

讯享网<fc-bubbles Click><img alt="Vue logo" src="./assets/logo.png"></fc-bubbles> 

在 Vite 中使用 Web Components 组件库

# npm 6.x npm create vite@latest vite-vue-app √ Select a framework: » vue √ Select a variant: » vue cd vite-vue-app npm install npm i fancy-components npm run dev 
讯享网// src/main.js import { 
    createApp } from 'vue' import App from './App.vue' import { 
    FcBubbles } from 'fancy-components' new FcBubbles() createApp(App).mount('#app') 
<!-- src\App.vue --> <!-- 注意 Click 大写 --> <fc-bubbles Click><img alt="Vue logo" src="./assets/logo.png" /></fc-bubbles> 

还要配置忽略的自定义元素:

讯享网// vite.config.js import { 
    defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' // https://vitejs.dev/config/ export default defineConfig({ 
    plugins: [ vue({ 
    template: { 
    compilerOptions: { 
    // 将所有 fc- 开头的标签名都视为自定义元素 isCustomElement: tag => tag.startsWith('fc-') } } }) ] }) 

不需要重启即可生效。

小讯
上一篇 2025-03-20 11:05
下一篇 2025-01-14 10:43

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/53474.html