查看原文
其他

八个 Web Components 前端框架,一定有一个你用得上

The following article is from 稀土掘金技术社区 Author 拜小白

大厂技术  高级前端  Node进阶

点击上方 程序员成长指北,关注公众号

回复1,加入高级Node交流群

通过之前的系列文章,大家对 Web Components 一定有了一个深入的了解。通过实战也对 Web Components 怎么写、怎么用比较熟悉了。

但是,在实现时我们不难发现,原生的 Web Component 对于封装组件其实并不是很流畅,需要对属性进行处理、需要对数据进行监听、需要对事件进行控制等等。这一切都在和效率开发背道而驰。

所以在针对这些不符合现在开发模式的情况,出现了很多 Web Components 前端框架。

Stencil

Stencil 是用于生成 Web Components 的编译器,由 Ionic 团队构建。Stencil 允许开发人员使用。TypeScript 和 JSX 等技术来定义组件,然后生成可在现代浏览器和旧版浏览器上运行的 100% 基于标准的 Web Component。当你的组件一旦经过 build 完成后,就会脱离 Stencil,不再依赖。并且 Stencil 相对原生 Web Components 提供了完善的项目目录架构和配置,与直接使用 Custom Elements 相比,Stencil 提供了额外的 API,使编写快速组件变得更加简单,Stencli 具有以下特性:

  • Virtual DOM
  • JSX 和异步渲染等 API 使快速
  • 强大的组件创建
  • Reactive data-binding 单向数据流
  • 组件懒加载
  • 无依赖性组件
  • 静态网站生成(SSG)
  • ...

开发人员体验也得到了调整,并带有实时重新加载和嵌入编译器的小型开发服务器。

通过 "npm init stencil" 我们可以去体验 Stencil 提供 cli 工具,Stencil 会提供保姆式的选项配置:

Stencil 组件看起来很像基于类的 React 组件,只是添加了 TypeScript 装饰器:

import { Component, Prop, h } from '@stencil/core';
import { format } from '../../utils/utils';

@Component({
  tag: 'my-component',
  styleUrl: 'my-component.css',
  shadow: true,
})
export class MyComponent {
  /**
   * The first name
   */
  @Prop() first: string;

  /**
   * The middle name
   */
  @Prop() middle: string;

  /**
   * The last name
   */
  @Prop() last: string;

  private getText(): string {
    return format(this.first, this.middle, this.last);
  }

  render() {
    return <div>Hello, World! I'm {this.getText()}</div>;
  }
}

上面的组件可以像任何其他 HTML 元素一样使用:

<my-component first="Stencil" last="'Don't call me a framework' JS"></my-component>

Omi

Omi 是腾讯开源的前端跨框架跨平台的框架。是下一代 Web 框架,Omi 的目标是去万物糟粕,合精华为一。Omi 是一个跨框架的框架,任何框架都可以使用 Omi 自定义原始,当然 Web Components 也可以。它具有以下特性:

  • 小巧并且高性能
  • 基于 Shadow/Ligit Dom 设计
  • Web Components + JSX/TSX 融合为一个框架 Omi
  • Shadow/Light DOM 与 Virtual DOM 融合,Omi 既使用了虚拟 DOM,也是使用真实 Shadow DOM,让视图更新更准确更迅速
  • 局部 CSS 最佳解决方案(Shadow DOM),社区为局部 CSS 折腾了不少框架和库(使用 js 或 json 写样式,如:Radium,jsxstyle,react-style;与 webpack 绑定使用生成独特的 className 文件名—类名—hash值,如:CSS Modules,Vue),还有运行时注入scoped atrr 的方式,都是 hack 技术;Shadow DOM Style 是最完美的方案
  • 对 custom elements 友好, 通过字符串 '0'或者'false'传递 false,通过:和Omi.$传递任意复杂类型
  • 符合浏览器的发展趋势以及 API 设计理念
  • ...

我们可以通过一下命令快速开始开发项目。

$ npm i omi-cli -g    # install cli
$ omi init my-app     # 初始化项目,也可以在空目录里执行 'omi init'
cd my-app           # 如果在空目录里执行 'omi init' 忽略这条命令
$ npm start           # 开发
$ npm run build       # 编译发布
import { WeElement, render, h, tag } from 'omi'

import './o-counter'
import './index.css'
import * as css from './index.less'
import logo from './logo.svg'

interface MyAppProps {
  name: string
}


@tag('my-app')
export default class extends WeElement<MyAppProps> {

  static css = css.default

  abc: string

  onCountChanged = (evt: CustomEvent) => {
    console.log(evt.detail)
  }

  render(props) {
    return (
      <div class="app">
        <header class="app-header">
          <img
            src={logo}
            class="app-logo"
            alt="logo"
          />
          <h1 class="app-title">Welcome to {props.name}</h1>
        </header>
        {this.abc}
        <o-counter onCountChanged={this.onCountChanged}></o-counter>

      </div>
    )
  }
}

render(<my-app></my-app>, '#root', {
  // if using OMI to build the whole application, ignore the attributs of DOM and use props of virtual dom
  ignoreAttrs: true
})

Omi 组件看起来也非常像 React 组件。相似度 95%,哈哈哈。如果你是一个 React 开发者一定感觉这语法非常的友好。

Slim.js

Slim.js 是一个开源的轻量级 Web Components 库,它为组件提供数据绑定和扩展能力,使用 es6 原生类继承。专注于帮助开发者更好的编写原生web组件,而不依赖于其他框架,但是也提供了良好的拓展性,开发者可以自由拓展。

  • slim.js 核心很小(压缩后不到 3kB),从名字也能看出它很小
  • slim.js 带有可选的内置指令——可以选择适合的指令,从而保持包很小
  • slim.js 速度很快——它使用浏览器的 Background Task API 按需创建绑定并释放内存作为后台任务(在所有主要浏览器上)。
  • slim.js 是可扩展的。您可以使用简单的 API 将您自己的自定义指令添加到注册表中,或者添加在组件生命周期的每一步执行您的代码的全局插件。
  • slim.js 基于自定义元素技术,因此您的用户界面可以在任何地方使用,并且不会干扰任何其他库或框架。
  • slim.js 使您能够编写核心组件、用户界面的复杂部分和整个 web 应用程序——您选择您的尺寸,slim.js将提供。
  • ...

通过以下命令可以快速开始 slim.js:

npm install slim-js
 # 或者
yarn add slim-js

或者从 CDN 使用:

IIFE / Global: <script src="https://unpkg.com/slim-js"></script>
Module: <script src="https://unpkg.com/slim-js?module"></script>
import { Slim } from 'slim-js';

const myHTML = `<h1>Welcome, {{this.username}}!</h1>`;

class AwesomeComponent extends Slim {
  constructor() {
    super();
    this.username = 'John Jimmy Junior';
  }
}

Slim.element('my-awesome-component', myHTML, AwesomeComponent);

slim.js 基于浏览器的原生 DOM API 的网络组件规范。如果您希望支持旧版浏览器,您可能需要添加一个 polyfill。将以下标记添加到您的主 HTML 文件中:

<script src="https://cdnjs.cloudflare.com/ajax/libs/webcomponentsjs/1.0.17/webcomponents-lite.js"></script>

Polymer

Polymer 是 Google 推出的 Web Components 库,支持数据的单向和双向绑定,兼容性较好,跨浏览器性能也较好;提供了一组用于创建 custom elements 的功能。这些功能旨在使 custom elements 像标准 DOM 元素一样工作更容易和更快。与标准 DOM 元素类似,Polymer 元素可以是:

  • 使用构造函数或 document.createElement
  • 使用特性或特性配置
  • 在每个实例中填充内部 DOM
  • 响应属性和属性的变化
  • 使用内部默认值或外部样式
  • 响应操纵其内部状态的方法
  • ...

使用以下命令可以运行一个 Polymer 程序:

npm install -g polymer-cli@next

git clone https://github.com/PolymerLabs/polymer-3-first-element.git

cd polymer-3-first-element

npm install

polymer serve --open

polymer 这语法和 Web Components 的原生语法真的非常的像。

import { PolymerElement, html } from '@polymer/polymer/polymer-element.js';
import '@polymer/iron-icon/iron-icon.js';

class IconToggle extends PolymerElement {
  static get template() {
    return html`
      <style>
        :host {
          display: inline-block;
        }
        iron-icon {
          fill: var(--icon-toggle-color, rgba(0,0,0,0));
          stroke: var(--icon-toggle-outline-color, currentcolor);
        }
        :host([pressed]) iron-icon {
          fill: var(--icon-toggle-pressed-color, currentcolor);
        }
      </style>
      <!-- shadow DOM goes here -->
      <iron-icon icon="[[toggleIcon]]"></iron-icon>
    `;
  }
  static get properties () {
    return {
      toggleIcon: {
        type: String
      },
      pressed: {
        type: Boolean,
        notify: true,
        reflectToAttribute: true,
        value: false
      }
    };
  }
  constructor() {
    super();
    this.addEventListener('click', this.toggle.bind(this));
  }
  toggle() {
    this.pressed = !this.pressed;
  }
}

customElements.define('icon-toggle', IconToggle);

hybrids

hybrids 是一个 JavaScript UI 框架,用于创建功能齐全的 Web 应用程序、组件库或具有独特的混合声明性和功能性架构的单个 Web Components。该框架的主要目标是为网络平台提供一套完整的工具——一切都没有外部依赖。它支持构建 UI 组件、管理复杂状态、使用客户端路由创建应用程序流以及针对全球市场本地化其内容。所有部分都遵循相同的独特概念,使其易于理解和使用!它具有:

  • 简单的结构,组件模型基于普通对象和纯函数,仍然在底层使用Web Components API
  • 无缝本地化,对组件内容自动翻译的内置支持使翻译无缝且易于集成
  • 该框架提供了一种方法来添加具有复数形式的动态消息、HTML 内容,或在模板上下文之外使用消息
  • 复杂状态管理,store 模块提供基于声明式模型定义的全局状态管理,内置对异步外部存储、关系、离线缓存等的支持
  • 结构化客户端路由,路由器模块为客户端应用程序提供了一个全局导航系统。它不是仅仅将 URL 与相应的组件匹配,而是依赖于树状结构的视图,这些视图在组件定义中有自己的路由配置。它使 URL 成为可选的,对对话框、受保护的视图等具有开箱即用的支持
  • ...

这里套用一个官网的例子,给大家演示一下:

import { define, html } from "hybrids";

export function increaseCount(host) {
  host.count += 1;
}

export default define({
  tag: "simple-counter",
  count: 0,
  render: ({ count }) => html`
    <button onclick="${increaseCount}">
      Count: ${count}
    </button>
  `
});

这代码相对前面几个实例,非常的简单,代码结构也非常的清晰。

X-Tag

X-Tag 是微软推出的开源库,支持 Web Components 规范,兼容Web Components。X-Tag 最初由 Mozilla 开发,现在由 Microsoft 的开发人员提供支持,它是一个开源 JavaScript 库,它包装了 W3C 标准 Web Components API 系列,为组件开发提供了一个紧凑、功能丰富的接口。虽然 X-Tag 可以轻松利用所有 Web Components API(自定义元素、Shadow DOM、模板和 HTML 导入),但它只需要自定义元素 API 支持即可运行。在没有原生自定义元素 API 的情况下,X-Tag 使用 Google 的 Polymer 框架所依赖的相同的 polyfill 。它的特点也很明显:

  • 标准,基于 Web Components API 构建
  • 高效,3k min/gzip中的强大功能
  • 可插拔,可以和其他库一起使用

它有两种版本:

  • x-tag + polyfills
  • Just x-tag no polyfills

这里演示一个 x-tag + polyfills 版本,看看效果:

<!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>
</head>
<body>
  <x-clock></x-clock>
</body>
<script src="../src/x-tag-polyfilled.min.js"></script>
<script src="../src/index.js"></script>
</html> 
//index.js
xtag.create('x-clock', class extends XTagElement {
  connectedCallback () {
    this.start();
  }
  start (){
    this.update();
    this._interval = setInterval(() => this.update(), 1000);
  }
  stop (){
    this._interval = clearInterval(this._data.interval);
  }
  update (){
    this.textContent = new Date().toLocaleTimeString();
  }
  'tap::event' (){
    if (this._interval) this.stop();
    else this.start();
  }
});

LitElement

LitElement 是一个简单的基类,用于使用 lit-html 创建快速、轻量级的 Web Components。LitElement 现在是Lit 库 monorepo的一部分。Lit 2 包括 lit-html 2.x 和 LitElement 3.x。LitElement 使用lit-html渲染到元素的Shadow DOM 中,并添加 API 来帮助管理元素属性和特性。LitElement 对属性的变化做出反应。

LitElement 具有以下特点:

  • 简单、现代、安全、小巧且快速
  • 允许您使用带有嵌入式 JavaScript 表达式的模板文字在 JavaScript 中编写 HTML 模板
  • lit-html 识别模板的静态和动态部分,因此它可以有效地只更新更改的部分
  • lit-html 不依赖于任何组件模型,它只专注于创建和更新 DOM
  • ...

这里也演示一个简单的例子,运行下面的 HTML 代码:

<!doctype html>
<html>
<head>
  <!-- Polyfills only needed for Firefox and Edge. -->
  <script src="https://unpkg.com/@webcomponents/webcomponentsjs@latest/webcomponents-loader.js"></script>
</head>
<body>
  <!-- Works only on browsers that support Javascript modules like
  Chrome, Safari, Firefox 60, Edge 17 -->
  <script type="module">
    import {LitElement, html, css} from 'https://unpkg.com/lit-element/lit-element.js?module';
    
    class MyElement extends LitElement {

      static get properties() {
        return {
          mood: {type: String}
        }
      }
      
      static get styles() {
        return css`.mood { color: green; }`;
      }

      render() {
        return html`Web Components are <span class="mood">${this.mood}</span>!`;
      }
    }

    customElements.define('my-element', MyElement);
  </script>
  
  <my-element mood="great"></my-element>
  
</body>
</html>


direflow

direflow 是一个 React组件 + web component +web componen t属性变化重新挂载 React 组件的 web component框架。direflow的响应式其实分为2块:组件内部响应式(通过React自身响应式流程),组件外部响应式(WebComponents属性变化监听重渲染组件)。如果外部属性不会经常变化的话,性能这块没有问题,因为组件内部的响应式完全是走了React自身的响应式。属性外部属性如果会经常变化的话,direflow框架在这块还有一定的优化空间。

direflow 具有以下特点:

  • 使用 React 的强大功能来创建您的组件构建它
  • Direflow 使开始开发变得非常容易!在几分钟内创建您的第一个 Direflow 组件
  • 创建丰富且完全独立的微前端
  • 构建整个 UI 库并在任何 UI 框架和库中使用它
  • ...

运行下面的命令自己尝试一下:

npm i -g direflow-cli

direflow create

cd <project-folder>
  
npm install

npm start

这完全就是在写 React。

  import React, { useContext } from 'react';
  import PropTypes from 'prop-types';
  import { EventContext, Styled } from 'direflow-component';
  import styles from './App.css';

  const App = (props) => {
    const dispatch = useContext(EventContext);

    const handleClick = () => {
      const event = new Event('my-event');
      dispatch(event);
    };

    const renderSampleList = props.sampleList.map((sample) => (
      <div key={sample} className='sample-text'>
        → {sample}
      </div>
    ));

    return (
      <Styled styles={styles}>
        <div className='app'>
          <div className='top'>
            <div className='header-image' />
          </div>
          <div className='bottom'>
            <div className='header-title'>{props.componentTitle}</div>
            <div>{renderSampleList}</div>
            <button className='button' onClick={handleClick}>
              Click me!
            </button>
          </div>
        </div>
      </Styled>
    );
  };

  App.defaultProps = {
    componentTitle: 'Test Direflow',
    sampleList: [
      'Create with React',
      'Build as Web Component',
      'Use it anywhere!',
    ],
  }

  App.propTypes = {
    componentTitle: PropTypes.string,
    sampleList: PropTypes.array,
  };

  export default App;

总结

尽管 Web Components 可能还没有全面的进入研发者的视野,现在还备受争议,但是它已经被很多大厂已经直接或者间接将它用于实践,并且在市场上也出现了很多 Web Components 库,可以轻松的使用和构建 Web Components,本文也列举了八个 Web Components 的前端框架:

  • Stencil:是一个用于构建可重用、可扩展的设计系统的工具链。生成可在每个浏览器中运行的小型、极快且 100% 基于标准的 Web Components。
  • Omi:是 Web Components + JSX/TSX 融合为一个框架,小巧的尺寸和高性能,融合和 React 和 Web Components 各自的优势。
  • Slim.js:是一个开源的轻量级 Web Components 库,它为组件提供数据绑定和扩展能力,使用 es6 原生类继承。专注于帮助开发者更好的编写原生web组件,而不依赖于其他框架,但是也提供了良好的拓展性,开发者可以自由拓展。
  • Polymer :是 Google 推出的 Web Components 库,支持数据的单向和双向绑定,兼容性较好,跨浏览器性能也较好;提供了一组用于创建 custom elements 的功能。这些功能旨在使 custom elements 像标准 DOM 元素一样工作更容易和更快。
  • hybrids:是一个 JavaScript UI 框架,用于创建功能齐全的 Web 应用程序、组件库或具有独特的混合声明性和功能性架构的单个 Web Components。
  • X-Tag:是微软推出的开源库,支持 Web Components 规范,兼容Web Components API。
  • LitElement:是一个简单的基类,用于使用 lit-html 创建快速、轻量级的 Web Components。
  • direflow:是一个 React组件 + web component +web componen t属性变化重新挂载 React 组件的 web component框架。

这些框架都有自己的特性,也各具自己的优缺点,在实战了中具体需要用哪一个 Web Components 前端框架完全取决于你自己。好啦,本文的内容到此结束了。

最后,我希望从这个 Web Components 系列教程中你可以学到了很多东西.
Node 社群



我组建了一个氛围特别好的 Node.js 社群,里面有很多 Node.js小伙伴,如果你对Node.js学习感兴趣的话(后续有计划也可以),我们可以一起进行Node.js相关的交流、学习、共建。下方加 考拉 好友回复「Node」即可。


   “分享、点赞在看” 支持一波👍

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存