你必须要了解的跨框架解决方案-Mitosis

2,320 阅读5分钟

写在前面

mitosis 是由 Angular 的创建者 Misko Hevery 编写的最新框架,是一个编译时框架,允许您在 JSX 中编写组件,一次编写组件,到处运行。可编译为 Vue、React、Solid、Angular、Svelte等。

mitosis 使用 JSX 的静态子集,灵感来自 Solid。这意味着可以将其解析为简单的 JSON 结构,然后构建为针对各种框架的序列化程序。

为什么使用mitosis

mitosis是一种非常强大和灵活的工具。到目前为止,已经确定三大典型场景可以从中受益。

image.png

(1)设计系统维护者

如果您是从一个 Web 框架开始的设计系统库的维护者,那么在为后续框架创建新版本时,您会感受到巨大的维护痛苦和重复劳动。

(2)使用多个 Web 框架的团队

如果您所在的团队使用了多个不同的Web前端框架,那么在所有前端之间协调设计系统以获得好的开发体验绝对是一场噩梦。 Mitosis 是消除此类问题的出色工具,因为它允许您在组件中定义一次设计并将它们部署到所有单独的前端框架。

(3)构建 Web SDK 的团队

如果您的团队正在为直接集成到 Web 框架并涉及向最终用户提供组件的产品构建 SDK,那么 mitosis 非常适合您的团队。

快速入门

(1)新建一个文件夹demo-mitosis

(2)进入该文件夹,初始化一个工程文件:npm init -y

(3)安装依赖

npm install '@builder.io/mitosis-cli' '@builder.io/mitosis'

(4)配置mitosis.config.js

module.exports = {

  files: 'src/**',

  targets: ['vue3', 'solid', 'svelte', 'react'],

};

(5)在demo-mitosis中新建一个src,代码主要放在该文件夹中

(6)打开package.json,加入

"scripts": {

    "build": "mitosis build"

  },

(7)npm run build

输出output文件,文件中包括对应输出的组件(或原生js文件),之后就可以在相应的vue、react等web应用中使用生成的组件。

基本目录结构如下图所示

image.png

src:mitosis代码

output:编译输出的组件(vue、react等)

mitosis.config.js:配置文件

(8)简单的mitosis案例

//mitosis//MyComponent.lite.tsx

export default function MyComponent() {

  return <div>Hello world!</div>;

}

编译输出vue、react组件如下图所示:

image.png

image.png

mitosis基本配置

mitosis是一个组件驱动的前端框架,每个mitosis组件应当有独立的文件命名,后缀.lite.tsx或.lite.jsx(如Vue的.vue后缀文件一样)。每个.lite.tsx文件导出一个函数。如下:

export default function MyComponent() {

  return <div>Hello world!</div>;

}

为了输出对应的组件,可以在mitosis.config.js中配置,输出targets;

targets: ['vue3', 'solid', 'svelte', 'react','html','webcomponent'];

如输出的Vue组件内容如下:

image.png

此外,在tsconfig.json添加如下内容支持TS语法。

{

  "compilerOptions": {

    "jsxImportSource""@builder.io/mitosis",

    "moduleResolution""node",

  },

  "include": [

    "src"

  ]

}

mitosis组件基本规则

mitosis样式处理

1)样式嵌入到mitosis组件中

export default function CSSExample() {

  return <div css={{ marginTop: '10px', color: 'red' }} />;

}

对应编译的Vue文件

image.png

从图中可以看出,嵌入到mitosis样式编译的vue文件后样式会嵌入到该vue文件的style中。

2)class、className名的样式

export default function CSSExample() {

  return <div css={{ marginTop: '10px', color: 'red' }} class="x1" className="x2"/>;

}

编译后生成的vue文件如下

image.png

从图中可以看出,通过class、className会编译为相应的类名样式。

Mitosis输出函数的Props参数

import {useStore} from "@builder.io/mitosis"

type Props = {

  message: string;

};

export default function MyComponent(props:Props) {

  const state = useStore({

    name'Foo',

  });

  return (

    <div>

      {props.message || 'Hello'} {state.name}! I can run in React, Vue, Solid or Svelte!

    </div>

  );

}

编译输出vue文件如下,从中可以看出mitosis的export函数参数props编译输出为vue的props参数。

image.png

useStore钩子

1)mitosis状态管理(响应式)

import {useStore} from "@builder.io/mitosis"

export default function MyComponent(){

  const state=useStore({

    name:'React',

  });

  return (

    <div>

      <h2>Hello, {state.name}</h2>

      <input onInput={(event)=>(state.name=event.target.value)} value={state.name} type="text" />

    </div>

  )

}

编译输出vue文件如下,从图中可以看出,编译生成vue响应式。

image.png

2)useStore中get函数

import {kebabCase} from 'lodash';import {useStore} from "@builder.io/mitosis"

export default function MyComponent(props){

  const state=useStore({

    name:"Vue",

    get transformedName() {

      return kebabCase(props.info);

    },

  });

  return (

    <div>

      <h2>Hello, {state.name}</h2>

      <input type="text" onInput={(event)=>state.name=event.target.value} value={state.name} />

      {state.transformedName}

    </div>

  )

}****

编译vue文件如下,从中可以看出mitosis中的get编译为vue的计算属性。

image.png

3)useStore中的方法

import {useStore} from "@builder.io/mitosis"

export default function MyComponent(){

  const state=useStore({

    name:"React",

    updateName(newName){

      state.name=newName

    }

  });

  return (

    <div css={{marginTop:"10px",color:"red"}}>

      <h2>Hello, {state.name}</h2>

      <input type="text" onInput={(event)=>state.updateName(event.target.value)} value={state.name}/>

    </div>

  )

}****

对应编译生成的vue文件如下,从中可以看出编译输出为vue的methods。

image.png

Mitosis控制流

1)Show

import { useStore,Show } from "@builder.io/mitosis";

export default function MyComponent(props){

  return (<div><Show when={props.showConents}><div>Hello, I may xxxx</div></Show></div>)

}****

对应编译输出vue如下,可以看出编译输出为vue的v-if。

image.png

2)For

import {useStore,For} from "@builder.io/mitosis";

export default function MyComponent(props){

  const state=useStore({

    myArray:[1,2,3],

  });

  return <For each={state.myArray}>{(theArrayItem,index)=><div>{theArrayItem}</div>}</For>

}****

编译输出vue如下,从中可以看出编译输出为vue的v-for。

image.png

mitosis插槽

import {Slot} from "@builder.io/mitosis";

export default function Layout(props){

  return(

    <div className="layout">

      <div className="top">

        <Slot name="top">Top default</Slot>

      </div>

      <div className="left">

        <Slot name="left" />

      </div>

      <div className="center">

        <Slot name="center" />

      </div>

      <Slot />

    </div>

  )

}

对应编译的Vue文件如下,直接编译对应vue的slot。

image.png

mitosis钩子(useRef,onMount,onUnMount,onUpdate)

1)useRef 编译后生成vue的ref,

2)onMount,生成vue的mounted,

3)onUnMount生成vue的unmounted,

4)onUpdate生成vue的computed+watch

import { useStore, useRef, Show,onMount,onUnMount,onUpdate } from '@builder.io/mitosis';

export default function MyComponent(props) {

  const inputRef = useRef<HTMLInputElement>(null);

  const state = useStore({

    name'Steve',

    a:'a',

    b:'b',

    onBlur() {

      // Maintain focus

      inputRef.focus();

    },

    get lowerCaseName() {

      return state.name.toLowerCase();

    },

  });

  onMount(()=>{

    console.log('我被挂载了!!!!')

  })

  onUnMount(()=>{

    console.log('卸载!!!');

  })

  onUpdate(()=>{

    console.log('Runs when a or b changes', state.a, state.b);

  },[state.a, state.b])

  return (

    <div>

      <Show when={props.showInput}>

        <input

          ref={inputRef}

          css={{ color: 'red' }}

          value={state.name}

          onBlur={() => state.onBlur()}

          onChange={(event) => (state.name = event.target.value)}

        />

      </Show>

      Hello {state.lowerCaseName}! I can run in React, Vue, Solid, or Liquid!

    </div>

  );

}

对应编译输出的vue如下:

image.png

基于mitosis的组件库

Papanasi,是一个与前端框架无关的UI组件库,其设计之初是为了可以将其嵌入到任何项目中,易与流行的前端框架结合使用,目前该框架已经支持与Angular、Preact、Qwik、React、Solid、Svelte、Vue和Web Components结合使用。

github:github.com/CKGrafico/p…

文档:papanasi.js.org/?path=/stor…

总结

mitosis就是相当于一个工具,提供了统一的语法,通过该语法书写的前端组件库可以提供给不同的前端框架(vue、react、solid等)使用,从而避免了目前主流的前端框架提供的组件库绝大多数是与框架进行绑定的不足(如当前的前端框架只提供了vue版本的,若以后要想扩展为react版本,就需要将所有的组件重新再写一遍)。

不足:mitosis目前还没有一个稳定的版本(当前版本V0.0.87),相关文档资源不完善。

参考链接

github.com/BuilderIO/m…

www.infoq.cn/article/gdc…

mitosis.builder.io/?outputTab=…