赞
踩
开发项目的时候为什么不手写原生 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 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,或者两者共存。
我们认为 Vue 和 Web Components 主要是互补的技术。Vue 为使用和创建定制元素提供了出色的支持。无论你是将自定义元素集成到现有的 Vue 应用程序中,还是使用 Vue 来构建和分发自定义元素都很方便。
从 Vue 和 React 官网可以看到它们都对 Web Components 有很好的支持。
可以在 Custom Elements Everywhere 查看“自定义元素”(Web Components 的功能)在各个框架中的互操作性得分。
Making sure frameworks and custom elements can be BFFs
翻译:确保框架和自定义元素可以成为永远的最好的朋友(Best Friend Forever)。
既然 WebComponents 不会取代前端框架,那为什么还要学习?
一方面,浏览器原生支持的组件化我们肯定是需要了解的,这是为了跟上时代的步伐。
另一方面,那些流行框架想要顺应时代的趋势,不被大浪淘沙的淘汰掉,也必须要符合浏览器的标准,才能够在前端领域成为常青树。
而且我们无法保证现在流行的框架在几年后依然流行,当初用 jQuery 的大部分人也觉得有生之年它不会被替代。
Web Components 最重要的一点,就是组件化的概念其实是相通的,并不是一个全新的很复杂的概念,所以如果你用过 MVVM 框架,Web Components 其实是非常易于理解的,并不像学习一门全新框架那么耗费精力。而且如果你用过 Vue,那么 Web Components 简直就是手到擒来,因为尤雨溪在创建 Vue 的时候就大量参考了 Web Components 的语法,导致 Web Components 现在的写法有一部分和 Vue 长得几乎一摸一样。
Web Components 还有一个难能可贵的一点,就是它并不是一门单一的技术,它总共有三种规范:
第四个规范 HTML Imports 已被弃用,原因是该规范从未在任何浏览器中实现,已经被 ES Modules 取代。
参考:
其中有 Custom Elements 和 Shadow DOM 都是可以在不依赖 Web Components 的情况下用在其他地方上去的,也就是说只要你的想象力足够丰富,脑洞够大,就可以利用它们把 DOM 玩出花来。
下面挑选了市面上既好玩,颜值又高的组件库来体验以下 Web Components:
官方网站: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 就是一款可以提供这些常用组件,可以用于日常开发的组件库。
官方网站: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>
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' ]
- 1
- 2
- 3
- 4
- 5
- 6
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>
# 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-') } } }) ] })
不需要重启即可生效。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。