speedy-jsx
v1.1.26
Published
响应式JavaScript框架
Maintainers
Readme
Speedy-JSX
响应式JavaScript框架,提供虚拟DOM、组件系统、状态管理和路由功能。
目录
特性
- 响应式数据系统
- 虚拟DOM渲染
- 组件化开发
- 内置路由
- 状态管理
- CSS-in-JS支持
- 异步组件
安装
npm install speedy-jsx
# 或
yarn add speedy-jsx快速开始
基本示例
import { render, ref } from 'speedy-jsx';
function Counter() {
const count = ref(0);
const increment = () => { count.value++ };
return () => (
<div>
<p>计数: {count.value}</p>
<button onClick={increment}>增加</button>
</div>
);
}
// 可以直接传递组件函数
render(Counter, document.getElementById('app'));组件开发
组件Props使用规范
在Speedy-JSX框架中,Props是响应式对象,需要遵循以下规则:
不要解构Props
// ❌ 错误:直接解构会丢失响应性
function MyComponent({ title, count }) {
return () => <div>{title}: {count}</div>;
}
// ✅ 正确:直接使用props对象引用属性
function MyComponent(props) {
return () => <div>{props.title}: {props.count}</div>;
}正确处理Children
在Speedy-JSX中,子元素作为函数参数传递给渲染函数:
// ✅ 正确:通过渲染函数的参数获取children
function Container(props) {
return (children) => (
<div className={props.className}>{children}</div>
);
}Props的默认值
function Button(props) {
return (children) => {
const type = props.type || "button";
const disabled = props.disabled || false;
return (
<button type={type} disabled={disabled}>
{children}
</button>
);
};
}响应式系统
创建响应式状态
import { ref, reactive, computed } from 'speedy-jsx';
// 创建响应式引用
const count = ref(0);
count.value++; // 通过.value访问和修改
// 创建响应式对象
const state = reactive({
count: 0,
text: 'hello'
});
state.count++; // 直接修改属性
// 创建计算属性
const doubleCount = computed(() => count.value * 2);
console.log(doubleCount.value); // 自动更新监听变化
import { watch, watchEffect } from 'speedy-jsx';
// 监听特定值
watch(count, (newValue, oldValue) => {
console.log(`count从${oldValue}变为${newValue}`);
});
// 自动收集依赖并监听
watchEffect(() => {
console.log(`当前count: ${count.value}, 两倍值: ${doubleCount.value}`);
});状态管理
创建Store
import { defineStore, ref } from 'speedy-jsx';
// 创建一个store
const counterStore = defineStore(() => {
const count = ref(0);
function increment() {
count.value++;
}
return {
count,
increment
};
});
// 使用store
function Counter() {
return () => (
<div>
<p>Count: {counterStore.count.value}</p>
<button onClick={counterStore.increment}>+1</button>
</div>
);
}持久化Store
import { definePersistStore, ref } from 'speedy-jsx';
const todoStore = definePersistStore(() => {
const todos = ref([]);
function addTodo(text) {
todos.value.push({ id: Date.now(), text, completed: false });
}
return { todos, addTodo };
}, {
key: 'app-todos', // 存储键名
storage: localStorage // 存储介质
});路由系统
Speedy-JSX提供了路由系统,支持哈希模式和历史模式、嵌套路由、动态参数、命名路由等功能。
基本用法
import { Router, RouterView, RouterLink } from 'speedy-jsx';
const routes = [
{ path: '/', component: Home },
{ path: '/about', component: About },
{ path: '/users/:id', component: UserProfile }
];
function App() {
return () => (
<Router routes={routes} mode="hash">
<nav>
<RouterLink to="/">首页</RouterLink>
<RouterLink to="/about">关于</RouterLink>
</nav>
<RouterView />
</Router>
);
}
// 直接传递组件函数
render(App, document.getElementById('app'));路由模式
Speedy路由系统支持两种导航模式:
// 哈希模式 (默认) - 使用URL的哈希部分(#)
<Router routes={routes} mode="hash">
// 历史模式 - 使用HTML5 History API
<Router routes={routes} mode="history">嵌套路由
Speedy支持无限层级的嵌套路由:
const routes = [
{
path: '/user',
component: UserLayout,
children: [
{ path: '/profile', component: UserProfile },
{ path: '/settings', component: UserSettings },
{
path: '/posts',
component: PostsLayout,
children: [
{ path: '/list', component: PostList },
{ path: '/:id', component: PostDetail }
]
}
]
}
];
// 在父组件中使用RouterView
function UserLayout() {
return () => (
<div>
<h2>用户中心</h2>
<RouterView /> {/* 子路由组件将在这里渲染 */}
</div>
);
}动态路由参数
使用冒号语法定义动态路由参数:
// 路由定义
const routes = [
{ path: '/users/:id', component: UserDetail },
{ path: '/articles/:category/:slug', component: ArticleDetail }
];
// 在组件中访问参数
import { useParams } from 'speedy-jsx';
function UserDetail() {
const params = useParams();
return () => (
<div>用户ID: {params.value.id}</div>
);
}查询参数
访问URL查询参数:
import { useQuery } from 'speedy-jsx';
function SearchPage() {
const query = useQuery();
return () => (
<div>
<h2>搜索结果</h2>
<p>关键词: {query.value.keyword}</p>
<p>页码: {query.value.page || 1}</p>
</div>
);
}
// 使用RouterLink传递查询参数
<RouterLink to={{ path: '/search', query: { keyword: 'speedy', page: 2 } }}>
搜索
</RouterLink>命名路由
可以为路由指定名称,并通过名称导航:
// 定义命名路由
const routes = [
{ path: '/', name: 'home', component: Home },
{ path: '/about', name: 'about', component: About },
{ path: '/users/:id', name: 'user-detail', component: UserDetail }
];
// 使用命名路由导航
<RouterLink to={{ name: 'user-detail', params: { id: 123 } }}>
查看用户
</RouterLink>
// 在代码中导航
import { useRouter } from 'speedy-jsx';
function NavigateButton() {
const router = useRouter();
const handleClick = () => {
router.value.push({ name: 'user-detail', params: { id: 123 } });
};
return () => (
<button onClick={handleClick}>查看用户</button>
);
}路由导航守卫
Speedy提供了全面的导航守卫系统,用于控制导航流程:
import { onBeforeEach, onBeforeResolve, onAfterEach } from 'speedy-jsx';
// 全局前置守卫 - 在导航开始前触发
onBeforeEach((to, from, next) => {
// 检查用户是否已登录
if (to.path.startsWith('/admin') && !isAuthenticated()) {
// 重定向到登录页
next('/login');
} else {
// 继续导航
next();
}
});
// 全局解析守卫 - 在导航解析完成后、组件渲染前触发
onBeforeResolve((to, from, next) => {
// 可以在这里加载数据
loadPageData(to.path).then(() => {
next();
});
});
// 全局后置钩子 - 在导航完成后触发
onAfterEach((to, from) => {
// 更新页面标题
document.title = to.meta.title || 'Speedy应用';
// 记录页面访问
trackPageView(to.path);
});路由元数据
可以为路由添加元数据,用于存储与路由相关的额外信息:
const routes = [
{
path: '/admin',
component: AdminLayout,
meta: {
requiresAuth: true,
title: '管理后台'
}
}
];
// 在导航守卫中使用元数据
onBeforeEach((to, from, next) => {
if (to.meta.requiresAuth && !isAuthenticated()) {
next('/login');
} else {
next();
}
});编程式导航
除了使用RouterLink外,还可以在代码中进行导航:
import { useRouter } from 'speedy-jsx';
function NavigationExample() {
const router = useRouter();
function navigate() {
// 导航到指定路径
router.value.push('/about');
// 导航到命名路由
router.value.push({ name: 'user-detail', params: { id: 123 } });
// 带查询参数的导航
router.value.push({ path: '/search', query: { q: 'speedy' } });
// 替换当前路由(不产生新的历史记录)
router.value.replace('/new-path');
// 前进、后退和跳转历史
router.value.back();
router.value.forward();
router.value.go(-2); // 后退两步
}
return () => (
<button onClick={navigate}>导航示例</button>
);
}活跃链接样式
RouterLink组件会根据当前路由自动添加活跃类:
<RouterLink
to="/about"
activeClass="active-link"
exactActiveClass="exact-active-link"
>
关于我们
</RouterLink>
<style>
.active-link {
color: blue; /* 当前路由或其子路由匹配时应用 */
}
.exact-active-link {
font-weight: bold; /* 仅当完全匹配当前路由时应用 */
}
</style>精确匹配模式
RouterLink 组件提供了 exact 属性,用于控制链接何时被视为活跃状态:
// 默认匹配模式(不使用exact):
// 当前路径是 /about 或以 /about/ 开头的路径(如 /about/team)时都会匹配
<RouterLink to="/about" activeClass="active">关于</RouterLink>
// 精确匹配模式:
// 只有当前路径完全等于 /about 时才会匹配
<RouterLink to="/about" exact={true} activeClass="active">关于</RouterLink>精确匹配模式对于构建导航菜单特别有用,可以防止父路由链接在访问子路由时保持活跃状态:
// 导航菜单示例
function NavMenu() {
return () => (
<nav>
<RouterLink to="/" exact={true} activeClass="active">首页</RouterLink>
<RouterLink to="/products" exact={true} activeClass="active">产品</RouterLink>
<RouterLink to="/products/new" activeClass="active">新产品</RouterLink>
<RouterLink to="/about" activeClass="active">关于</RouterLink>
</nav>
);
}上面的例子中,当访问 /products/new 路径时:
- 设置了
exact={true}的/products链接不会激活 - 而
/products/new链接会激活
这样可以更精确地控制导航项的视觉反馈,提升用户体验。
路由重定向
可以设置路由重定向,将一个路径自动导向另一个路径:
const routes = [
{ path: '/', component: Home },
{ path: '/home', redirect: '/' },
// 动态重定向
{
path: '/old-path/:id',
redirect: to => {
// 可以返回字符串路径
return `/new-path/${to.params.id}`;
// 或返回路由对象
return {
path: `/new-path/${to.params.id}`,
query: { ...to.query, source: 'redirect' }
};
}
}
];404页面处理
可以添加通配符路由作为404页面:
const routes = [
{ path: '/', component: Home },
{ path: '/about', component: About },
{ path: '/404', component: NotFound },
];嵌套路由404处理
Speedy 路由系统支持为每个嵌套级别配置独立的 404 页面,提供更精确的路由控制:
const routes = [
{
path: '/user',
component: UserLayout,
children: [
{ path: '/profile', component: UserProfile },
// 用户模块专用的404页面,只会捕获/user/下的未匹配路径
{ path: '/404', component: UserNotFound }
]
},
{
path: '/blog',
component: BlogLayout,
children: [
{ path: '/posts', component: BlogPosts },
// 博客模块专用的404页面
{ path: '/404', component: BlogNotFound }
]
},
// 全局404页面,捕获所有其他未匹配的路径
{ path: '/404', component: GlobalNotFound }
];当路由系统无法匹配一个路径时,它会:
- 首先在当前路径的父级路由中查找
/404子路由 - 如果找到,则渲染该嵌套404组件
- 如果未找到,则继续向上级路由查找
- 最终回退到全局
/404路由
这种设计允许为不同的应用模块提供专门的404页面,显示更相关的错误信息和导航选项。
样式系统
Speedy提供了强大而灵活的CSS-in-JS样式系统,包含四个核心API:scope、keyframes、media和global,以及标签式API用于创建样式化组件。
作用域样式 (scope)
使用styled.scope创建局部作用域样式,自动生成唯一类名避免样式冲突:
import { styled } from 'speedy-jsx';
// 创建局部作用域样式
const styles = styled.scope({
container: {
display: 'flex',
flexDirection: 'column',
padding: '20px',
backgroundColor: '#f5f5f5',
borderRadius: '8px',
boxShadow: '0 2px 4px rgba(0,0,0,0.1)'
},
title: {
fontSize: '24px',
color: '#333',
marginBottom: '16px'
},
content: {
fontSize: '16px',
lineHeight: '1.5',
color: '#666'
}
});
// 使用样式
function Card() {
return () => (
<div classStyle={[styles.container]}>
<h2 classStyle={[styles.title]}>卡片标题</h2>
<p classStyle={[styles.content]}>卡片内容</p>
</div>
);
}样式通过classStyle属性应用于元素,支持多个样式的数组形式。每个样式都会生成唯一的类名,确保样式隔离和组件复用。
样式化组件 (Styled Components)
使用标签式API创建样式化组件,类似styled-components:
import { styled } from 'speedy-jsx';
// 创建样式化按钮组件
const Button = styled.button`
background-color: ${props => props.$primary ? '#0070f3' : '#f0f0f0'};
color: ${props => props.$primary ? 'white' : '#333'};
padding: ${props => props.$size === 'large' ? '12px 24px' : '8px 16px'};
border-radius: 4px;
border: none;
font-size: ${props => props.$size === 'large' ? '16px' : '14px'};
font-weight: 500;
cursor: pointer;
transition: all 0.2s ease;
&:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
opacity: ${props => props.$hoverable ? 0.9 : 1};
}
&:active {
transform: translateY(0);
}
`;
// 使用样式化组件
function App() {
return () => (
<div>
<Button $primary={true} $size="large" $hoverable={true}>
主要按钮
</Button>
<Button $primary={false} $size="small" $hoverable={true}>
次要按钮
</Button>
</div>
);
}特点:
- 自动生成唯一类名
- 支持动态属性(以
$开头的props属性) - 支持嵌套选择器(使用
&引用自身) - 响应式监听props变化自动更新样式
关键帧动画
使用styled.keyframes创建CSS动画:
import { styled } from 'speedy-jsx';
// 定义一个淡入动画
const fadeIn = styled.keyframes({
'0%': {
opacity: 0,
transform: 'translateY(20px)'
},
'50%': {
opacity: 0.5,
transform: 'translateY(10px)'
},
'100%': {
opacity: 1,
transform: 'translateY(0)'
}
});
// 定义一个脉冲动画
const pulse = styled.keyframes({
'0%': { transform: 'scale(1)' },
'50%': { transform: 'scale(1.05)' },
'100%': { transform: 'scale(1)' }
});
// 在样式中使用动画
const styles = styled.scope({
animatedBox: {
animation: `${fadeIn} 0.5s ease-out forwards`,
padding: '20px',
backgroundColor: '#f0f0f0'
},
pulsingButton: {
animation: `${pulse} 1s infinite ease-in-out`,
padding: '10px 20px',
backgroundColor: '#0070f3',
color: 'white',
border: 'none',
borderRadius: '4px'
}
});
function AnimationExample() {
return () => (
<div>
<div classStyle={[styles.animatedBox]}>
这个元素会淡入
</div>
<button classStyle={[styles.pulsingButton]}>
脉冲按钮
</button>
</div>
);
}媒体查询
使用styled.media创建响应式布局:
import { styled } from 'speedy-jsx';
// 创建基础样式
const styles = styled.scope({
grid: {
display: 'grid',
gridTemplateColumns: 'repeat(4, 1fr)',
gap: '20px',
padding: '20px'
},
card: {
padding: '20px',
borderRadius: '8px',
backgroundColor: 'white',
boxShadow: '0 2px 4px rgba(0,0,0,0.1)',
transition: 'transform 0.3s ease'
}
});
// 添加响应式媒体查询
styled.media({
// 平板设备
'(max-width: 992px)': {
[styles.grid.className]: {
gridTemplateColumns: 'repeat(3, 1fr)'
}
},
// 小平板设备
'(max-width: 768px)': {
[styles.grid.className]: {
gridTemplateColumns: 'repeat(2, 1fr)'
},
[styles.card.className]: {
padding: '15px'
}
},
// 移动设备
'(max-width: 480px)': {
[styles.grid.className]: {
gridTemplateColumns: '1fr',
gap: '15px',
padding: '10px'
},
[styles.card.className]: {
padding: '10px'
}
}
});
function ResponsiveGrid() {
return () => (
<div classStyle={[styles.grid]}>
<div classStyle={[styles.card]}>卡片1</div>
<div classStyle={[styles.card]}>卡片2</div>
<div classStyle={[styles.card]}>卡片3</div>
<div classStyle={[styles.card]}>卡片4</div>
</div>
);
}全局样式
使用styled.global添加全局样式规则:
import { styled } from 'speedy-jsx';
// 添加全局样式 - 通常在应用入口处调用一次
styled.global({
'html, body': {
margin: 0,
padding: 0,
fontFamily: 'system-ui, -apple-system, sans-serif',
boxSizing: 'border-box',
backgroundColor: '#f9f9f9'
},
'*, *::before, *::after': {
boxSizing: 'inherit'
},
'a': {
color: '#0070f3',
textDecoration: 'none',
transition: 'color 0.2s ease'
},
'a:hover': {
color: '#0051bb',
textDecoration: 'underline'
},
'.container': {
maxWidth: '1200px',
margin: '0 auto',
padding: '0 20px'
},
// 支持嵌套选择器
'form': {
'input, select, textarea': {
padding: '8px 12px',
border: '1px solid #ddd',
borderRadius: '4px',
fontSize: '16px'
},
'button': {
padding: '8px 16px',
backgroundColor: '#0070f3',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: 'pointer'
}
}
});组合使用样式系统
全面展示样式系统功能的综合示例:
import { styled } from 'speedy-jsx';
import { ref } from 'speedy-jsx';
// 1. 创建关键帧动画
const fadeIn = styled.keyframes({
'0%': { opacity: 0, transform: 'translateY(10px)' },
'100%': { opacity: 1, transform: 'translateY(0)' }
});
// 2. 创建全局样式
styled.global({
'body': {
margin: 0,
fontFamily: 'sans-serif',
backgroundColor: '#f5f5f5'
}
});
// 3. 创建局部样式
const styles = styled.scope({
app: {
maxWidth: '800px',
margin: '40px auto',
padding: '20px',
backgroundColor: 'white',
borderRadius: '8px',
boxShadow: '0 2px 8px rgba(0,0,0,0.1)',
animation: `${fadeIn} 0.5s ease-out`
},
header: {
marginBottom: '24px',
borderBottom: '1px solid #eee',
paddingBottom: '16px'
},
title: {
fontSize: '28px',
fontWeight: '600',
color: '#333',
margin: '0 0 8px 0'
},
subtitle: {
fontSize: '16px',
color: '#666',
margin: 0
},
content: {
display: 'flex',
flexDirection: 'column',
gap: '20px'
}
});
// 4. 创建样式化组件
const Card = styled.div`
padding: 20px;
border-radius: 8px;
background-color: ${props => props.$dark ? '#333' : '#f9f9f9'};
color: ${props => props.$dark ? '#fff' : '#333'};
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
transition: all 0.3s ease;
&:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}
h3 {
margin-top: 0;
font-size: 18px;
}
p {
margin-bottom: 0;
line-height: 1.5;
}
`;
// 5. 添加响应式媒体查询
styled.media({
'(max-width: 768px)': {
[styles.app.className]: {
margin: '20px 16px',
padding: '16px'
},
[styles.title.className]: {
fontSize: '24px'
}
}
});
// 主应用组件
function StyleSystemDemo() {
const darkMode = ref(false);
const toggleMode = () => { darkMode.value = !darkMode.value };
return () => (
<div classStyle={[styles.app]}>
<header classStyle={[styles.header]}>
<h1 classStyle={[styles.title]}>Speedy 样式系统演示</h1>
<p classStyle={[styles.subtitle]}>展示样式系统的全部功能</p>
</header>
<div classStyle={[styles.content]}>
<button onClick={toggleMode}>
切换{darkMode.value ? '浅色' : '深色'}模式
</button>
<Card $dark={darkMode.value}>
<h3>样式化组件</h3>
<p>这是使用styled.div创建的卡片组件,支持动态属性和嵌套选择器。</p>
</Card>
</div>
</div>
);
}高级特性
样式系统还提供以下高级特性:
- 嵌套选择器:支持在样式对象中嵌套子选择器
- 响应式props:样式化组件中可以使用响应式属性动态更新样式
- 自动类名生成:确保样式隔离和避免冲突
异步组件
import { Async, Pending, Then, Catch } from 'speedy-jsx';
function DataLoader() {
const fetchData = () => fetch('https://api.example.com/data');
return () => (
<Async promise={fetchData}>
<Pending>
<p>数据加载中...</p>
</Pending>
<Then>
{(data) => <p>加载成功: {JSON.stringify(data)}</p>}
</Then>
<Catch>
{(error) => <p>加载失败: {error.message}</p>}
</Catch>
</Async>
);
}自定义指令
Speedy提供了自定义指令系统,用于复用与DOM元素交互的逻辑。指令是一种特殊的函数,可以对渲染的DOM元素执行底层操作。
注册指令
import { directive } from 'speedy-jsx';
// 注册一个简单的show指令
directive("show", (el, update) => {
// 设置update回调函数处理值变更
update((oldValue, newValue) => {
el.style.display = newValue ? 'block' : 'none';
});
// 可以注册多个update回调处理不同的逻辑
update((oldValue, newValue) => {
// 例如,添加过渡效果
if (newValue) {
el.style.opacity = '1';
el.style.transition = 'opacity 0.3s';
} else {
el.style.opacity = '0';
}
});
// 返回清理函数(可选)
return () => {
// 指令被移除时执行的清理操作
console.log("清理show指令");
};
});使用指令
在JSX或模板中,指令通过use-前缀添加到元素上:
function ConditionalDisplay() {
const visible = ref(true);
const toggle = () => {
visible.value = !visible.value;
};
return () => (
<div>
<button onClick={toggle}>切换显示</button>
<div use-show={visible.value}>
这个元素会根据visible的值显示或隐藏
</div>
</div>
);
}指令生命周期
指令有简单的生命周期:
- 初始化:指令首次应用到元素时执行
- 更新:当指令的值发生变化时,通过update回调处理
- 清理:当元素被卸载或指令被移除时执行
指令的最佳实践
- 单一职责:每个指令应该只关注一个特定功能
- 性能考虑:避免在指令中执行重复的昂贵操作
- 清理资源:始终返回清理函数来释放资源,如事件监听器
- 响应式值:指令值通常应该是响应式的,以便在值变化时触发更新
依赖注入
import { provide, inject } from 'speedy-jsx';
// 在父组件中提供数据
function Parent() {
provide('theme', {
primary: '#3498db',
secondary: '#2ecc71'
});
return () => (
<div>
<Child />
</div>
);
}
// 在子组件中注入数据
function Child() {
const theme = inject('theme', { primary: '#000', secondary: '#fff' });
return () => (
<div style={{ color: theme.primary }}>
子组件内容
</div>
);
}批量更新和效果控制
import { batch, nextTick } from 'speedy-jsx';
// 批量更新
batch(() => {
count1.value++;
count2.value++;
count3.value++;
}); // 只会触发一次更新
// 下一个DOM更新后执行
nextTick(() => {
console.log('DOM已更新');
});生命周期钩子
import { onMounted, onUpdated, onUnmounted } from 'speedy-jsx';
function MyComponent() {
onMounted(() => {
console.log('组件已挂载');
});
onUpdated(() => {
console.log('组件已更新');
});
onUnmounted(() => {
console.log('组件已卸载');
});
return () => <div>组件内容</div>;
}资源管理与清理
Speedy提供了强大的资源管理机制,通过$runScope()函数可以轻松管理组件中的各种资源,确保在组件卸载时自动清理,避免内存泄漏。
基本用法
import { $runScope } from 'speedy-jsx';
function TimerComponent() {
const count = ref(0);
// 获取运行作用域
const run = $runScope();
onMounted(() => {
// 设置定时器,会在组件卸载时自动清理
run.setInterval(() => {
count.value++;
}, 1000);
});
return () => <div>计数: {count.value}</div>;
}可用的资源管理API
$runScope()提供了多种资源管理API:
定时器管理
function TimerExample() {
const message = ref('');
const run = $runScope();
onMounted(() => {
// 设置定时器,自动清理
run.setTimeout(() => {
message.value = '已延迟3秒显示';
}, 3000);
// 设置循环定时器,自动清理
run.setInterval(() => {
console.log('每秒执行一次');
}, 1000);
});
return () => <div>{message.value}</div>;
}事件监听器
function EventListenerExample() {
const mousePosition = ref({ x: 0, y: 0 });
const run = $runScope();
onMounted(() => {
// 添加事件监听器,自动移除
run.onWindow('mousemove', (e) => {
mousePosition.value = { x: e.clientX, y: e.clientY };
});
// 也可以使用更通用的on方法
const button = document.getElementById('my-button');
if (button) {
run.on(button, 'click', () => {
console.log('按钮被点击');
});
}
});
return () => (
<div>鼠标位置: {mousePosition.value.x}, {mousePosition.value.y}</div>
);
}动画帧管理
function AnimationExample() {
const position = ref(0);
const run = $runScope();
onMounted(() => {
function animate() {
position.value += 5;
if (position.value < 500) {
run.requestAnimationFrame(animate);
}
}
// 开始动画,自动取消
run.requestAnimationFrame(animate);
});
return () => (
<div style={{ transform: `translateX(${position.value}px)` }}>
移动的元素
</div>
);
}网络请求管理
function FetchExample() {
const data = ref(null);
const loading = ref(false);
const error = ref(null);
const run = $runScope();
function loadData() {
loading.value = true;
error.value = null;
// 创建可取消的fetch请求
run.fetch('https://api.example.com/data')
.then(response => response.json())
.then(result => {
data.value = result;
loading.value = false;
})
.catch(err => {
error.value = err.message;
loading.value = false;
});
}
onMounted(loadData);
return () => (
<div>
{loading.value && <p>加载中...</p>}
{error.value && <p>错误: {error.value}</p>}
{data.value && <p>数据: {JSON.stringify(data.value)}</p>}
<button onClick={loadData}>重新加载</button>
</div>
);
}WebSocket管理
function ChatExample() {
const messages = ref([]);
const connection = ref(null);
const run = $runScope();
onMounted(() => {
// 创建WebSocket连接,自动关闭
const socket = run.createWebSocket('wss://chat.example.com');
socket.onmessage = (event) => {
messages.value.push(JSON.parse(event.data));
};
socket.onopen = () => {
console.log('连接已建立');
};
connection.value = socket;
});
function sendMessage(text) {
if (connection.value && connection.value.readyState === WebSocket.OPEN) {
connection.value.send(JSON.stringify({ text }));
}
}
return () => (
<div>
<div className="messages">
{messages.value.map((msg, i) => (
<div key={i}>{msg.text}</div>
))}
</div>
<button onClick={() => sendMessage('Hello')}>发送消息</button>
</div>
);
}观察器管理
function ObserverExample() {
const isVisible = ref(false);
const elementSize = ref({ width: 0, height: 0 });
const run = $runScope();
onMounted(() => {
const targetElement = document.getElementById('observed-element');
if (targetElement) {
// 创建交叉观察器,自动断开连接
const intersectionObserver = run.createIntersectionObserver(
(entries) => {
isVisible.value = entries[0].isIntersecting;
},
{ threshold: 0.1 }
);
intersectionObserver.observe(targetElement);
// 创建尺寸观察器,自动断开连接
const resizeObserver = run.createResizeObserver((entries) => {
const rect = entries[0].contentRect;
elementSize.value = { width: rect.width, height: rect.height };
});
resizeObserver.observe(targetElement);
}
});
return () => (
<div>
<div id="observed-element" style={{ height: '200px', background: '#f0f0f0' }}>
被观察的元素
</div>
<p>元素{isVisible.value ? '可见' : '不可见'}</p>
<p>尺寸: {elementSize.value.width} x {elementSize.value.height}</p>
</div>
);
}自定义清理函数
function CustomCleanupExample() {
const thirdPartyLib = ref(null);
const run = $runScope();
onMounted(() => {
// 初始化第三方库
const instance = new SomeThirdPartyLib();
instance.initialize();
thirdPartyLib.value = instance;
// 注册自定义清理函数
run.onCleanup(() => {
if (thirdPartyLib.value) {
thirdPartyLib.value.destroy();
thirdPartyLib.value = null;
}
});
});
return () => <div>第三方库示例</div>;
}可取消的Promise
function CancelablePromiseExample() {
const data = ref(null);
const loading = ref(false);
const run = $runScope();
function loadData() {
loading.value = true;
// 创建可取消的Promise
const { promise } = run.createCancelablePormise((resolve, reject) => {
// 模拟长时间运行的异步操作
setTimeout(() => {
resolve({ result: 'Success' });
}, 3000);
});
promise
.then(result => {
data.value = result;
loading.value = false;
})
.catch(err => {
if (err.canceled) {
console.log('操作已取消');
} else {
console.error('发生错误:', err);
}
loading.value = false;
});
}
onMounted(loadData);
return () => (
<div>
{loading.value ? <p>加载中...</p> : <p>数据: {JSON.stringify(data.value)}</p>}
<button onClick={loadData}>重新加载</button>
</div>
);
}避免内存泄漏的最佳实践
使用$runScope()可以有效避免以下常见的内存泄漏源:
- 未清理的定时器: 使用
run.setTimeout和run.setInterval代替原生方法 - 未移除的事件监听器: 使用
run.on、run.onWindow和run.onDocument添加事件监听 - 未关闭的网络连接: 使用
run.fetch和run.createWebSocket管理网络请求 - 未断开连接的观察器: 使用
run.create*Observer系列方法创建各种观察器 - 第三方库资源: 使用
run.onCleanup注册自定义清理函数
通过这些API,您可以编写更加健壮的组件,无需担心资源泄漏问题。
API文档
完整的API文档请参考类型定义文件。
