mcpField
R
react-to-taro

react-to-taro

github.com/dafang開発351 回インストール43 週間前に更新
リポジトリ

react-to-taro について

React Web アプリを WeChat ミニプログラム向けの Taro 4.x コードへ変換するためのスキルです。ソース分析、変換指示の生成、結果の検証を行うスクリプトと、プラットフォーム固有の変換ルールを備えています。

主な機能

  1. 01React ソース分析スクリプト
  2. 02変換指示の生成
  3. 03Taro 出力の検証
  4. 04JSX、イベント、ルーティングの変換ガイド
  5. 05API、スタイル、プラットフォーム制約のルール

活用シーン

  1. 01React アプリの WeChat ミニプログラム移行計画
  2. 02React フォームやイベントハンドラーの変換
  3. 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.md
best-practices.md
components.md
scripts/
analyze.js
generate-transforms.js
validate.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

ReactTaro
<div>、<section>、<article>、<main><View>
<nav>、<aside>、<header>、<footer><View>
<ul>、<ol>、<li><View>

テキスト要素 → Text

ReactTaro制約
<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

ReactTaroデフォルト
<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:イベントの変換

イベントのマッピング

ReactTaro詳細
onClickonClickITouchEvent
onChange(input)onInpute.detail.value
onKeyDown(Enter)onConfirme.detail.value
onFocusonFocuse.detail
onBluronBlure.detail
onScrollonScrollscrollTop, 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 RouterTaro
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)

コメント