概览

Mitosis 受到许多现代框架的启发。您会看到组件看起来像 React 组件,并使用类似 React 的 hooks,但具有类似 Vue 的简单可变状态,使用类似 Solid 的静态形式的 JSX,像 Svelte 一样编译,并且使用类似 Angular 的简单、规范的结构。

示例 Mitosis 组件:

  1. import { For, Show, useStore } from '@builder.io/mitosis';
  2. export default function MyComponent(props) {
  3. const state = useStore({
  4. newItemName: 'New item',
  5. list: ['hello', 'world'],
  6. addItem() {
  7. state.list = [...state.list, state.newItemName];
  8. },
  9. });
  10. return (
  11. <div>
  12. <Show when={props.showInput}>
  13. <input
  14. value={state.newItemName}
  15. onChange={(event) => (state.newItemName = event.target.value)}
  16. />
  17. </Show>
  18. <div css={{ padding: '10px' }}>
  19. <button onClick={() => state.addItem()}>Add list item</button>
  20. <div>
  21. <For each={state.list}>{(item) => <div>{item}</div>}</For>
  22. </div>
  23. </div>
  24. </div>
  25. );
  26. }

组件

Mitosis 类似于大多数现代前端框架,以组件为驱动。每个 Mitosis 组件应位于自己的文件中,并且是单一的默认导出。它们是简单的函数,返回 JSX 元素。

  1. export default function MyComponent() {
  2. return <div>Hello world!</div>;
  3. }

样式

css

通过 dom 元素和组件上的 css 属性进行样式设置。它接受驼峰式的 CSS 属性(类似于 DOM 元素上的 style 对象)和作为有效 CSS 字符串的属性。

  1. export default function CSSExample() {
  2. return <div css={{ marginTop: '10px', color: 'red' }} />;
  3. }

还可以将媒体查询作为键,值为样式对象包含在内。

  1. export default function ResponsiveExample() {
  2. return (
  3. <div
  4. css={{
  5. marginTop: '10px',
  6. '@media (max-width: 500px)': {
  7. marginTop: '0px',
  8. },
  9. }}
  10. />
  11. );
  12. }

class vs className

Mitosis 更喜欢您使用 class 提供类名字符串,但也允许您提供 className。如果在同一个组件中同时使用两者,它将尝试合并两者。我们建议只使用一个(最好是 class,因为 Mitosis 内部首选它)。

状态

状态由 useStore hook 提供。当前此值的名称必须为 state,如下所示:

  1. export default function MyComponent() {
  2. const state = useStore({
  3. name: 'Steve',
  4. });
  5. return (
  6. <div>
  7. <h2>Hello, {state.name}</h2>
  8. <input onInput={(event) => (state.name = event.target.value)} value={state.name} />
  9. </div>
  10. );
  11. }

如果初始状态值是计算值(基于 props 或某个函数的输出),则不能将其内联。而是使用 getter 方法:

  1. import { kebabCase } from 'lodash';
  2. export default function MyComponent(props) {
  3. const state = useStore({
  4. name: 'Steve',
  5. get transformedName() {
  6. return kebabCase('Steve');
  7. },
  8. get transformedName() {
  9. return props.name;
  10. },
  11. });
  12. return (
  13. <div>
  14. <h2>Hello, {state.name}</h2>
  15. <input onInput={(event) => (state.name = event.target.value)} value={state.name} />
  16. </div>
  17. );
  18. }

组件在状态值更改时会自动更新。

方法

state 对象还可以包含方法。

  1. export default function MyComponent() {
  2. const state = useStore({
  3. name: 'Steve',
  4. updateName(newName) {
  5. state.name = newName;
  6. },
  7. });
  8. return (
  9. <div>
  10. <h2>Hello, {state.name}</h2>
  11. <input onInput={(event) => state.updateName(event.target.value)} value={state.name} />
  12. </div>
  13. );
  14. }

控制流

Builder 中的控制流是静态的,类似于 Solid。与 React 中的自由形式 JavaScript 不同,您必须使用诸如 <Show><For> 这样的控制流组件。

Show

  1. export declare function Show<T>(props: {
  2. when: T | undefined | null | false;
  3. else?: JSX.Element;
  4. children?: JSX.Element | null;
  5. }): any;

用于条件逻辑的 <Show>。它接受一个 when 属性,表示匹配的条件。当条件为真时,子项将呈现,否则它们将不呈现。

  1. export default function MyComponent(props) {
  2. return (
  3. <>
  4. <Show when={props.showContents} else={<span {...props.attributes}>{props.text}</span>}>
  5. Hello, I may or may not show!
  6. </Show>
  7. ;
  8. </>
  9. );
  10. }

For

用于重复项的 <For>,例如遍历数组。它接受一个 each 属性,表示要迭代的数组。此组件将一个函数作为子项,该函数将相关项目和索引传递给该函数,如下所示:

  1. export default function MyComponent(props) {
  2. const state = useStore({
  3. myArray: [1, 2, 3],
  4. });
  5. return <For each={state.myArray}>{(theArrayItem, index) => <div>{theArrayItem}</div>}</For>;
  6. }

Children

我们使用标准方法通过 props.children 传递子项。

  1. export default function MyComponent(props) {
  2. return <div>{props.children}</div>;
  3. }

对于 Web 组件,您需要使用 ShadowDom 元数据

Slot

当要注册命名插槽时,使用 slot 属性进行注册。

  1. <div>
  2. <Layout
  3. slotTop={<NavBar/>}
  4. slotLeft={<Sidebar/>}
  5. slotCenter={<Content/>}
  6. />
  7. anything else
  8. </Layout>
  9. </div>

在此示例中,我们为 Layout 组件注册了 top、left 和 center。

如果 Layout 组件也是 Mitosis 组件,

那么我们只需在 props 中使用引用。

  1. export default function Layout(props) {
  2. return (
  3. <div className="layout">
  4. <div className="top">{props.slotTop}</div>
  5. <div className="left">{props.slotLeft}</div>
  6. <div className="center">{props.slotCenter}</div>
  7. {props.children}
  8. </div>
  9. );
  10. }

或者使用由组件提供的 Slot 组件

  1. import { Slot } from '@builder.io/mitosis';
  2. export default function Layout(props) {
  3. return (
  4. <div className="layout">
  5. <div className="top">
  6. <Slot name="top" />
  7. </div>
  8. <div className="left">
  9. <Slot name="left" />
  10. </div>
  11. <div className="center">
  12. <Slot name="center" />
  13. </div>
  14. <Slot />
  15. </div>
  16. );
  17. }

对于 vue 组件,slot 属性将被编译为命名插槽

  1. <div class="layout">
  2. <div class="top"><slot name="top" /></div>
  3. <div class="left"><slot name="left" /></div>
  4. <div class="center"><slot name="center" /></div>
  5. <slot />
  6. </div>

Mitosis 一次只编译一个组件,只关注为每个框架输出正确的方法。对于上面的两个示例,以下是 Angular 和 HTML 的输出。

  1. <div>
  2. <layout>
  3. <sidebar left></sidebar>
  4. <nav-bar top></nav-bar>
  5. <content center></content>
  6. anything else
  7. </layout>
  8. <div></div>
  9. </div>
  1. @Component({
  2. selector: 'layout',
  3. template: `
  4. <div class="layout">
  5. <div class="top">
  6. <ng-content select="[top]"></ng-content>
  7. </div>
  8. <div class="left">
  9. <ng-content select="[left]"></ng-content>
  10. </div>
  11. <div class="center">
  12. <ng-content select="[center]"></ng-content>
  13. </div>
  14. <ng-content></ng-content>
  15. </div>
  16. `,
  17. })
  18. class LayoutComponent {}

对于 Web 组件,您需要使用 ShadowDom 元数据来命名插槽

对于 Web 组件,您需要使用 ShadowDom 元数据的命名插槽

默认 Slot 内容

  1. import { Slot } from '@builder.io/mitosis';
  2. export default function Layout(props) {
  3. return (
  4. <div className="layout">
  5. <div className="top">
  6. <Slot name="top">Top default</Slot>
  7. </div>
  8. <div className="left">
  9. <Slot name="left" />
  10. </div>
  11. <div className="center">
  12. <Slot name="center" />
  13. </div>
  14. <Slot>Default child</Slot>
  15. </div>
  16. );
  17. }