react-to-taro について
React Web アプリを WeChat ミニプログラム向けの Taro 4.x コードへ変換するためのスキルです。ソース分析、変換指示の生成、結果の検証を行うスクリプトと、プラットフォーム固有の変換ルールを備えています。
主な機能
- 01React ソース分析スクリプト
- 02変換指示の生成
- 03Taro 出力の検証
- 04JSX、イベント、ルーティングの変換ガイド
- 05API、スタイル、プラットフォーム制約のルール
活用シーン
- 01React アプリの WeChat ミニプログラム移行計画
- 02React フォームやイベントハンドラーの変換
- 03変換後の Taro コードに未対応パターンがないか確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ dafang/ taro-weapp-plugin --skill react-to-taroインストール後、Claude Code で/react-to-taroを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/apis.mdbest-practices.mdcomponents.mdscripts/analyze.jsgenerate-transforms.jsvalidate.js詳細
React から Taro WeChat ミニプログラムへのコンパイラー
React Web アプリケーションを、WeChat ミニプログラム向けに最適化された Taro 4.x コードへトランスパイルする、高度な自律型エージェントスキルです。Taro 4.x のアーキテクチャ、パフォーマンスパターン、コンポーネント API、weapp-tailwindcss との連携について深い知識を備えています。
スクリプト(補助スクリプト)
このスキルには、変換作業を分析・検証するための補助スクリプトが 3 つ用意されています。
1. analyze.js - コード分析ツール
React のソースコードをスキャンし、変換レポートを生成して、対応が必要な箇所を示します。
node scripts/analyze.js <file-or-directory>
# 输出: taro-migration-report.json
検出内容:
- JSX 要素(div、span、img、input など)
- イベントハンドラー(onChange、onKeyDown など)
- ルーティングコード(react-router-dom)
- Web API(axios、localStorage、DOM)
2. generate-transforms.js - 変換指示の生成ツール
分析レポートを読み取り、エージェントが実行する具体的な変換指示を生成します。
node scripts/generate-transforms.js taro-migration-report.json
# 输出: taro-transforms.json
3. validate.js - コード検証ツール
変換後の Taro コードが規約に準拠しているか確認します。
node scripts/validate.js <file-or-directory>
# 验证通过返回 0,失败返回 1
検証ルール:
- Web のネイティブ要素がないこと
- react-router-dom がないこと
- DOM API/localStorage がないこと
- e.target.value がないこと(e.detail.value を使用してください)
- undefined の state がないこと
推奨ワークフロー
# 1. 分析源代码
node scripts/analyze.js ./src
# 2. 生成转换指令
node scripts/generate-transforms.js taro-migration-report.json
# 3. Agent 根据指令执行转换 (手动)
# 4. 验证转换结果
node scripts/validate.js ./src-taro
入力コンテキスト
変換対象となる React Web のソースコードを受け取ります。ファイルの種類を特定してください。
- component:React の関数コンポーネント/クラスコンポーネント
- page:ルーティングを含む React ページ
- app_entry:ルートを含むアプリのエントリーポイント
- utility:ヘルパー関数/フック
運用プロトコル
出力は本番環境で使用できるコードのみにしてください。「analysis」を明示的に求められない限り、Markdown による説明は提供しないでください。
ルールセット A:インポートの変換
削除リスト(以下を削除)
// React Router
import { Link, useNavigate, useLocation, useParams, Outlet, NavLink } from 'react-router-dom'
// Web Animation Libraries
import { motion, AnimatePresence } from 'framer-motion'
// Direct Axios
import axios from 'axios'
// Browser APIs
import { createPortal } from 'react-dom'
追加リスト(以下を追加)
// Core Taro (Always)
import Taro, { useLoad, useDidShow, useReady } from '@tarojs/taro'
// Components (As Needed)
import {
View, Text, Image, Button, Input, Textarea,
ScrollView, Swiper, SwiperItem, RichText,
CustomWrapper, Form, Navigator
} from '@tarojs/components'
// Types (TypeScript)
import type { CommonEvent, ITouchEvent } from '@tarojs/components'
ルールセット B:JSX 要素のマッピング
コンテナー要素 → View
| React | Taro |
|---|---|
<div>、<section>、<article>、<main> | <View> |
<nav>、<aside>、<header>、<footer> | <View> |
<ul>、<ol>、<li> | <View> |
テキスト要素 → Text
| React | Taro | 制約 |
|---|---|---|
<span>、<p>、<h1>-<h6> | <Text> | テキストのみ |
<label>、<strong>、<em> | <Text> | 内側にブロック要素を含めない |
重要:Text に View を含めることはできません。必要に応じて分割してください。
// INVALID
<Text><View>Block</View></Text>
// VALID
<View><Text>Text</Text><View>Block</View></View>
メディア要素 → Image
| React | Taro | デフォルト |
|---|---|---|
<img src alt> | <Image src mode> | mode="widthFix" |
mode の選択:
- 幅に合わせる場合:
mode="widthFix" - 高さ固定/正方形の場合:
mode="aspectFill"
ループ内:lazyLoad プロパティを追加してください。
フォーム要素
Input
// BEFORE
<input type="text" value={v} onChange={e => set(e.target.value)} />
// AFTER
<Input type="text" value={v} onInput={e => set(e.detail.value)} />
Password
// BEFORE
<input type="password" />
// AFTER
<Input type="text" password />
キーボードでの送信
// BEFORE
onKeyDown={e => e.key === 'Enter' && submit()}
// AFTER
onConfirm={() => submit()}
ルールセット C:イベントの変換
イベントのマッピング
| React | Taro | 詳細 |
|---|---|---|
onClick | onClick | ITouchEvent |
onChange(input) | onInput | e.detail.value |
onKeyDown(Enter) | onConfirm | e.detail.value |
onFocus | onFocus | e.detail |
onBlur | onBlur | e.detail |
onScroll | onScroll | scrollTop, scrollLeft |
重要なパターン
// React: e.target.value
onChange={e => setValue(e.target.value)}
// Taro: e.detail.value
onInput={e => setValue(e.detail.value)}
イベントの伝播
// Use e.stopPropagation() - NOT catchTap
onClick={e => { e.stopPropagation(); action() }}
カスタムイベントは on で始める必要があります
// INVALID
<Component handleClick={fn} callback={fn} />
// VALID
<Component onClick={fn} onCallback={fn} />
ルールセット D:ナビゲーションとルーティング
フックの置き換え
// BEFORE
const navigate = useNavigate()
const location = useLocation()
const { id } = useParams()
// AFTER
import Taro, { useLoad } from '@tarojs/taro'
useLoad((params) => {
const { id } = params
})
// Or anywhere:
const params = Taro.getCurrentInstance().router?.params
ナビゲーション操作
| React Router | Taro |
|---|---|
navigate('/path') | Taro.navigateTo({ url: '/pages/path/index' }) |
navigate('/path', { replace: true }) | Taro.redirectTo({ url: '/pages/path/index' }) |
navigate(-1) | Taro.navigateBack() |
| TabBar のルート | Taro.switchTab({ url: '/pages/tab/index' }) |
パスの規約
React: /products/:id
Taro: /pages/products/index?id=xxx
Link の変換
// BEFORE
<Link to="/about">About</Link>
// AFTER - Option 1
<Navigator url="/pages/about/index"><Text>About</Text></Navigator>
// AFTER - Option 2
<View onClick={() => Taro.navigateTo({ url: '/pages/about/index' })}>
<Text>About</Text>
</View>
ルールセット E:API シムレイヤー
HTTP リクエスト
// BEFORE (axios)
const { data } = await axios.get('/api/users', { params: { page: 1 } })
// AFTER (Taro)
const res = await Taro.request({
url: 'https://api.example.com/api/users',
method: 'GET',
data: { page: 1 }
})
const data = res.data // Note: res.statusCode, not res.status
ストレージ
// BEFORE
localStorage.setItem('key', JSON.stringify(data))
const data = JSON.parse(localStorage.getItem('key'))
// AFTER
Taro.setStorageSync('key', data)
const data = Taro.getStorageSync('key')
フィードバック
// BEFORE
alert('Message')
confirm('Sure?')
// AFTER
Taro.showToast({ title: 'Message', icon: 'none' })
const { confirm } = await Taro.showModal({ title: 'Confirm', content: 'Sure?' })
DOM クエリ
// BEFORE
document.getElementById('el').getBoundingClientRect()
// AFTER
Taro.createSelectorQuery().select('#el').boundingClientRect().exec()
ルールセット F:スタイルとレイアウト
weapp-tailwindcss による Tailwind CSS
公式ドキュメント:https://tw.icebreaker.top/docs/quick-start/frameworks/taro
依存関係のインストール
npm install weapp-tailwindcss tailwindcss autoprefixer postcss -D
Webpack5 の設定(config/index.ts)
const { UnifiedWebpackPluginV5 } = require('weapp-tailwindcss/webpack')
export default defineConfig<'webpack5'>(async (merge) => {
const baseConfig = {
compiler: {
type: 'webpack5',
prebundle: { enable: false } // 建议关闭
},
mini: {
webpackChain(chain, webpack) {
chain.merge({
plugin: {
install: {
plugin: UnifiedWebpackPluginV5,
args: [{ rem2rpx: true }]
}
}
})
}
}
}
})
Vite の設定(代替案)
import type { Plugin } from 'vite'
import tailwindcss from 'tailwindcss'
import { UnifiedViteWeappTailwindcssPlugin as uvtw } from 'weapp-tailwindcss/vite'
const baseConfig: UserConfigExport<'vite'> = {
compiler: {
type: 'vite',
vitePlugins: [
{
name: 'postcss-config-loader-plugin',
config(config) {
if (typeof config.css?.postcss === 'object') {
config.css?.postcss.plugins?.unshift(tailwindcss())
}
},
},
uvtw({
rem2rpx: true,
disabled: process.env.TARO_ENV === 'h5' || process.env.TARO_ENV === 'harmony',
injectAdditionalCssVarScope: true,
})
] as Plugin[]
}
}
注意事項
- WeChat 開発者ツールの「コードの自動ホットリロード」機能をオフにしてください。オンのままでは、スタイルが反映されない場合があります。
- NutUI または @tarojs/plugin-html と併用する場合は、公式の注意事項を確認してください。
- prebundle 機能を無効にすることをおすすめします(
prebundle: { enable: false })。
使用方法
className の文字列は変更せずに使用してください。weapp-tailwindcss が自動的に変換します。
<View className="flex items-center p-4 bg-white">
ビューポートの修正
// 100vh doesn't work correctly
// BEFORE
<div className="h-screen">
// AFTER
<View className="min-h-screen">
セーフエリア(iPhone X 以降)
// Fixed bottom elements need safe area padding
<View style={{ paddingBottom: 'env(safe-area-inset-bottom)' }}>
RULESET G:パフォーマンスの最適化
リスト用のCustomWrapper
// Wrap list items to isolate updates
{items.map(item => (
<CustomWrapper key={item.id}>
<ItemCard item={item} />
</CustomWrapper>
))}
画像の遅延読み込み
// In ScrollView or loops
<Image src={url} lazyLoad mode="widthFix" />
スクロール可能なリスト用のScrollView
// BEFORE
<div className="overflow-y-auto h-96">
// AFTER
<ScrollView scrollY className="h-96" onScrollToLower={loadMore}>
アプリの設定
// Enable lazy loading for large apps
export default defineAppConfig({
lazyCodeLoading: 'requiredComponents'
})
RULESET H:プラットフォームの制約
JSXの制限事項
// INVALID: Only .map() allowed
{items.filter(x => x.active).map(...)}
// VALID: Pre-process
const activeItems = items.filter(x => x.active)
{activeItems.map(...)}
Propsの制限事項
- 関数のpropsは
onで始める必要があります - 状態で
undefinedを使用しないでください(nullを使用してください) - カスタムprop名に
id、class、styleを使用しないでください - すべてのオプションのpropsに
defaultPropsを設定してください
コードスタイル
// USE single quotes
const name = 'John'
<View className='container'>
// DON'T destructure process.env
if (process.env.NODE_ENV === 'development') {}
Nullの安全性
// Components may render before data loads
// ALWAYS use optional chaining
<Text>{data?.name || 'Loading...'}</Text>
クイックリファレンス
インポートテンプレート
import Taro, { useLoad, useDidShow } from '@tarojs/taro'
import { View, Text, Image, Button, Input, ScrollView } from '@tarojs/components'
import type { CommonEvent, ITouchEvent } from '@tarojs/components'
イベントテンプレート
onInput={(e) => setValue(e.detail.value)}
onClick={(e) => { e.stopPropagation(); action() }}
onConfirm={(e) => submit(e.detail.value)}
ナビゲーションテンプレート
Taro.navigateTo({ url: '/pages/target/index?id=' + id })
Taro.redirectTo({ url: '/pages/target/index' })
Taro.navigateBack()
Taro.switchTab({ url: '/pages/tab/index' })
APIテンプレート
const res = await Taro.request({ url, method: 'GET', data })
Taro.showToast({ title: 'Message', icon: 'none' })
Taro.setStorageSync('key', value)