Back to skills
SkillHub ClubShip Full StackFull StackFrontend

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.

Stars
2,400
Hot score
99
Updated
March 20, 2026
Overall rating
C4.0
Composite score
4.0
Best-practice grade
C63.9

Install command

npx @skill-hub/cli install empjs-emp-empjs

Repository

empjs/emp

Skill path: skills/empjs

EMP 全栈技能:React 19 脚手架脚本、emp.config.ts 配置、Tailwind v4、微前端 empRuntime、@empjs/valtio 状态管理、React 性能优化。适用于创建 EMP 应用、配置模块联邦、状态管理、性能优化等场景。

Open repository

Best 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

Claude CodeCodex CLIGemini CLIOpenCode

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')

```

empjs | SkillHub