empjs
EMP 全栈技能:React 19 脚手架脚本、emp.config.ts 配置、Tailwind v4、微前端 empRuntime、@empjs/valtio 状态管理、React 性能优化。适用于创建 EMP 应用、配置模块联邦、状态管理、性能优化等场景。
Packaged view
This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.
Install command
npx @skill-hub/cli install empjs-emp-empjs
Repository
Skill path: skills/empjs
EMP 全栈技能:React 19 脚手架脚本、emp.config.ts 配置、Tailwind v4、微前端 empRuntime、@empjs/valtio 状态管理、React 性能优化。适用于创建 EMP 应用、配置模块联邦、状态管理、性能优化等场景。
Open repositoryBest for
Primary workflow: Ship Full Stack.
Technical facets: Full Stack, Frontend.
Target audience: everyone.
License: Unknown.
Original source
Catalog source: SkillHub Club.
Repository owner: empjs.
This is still a mirrored public skill entry. Review the repository before installing into production workflows.
What it helps with
- Install empjs into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
- Review https://github.com/empjs/emp before adding empjs to shared team environments
- Use empjs for development workflows
Works across
Favorites: 0.
Sub-skills: 0.
Aggregator: No.
Original source / Raw SKILL.md
---
name: empjs
description: EMP 全栈技能:React 19 脚手架脚本、emp.config.ts 配置、Tailwind v4、微前端 empRuntime、@empjs/valtio 状态管理、React 性能优化。适用于创建 EMP 应用、配置模块联邦、状态管理、性能优化等场景。
---
# EMP 全栈技能
整合 EMP React 项目、微前端、Tailwind v4、状态管理、性能优化于一体。全部内容已本地化。
## 一、React 19 Startkit 脚手架
快速创建 EMP React 19 项目。在项目根目录执行:
```bash
node skills/empjs/scripts/create-emp-react19.js <项目名> [目标目录]
```
示例:
```bash
node skills/empjs/scripts/create-emp-react19.js my-app
node skills/empjs/scripts/create-emp-react19.js my-app ./projects
```
生成后执行 `cd <项目名> && pnpm install && pnpm dev`。
生成结构:`package.json`、`emp.config.ts`、`tsconfig.json`、`src/index.html`、`src/index.tsx`、`src/bootstrap.tsx`、`src/App.tsx`、`src/style.css`、`src/global.d.ts`。
## 二、快速开始
### 最少配置
```typescript
// emp.config.ts
import { defineConfig } from '@empjs/cli'
import pluginReact from '@empjs/plugin-react'
export default defineConfig(store => ({
plugins: [pluginReact()],
server: { port: 8000, open: false },
}))
```
### 必需文件结构
```
project/
├── emp.config.ts
├── package.json
├── src/
│ └── index.tsx
└── tsconfig.json # extends: "@empjs/cli/tsconfig/react"
```
### 启动命令
```bash
pnpm dev # 开发
pnpm build # 构建
pnpm start # 预览
```
## 三、package.json scripts 补全
```json
{
"scripts": {
"dev": "emp dev",
"dev:open": "emp dev --open",
"dev:test": "emp dev --env test",
"dev:prod": "emp dev --env prod",
"dev:doctor": "emp dev --doctor",
"dev:profile": "emp dev --profile",
"build": "emp build",
"build:test": "emp build --env test",
"build:prod": "emp build --env prod",
"build:watch": "emp build --watch --serve",
"build:ts": "emp build --ts",
"build:stat": "emp build --analyze",
"build:doctor": "emp build --doctor",
"build:profile": "emp build --profile",
"start": "emp serve",
"serve": "emp serve",
"stat": "emp build --analyze",
"dts": "emp dts",
"emp": "emp"
}
}
```
## 四、emp.config.ts 核心配置
| 配置 | 说明 | 默认 |
|------|------|------|
| `plugins` | 插件数组,React 需 `pluginReact()` | - |
| `server.port` | 端口 | 8000 |
| `appSrc` | 源码目录 | `src` |
| `appEntry` | 入口文件 | `index.{ts,tsx,jsx,js}` |
详见 [references/empconfig.md](references/empconfig.md)。
## 五、插件
### 必需
- `@empjs/plugin-react`:React 项目必需
### 可选
- `@empjs/plugin-tailwindcss`:Tailwind v4 默认,`@import "tailwindcss"`
- `@empjs/plugin-lightningcss`:LightningCSS,px_to_rem、px_to_viewport
- `@empjs/share`:模块联邦共享
## 六、Tailwind v4
```bash
pnpm add @empjs/plugin-tailwindcss tailwindcss
```
```typescript
plugins: [pluginReact(), pluginTailwindcss()]
```
```css
/* src/style.css */
@import "tailwindcss";
```
详见 [references/tailwind-v4.md](references/tailwind-v4.md)。
## 七、微前端 empRuntime
### 标准 React 模板
```typescript
import { externalReact, pluginRspackEmpShare } from '@empjs/share'
pluginRspackEmpShare({
empRuntime: {
framework: {
global: 'EMP_ADAPTER_REACT',
libs: [`https://unpkg.com/@empjs/[email protected]/dist/reactRouter.${store.mode}.umd.js`],
},
runtime: {
lib: `https://unpkg.com/@empjs/[email protected]/output/sdk.js`,
},
setExternals: externalReact,
},
})
```
### Host 暴露 / Remote 消费
```typescript
// Host
exposes: { './App': './src/App' }
// Remote
remotes: { mfHost: `mfHost@http://${store.server.ip}:6001/emp.json` }
```
### 运行时注册
```typescript
import { loadRemote, registerRemotes } from '@empjs/share/sdk'
registerRemotes([{ name: 'mfHost', entry: 'http://localhost:6001/emp.json' }])
const Host = lazy(() => loadRemote('mfHost/App'))
```
详见 [references/runtime-build.md](references/runtime-build.md)。
## 八、状态管理 @empjs/valtio
```bash
pnpm add @empjs/valtio
```
| 场景 | 使用 |
|------|------|
| 全局单例 | `createStore(initialState)` |
| 组件内局部 | `useStore(initialState)` |
**调用闭环**:读用 `snap`,写用 `store.set` / `store.update`。勿直接读 `store.xxx` 做渲染。
完整指南见 [references/valtio-skill.md](references/valtio-skill.md),用法见 [references/valtio-usage.md](references/valtio-usage.md),API 见 [references/valtio-api.md](references/valtio-api.md),示例见 [references/valtio-examples.md](references/valtio-examples.md)。
## 九、React 性能优化
**状态管理**:用 valtio 替代 useState/useReducer/useCallback。
**其他规则**(适配 React 18/19):
- 消除瀑布:`Promise.all()`、Suspense
- 包体积:直接导入、`React.lazy`、hydration 后加载第三方
- 渲染:`content-visibility: auto`、三元条件、Activity(React 19)
- JS:Set/Map 查找、localStorage 缓存、`toSorted()`、早 return
详见 [references/react-performance.md](references/react-performance.md)。
## 十、参考索引
| 文档 | 内容 |
|------|------|
| [empconfig.md](references/empconfig.md) | emp.config.ts 完整配置 |
| [tailwind-v4.md](references/tailwind-v4.md) | Tailwind v4 配置 |
| [runtime-build.md](references/runtime-build.md) | empRuntime、remotes、polyfill |
| [valtio-skill.md](references/valtio-skill.md) | @empjs/valtio 完整使用指南(本地化自 valtio-best-practices) |
| [valtio-usage.md](references/valtio-usage.md) | valtio 按用法说明 |
| [valtio-api.md](references/valtio-api.md) | valtio API 与类型 |
| [valtio-examples.md](references/valtio-examples.md) | valtio 示例索引 |
| [react-performance.md](references/react-performance.md) | React 性能规则 |
| [examples.md](references/examples.md) | 配置示例 |
| [startkit.md](references/startkit.md) | React 19 脚手架说明 |
### 脚本
| 脚本 | 用途 |
|------|------|
| [scripts/create-emp-react19.js](scripts/create-emp-react19.js) | 创建 EMP React 19 项目脚手架 |
---
## Referenced Files
> The following files are referenced in this skill and included for context.
### references/empconfig.md
```markdown
# emp.config.ts 配置参考
## 配置文件命名
支持以下文件名(按优先级):
- `emp-config.ts` / `emp-config.js`
- `emp.config.ts` / `emp.config.js`
- `emp-config.mjs` / `emp-config.cjs` / `emp-config.mts` / `emp-config.cts`
- `emp.config.mjs` / `emp.config.cjs` / `emp.config.mts` / `emp.config.cts`
## 顶层配置
| 字段 | 类型 | 默认 | 说明 |
|------|------|------|------|
| `appSrc` | string | `'src'` | 源码目录 |
| `appEntry` | string | `'index.{ts,tsx,jsx,js}'` | 入口文件名,entries 设置后失效 |
| `base` | string | `undefined` | publicPath,业务模式默认 auto |
| `plugins` | Function[] | - | 插件数组,每项为 `(store) => rsConfig` |
| `server` | ServerType | - | 开发服务器配置 |
| `build` | BuildType | - | 构建配置 |
| `html` | HtmlType | - | HTML 模板配置 |
| `entries` | Record | - | 多入口配置 |
| `resolve` | Resolve | - | 模块解析(alias、extensions) |
| `define` | Record | - | 全局变量注入 |
| `chain` | (chain) => void | - | Rspack 链式配置 |
| `lifeCycle` | LifeCycleOptions | - | 生命周期钩子 |
## server
| 字段 | 默认 | 说明 |
|------|------|------|
| `host` | `'0.0.0.0'` | 访问 host |
| `port` | `8000` | 端口 |
| `open` | `false`(darwin 为 true) | 自动打开浏览器 |
| `hot` | `true` | 热更新 |
| `proxy` | - | 代理配置,需为数组 |
## build
| 字段 | 默认 | 说明 |
|------|------|------|
| `outDir` | `'dist'` | 输出目录 |
| `target` | `'es5'` | 目标环境 es5/es2017 等 |
| `sourcemap` | 开发 true | 源码映射 |
| `minify` | 生产 true | 压缩 |
| `polyfill.mode` | - | `'entry'` \| `'usage'` |
| `polyfill.entryCdn` | - | polyfill CDN 地址 |
| `polyfill.browserslist` | - | 浏览器兼容 |
## html
| 字段 | 默认 | 说明 |
|------|------|------|
| `template` | - | 自定义 HTML 模板路径 |
| `title` | `'EMP'` | 页面标题 |
| `favicon` | - | favicon |
| `inject` | `'body'` | 脚本注入位置 |
```
### references/tailwind-v4.md
```markdown
# Tailwind v4 配置参考
## 默认配置(推荐)
```typescript
// emp.config.ts
import pluginTailwindcss from '@empjs/plugin-tailwindcss'
plugins: [pluginReact(), pluginTailwindcss()]
```
```css
/* src/style.css */
@import "tailwindcss";
```
```ts
// src/index.tsx
import 'src/style.css'
import('./bootstrap')
```
## package.json
```json
{
"devDependencies": {
"@empjs/plugin-tailwindcss": "workspace:^",
"tailwindcss": "^4.x"
}
}
```
## 分层导入(可选)
```css
@import "tailwindcss/theme.css" layer(theme);
@import "tailwindcss/preflight.css" layer(base);
@import "tailwindcss/utilities.css" layer(utilities);
```
## 作用域样式(微前端隔离)
```css
@import "tailwindcss/theme.css" layer(theme);
.scope-container {
@import "tailwindcss/preflight.css" layer(base);
@import "tailwindcss/utilities.css" layer(utilities);
}
```
## 与 empRuntime 共存
Tailwind 与 `pluginRspackEmpShare` 可同时使用,顺序无严格要求。
```
### references/runtime-build.md
```markdown
# empRuntime 与构建引用
## empRuntime 结构
```typescript
empRuntime: {
framework: {
global: string, // 全局变量名,如 'EMP_ADAPTER_REACT'
libs: string[], // 框架 UMD CDN 列表
},
runtime: {
lib: string, // @empjs/share sdk.js CDN
},
setExternals: (o, global?) => void, // 自定义 externals
}
```
## 版本与 CDN 速查
| 包 | 用途 | 示例 URL |
|----|------|----------|
| @empjs/cdn-react | React + Router UMD | `https://unpkg.com/@empjs/[email protected]/dist/reactRouter.${store.mode}.umd.js` |
| @empjs/cdn-react-wouter | React + Wouter | `https://cdn.jsdelivr.net/npm/@empjs/[email protected]/dist/reactRouter.${store.mode}.umd.js` |
| @empjs/share | SDK 运行时 | `https://unpkg.com/@empjs/[email protected]/output/sdk.js` |
`store.mode` 为 `development` 或 `production`。
## 构建时 remotes
```typescript
remotes: {
mfHost: `mfHost@http://${store.server.ip}:6001/emp.json`,
}
```
开发可用 `store.server.ip`,生产替换为实际域名。
## 运行时 registerRemotes
```typescript
import { registerRemotes, loadRemote } from '@empjs/share/sdk'
registerRemotes([
{ name: 'mfHost', entry: 'http://localhost:6001/emp.json' },
])
const Component = lazy(() => loadRemote('mfHost/App'))
```
## forceRemotes
覆盖构建时 remotes,用于本地调试或环境切换:
```typescript
forceRemotes: {
rtLayout: '$@http://127.0.0.1:4004/emp.json',
},
```
## polyfill 引用
| 场景 | entryCdn |
|------|----------|
| 通用 | `https://unpkg.com/@empjs/[email protected]/dist/es.js` |
| 新内核 | `https://unpkg.com/@empjs/[email protected]/dist/c71.js` |
## experiments
```typescript
experiments: {
asyncStartup: true, // 异步启动,常用
}
```
## 项目结构速查
| 项目 | 角色 | 端口 | 说明 |
|------|------|------|------|
| tailwind-4 | 单应用 | - | Tailwind v4 + empRuntime |
| mf-host | Host | 6001 | 暴露 App、CountComp、Section |
| mf-app | Remote | 6002 | 消费 mfHost |
| rtHost | Host | 4000 | 消费 rtProvider、rtLayout |
| rtProvider | Remote | 4001 | 暴露 App,消费 rtLayout |
| rtLayout | Remote | 4004 | 暴露 App、logo |
```
### references/valtio-skill.md
```markdown
# @empjs/valtio 使用指南
基于 **Valtio v2** 的增强状态库,在 `proxy` + `useSnapshot` 之上提供开箱即用的 `createStore` / `useStore`、历史、派生、持久化与 Store 方法封装。
**安装**:`pnpm add @empjs/valtio` | **文档**:<https://valtio.empjs.dev/>
## 何时用 createStore vs useStore
| 场景 | 使用 |
|------|------|
| 单例、跨组件共享(如主题、用户、全局计数) | `createStore(initialState, options?)` |
| 组件内独立状态、每实例一份(表单、编辑器、画板) | `useStore(initialState, options?)` |
## 按使用方法速查
| 用法 | 说明 | 详见 |
|------|------|------|
| 常规 store | 读 `useSnapshot()` / `snap`,写 `set` / `update` | [valtio-usage.md](valtio-usage.md#1-常规-store) |
| 带历史 | `history: {}`,读 `snap.value`,写 `store.value.xxx`,`snap.undo()` / `snap.redo()`,`snap.history.nodes.length` 为步数 | [valtio-usage.md](valtio-usage.md#2-带历史的-store) |
| 带派生 | `derive: (get, proxy) => ({ ... })`,返回 `{ base, derived }` 或 `[baseSnap, baseStore, derivedSnap]` | [valtio-usage.md](valtio-usage.md#3-带派生的-store) |
| 集合 Map/Set | `createMap` / `createSet` 放入 store 或 useStore | [valtio-usage.md](valtio-usage.md#4-集合-createmap--createset) |
| 持久化 | `store.persist('key')` 与 localStorage 双向同步 | [valtio-usage.md](valtio-usage.md#5-持久化) |
| 订阅 | `subscribe` / `subscribeKey` / `subscribeKeys`、`batch(fn)` 合并多次写为一次通知 | [valtio-usage.md](valtio-usage.md#6-订阅与-batch) |
## 调用闭环(重要)
1. **读**:只用 `snap`(来自 `store.useSnapshot()` 或 `useStore` 的 `snap`),不要直接读 `store.xxx` 做渲染,否则不触发订阅。
2. **写**:用 store 方法(`set`、`update`、`store.key = value` 等),写后所有订阅该路径的组件会重渲染。
3. **历史 store**:读用 `snap.value.xxx`,写用 `store.value.xxx = y`;撤销/重做用 `snap.undo()` / `snap.redo()`;当前记录步数可用 `snap.history?.nodes?.length`。
## 常见错误与注意点
- **"Please use proxy object"**:传给 `snapshot`/`useSnapshot`/`subscribe` 的必须是 proxy(createStore/useStore 返回的 store 或 base),不要传普通对象。
- **派生函数签名**:`derive: (get, proxy) => derivedObject`,`get(proxy)` 得当前快照,返回纯对象,不要写副作用。
- **集合 key 名**:勿用 key 名 `"set"`,会与 `store.set(key, value)` 方法冲突。
## 类型要点(TypeScript)
- `createStore(initialState)` → `T & StoreBaseMethods<T>`;带 `history` → `HistoryStoreWithSnapshot<T>`;带 `derive` → `{ base, derived }`。
- `useStore` 常规 → `[Snapshot<T>, T & StoreBaseMethods<T>]`;带 derive → `[Snapshot<T>, T & StoreBaseMethods<T>, D]`;带 history → `[WithHistorySnapshot<T>, HistoryStore<T>]`。
- 初始状态可用 `InitialStateOrFn<T>`(即 `T | (() => T)`)惰性初始化。
## 更多资源
- **按使用方法详细说明**:[valtio-usage.md](valtio-usage.md)
- **API 与类型**:[valtio-api.md](valtio-api.md)
- **示例索引**:[valtio-examples.md](valtio-examples.md)
```
### references/valtio-usage.md
```markdown
# @empjs/valtio 按使用方法说明
按「用法」组织:常规 store、带历史、带派生、集合、持久化、订阅与 batch。每节包含用法要点与简短示例。
## 目录
1. [常规 Store](#1-常规-store)
2. [带历史的 Store](#2-带历史的-store)
3. [带派生的 Store](#3-带派生的-store)
4. [集合 createMap / createSet](#4-集合-createmap--createset)
5. [持久化](#5-持久化)
6. [订阅与 batch](#6-订阅与-batch)
7. [调用闭环](#调用闭环重要)
8. [常见错误](#常见错误)
---
## 1. 常规 Store
**createStore**:模块级单例,跨组件共享。读用 `store.useSnapshot()`,写用 `store.set` / `store.update`。
```ts
import { createStore } from '@empjs/valtio'
const store = createStore({ count: 0, name: '' }, { name: 'AppStore' })
// 组件内
const snap = store.useSnapshot()
store.set('count', snap.count + 1)
store.update({ name: 'Alice' })
```
**useStore**:组件内每实例独立。返回 `[snap, store]`,读用 `snap`,写用 `store`。
```ts
import { useStore } from '@empjs/valtio'
const [snap, store] = useStore({ count: 0 })
// 或惰性初始化
const [snap, store] = useStore(() => ({ count: 0 }))
store.set('count', snap.count + 1)
```
## 2. 带历史的 Store
传入 `{ history: {} }`。读用 `snap.value.xxx`,写用 `store.value.xxx = y`;撤销/重做用 `snap.undo()` / `snap.redo()`;当前记录步数 `snap.history?.nodes?.length`。
```ts
const store = createStore({ count: 0 }, { history: {} })
const snap = store.useSnapshot()
store.value.count = snap.value.count + 1
snap.undo()
```
## 3. 带派生的 Store
**createStore** 传入 `{ derive: (get, proxy) => ({ ... }) }`,返回 `{ base, derived }`。base 写、derived 只读。
```ts
const { base, derived } = createStore(
{ a: 1, b: 2 },
{ derive: (get, p) => ({ sum: get(p).a + get(p).b }) }
)
const baseSnap = base.useSnapshot()
const derivedSnap = derived.useSnapshot() // { sum }
base.update({ a: 10 })
```
**useStore** 带 derive 返回 `[baseSnap, baseStore, derivedSnap]`。
```ts
const [baseSnap, baseStore, derivedSnap] = useStore(
() => ({ a: 1, b: 2 }),
{ derive: (get, p) => ({ sum: get(p).a + get(p).b }) }
)
baseStore.update({ a: baseSnap.a + 1 })
```
## 4. 集合 createMap / createSet
**全局**:在 createStore 初始状态里放 `createMap` / `createSet`,组件内 `store.useSnapshot()` 读、`store.map` / `store.tagSet` 写。
```ts
import { createMap, createSet, createStore } from '@empjs/valtio'
const collectionsStore = createStore(
{
map: createMap([['a', 1], ['b', 2]]),
tagSet: createSet(['x']),
},
{ name: 'CollectionsStore' }
)
// 组件内
const snap = collectionsStore.useSnapshot()
const entries = Array.from(snap.map.entries())
collectionsStore.map.set('c', 3)
collectionsStore.tagSet.add('y')
collectionsStore.map.delete('a')
collectionsStore.tagSet.clear()
```
**局部**:在 useStore 初始状态里放 map/set,读 `snap.map` / `snap.tagSet`,写 `store.map` / `store.tagSet`。注意勿用 key 名 `"set"`(与 `store.set` 冲突)。
## 5. 持久化
`store.persist('key')` 与 localStorage 双向同步(toJSON/fromJSON),返回取消订阅函数。
```ts
const store = createStore({ theme: 'light' })
store.persist('app-settings')
```
## 6. 订阅与 batch
- **subscribe(cb, notifyInSync?)**:全量变更订阅
- **subscribeKey(key, cb)**:单 key 变更回调
- **subscribeKeys(keys, cb)**:多 key,`cb(key, value)`
- **batch(fn)**:fn 内多次写合并为一次通知
```ts
store.subscribeKey('count', (value) => console.log('count', value))
store.subscribeKeys(['count', 'name'], (key, value) => console.log(key, value))
store.batch((s) => {
s.count = 1
s.name = 'x'
})
// 只触发一次订阅
```
## 调用闭环(重要)
1. **读**:只用 `snap`,不要直接读 `store.xxx` 做渲染
2. **写**:用 store 方法(`set`、`update`、`store.key = value` 等)
3. **历史 store**:读用 `snap.value.xxx`,写用 `store.value.xxx = y`
## 常见错误
- **"Please use proxy object"**:传给 `snapshot`/`useSnapshot`/`subscribe` 的必须是 proxy(createStore/useStore 返回的 store 或 base),不要传普通对象
- **派生函数签名**:`derive: (get, proxy) => derivedObject`,`get(proxy)` 得当前快照,返回纯对象,不要写副作用
- **集合 key 名**:勿用 key 名 `"set"`,会与 `store.set(key, value)` 方法冲突
更多类型与签名见 [valtio-api.md](valtio-api.md),完整示例见 [valtio-examples.md](valtio-examples.md)。
```
### references/valtio-api.md
```markdown
# @empjs/valtio API 参考
基于 Valtio v2 的导出与类型整理。
## 目录
1. [类型](#类型)
2. [createStore](#createstore)
3. [useStore](#usestore)
4. [StoreBaseMethods](#storebasemethods)
5. [增强 Store / History / Derive](#增强-store--history--derive)
6. [集合与再导出](#集合与再导出)
---
## 类型
| 类型 | 说明 |
|------|------|
| `Unsubscribe` | `() => void`,取消订阅 |
| `InitialStateOrFn<T>` | `T \| (() => T)`,初始状态或惰性函数 |
| `DeriveFn<T, D>` | `(get, proxy) => TDerived`,派生函数 |
| `CreateOptionsBase` | `{ devtools?: boolean; name?: string }` |
| `WithHistoryOptions` | 与 valtio-history 的 `proxyWithHistory` 第二参数一致 |
| `CreateOptions<T>` | Base + `history?` + `derive?` |
| `StoreBaseMethods<T>` | 增强方法集:`useSnapshot`、`set`、`reset`、`batch`、`subscribe` 等 |
| **`EmpStore<T>`** | **`T & StoreBaseMethods<T>`**,推荐用于类型标注:createStore/useStore 常规返回、子组件 props 收「可读可写 store」时用此类型 |
| `WithHistorySnapshot<T>` | `{ value, history, isUndoEnabled, isRedoEnabled, undo, redo }`;`history.nodes.length` 为当前记录步数 |
| `StoreWithDerived<T, D>` | `{ base: T & StoreBaseMethods<T>; derived: object & { useSnapshot(): D } }` |
---
## createStore
```ts
function createStore<T>(initialState: T, options?: CreateOptionsBase): EmpStore<T>
function createStore<T>(initialState: T, options: { history: WithHistoryOptions }): HistoryStoreWithSnapshot<T>
function createStore<T, D>(initialState: T, options: { derive: DeriveFn<T, D> }): StoreWithDerived<T, D>
```
- **常规**:返回 `EmpStore<T>`;`options` 可选,支持 `devtools`、`name`;开发环境默认挂 devtools(可 `devtools: false` 关闭)
- **history**:与 `valtio-history` 的 `proxyWithHistory` 一致,选项原样透传
- **derive**:使用 `derive-valtio`,返回 `{ base, derived }`;base 写、derived 只读派生
---
## useStore
```ts
function useStore<T>(initialState: InitialStateOrFn<T>): [Snapshot<T>, EmpStore<T>]
function useStore<T>(initialState: InitialStateOrFn<T>, options: { history: WithHistoryOptions }): [WithHistorySnapshot<T>, HistoryStore<T>]
function useStore<T, D>(initialState: InitialStateOrFn<T>, options: { derive: DeriveFn<T, D> }): [Snapshot<T>, EmpStore<T>, D]
```
- 常规返回 `[snap, store]`,`store` 为 `EmpStore<T>`;组件内每实例独立;`initialState` 可为函数实现惰性初始化
- **useStore 的 options** 仅支持 `history`、`derive`,无 `devtools`/`name`
---
## StoreBaseMethods
增强后 store 上的方法(`createStore` 常规返回与 `useStore` 返回的 store 均具备):
| 方法 | 签名 | 说明 |
|------|------|------|
| `getSnapshot` | `() => Snapshot<T>` | 当前只读快照 |
| `useSnapshot` | `() => Snapshot<T>` | React 内订阅用 |
| `subscribe` | `(cb, notifyInSync?) => Unsubscribe` | 全量订阅 |
| `subscribeKey` | `(key, cb) => Unsubscribe` | 单 key 订阅 |
| `subscribeKeys` | `(keys, cb) => Unsubscribe` | 多 key 订阅,cb(key, value) |
| `update` | `(partial: Partial<T>) => void` | 批量更新 |
| `set` | `(key, value) => void` | 单字段更新 |
| `setNested` | `(path: string, value) => void` | 点号路径更新 |
| `delete` | `(key) => void` | 删除字段 |
| `reset` | `(initialState?: T) => void` | 重置为初始状态(仅清数据字段,保留方法) |
| `ref` | `(value) => value` | 存非代理引用(valtio `ref`) |
| `batch` | `(fn: (store) => void) => void` | 批量写(单次调度) |
| `clone` | `() => T & StoreBaseMethods<T>` | deepClone 快照后新 proxy + 增强 |
| `toJSON` | `() => Record` | 可序列化字段(去掉 function/symbol) |
| `fromJSON` | `(json) => void` | 从对象写回 store |
| `persist` | `(key: string) => Unsubscribe` | localStorage 双向同步 |
| `debug` | `(label?) => void` | console 打印当前快照 |
---
## 增强 Store / History / Derive
- **EmpStore**:类型 `T & StoreBaseMethods<T>`,用于标注「可读可写 store」;推荐用 `const initialState` + `type State = typeof initialState`,子组件收 `EmpStore<State>`,即可读(useSnapshot)也可写(set/reset/直接赋值)
- **enhanceStore**:内部使用,为 `proxy` 挂载上述 `StoreBaseMethods`,对外通过 `createStore`/`useStore` 暴露
- **History**:`createStore(..., { history: {} })` 或 `useStore(..., { history: {} })`;读用 `snap.value`,写用 `store.value.xxx`;撤销/重做见 `WithHistorySnapshot`;当前步数 `snap.history?.nodes?.length`
- **Derive**:`derive(get, proxy)` 中 `get(proxy)` 取当前快照;返回对象会变为派生 proxy,仅读、自动随 base 更新
---
## 集合与再导出
- **createMap** / **createSet**:对 `valtio/utils` 的 `proxyMap`、`proxySet` 的封装,用于在 store 或组件内做响应式 Map/Set
- **再导出**(可从 `@empjs/valtio` 直接使用):`proxy`、`snapshot`、`subscribe`、`subscribeKey`、`ref`、`useSnapshot`、`proxyWithHistory`、`proxyMap`、`proxySet`、`derive`、`devtools`
```
### references/valtio-examples.md
```markdown
# @empjs/valtio 示例索引
按用法分文件,来源与 valtio-official 应用一致。按需查阅对应文件。
| 文件 | 内容 |
|------|------|
| [best-practices.md](https://github.com/empjs/valtio-best-practices/blob/main/skills/empjs-valtio/examples/best-practices.md) | EmpStore 类型、全局 store、父传子传 store |
| [regular.md](https://github.com/empjs/valtio-best-practices/blob/main/skills/empjs-valtio/examples/regular.md) | 常规 createStore(全局)、常规 useStore(局部) |
| [history.md](https://github.com/empjs/valtio-best-practices/blob/main/skills/empjs-valtio/examples/history.md) | 带历史的 createStore、带历史的 useStore(含步数) |
| [derive.md](https://github.com/empjs/valtio-best-practices/blob/main/skills/empjs-valtio/examples/derive.md) | 带派生的 createStore、带派生的 useStore |
| [async.md](https://github.com/empjs/valtio-best-practices/blob/main/skills/empjs-valtio/examples/async.md) | 异步 createStore、异步 useStore(方法在 store 内) |
| [collections.md](https://github.com/empjs/valtio-best-practices/blob/main/skills/empjs-valtio/examples/collections.md) | 集合:全局 createStore + Map/Set、局部 useStore + Map/Set |
| [persist.md](https://github.com/empjs/valtio-best-practices/blob/main/skills/empjs-valtio/examples/persist.md) | 持久化 store.persist |
| [subscribe.md](https://github.com/empjs/valtio-best-practices/blob/main/skills/empjs-valtio/examples/subscribe.md) | subscribeKey/subscribeKeys、细粒度订阅、batch |
| [performance.md](https://github.com/empjs/valtio-best-practices/blob/main/skills/empjs-valtio/examples/performance.md) | 长列表 + batch + content-visibility |
更多用法见 [valtio-usage.md](valtio-usage.md),API 见 [valtio-api.md](valtio-api.md)。
完整示例仓库:<https://github.com/empjs/valtio-best-practices>
```
### references/react-performance.md
```markdown
# EMP React 性能规则详解
适配自 Vercel React 最佳实践,状态管理用 @empjs/valtio。
## 目录
1. [消除瀑布](#1-消除瀑布)
2. [包体积](#2-包体积)
3. [客户端数据](#3-客户端数据)
4. [状态管理 → valtio](#4-状态管理--empjsvaltio)
5. [渲染](#5-渲染)
6. [JS 性能](#6-js-性能)
7. [高级](#7-高级)
---
## 1. 消除瀑布
### async-parallel
独立请求用 `Promise.all()`:
```ts
const [user, posts] = await Promise.all([fetchUser(), fetchPosts()])
```
### async-defer-await
await 移到真正需要的分支,避免阻塞不需要的分支。
### async-suspense-boundaries
用 Suspense 包裹异步组件,流式加载。
## 2. 包体积
### bundle-barrel-imports
直接导入:`import { X } from 'lib/x'` 而非 `import { X } from 'lib'`(barrel 会拉全量)。
### bundle-dynamic-imports
重组件用 `React.lazy`:
```tsx
const Heavy = React.lazy(() => import('./Heavy'))
<Suspense fallback={...}><Heavy /></Suspense>
```
### bundle-defer-third-party
分析、日志等 hydration 后加载:
```tsx
useEffect(() => {
import('@vercel/analytics').then(m => m.inject())
}, [])
```
### bundle-preload
hover/focus 时预加载:
```tsx
<button onMouseEnter={() => import('./Editor')} onFocus={...}>
打开编辑器
</button>
```
## 3. 客户端数据
### client-swr-dedup
用 SWR 自动去重请求。
### client-passive-listeners
touch/wheel 加 `{ passive: true }`,避免阻塞滚动。
### client-localstorage-schema
localStorage 加版本前缀,只存必要字段,try-catch。
## 4. 状态管理 → @empjs/valtio
原 useState/useReducer/useCallback 规则改用 valtio:
- 全局:`createStore` + `useSnapshot`
- 局部:`useStore`
- 派生:`derive` 替代 useMemo
- 细粒度:`subscribeKey` 替代订阅整块
- 批量:`batch` 合并写
## 5. 渲染
### rendering-content-visibility
长列表:`content-visibility: auto` + `contain-intrinsic-size`。
### rendering-hoist-jsx
静态 JSX 提到组件外,避免每次渲染创建。
### rendering-conditional-render
用 `count > 0 ? <Badge /> : null`,不用 `count && <Badge />`(0 会渲染)。
### rendering-activity (React 19)
`<Activity mode={isOpen ? 'visible' : 'hidden'}>` 保留 DOM/状态,避免频繁挂载。
### rendering-hydration-no-flicker
客户端数据用 inline script 同步设置,避免闪烁。
## 6. JS 性能
### js-batch-dom-css
用 class 或 cssText 批量改样式,避免多次 reflow。
### js-index-maps
重复 find 用 Map 建索引,O(n)→O(1)。
### js-cache-storage
localStorage 读缓存到 Map,避免重复 I/O。
### js-tosorted-immutable
用 `arr.toSorted()` 替代 `arr.sort()`,不修改原数组。
### js-early-exit
函数内早 return,减少嵌套。
## 7. 高级
### advanced-event-handler-refs
事件处理存 ref,避免闭包重建。
### advanced-use-latest
`useLatest(callback)` 稳定 callback ref。
```
### references/examples.md
```markdown
# EMP React 配置示例
## 最少配置
```typescript
import { defineConfig } from '@empjs/cli'
import pluginReact from '@empjs/plugin-react'
export default defineConfig(store => ({
plugins: [pluginReact()],
server: { port: 8000 },
}))
```
## 标准 React 项目
```typescript
import { defineConfig } from '@empjs/cli'
import pluginReact from '@empjs/plugin-react'
export default defineConfig(store => ({
plugins: [pluginReact()],
appSrc: 'src',
appEntry: 'index.tsx',
server: {
port: 8000,
open: true,
hot: true,
},
html: {
template: 'src/index.html',
title: '我的应用',
},
build: {
outDir: 'dist',
sourcemap: true,
target: 'es2017',
polyfill: {
entryCdn: 'https://unpkg.com/@empjs/[email protected]/dist/es.js',
},
},
resolve: {
alias: { '@': store.resolve('src') },
},
}))
```
## 多入口
```typescript
export default defineConfig(store => ({
plugins: [pluginReact()],
entries: {
'index.tsx': { title: '首页' },
'about.tsx': { title: '关于', template: 'src/about.html' },
},
server: { port: 8000 },
}))
```
## 带代理
```typescript
export default defineConfig(store => ({
plugins: [pluginReact()],
server: {
port: 8000,
proxy: [
{ context: ['/api'], target: 'http://localhost:3001', changeOrigin: true },
],
},
}))
```
## 带 Tailwind v4
```typescript
import pluginTailwindcss from '@empjs/plugin-tailwindcss'
plugins: [pluginReact(), pluginTailwindcss()]
```
## 微前端 Host
```typescript
import { externalReact, pluginRspackEmpShare } from '@empjs/share'
pluginRspackEmpShare({
name: 'mfHost',
manifest: true,
exposes: { './App': './src/App' },
empRuntime: {
framework: {
global: 'EMP_ADAPTER_REACT',
libs: [`https://unpkg.com/@empjs/[email protected]/dist/reactRouter.${store.mode}.umd.js`],
},
runtime: { lib: `https://unpkg.com/@empjs/[email protected]/output/sdk.js` },
setExternals: externalReact,
},
})
```
## 微前端 Remote
```typescript
pluginRspackEmpShare({
name: 'mfApp',
remotes: {
mfHost: `mfHost@http://${store.server.ip}:6001/emp.json`,
},
empRuntime: { /* 与 Host 一致 */ },
dts: { consumeTypes: true },
})
```
```
### references/startkit.md
```markdown
# EMP React 19 Startkit 说明
## 脚本用法
```bash
node skills/empjs/scripts/create-emp-react19.js <项目名> [目标目录]
```
从 EMP 仓库根目录执行。目标目录默认为当前目录。
## 生成文件结构
```
<项目名>/
├── package.json # React 19、@empjs/cli、@empjs/plugin-react
├── emp.config.ts # 最少配置,含 resolve alias
├── tsconfig.json # extends @empjs/cli/tsconfig/react
├── src/
│ ├── index.html # 含 <div id="emp-root">
│ ├── index.tsx # 入口,import bootstrap
│ ├── bootstrap.tsx # createRoot + render App
│ ├── App.tsx # 示例组件
│ ├── style.css # 可选 Tailwind 入口
│ └── global.d.ts # EMP 类型引用
```
## 生成后步骤
```bash
cd <项目名>
pnpm install
pnpm dev
```
## 可选扩展
- **Tailwind v4**:`pnpm add @empjs/plugin-tailwindcss tailwindcss`,在 emp.config 添加 `pluginTailwindcss()`,在 style.css 添加 `@import "tailwindcss"`
- **状态管理**:`pnpm add @empjs/valtio`
- **微前端**:`pnpm add @empjs/share`,配置 `pluginRspackEmpShare`
```
### scripts/create-emp-react19.js
```javascript
#!/usr/bin/env node
/**
* EMP React 19 Startkit 脚手架脚本
* 用法: node create-emp-react19.js <项目名> [目标目录]
* 示例: node create-emp-react19.js my-app
* node create-emp-react19.js my-app ./projects
*/
import fs from 'node:fs'
import path from 'node:path'
const projectName = process.argv[2]
const targetDir = process.argv[3] || '.'
if (!projectName) {
console.error('用法: node create-emp-react19.js <项目名> [目标目录]')
process.exit(1)
}
const root = path.resolve(targetDir, projectName)
function write(filePath, content) {
const full = path.join(root, filePath)
fs.mkdirSync(path.dirname(full), { recursive: true })
fs.writeFileSync(full, content.trim() + '\n', 'utf8')
}
const pkgName = projectName.replace(/[^a-z0-9-]/gi, '-').toLowerCase()
write('package.json', JSON.stringify({
name: pkgName,
version: '1.0.0',
type: 'module',
scripts: {
dev: 'emp dev',
build: 'emp build',
start: 'emp serve',
stat: 'emp build --analyze',
emp: 'emp',
},
devDependencies: {
'@empjs/cli': '^3.12.0',
'@empjs/plugin-react': '^3.12.0',
'@types/react': '^19',
'@types/react-dom': '^19',
},
dependencies: {
react: '^19',
'react-dom': '^19',
},
}, null, 2))
write('emp.config.ts', `import { defineConfig } from '@empjs/cli'
import pluginReact from '@empjs/plugin-react'
export default defineConfig(store => ({
plugins: [pluginReact()],
appSrc: 'src',
appEntry: 'index.tsx',
server: { port: 8000, open: true },
html: { template: 'src/index.html', title: '${pkgName}' },
build: {
target: 'es2017',
polyfill: {
entryCdn: 'https://unpkg.com/@empjs/[email protected]/dist/es.js',
},
},
resolve: { alias: { '@': store.resolve('src') } },
}))`)
write('tsconfig.json', JSON.stringify({
extends: '@empjs/cli/tsconfig/react',
compilerOptions: { baseUrl: './' },
include: ['src'],
exclude: ['dist', 'node_modules'],
}, null, 2))
write('src/index.html', `<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>${pkgName}</title>
</head>
<body>
<div id="emp-root"></div>
</body>
</html>`)
write('src/index.tsx', `import './style.css'
import('./bootstrap')`)
write('src/style.css', `/* 可选:添加 Tailwind v4
@import "tailwindcss";
*/`)
write('src/bootstrap.tsx', `import { createRoot } from 'react-dom/client'
import App from './App'
const dom = document.getElementById('emp-root')!
const root = createRoot(dom)
root.render(<App />)`)
write('src/App.tsx', `export default function App() {
return (
<div style={{ padding: 24, fontFamily: 'system-ui' }}>
<h1>EMP React 19</h1>
<p>项目已就绪,运行 <code>pnpm dev</code> 启动开发服务器。</p>
</div>
)
}`)
write('src/global.d.ts', `/// <reference types="@empjs/cli/client" />`)
console.log(`✓ EMP React 19 项目已创建: ${root}`)
console.log(' 下一步:')
console.log(` cd ${projectName}`)
console.log(' pnpm install')
console.log(' pnpm dev')
```