diff --git a/ReadMe.md b/ReadMe.md index fb407d7..79db02d 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -2,7 +2,7 @@ ![WebCell logo](https://web-cell.dev/WebCell-0.f9823b00.png) -[简体中文](./ReadMe-zh.md) | English +[简体中文](./guide/ReadMe-zh.md) | English [Web Components][1] engine based on VDOM, [JSX][2], [MobX][3] & [TypeScript][4] @@ -546,11 +546,11 @@ We recommend these libraries to use with WebCell: - [x] [Server-side Render][51] - [x] [Async Component loading][52] -## [v2 to v3 migration][53] +## [v2 to v3 migration](./guide/Migrating.md) ## More guides -1. [Development contribution][54] +1. [Development contribution](./guide/Contributing.md) [1]: https://www.webcomponents.org/ [2]: https://facebook.github.io/jsx/ @@ -604,5 +604,3 @@ We recommend these libraries to use with WebCell: [50]: https://github.com/EasyWebApp/Parcel-transformer-MDX [51]: https://developer.chrome.com/docs/css-ui/declarative-shadow-dom [52]: https://legacy.reactjs.org/docs/react-api.html#reactlazy -[53]: https://github.com/EasyWebApp/WebCell/blob/main/Migrating.md -[54]: https://github.com/EasyWebApp/WebCell/blob/main/Contributing.md diff --git a/Contributing.md b/guide/Contributing.md similarity index 100% rename from Contributing.md rename to guide/Contributing.md diff --git a/guide/Migrating-zh.md b/guide/Migrating-zh.md new file mode 100644 index 0000000..5764254 --- /dev/null +++ b/guide/Migrating-zh.md @@ -0,0 +1,200 @@ +# WebCell 从 v2 到 v3 的迁移 + +## 类 React 状态管理已被完全移除 + +**WebCell v3** 受到了 [**MobX** 的 **本地可观察状态** 思想][1] 的深刻启发,[不仅仅是 React][2],Web Components 可以更轻松地管理 **内部状态和逻辑**,无需任何复杂的操作: + +1. 状态类型声明 +2. `this.state` 声明及其类型注解/断言 +3. `this.setState()` 方法的调用及其回调 +4. 令人困惑的 _Hooks API_... + +只需像管理 **全局状态** 一样声明一个 **状态存储类**,并在 `this`(即 **Web Component 实例**)上初始化它。然后像 [MobX][3] 一样使用并观察这些状态,一切就完成了。 + +```diff +import { + component, ++ observer, +- mixin, +- createCell, +- Fragment +} from 'web-cell'; ++import { observable } from 'mobx'; + +-interface State { ++class State { ++ @observable +- key: string; ++ accessor key = ''; +} + +@component({ + tagName: 'my-tag' +}) ++@observer +-export class MyTag extends mixin<{}, State>() { ++export class MyTag extends HTMLElement { +- state: Readonly = { +- key: 'value' +- }; ++ state = new State(); + +- render({}: any, { key }: State) { ++ render() { ++ const { key } = this.state; + + return <>{value}; + } +} +``` + +同时,`shouldUpdate() {}` 生命周期方法已被移除。你只需在 `State` 类的方法中,在状态改变之前控制逻辑即可。 + +## DOM 属性变为可观察数据 + +**DOM 属性** 不同于 React 的 props,它们是 **响应式的**。它们不仅负责 **更新组件视图**,还会与 **HTML 属性同步**。 + +MobX 的 [`@observable`][4] 和 [`reaction()`][5] 是实现上述功能的优秀 API,代码也非常清晰,因此我们添加了 `mobx` 包作为依赖: + +```shell +npm install mobx +``` + +另一方面,[`mobx-web-cell` 适配器][6] 已经合并到了核心包中。 + +```diff ++import { JsxProps } from 'dom-renderer'; +import { +- WebCellProps, + component, + attribute, +- watch, ++ observer, +- mixin, +- createCell, +- Fragment +} from 'web-cell'; +-import { observer } from 'mobx-web-cell'; ++import { observable } from 'mobx'; + +-export interface MyTagProps extends WebCellProps { ++export interface MyTagProps extends JsxProps { + count?: number +} + +@component({ + tagName: 'my-tag' +}) +@observer +-export class MyTag extends mixin() { ++export class MyTag extends HTMLElement { ++ declare props: MyTagProps; + + @attribute +- @watch ++ @observable +- count = 0; ++ accessor count = 0; + +- render({ count }: MyTagProps) { ++ render() { ++ const { count } = this; + + return <>{count}; + } +} +``` + +## 使用 Shadow DOM 的 `mode` 选项控制渲染目标 + +### 渲染到 `children` + +```diff +import { + component, +- mixin +} from 'web-cell'; + +@component({ + tagName: 'my-tag', +- renderTarget: 'children' +}) +-export class MyTag extends mixin() { ++export class MyTag extends HTMLElement { +} +``` + +### 渲染到 `shadowRoot` + +```diff +import { + component, +- mixin +} from 'web-cell'; + +@component({ + tagName: 'my-tag', +- renderTarget: 'shadowRoot' ++ mode: 'open' +}) +-export class MyTag extends mixin() { ++export class MyTag extends HTMLElement { +} +``` + +## 将 Shadow CSS 注入移动到 `render()` + +这样使得 **Shadow CSS** 可以随着可观察数据的更新而响应。 + +```diff ++import { stringifyCSS } from 'web-utility'; +import { + component, +- mixin +} from 'web-cell'; + +@component({ + tagName: 'my-tag', +- renderTarget: 'shadowRoot', ++ mode: 'open', +- style: { +- ':host(.active)': { +- color: 'red' +- } +- } +}) +-export class MyTag extends mixin() { ++export class MyTag extends HTMLElement { + render() { + return <> ++ + test + ; + } +} +``` + +## 替换部分 API + +1. `mixin()` => `HTMLElement` 及其子类 +2. `mixinForm()` => `HTMLElement` 和 `@formField` +3. `@watch` => `@observable accessor` + +## 附录:v3 原型 + +1. [旧架构](https://codesandbox.io/s/web-components-jsx-i7u60?file=/index.tsx) +2. [现代架构](https://codesandbox.io/s/mobx-web-components-pvn9rf?file=/src/WebComponent.ts) +3. [MobX 精简版](https://codesandbox.io/s/mobx-lite-791eg?file=/src/index.ts) + +[1]: https://github.com/mobxjs/mobx/blob/mobx4and5/docs/refguide/observer-component.md#local-observable-state-in-class-based-components +[2]: https://fcc-cd.dev/article/translation/3-reasons-why-i-stopped-using-react-setstate/ +[3]: https://github.com/mobxjs/mobx/tree/mobx4and5/docs +[4]: https://github.com/mobxjs/mobx/blob/mobx4and5/docs/refguide/observable-decorator.md +[5]: https://github.com/mobxjs/mobx/blob/mobx4and5/docs/refguide/reaction.md +[6]: https://github.com/EasyWebApp/WebCell/tree/v2/MobX diff --git a/Migrating.md b/guide/Migrating.md similarity index 100% rename from Migrating.md rename to guide/Migrating.md diff --git a/ReadMe-zh.md b/guide/ReadMe-zh.md similarity index 98% rename from ReadMe-zh.md rename to guide/ReadMe-zh.md index 1fe36a4..208a729 100644 --- a/ReadMe-zh.md +++ b/guide/ReadMe-zh.md @@ -543,11 +543,11 @@ https://github.com/EasyWebApp/DOM-Renderer?tab=readme-ov-file#nodejs--bun - [x] [服务器端渲染][51] - [x] [异步组件加载][52] -## [v2 到 v3 迁移][53] +## [v2 到 v3 迁移](./Migrating-zh.md) ## 更多指南 -1. [开发贡献][54] +1. [开发贡献](./Contributing.md) [1]: https://www.webcomponents.org/ [2]: https://facebook.github.io/jsx/ @@ -601,5 +601,3 @@ https://github.com/EasyWebApp/DOM-Renderer?tab=readme-ov-file#nodejs--bun [50]: https://github.com/EasyWebApp/Parcel-transformer-MDX [51]: https://developer.chrome.com/docs/css-ui/declarative-shadow-dom?hl=zh-cn [52]: https://legacy.reactjs.org/docs/react-api.html#reactlazy -[53]: https://github.com/EasyWebApp/WebCell/blob/main/Migrating.md -[54]: https://github.com/EasyWebApp/WebCell/blob/main/Contributing.md diff --git a/pack-docs.sh b/pack-docs.sh index 51e9245..ae5d7be 100755 --- a/pack-docs.sh +++ b/pack-docs.sh @@ -1,18 +1,34 @@ +mkdir dist/ +cp ReadMe.md dist/ +cp guide/*.md dist/ +cd dist/ +# remove Markdown suffix, because TypeDoc will copy Markdown file to `/media` folder +replace ".md\)" ")" *.md +replace "guide/" "" ReadMe.md + # generate multilingual file -for file in ReadMe-*.md; do +for file in *.md; do typedoc --readme $file - mv docs/index.html ${file%.md}.html + mv docs/index.html ./${file%.md}.html done +cd ../ # generate docs typedoc source/ -# copy html file to docs folder, replace link +mv dist/*.html docs/ +rm -r dist/docs dist/*.md + +cd docs +# default language +mv ReadMe.html index.html + +# replace ReadMe-* to *, change URL in *.html for file in ReadMe-*.html; do # example: mv ReadMe-zh.html docs/zh.html - mv $file docs/"${file#ReadMe-}" + mv $file "${file#ReadMe-}" - # example: replace ReadMe-zh.md zh.html - replace "./${file%.html}.md" "./${file#ReadMe-}" docs/*.html + # example: remove ReadMe- + replace "./ReadMe-" "./" *.html done