WebCell

WebCell

WebCell logo

简体中文 | English

基于 VDOM、JSXMobXTypeScriptWeb 组件 引擎

NPM 依赖性 CI 和 CD

反 996 许可证 UI 库推荐榜单

幻灯片 Gitter

编辑 WebCell 示例

NPM

特性 WebCell 3 WebCell 2 React Vue
JS 语言 TypeScript 5 TypeScript 4 ECMAScript 或 TypeScript ECMAScript 或 TypeScript
JS 语法 ES 装饰器 stage-3 ES 装饰器 stage-2
XML 语法 JSX import JSX factory JSX factory/import HTML/Vue 模板或 JSX(可选)
DOM API Web 组件 Web 组件 HTML 5+ HTML 5+
视图渲染器 DOM Renderer 2 SnabbDOM (内置) SnabbDOM(分叉)
state API MobX @observable this.state this.stateuseState() this.$dataref()
props API MobX @observable @watch this.propsprops => {} this.$propsdefineProps()
状态管理 MobX 6+ MobX 4/5 Redux VueX
页面路由器 JSX 标签 JSX 标签 + JSON 数据 JSX 标签 JSON 数据
资源打包工具 Parcel 2 Parcel 1 webpack Vite
npm install dom-renderer mobx web-cell

演示和 GitHub 模板

npm install parcel @parcel/config-default @parcel/transformer-typescript-tsc -D
{
"scripts": {
"start": "parcel source/index.html --open",
"build": "parcel build source/index.html --public-url ."
}
}
{
"compilerOptions": {
"target": "ES6",
"module": "ES2020",
"moduleResolution": "Node",
"useDefineForClassFields": true,
"jsx": "react-jsx",
"jsxImportSource": "dom-renderer"
}
}
{
"extends": "@parcel/config-default",
"transformers": {
"*.{ts,tsx}": ["@parcel/transformer-typescript-tsc"]
}
}
<script src="https://polyfill.web-cell.dev/feature/ECMAScript.js"></script>
<script src="https://polyfill.web-cell.dev/feature/WebComponents.js"></script>
<script src="https://polyfill.web-cell.dev/feature/ElementInternals.js"></script>

<script src="source/MyTag.tsx"></script>

<my-tag></my-tag>
import { DOMRenderer } from 'dom-renderer';
import { FC, PropsWithChildren } from 'web-cell';

const Hello: FC<PropsWithChildren> = ({ children = '世界' }) => (
<h1>你好,{children}</h1>
);

new DOMRenderer().render(<Hello>WebCell</Hello>);
import { DOMRenderer } from 'dom-renderer';
import { component } from 'web-cell';

@component({
tagName: 'hello-world',
mode: 'open'
})
class Hello extends HTMLElement {
render() {
return (
<h1>
你好, <slot />!
</h1>
);
}
}

new DOMRenderer().render(
<>
<Hello>WebCell</Hello>
{/* 或 */}
<hello-world>WebCell</hello-world>
</>
);
import { DOMRenderer } from 'dom-renderer';
import { observable } from 'mobx';
import { WebCell, component, attribute, observer } from 'web-cell';

interface HelloProps {
name?: string;
}

interface Hello extends WebCell<HelloProps> {}

@component({ tagName: 'hello-world' })
@observer
class Hello extends HTMLElement implements WebCell<HelloProps> {
@attribute
@observable
accessor name = '';

render() {
return <h1>你好,{this.name}!</h1>;
}
}

new DOMRenderer().render(<Hello name="WebCell" />);

// 或在 TypeScript 中提示 HTML 标签属性

declare global {
namespace JSX {
interface IntrinsicElements {
'hello-world': HelloProps;
}
}
}
new DOMRenderer().render(<hello-world name="WebCell" />);
import { DOMRenderer } from 'dom-renderer';
import { observable } from 'mobx';
import { FC, observer } from 'web-cell';

class CounterModel {
@observable
accessor times = 0;
}

const couterStore = new CounterModel();

const Counter: FC = observer(() => (
<button onClick={() => (couterStore.times += 1)}>
计数:{couterStore.times}
</button>
));

new DOMRenderer().render(<Counter />);
import { DOMRenderer } from 'dom-renderer';
import { observable } from 'mobx';
import { component, observer } from 'web-cell';

@component({ tagName: 'my-counter' })
@observer
class Counter extends HTMLElement {
@observable
accessor times = 0;

handleClick = () => (this.times += 1);

render() {
return <button onClick={this.handleClick}>计数:{this.times}</button>;
}
}

new DOMRenderer().render(<Counter />);
import { component } from 'web-cell';
import { stringifyCSS } from 'web-utility';

@component({
tagName: 'my-button',
mode: 'open'
})
export class MyButton extends HTMLElement {
style = stringifyCSS({
'.btn': {
color: 'white',
background: 'lightblue'
}
});

render() {
return (
<>
<style>{this.style}</style>

<a className="btn">
<slot />
</a>
</>
);
}
}
import { component } from 'web-cell';

@component({
tagName: 'my-button',
mode: 'open'
})
export class MyButton extends HTMLElement {
render() {
return (
<>
<link
rel="stylesheet"
href="https://unpkg.com/bootstrap@5.3.3/dist/css/bootstrap.min.css"
/>
<a className="btn">
<slot />
</a>
</>
);
}
}
.btn {
color: white;
background: lightblue;
}
import { WebCell, component } from 'web-cell';

import styles from './scoped.css' assert { type: 'css' };

interface MyButton extends WebCell {}

@component({
tagName: 'my-button',
mode: 'open'
})
export class MyButton extends HTMLElement implements WebCell {
connectedCallback() {
this.root.adoptedStyleSheets = [styles];
}

render() {
return (
<a className="btn">
<slot />
</a>
);
}
}
import { component, on } from 'web-cell';

@component({ tagName: 'my-table' })
export class MyTable extends HTMLElement {
@on('click', ':host td > button')
handleEdit(event: MouseEvent, { dataset: { id } }: HTMLButtonElement) {
console.log(`编辑行:${id}`);
}

render() {
return (
<table>
<tr>
<td>1</td>
<td>A</td>
<td>
<button data-id="1">编辑</button>
</td>
</tr>
<tr>
<td>2</td>
<td>B</td>
<td>
<button data-id="2">编辑</button>
</td>
</tr>
<tr>
<td>3</td>
<td>C</td>
<td>
<button data-id="3">编辑</button>
</td>
</tr>
</table>
);
}
}
import { observable } from 'mobx';
import { component, observer, reaction } from 'web-cell';

@component({ tagName: 'my-counter' })
@observer
export class Counter extends HTMLElement {
@observable
accessor times = 0;

handleClick = () => (this.times += 1);

@reaction(({ times }) => times)
echoTimes(newValue: number, oldValue: number) {
console.log(`新值:${newValue},旧值:${oldValue}`);
}

render() {
return <button onClick={this.handleClick}>计数:{this.times}</button>;
}
}
import { DOMRenderer } from 'dom-renderer';
import { WebField, component, formField, observer } from 'web-cell';

interface MyField extends WebField {}

@component({
tagName: 'my-field',
mode: 'open'
})
@formField
@observer
class MyField extends HTMLElement implements WebField {
render() {
const { name } = this;

return (
<input
name={name}
onChange={({ currentTarget: { value } }) =>
(this.value = value)
}
/>
);
}
}

new DOMRenderer().render(
<form method="POST" action="/api/data">
<MyField name="test" />

<button>提交</button>
</form>
);
import { FC } from 'web-cell';

const AsyncTag: FC = () => <div>异步</div>;

export default AsyncTag;
import { DOMRenderer } from 'dom-renderer';
import { lazy } from 'web-cell';

const AsyncTag = lazy(() => import('./AsyncTag'));

new DOMRenderer().render(<AsyncTag />);
import { DOMRenderer } from 'dom-renderer';
import { AnimateCSS } from 'web-cell';

new DOMRenderer().render(
<AnimateCSS type="fadeIn" component={props => <h1 {...props}>淡入</h1>} />
);
npm install jsdom
import 'web-cell/polyfill';

https://github.com/EasyWebApp/DOM-Renderer?tab=readme-ov-file#nodejs--bun

  1. connectedCallback
  2. disconnectedCallback
  3. attributeChangedCallback
  4. adoptedCallback
  5. updatedCallback
  6. mountedCallback
  7. formAssociatedCallback
  8. formDisabledCallback
  9. formResetCallback
  10. formStateRestoreCallback
  1. 基础
  2. 仪表盘
  3. 移动端
  4. 静态网站

我们建议将这些库与 WebCell 一起使用:

  1. 开发贡献
MMNEPVFCICPMFPCPTTAAATR