← 返回 Skills 市场
aiswot

Vue To Umijs

作者 aiswot · GitHub ↗ · v0.1.0 · MIT-0
cross-platform ✓ 安全检测通过
28
总下载
1
收藏
0
当前安装
1
版本数
在 OpenClaw 中安装
/install vue2umijs
功能描述
Migrate Vue 2/3 projects to React on UmiJS (@umijs/max): conventions, constraints, stack mapping, and syntax examples (single SKILL.md). Use for .vue → Umi p...
使用说明 (SKILL.md)

Vue → UmiJS + React migration

Goal: move a Vue codebase to UmiJS (@umijs/max) + React 18 + antd, with behavior parity and Umi’s routing, data flow, and folder conventions.

Document layout: rules and constraints first; Stack and Umi mapping next; Syntax examples last.

Scope

  • Source: Vue 2 / 3 (Options API, Composition API, SFC).
  • Target: Umi 4 + @umijs/max + React 18; function components and hooks; TypeScript for public APIs.
  • Default stack: antd; src/models + useModel; React Router v6 (via Umi); Less + *.module.less; pages as PageName/index.tsx + PageName/index.module.less.

Principles

  1. Parity first: routing, URL, guards, loading/errors, and UX before micro-optimizations.
  2. Single source of truth: no duplicate domain state across store and local state unless there is an explicit draft/edit buffer.
  3. Side-effect boundaries: useEffect (or Umi equivalents) for subscriptions and async with cleanup; cancel in-flight work with AbortController where needed.
  4. No Vue runtime: replace plugins, mixins, filters, and global buses with hooks, Umi models, and explicit modules.
  5. Closures and deps: dependency arrays reflect real deps; derive with render/useMemo, not effects for pure derivation.

API mapping (summary)

Area Direction
Page .vue PageName/index.tsx + index.module.less
Child .vue Foo.tsx + Foo.module.less (same folder, matching basename)
Component API defineComponentfunction Name(props: NameProps); emitonXxx
ref / reactive / computed / watch useState / useRef, useReducer, useMemo, useEffect (deps + cleanup)
Vue Router Umi config / routes; useNavigate, useParams, etc. — see Routing and data below
Pinia / Vuex src/models + useModel by domain; prefer useRequest for local fetch/cache
Element UI / Element Plus Map to antd with matching interaction and validation semantics

Constraints

  1. One router tree: use Umi nested routes/layouts; do not add a standalone BrowserRouter beside Umi; avoid full-page navigations that break SPA where the old app used in-app routing.
  2. Typing: avoid any on exported components, route params, and model shapes.
  3. Security and a11y: no untrusted dangerouslySetInnerHTML; preserve focus, labels, and keyboard behavior; env vars and URL semantics stay aligned or are explicitly documented.
  4. Styles: index.module.less next to index.tsx for pages; child components: .tsx + .module.less with the same basename.
  5. Vue-specific features: custom directives, plugins, defineExpose + parent refs must map to explicit React/Umi patterns (controlled props, forwardRef/useImperativeHandle, permission guards)—never silently drop behavior.
  6. Async and lists: clean up listeners and requests on unmount or dep changes; use stable ids for list keys, not indexes when order changes.
  7. Do not: run side effects during render; use useEffect for pure derivation; recreate EventBus / mixin / filter pipelines; mirror props to state without need; introduce a second global state stack for the same domain without a plan (prefer Umi models).

Additional constraints (often missed—verify explicitly)

  1. i18n: migrate vue-i18n keys/namespaces and locale switching to the chosen solution (including Umi locale plugins); avoid leftover hard-coded copy or mixed languages.
  2. HTTP and errors: use the shared request wrapper (and @umijs/max request APIs) so interceptors, error codes, auth headers, and global toasts match the old Vue layer; avoid ad-hoc fetch that bypasses global handling.
  3. Environment: map VITE_* / import.meta.env to Umi env / define; never ship secrets or internal-only URLs in the client bundle.
  4. Forms and tables: align Element validation rules, async validation, and blur/submit timing with antd Form; keep Table rowKey, pagination, sort, and filter params aligned with backend contracts; validate dynamic forms (Form.List, etc.) behavior-by-behavior.
  5. API scalars: keep explicit conversion between backend 0/1, stringly numbers, enums, and UI booleans—prefer normalization at submit/response boundaries to avoid silent drift.
  6. Build and deploy: publicPath / base, static asset URLs, and CDN prefixes match the old site or are documented; avoid production 404s for assets.
  7. Dev / integration: move mocks and dev proxy from the Vue setup into Umi conventions (mock/, proxy) so local and joint-debug environments stay consistent.
  8. SSR vs CSR: with Umi SSR, do not use window / document / localStorage on the server render path; with CSR-only, document any first-screen / SEO differences vs the legacy app.

Pre-release checklist

  • Routing, query, and auth behavior matches the legacy app (or differences are documented).
  • useEffect deps are complete; async work is cancelled or ignored when stale.
  • Pages include index.tsx + index.module.less; child style files match basename.
  • Custom directives/plugins/exposed methods have a mapped implementation or migration note.
  • Request layer, env vars, and deploy paths checked against Additional constraints.
  • Sample pass on critical forms/tables and i18n paths vs legacy behavior.

Style and structure

  • Prefer function components and hooks; emitonXxx.
  • Keep feature boundaries similar to the Vue repo; page folder naming: see Vue SFC styles → Umi components below.

Stack and Umi mapping

Umi/antd APIs follow the official docs.

Target stack

Layer Choice
App framework @umijs/max (Umi 4)
UI Ant Design (antd)
Global state Umi data flow: src/models + useModel
Runtime React 18
Routing React Router v6 (via Umi; configure in config + routes)

Styling: Less + CSS Modules (*.module.less). Pages: PageName/index.tsx + PageName/index.module.less.

Routing and data (Umi)

Typical Vue pattern Umi + React
Fetch in onMounted after navigation useRequest, useModel, or useEffect + project request in page/layout
beforeEach auth access, route wrappers, or layout-level guards (see Umi docs)
Navigation / URL params useNavigate, useParams, useSearchParams, useLocation from @umijs/max or umi

Do not add a second standalone BrowserRouter root outside Umi.

Pinia / Vuex → Umi data flow

Vue Umi
Modular store src/models split by domain + useModel
Getters / derived Logic in models or useMemo in components
Async actions Model effects, or useRequest in pages then update model

Prefer useRequest for local request/cache when global state is not needed.

Vue SFC styles → Umi components

Vue React (Umi)
Page \x3Cstyle scoped> PageDir/index.tsx + index.module.less, import styles from './index.module.less'
Child scoped Foo.tsx + Foo.module.less in the same folder
\x3Cstyle lang="less"> Same content in the matching *.module.less

For HTTP, i18n, env, deploy, and mock constraints, see Additional constraints above.

Syntax examples

Vue vs React syntax; use @umijs/max, antd, and the rules above in real pages.

1. Counter

Vue 3 (\x3Cscript setup>)

\x3Cscript setup lang="ts">
import { ref } from 'vue'
const count = ref(0)
function inc() {
  count.value++
}
\x3C/script>
\x3Ctemplate>
  \x3Cbutton type="button" @click="inc">{{ count }}\x3C/button>
\x3C/template>

React

import { useState } from 'react'

export function Counter() {
  const [count, setCount] = useState(0)
  return (
    \x3Cbutton type="button" onClick={() => setCount((c) => c + 1)}>
      {count}
    \x3C/button>
  )
}

2. Conditional list

Vue

\x3Ctemplate>
  \x3Cul v-if="items.length">
    \x3Cli v-for="item in items" :key="item.id">{{ item.name }}\x3C/li>
  \x3C/ul>
  \x3Cp v-else>No data\x3C/p>
\x3C/template>

React

return items.length ? (
  \x3Cul>
    {items.map((item) => (
      \x3Cli key={item.id}>{item.name}\x3C/li>
    ))}
  \x3C/ul>
) : (
  \x3Cp>No data\x3C/p>
)

3. Controlled input

Vue

\x3Cinput v-model="text" />

React

const [text, setText] = useState('')
\x3Cinput value={text} onChange={(e) => setText(e.target.value)} />

4. Child events (emit)

Vue

\x3Cscript setup lang="ts">
const emit = defineEmits\x3C{ (e: 'update', v: number): void }>()
function notify() {
  emit('update', 1)
}
\x3C/script>

React

type Props = { onUpdate: (v: number) => void }
export function Child({ onUpdate }: Props) {
  function notify() {
    onUpdate(1)
  }
}

5. computed and watch

Vue

const doubled = computed(() => count.value * 2)
watch(count, (v) => console.log(v))

React

const doubled = useMemo(() => count * 2, [count])
useEffect(() => {
  console.log(count)
}, [count])

6. Provide / inject

Vue

provide('theme', 'dark')
const theme = inject('theme')

React

import { createContext, useContext, useMemo, useState } from 'react'

type Theme = 'light' | 'dark'
type ThemeContextValue = {
  theme: Theme
  setTheme: (v: Theme) => void
}

const ThemeContext = createContext\x3CThemeContextValue | null>(null)

export function ThemeProvider({ children }: { children: React.ReactNode }) {
  const [theme, setTheme] = useState\x3CTheme>('light')
  const value = useMemo(() => ({ theme, setTheme }), [theme])
  return \x3CThemeContext.Provider value={value}>{children}\x3C/ThemeContext.Provider>
}

export function useTheme() {
  const ctx = useContext(ThemeContext)
  if (!ctx) throw new Error('useTheme must be used within ThemeProvider')
  return ctx
}

// usage:
// const { theme, setTheme } = useTheme()

7. Cleanup

Vue

onUnmounted(() => window.removeEventListener('resize', onResize))

React

useEffect(() => {
  window.addEventListener('resize', onResize)
  return () => window.removeEventListener('resize', onResize)
}, [])

8. Scoped slot → render prop

Vue

\x3CChild v-slot="{ row }">
  \x3Cspan>{{ row.name }}\x3C/span>
\x3C/Child>

React

\x3CChild renderRow={(row) => \x3Cspan>{row.name}\x3C/span>} />

9. Route params

Vue

const route = useRoute()
const id = route.params.id as string

Umi (same as React Router v6)

import { useParams } from '@umijs/max'
// or import { useParams } from 'umi'

const { id } = useParams\x3C{ id: string }>()

10. React mental model: closure and deps

Common but not recommended

// stale closure: interval always sees initial count
useEffect(() => {
  const timer = setInterval(() => {
    setCount(count + 1)
  }, 1000)
  return () => clearInterval(timer)
}, []) // count is missing

Recommended

// use functional update to avoid stale closure
useEffect(() => {
  const timer = setInterval(() => {
    setCount((c) => c + 1)
  }, 1000)
  return () => clearInterval(timer)
}, [])

Common but not recommended

// derives data via effect + extra state
const [fullName, setFullName] = useState('')
useEffect(() => {
  setFullName(`${user.firstName} ${user.lastName}`)
}, [user.firstName, user.lastName])

Recommended

// derive directly in render / useMemo
const fullName = useMemo(
  () => `${user.firstName} ${user.lastName}`,
  [user.firstName, user.lastName]
)
安全使用建议
This skill appears safe as a documentation-only migration aid. As with any coding assistant guidance, review generated code changes before applying them, especially changes involving routing, authentication headers, environment variables, and build/deployment settings.
功能分析
Type: OpenClaw Skill Name: vue2umijs Version: 0.1.0 The skill bundle provides legitimate instructions and syntax mappings for migrating Vue 2/3 projects to UmiJS and React. It includes security-conscious constraints, such as avoiding 'dangerouslySetInnerHTML' and ensuring environment secrets are not exposed in client bundles (SKILL.md). No evidence of malicious intent, data exfiltration, or prompt injection was found.
能力评估
Purpose & Capability
The stated purpose—helping migrate Vue 2/3 projects to React on UmiJS—is coherent with the SKILL.md content, which provides conventions, constraints, mappings, and examples.
Instruction Scope
The instructions are scoped to source-code migration practices and include safety-oriented constraints such as avoiding untrusted dangerouslySetInnerHTML and not shipping client-side secrets.
Install Mechanism
There is no install spec, no binaries, and no executable setup. README.md describes manual skill installation methods only.
Credentials
The skill declares no required environment variables, credentials, config paths, or OS-specific access. Environment guidance is limited to migration documentation.
Persistence & Privilege
No persistence, background workers, account privileges, credential access, or autonomous runtime behavior are present in the artifacts.
如何使用
  1. 确保已安装 OpenClaw(本地或 Docker 部署)
  2. 在对话框中输入安装命令:/install vue2umijs
  3. 安装完成后,直接呼叫该 Skill 的名称或使用 /vue2umijs 触发
  4. 根据 Skill 的参数说明提供必要输入,即可获得结构化输出
版本历史
v0.1.0
Initial release of vue-to-umijs migration guide. - Provides detailed conventions, constraints, and mappings for migrating Vue 2/3 projects to UmiJS with React 18 and antd. - Outlines API and syntax migrations from Vue (including SFC, router, and state patterns) to Umi’s folder, routing, and data conventions. - Includes tables for key stack and behavior mappings (e.g., component structure, router, state, UI libraries). - Emphasizes parity, typing, cleanup, and accessibility requirements. - Lists additional migration gotchas (i18n, requests, environment, forms/tables, API normalization, deployment, mocks). - Supplies a pre-release checklist to ensure completeness and alignment with legacy behavior.
元数据
Slug vue2umijs
版本 0.1.0
许可证 MIT-0
累计安装 0
当前安装数 0
历史版本数 1
常见问题

Vue To Umijs 是什么?

Migrate Vue 2/3 projects to React on UmiJS (@umijs/max): conventions, constraints, stack mapping, and syntax examples (single SKILL.md). Use for .vue → Umi p... 它是一个面向 Claude Code / OpenClaw 的 AI Agent Skill 插件,目前累计下载 28 次。

如何安装 Vue To Umijs?

在 OpenClaw 或 Claude Code 对话框中运行命令「/install vue2umijs」即可一键安装,无需额外配置。

Vue To Umijs 是免费的吗?

是的,Vue To Umijs 完全免费,采用 MIT-0 许可证,可自由下载、安装和使用。

Vue To Umijs 支持哪些平台?

Vue To Umijs 跨平台运行,可在任意部署了 OpenClaw / Claude Code 的环境中使用(cross-platform)。

谁开发了 Vue To Umijs?

由 aiswot(@aiswot)开发并维护,当前版本 v0.1.0。

💬 留言讨论