Web 组件标准实践

水歌 —— WebCell 引擎作者


水歌其人

  • Web/JavaScript 全栈开发者
  • WebCell 等多个开源项目的作者
  • jQuery、Babel 等多个国际开源项目的贡献者
  • freeCodeCamp 成都社区负责人
  • 开源社官网开发组长、执委会成员
  • 微软 MVP

Web 组件的概念

将网页中某块界面的 HTML 结构CSS 样式JS 逻辑,封装成一个可移植的模块


Web 组件技术简史


上古时代

  • W3C: <frameset /><iframe />

  • IE: HTML Component (HTC)


中古时代

function MyComponent(options) {
  this.options = options;
}

$.extend(MyComponent.prototype, {
  method: function () {}
});

$.fn.myComponent = function (options) {
  this.each(function () {
    $.data(this, 'instance', new MyComponent(options));
  });
};

近代

import React from 'react';

export default class MyComponent extends React.Component {
  render() {
    return <h1>Hello, World!</h1>;
  }
}

历史问题

  • 隔离性好的,运行时过重

  • 易用性好的,工程化不佳

  • 实用性强的,工具链过重


Web 组件标准

https://www.webcomponents.org/


标准集

  • HTML 5.3
  • DOM 4.1
  • CSS Variables level 1
  • ECMAScript 6+

主要目标

  • 运行时 轻量级 隔离环境

  • 框架无关的 DOM 接口


原生写法


样式定制

my-component {
  --my-color: blue;
}
:host {
}
:host(.class) {
}
:host-context(.class) {
}

::slotted(*) {
  color: var(--my-color);
}

生命周期

class MyComponent extends HTMLElement {
  static get observedAttributes() {
    return ['title'];
  }
  attributeChangedCallback(attrName, oldVal, newVal) {}

  connectedCallback() {}
  disconnectedCallback() {}

  adoptedCallback() {}
}

扩展原生

customElements.define('my-button', class extends HTMLButtonElement {}, {
  extends: 'button'
});
<button is="my-button">按钮</button>

规范模式

不要接管一切

<my-button>
  <!-- shadow-root -->
  <button><slot></slot></button>
  <!-- shadow-root -->
  按钮
</my-button>

渐进增强,优雅降级

<!-- 良 -->
<my-button>
  <!-- shadow-root -->
  <slot></slot>
  <!-- shadow-root -->
  <button>按钮</button>
</my-button>

<!-- 优 -->
<button is="my-button">
  <!-- shadow-root -->
  <slot></slot>
  <!-- shadow-root -->
  按钮
</button>

兼容性

原生

polyfill

IE 11 +


工程问题

  • 数据绑定

  • 资源打包

  • 路由定义

  • 状态管理


WebCell

优雅、轻量的 Web 组件引擎


声明式组件


官方适配 MobX

import { createCell } from 'web-cell';
import { observer } from 'mobx-web-cell';

import { app } from '../model';

export default observer(function PageIndex() {
  return <div onClick={app.increase}>count: {app.count}</div>;
});

import { createCell, component, mixin } from 'web-cell';
import { observer } from 'mobx-web-cell';

import { app } from '../model';

@observer
@component({
  tagName: 'page-index'
})
export default class PageIndex extends mixin() {
  render() {
    return <div onClick={app.increase}>count: {app.count}</div>;
  }
}

极简路由 —— Cell Router

路径即状态,容器即组件


import { documentReady, render, createCell, Fragment } from 'web-cell';
import { History, PageProps, CellRouter } from 'cell-router/source';

const history = new History();

function TestPage({ path, history, defaultSlot, ...data }: PageProps) {
  return (
    <ul>
      <li>Path: {path}</li>
      <li>Data: {JSON.stringify(data)}</li>
    </ul>
  );
}

documentReady.then(() =>
  render(
    <>
      <nav>
        <a href="test?a=1">Test</a>
        <a href="example?b=2">Example</a>
      </nav>
      <CellRouter
        className="router"
        history={history}
        routes={[{ paths: ['test', /^example/], component: TestPage }]}
      />
    </>
  )
);

异步加载页面

tsconfig.json

{
  "compilerOptions": {
    "module": "ESNext"
  }
}

source/page/index.ts

export default [
  {
    paths: ['', 'home'],
    component: async () => (await import('./Home.tsx')).default
  },
  {
    paths: ['list'],
    component: async () => (await import('./List.tsx')).default
  }
];

开箱即用

npm init -y

npm install web-cell mobx mobx-web-cell cell-router web-utility boot-cell

npm install parcel-bundler -D

官方组件库 —— BootCell


官方组件库 —— Material Cell


竞争对手

Google Polymer

Ionic Stencil

Tencent Omi


https://web-cell.dev/


上一篇
PWA 即刻上手 PWA 即刻上手
近两年国内前端界很流行小程序,但大多数人都不知道小程序很多概念是鹅厂“借鉴”自 Google 主导的 Progressive Web App(渐进式网页应用)标准 —— “独立”运行:在“系统正在运行的应用”列表中独立显示 本地缓存:页面
2019-10-31
下一篇
结构化简历数据 结构化简历数据
参加工作久了,经历公司多了,每次换工作写简历都烦得要死 —— 常用招聘网站更新简历:简历编辑器改版了,操作习惯大变不说,数据格式很多也对不上号 注册新招聘网站填简历:所需内容和之前其它网站差不多,但填法大相径庭 所以,虽说每次只加一项前
2019-08-14
目录