跨框架组件的开发,是一个兼顾用户体验(UX)和开发者体验(DX)的重要问题。随着前端技术生态的快速发展,如何开发能够适配不同框架的组件已成为UI一致性、业务多技术栈需求以及渐进式重构中的重要挑战。
一、跨框架组件的使用场景
1. 大型业务的多技术栈需求
大型业务企业的前端团队可能使用不同的技术栈(如 React 和 Vue)。通过跨框架组件库,可以实现组件复用,减少重复开发的成本,同时保持设计一致性。
2.UI交互的一致性
一个成功的设计系统需要在不同框架和平台中提供一致的用户体验。跨框架组件能确保设计一致性和交互统一,从而提高用户满意度。
3.第三方库或插件
第三方组件库(如日期选择器、图表库等)需要适配多种框架以覆盖更广泛的用户群体,提升组件的可复用性。
4.渐进式重构
在技术栈迁移过程中(如从 AngularJS 迁移到 React),跨框架组件可以帮助团队在不同框架之间平滑过渡,同时兼容旧框架和新框架。
二、解决方案
在实际开发中,有以下四种常见方案可以帮助我们实现跨框架组件的目标:
1. Web Components
Web Components 是浏览器原生支持的技术,它由 Shadow DOM、Custom Elements 和 HTML Templates 组成,可以在任何框架中使用。
优点
- 原生支持,无需依赖框架。
- 跨框架兼容性好。
- 可封装复杂逻辑并隐藏实现细节。
缺点
- 与框架的状态管理和事件机制集成较复杂。
- 学习曲线较高,部分 API 浏览器兼容性有限。
代码示例
class MyButton extends HTMLElement {
// 监听 label 属性变化
static get observedAttributes() {
return ['label'];
}
constructor() {
super();
this.attachShadow({ mode: 'open' });
// 创建样式
const style = document.createElement('style');
style.textContent = `
button {
background-color: var(--button-bg-color, #007bff);
color: var(--button-text-color, white);
border: none;
border-radius: var(--button-border-radius, 4px);
padding: var(--button-padding, 10px 20px);
font-size: var(--button-font-size, 16px);
cursor: pointer;
}
button:hover {
background-color: var(--button-hover-bg-color, #0056b3);
}
`;
// 创建按钮元素
this.button = document.createElement('button');
// 添加样式和按钮到 Shadow DOM
this.shadowRoot.appendChild(style);
this.shadowRoot.appendChild(this.button);
}
attributeChangedCallback(name, oldValue, newValue) {
if (name === 'label') {
this.button.textContent = newValue;
}
}
set label(value) {
this.setAttribute('label', value);
}
get label() {
return this.getAttribute('label');
}
}
customElements.define('my-button', MyButton);
在 React 中使用:
import { useEffect, useRef } from 'react';
import '../components/UserCard/index.js'
const Home = () => {
const buttonRef = useRef();
useEffect(() => {
const handleClick = () => {
alert('React handled click!');
};
const button = buttonRef.current;
if (button) {
button.addEventListener('click', handleClick);
return () => {
button.removeEventListener('click', handleClick);
};
}
}, []);
return <my-button ref={buttonRef} label="React Button"></my-button>;
};
export default Home;
2. Mitosis
官方Slogan:Write components once, run everywhere.
Mitosis 是一个开源工具,它能将 JSX 组件转换为适用于诸如 Angular、React、Qwik、Vue、Svelte、Solid 以及 React Native 等框架的全功能组件。开发者编写一次代码,可以生成多种框架的实现。
Mitosis 有点类似于 Taro 在小程序开发中的存在,一次React或Vue开发和编译后,可以在多个不同的小程序中运行。
优点
- 一次开发,多框架生成。
- 集成现有技术栈,降低维护成本。
缺点
- 生成的代码可能较复杂,不适合高度定制化场景。
- 需要依赖 Mitosis 的生态, 有自身的语法,有一定的学习成本。
代码示例
编写一个 Mitosis 组件:
import { useState } from "@builder.io/mitosis";
export default function MyComponent(props) {
const [name, setName] = useState("Steve");
return (
<div>
<input
css={{
color: "red",
}}
value={name}
onChange={(event) => setName(event.target.value)}
/>
Hello! I can run natively in React, Vue, Svelte, Qwik, and many more frameworks!
</div>
);
}
可以编译成Vue组件代码:
<template>
<div>
<input
class="input"
:value="name"
@change="async (event) => (name = event.target.value)"
/>
Hello! I can run natively in React, Vue, Svelte, Qwik, and many more
frameworks!
</div>
</template>
<script setup>
import { ref } from "vue";
const name = ref("Steve");
</script>
<style scoped>
.input {
color: red;
}
</style>
编译成 Angular 组件代码:
import { NgModule } from "@angular/core";
import { CommonModule } from "@angular/common";
import { Component } from "@angular/core";
@Component({
selector: "my-component",
template: `
<div>
<input
class="input"
[attr.value]="name"
(change)="name = $event.target.value"
/>
Hello! I can run natively in React, Vue, Svelte, Qwik, and many more
frameworks!
</div>
`,
styles: [
`
:host {
display: contents;
}
.input {
color: red;
}
`,
],
})
export default class MyComponent {
name = "Steve";
}
@NgModule({
declarations: [MyComponent],
imports: [CommonModule],
exports: [MyComponent],
})
export class MyComponentModule {}
3. Ark UI
Ark UI 是一个无框架依赖的组件库,提供框架无关的核心逻辑和 API,开发者可以将它使用到 React、Vue 或 Solid 开发中。
优点
- 提供基础逻辑,适配各框架。
- 社区支持活跃,文档完善。
缺点
- 需要手动适配 UI 层。
- 对新手开发者不够友好。
比较类似的一个UI组件库是 Park UI。
官方介绍:Beautifully designed components built with Ark UI and Panda CSS that work with a variety of JS frameworks.
4. Zag.js
Slogan:UI components powered by Finite State Machines.
Zag.js 是一个声明式的 UI 组件逻辑框架,专注于逻辑与 UI 分离。它提供了框架无关的状态管理和交互逻辑,开发者可以通过适配器将逻辑集成到 React、Vue、Solid 和 Svelte中。
优点
- 将复杂组件的逻辑抽象为状态机(State Machines),提供清晰的状态管理和事件处理流程,更易于调试和维护复杂的组件交互。
缺点
- 有特定的语法,有一定学习成本
代码示例
实现一个跨框架的数字输入组件:
React代码示例:
import * as numberInput from "@zag-js/number-input"
import { useMachine, normalizeProps } from "@zag-js/react"
export function NumberInput() {
const [state, send] = useMachine(numberInput.machine({ id: "1" }))
const api = numberInput.connect(state, send, normalizeProps)
return (
<div {...api.getRootProps()}>
<label {...api.getLabelProps()}>Enter number:</label>
<div>
<button {...api.getDecrementTriggerProps()}>DEC</button>
<input {...api.getInputProps()} />
<button {...api.getIncrementTriggerProps()}>INC</button>
</div>
</div>
)
}
Solid代码示例:
import * as numberInput from "@zag-js/number-input"
import { normalizeProps, useMachine } from "@zag-js/solid"
import { createMemo, createUniqueId } from "solid-js"
export function NumberInput() {
const [state, send] = useMachine(numberInput.machine({ id: createUniqueId() }))
const api = createMemo(() => numberInput.connect(state, send, normalizeProps))
return (
<div {...api().getRootProps()}>
<label {...api().getLabelProps()}>Enter number:</label>
<div>
<button {...api().getDecrementTriggerProps()}>DEC</button>
<input {...api().getInputProps()} />
<button {...api().getIncrementTriggerProps()}>INC</button>
</div>
</div>
)
}
Vue 代码示例:
<script setup>
import * as numberInput from "@zag-js/number-input";
import { normalizeProps, useMachine } from "@zag-js/vue";
import { computed } from "vue";
const [state, send] = useMachine(numberInput.machine({ id: "1" }));
const api = computed(() =>
numberInput.connect(state.value, send, normalizeProps)
);
</script>
<template>
<div ref="ref" v-bind="api.getRootProps()">
<label v-bind="api.getLabelProps()">Enter number</label>
<div>
<button v-bind="api.getDecrementTriggerProps()">DEC</button>
<input v-bind="api.getInputProps()" />
<button v-bind="api.getIncrementTriggerProps()">INC</button>
</div>
</div>
</template>
三、总结
在实际开发中,选择跨框架组件的实现方式需要根据项目业务特点、团队技术背景和未来的可维护性等方面来权衡。
希望这篇文章能帮助你更好地理解如何开发和使用跨框架组件,并为你的项目选择最优方案!
欢迎关注工种号【码界交流圈】好文第一时间分享不迷路!~
标签:const,框架,解决方案,前端,React,组件,import,button From: https://blog.csdn.net/2401_87546826/article/details/144370385