name: uniapp-expert description: uni-app 跨平臺開發專家技能,涵蓋 Vue2/Vue3 開發、Vue2→Vue3 遷移實戰、微信小程式自動化測試。融合真實專案踩坑經驗,提供從開發到測試的完整解決方案。
uni-app 全棧開發專家 · Vue2→Vue3 遷移 · 微信小程式自動化測試
| 特性 | Options API | Composition API | 推薦 |
|---|---|---|---|
| 程式碼組織 | 按選項型別分散 | 按功能邏輯集中 | Composition |
| 邏輯複用 | mixins(隱式) | Composables(顯式) | Composition |
| 型別推導 | 較弱 | 強(配合 TS) | Composition |
| 學習曲線 | 簡單 | 稍陡 | 新專案用 Composition |
| 相容性 | - | Vue3 完全支援 | - |
💡 建議:新專案使用 Composition API(
<script setup>),享受更好的型別推導和邏輯複用。
<script setup> 語法糖<script setup> 是 Vue3 新增的語法糖,讓元件程式碼更簡潔:
<script setup>
import { ref, computed, onMounted } from 'vue'
// 響應式資料
const count = ref(0)
const doubled = computed(() => count.value * 2)
// 方法
function increment() {
count.value++
}
// 生命週期
onMounted(() => {
console.log('元件掛載完成')
})
// 暴露給模板
defineExpose({ count, increment })
</script>
優勢: - 更少的樣板程式碼 - 自動推斷型別 - 更好的 Tree-shaking
| Composable | 功能 | 使用場景 |
|---|---|---|
useRequest |
請求封裝 | API 呼叫、loading 狀態 |
useLoading |
載入狀態 | 非同步操作反饋 |
useToast |
輕提示 | 操作成功/失敗提示 |
useModal |
彈窗封裝 | 確認框、對話方塊 |
// 示例:useLoading
import { ref } from 'vue'
export function useLoading(initial = false) {
const loading = ref(initial)
const start = () => { loading.value = true }
const stop = () => { loading.value = false }
const withLoading = async (fn) => {
start()
try {
return await fn()
} finally {
stop()
}
}
return { loading, start, stop, withLoading }
}
// 使用
const { loading, withLoading } = useLoading()
await withLoading(() => fetchData())
| 規範 | 說明 |
|---|---|
響應式資料用 ref/reactive |
不要直接賦值(需要 .value 或解構) |
大物件用 reactive |
避免 ref 解構丟失響應性 |
| Props 定義型別 | 使用 defineProps with TypeScript 或 propTypes |
事件用 emit |
清晰定義事件名,建議常量 |
| 組合式邏輯抽離 | 超過 50 行考慮抽成 Composable |
避免 watch 濫用 |
優先用 computed |
// ❌ 錯誤:丟失響應性
const obj = reactive({ count: 0 })
const { count } = obj // count 不再是響應式
// ✅ 正確:保持響應性
const obj = reactive({ count: 0 })
const count = toRef(obj, 'count') // 或
const { count } = toRefs(obj) // 解構後仍響應式
處理 uni-app Vue2 → Vue3 遷移任務時,必須執行以下檢查:
| Vue2 | Vue3 | 說明 |
|---|---|---|
destroyed |
unmounted |
Vue 元件生命週期 |
beforeDestroy |
beforeUnmount |
Vue 元件生命週期 |
onUnload |
保留不變 | uni-app 頁面生命週期,全平臺支援 |
⚠️ 重要區分:uni-app 頁面生命週期(onLoad/onShow/onUnload 等)全部保留,只有 Vue 元件的生命週期鉤子有變化。
| Vue2 | Vue3 |
|---|---|
new Vue() |
createSSRApp() |
Vue.prototype |
app.config.globalProperties |
Vue.use() |
app.use() |
| Vue2 | Vue3 | 說明 |
|---|---|---|
v-model: value |
v-model: modelValue |
預設 model 名 |
v-model: input |
v-model: update:modelValue |
預設事件名 |
slot="xxx" |
v-slot:xxx 或 #xxx |
具名插槽 |
.sync 修飾符 |
v-model:xxx |
雙向繫結 |
v-if + v-for 同一元素 |
分離 | Vue3 v-if 優先順序更高 |
{{ value \| filter }} |
計算屬性或方法 | 過濾器已移除 |
| Vue2 | Vue3 |
|---|---|
| 無 | 推薦宣告 emits 選項 |
inheritAttrs: false |
保留 |
functional: true |
已移除 |
💡 選項式 API 相容性:Vue2 的選項式 API(Options API)在 Vue3 中仍然相容。如果 Vue 元件邏輯簡單、檔案較大,直接轉換容易出錯,建議: 1. 詢問使用者是否需要轉換 2. 簡單頁面可保留選項式語法 3. 複雜頁面(如涉及多個 mixins、複雜響應式邏輯)才建議轉為 Composition API
Vue3 仍支援 Mixins,但有更好的替代方案:
| Vue2 | Vue3 | 說明 |
|---|---|---|
mixins: [xxx] |
mixins: [xxx] |
仍然支援 |
| 無 | extends 已保留但少用 |
- |
| - | 推薦使用 Composables | useXxx() 函式 |
// ❌ Mixins(隱式依賴,不清晰)
// mixin.js
export default {
data() { return { count: 0 } },
methods: { increment() { this.count++ } }
}
// 元件中使用 - 不知道 count 來自哪裡
// ✅ Composables(顯式依賴,更清晰)
// useCounter.js
import { ref } from 'vue'
export function useCounter() {
const count = ref(0)
const increment = () => count.value++
return { count, increment }
}
// 元件中使用 - 明確知道依賴
import { useCounter } from '@/composables/useCounter'
const { count, increment } = useCounter()
| 場景 | 建議 |
|---|---|
| 簡單 mixin(少量 data/methods) | 保留,繼續使用 |
| 複雜 mixin(多個生命週期鉤子) | 考慮改為 Composables |
| 新增邏輯 | 強烈建議使用 Composables |
📝 生命週期鉤子合併規則(瞭解即可): - 多個 mixin 的同名生命週期鉤子會全部呼叫 - 執行順序:mixin1 → mixin2 → 元件自身 - 如需在元件中訪問 mixin 資料,使用
this.xxx
| Vue2 | Vue3 |
|---|---|
/deep/ |
::v-deep 或 :deep() |
⚠️ 超長字串處理:CSS 中的 base64 編碼圖片、大型 data URI 等超長行,在轉寫過程中容易出現字元截斷或格式錯誤。轉換後必須驗證檔案完整性,特別是包含
data:image/或background-image的樣式。
| Vue2 | Vue3 |
|---|---|
| Vuex | Pinia |
new Vuex.Store() |
createPinia() |
$store |
useStore() |
📝 Pinia 遷移三步閉環: 1. 建立 store (
store/index.js) 2. 在main.js中app.use(pinia)3. 元件中import { useStore } from '@/store'📝 App.vue 升級要點: 1. 使用
<script setup>語法(簡化寫法) 2. 全域性變數改用 Pinia 或獨立的 globalState.js 3. 應用級生命週期使用onLaunch(非onMounted)
| Vue2 | Vue3 |
|---|---|
require/module.exports |
import/export |
Vue.observable |
reactive/ref |
this.$listeners |
this.$attrs |
⚠️ 複雜 JS 檔案轉寫警告:涉及複雜閉包、動態 require、迴圈依賴等邏輯時,CommonJS → ES Module 轉寫容易出現錯誤。建議:先人工 review,再決定是否自動轉換。
uni.request 支援兩種呼叫方式,Vue2 和 Vue3 都完全支援:
uni.request({
url: 'https://api.example.com',
success: (res) => {
if (res.statusCode === 200) {
console.log(res.data)
}
},
fail: (err) => {
console.error(err)
}
})
// async/await
const res = await uni.request({
url: 'https://api.example.com'
})
if (res.statusCode === 200) {
console.log(res.data)
}
// 或 Promise 鏈式
uni.request({
url: 'https://api.example.com'
}).then(res => {
console.log(res.data)
}).catch(err => {
console.error(err)
})
⚠️ 注意:
let [error, res] = await uni.request()這種寫法不是 uni-app 的標準用法,那是 axios 的風格。
uni_modules 相容性vue.config.js 配置執行自動化測試前,必須確認以下環境配置:
| 檢查項 | 說明 | 驗證方法 |
|---|---|---|
| 微信開發者工具開啟自動化 | 工具設定 → 安全設定 → 開啟"允許自動化" | 工具介面操作 |
| 不校驗合法域名 | 開發階段必須關閉,否則請求會失敗 | 工具介面操作 |
| 自動化埠已開啟 | 通過 cli auto 命令開啟 |
netstat -ano \| findstr 9421 |
| 編譯輸出目錄存在 | 必須用 dev/mp-weixin 目錄 |
檢查 unpackage/dist/dev/mp-weixin |
⚠️ 常見失敗原因: - 自動化埠未開 → 報錯
WebSocket connection failed- 未開啟自動化許可權 → 報錯automation disabled- 用錯編譯目錄 → 自動化開啟的是舊版本
| 概念 | 說明 |
|---|---|
| 23459 埠 | 微信開發者工具 HTTP 服務埠(IDE 管理介面用),不能用於自動化 |
| 9420/9421 埠 | 自動化專用 WebSocket 埠,需要通過 cli auto 命令開啟 |
| uni-app 原始碼目錄 | 微信開發者工具無法直接開啟 |
| 編譯輸出目錄 | unpackage/dist/ 下有兩個子目錄 |
unpackage/dist/
├── dev/mp-weixin # 開發/執行模式(HBuilderX 執行),自動化測試建議用這個
└── build/mp-weixin # 發行/打包模式(HBuilderX 發行)
# HBuilderX:執行 → 執行到小程式模擬器 → 微信開發者工具
# 或 CLI:
npm run dev:mp-weixin
# Windows PowerShell
& "C:\Program Files (x86)\Tencent\微信web開發者工具\cli.bat" auto --project "編譯輸出目錄" --auto-port 9421
netstat -ano | findstr "9421"
# 看到 LISTENING 即可
$env:PYTHONIOENCODING="utf-8"; python auto_test.py
自動化測試的核心是定位元素,微信小程式使用類似 CSS 的選擇器:
| 選擇器 | 示例 | 說明 | 推薦度 |
|---|---|---|---|
| class | .category-item |
推薦使用,最穩定 | ⭐⭐⭐⭐⭐ |
| id | #my-input |
注意小程式 id 會加字首 id- |
⭐⭐⭐ |
| tag | view |
不推薦,泛用性太強 | ⭐ |
| 層級 | view .btn |
後代選擇器 | ⭐⭐⭐⭐ |
| 屬性 | view[disabled] |
屬性選擇器 | ⭐⭐⭐ |
# 推薦寫法
runner.click(".confirm-btn") # class 選擇器
runner.input("input[name='phone']", "13800138000")
# 不推薦寫法
runner.click("view") # 太泛
runner.click("#the-id") # id 可能有字首
💡 除錯技巧:如果 selector 找不到元素,先用
.screenshot()截一張圖,確認頁面渲染正確,再用開發者工具檢查 WXML 結構。
來源於7w4.net。
import sys
sys.path.insert(0, r"path_to_weapp-automation_skill\scripts")
from weapp_automation import AutomationConfig, WeappTestRunner
config = AutomationConfig(
project_path=r"path_to_your_miniapp\unpackage\dist\dev\mp-weixin",
ws_endpoint="ws://localhost:9421"
)
runner = WeappTestRunner(config)
results = (runner
.navigate("pages/home/home") # 導航到頁面
.wait(2) # 等待2秒
.click(".category-item") # 點選元素(不是 tap!)
.input("input", "test text") # 輸入文本(引數名是 text 不是 value!)
.screenshot("result.png") # 截圖
.get_results()) # 執行並返回結果
| 方法 | 說明 | 注意事項 |
|---|---|---|
.navigate(path) |
導航到頁面 | 路徑不帶 / 字首 |
.click(selector) |
點選元素 | ⚠️ 不是 tap |
.input(selector, text) |
輸入文本 | ⚠️ 引數名是 text 不是 value |
.wait(seconds) |
等待 | |
.screenshot(filename) |
截圖 | |
.get_results() |
執行並返回結果 | 返回結構 {"result": {"success": ..., "message": ...}} |
| 報錯 | 原因 | 解決方案 |
|---|---|---|
'WeappTestRunner' object has no attribute 'tap' |
方法名錯誤 | 改為 click |
WebSocket connection failed |
自動化埠未開 | 檢查 cli auto 命令是否執行成功 |
input failed: unknown error |
selector 找不到元素 | 檢查頁面實際 WXML 結構 |
check if target project window is opened with automation enabled |
專案未以自動化模式開啟 | 重新執行 cli auto 流程 |
import sys
sys.path.insert(0, r"path_to_weapp-automation_skill\scripts")
from weapp_automation import AutomationConfig, WeappTestRunner
# 頁面配置列表
PAGES = [
("01", "pages/home/home", "首頁"),
# 新增更多頁面...
]
def no_interaction(runner):
"""無需互動的頁面"""
return runner
def interact_page(runner):
"""互動測試"""
return runner.input("input", "1 2 3 4").wait(1)
# 執行測試
config = AutomationConfig(
project_path=r"your_project_path\unpackage\dist\dev\mp-weixin",
ws_endpoint="ws://localhost:9421"
)
for num, path, desc, interact_fn in PAGES:
runner = WeappTestRunner(config)
chain = runner.navigate(path).wait(2)
chain = interact_fn(chain)
result = chain.screenshot(f"{num}_{path.replace('/', '_')}.png").get_results()
status = "✅" if result.get("result", {}).get("success") else "❌"
print(f"{status} {desc} 測試完成")
# 測試結果彙總
print("\n========== 測試彙總 ==========")
print(f"總頁面數: {len(PAGES)}")
print(f"成功: {sum(1 for r in results if r['status']=='success')}")
print(f"失敗: {sum(1 for r in results if r['status']=='failed')}")
import json
from datetime import datetime
results = []
start_time = datetime.now()
for num, path, desc, interact_fn in PAGES:
try:
runner = WeappTestRunner(config)
chain = runner.navigate(path).wait(2)
chain = interact_fn(chain)
screenshot = f"sresults/{num}_{path.replace('/', '_')}.png"
result = chain.screenshot(screenshot).get_results()
success = result.get("result", {}).get("success", False)
results.append({
"num": num,
"path": path,
"desc": desc,
"status": "success" if success else "failed",
"message": result.get("result", {}).get("message", ""),
"screenshot": screenshot
})
print(f"{'✅' if success else '❌'} {desc}")
except Exception as e:
results.append({
"num": num,
"path": path,
"desc": desc,
"status": "error",
"message": str(e),
"screenshot": None
})
print(f"❌ {desc} - 異常: {e}")
# 輸出報告
end_time = datetime.now()
duration = (end_time - start_time).total_seconds()
report = {
"summary": {
"total": len(PAGES),
"success": sum(1 for r in results if r["status"] == "success"),
"failed": sum(1 for r in results if r["status"] in ("failed", "error")),
"duration": f"{duration:.1f}s"
},
"results": results
}
with open("test_report.json", "w", encoding="utf-8") as f:
json.dump(report, f, ensure_ascii=False, indent=2)
print(f"\n📊 測試完成,耗時 {duration:.1f}s")
print(f"📄 報告已儲存: test_report.json")
uni-app-project/
├── pages/ # 頁面目錄
├── components/ # 公共元件
├── static/ # 靜態資源
├── utils/ # 工具函式(CommonJS → ES Module)
├── store/ # Pinia store(Vue3)
│ ├── index.js # Pinia 例項
│ └── modules/ # Store 模組
├── common/js/ # 公共 JS
├── uni_modules/ # 第三方模組(Vue2 語法保持不變)
├── App.vue # 應用例項
├── main.js # 應用入口
├── pages.json # 頁面路由配置
├── manifest.json # 應用配置
└── vue.config.js # Vue 配置
收到遷移類文件後,應該: 1. 提取【易錯點】和【注意事項】 2. 轉化為【檢查清單】 3. 執行任務時同步檢查,而不是"讀完再檢查"
.min.js 和 .js 檔案要區分清楚出現問題時,優先: 1. 檢視相關文件/日誌 2. 檢查檔案引用關係 3. 最後才憑經驗猜測
本技能依賴或參考以下資源:
| 資源 | 連結 | 說明 |
|---|---|---|
| uni-app 官方文件 | https://uniapp.dcloud.io/ | 核心參考文件 |
| Vue3 官方文件 | https://vuejs.org/ | Composition API 語法參考 |
| 微信開發者工具 | https://developers.weixin.qq.com/miniprogram/dev/devtools/cli.html | CLI 自動化介面 |
⭐ 技能已內嵌:以下技能已複製到
skills/子目錄,釋出時無需額外安裝。
| 技能 | 路徑 | 說明 |
|---|---|---|
| Vue | skills/vue/ |
Vue 通用開發知識,可補充 Vue 基礎 |
| Vue Expert | skills/vue-expert/ |
Vue3 進階用法,TypeScript 整合 |
| weapp-automated-testing | skills/weapp-automated-testing/ |
微信小程式測試,基礎 API |
| 資源 | 型別 | 推薦理由 |
|---|---|---|
| Vue3 Composition API 入門 | 文章 | 快速上手 Composition API |
| Pinia 官方文件 | 文件 | 狀態管理最佳實踐 |
| uni-app 遷移指南 | 官方文件 | 官方遷移方案參考 |
版本:v1.0.0(2026-04-06)
更新內容:
- 增加選項式 API 相容性說明
- 增加複雜 JS 轉寫警告
- 增加超長字串處理警告
- 增加 App.vue 升級要點
- 增加 Mixins 處理建議(對比 Composables)
- 增加自動化測試環境準備檢查清單
- 增加 WXML 選擇器語法說明
- 增加測試結果彙總指令碼(進階版)
- 增加 Vue3 開發指南(Composition API 最佳實踐)
- 增加 References(官方文件 + 內嵌技能 + 學習資源)
- 內嵌相關技能:Vue、Vue Expert、weapp-automated-testing 已複製到 skills/ 子目錄
標籤:uni-app, Vue3, 遷移, 微信小程式, 自動化測試
這個技能包質量較好,內容全面且實用。它覆蓋了 Vue 開發和小程式測試兩個主要方向,提供了詳細的遷移指南和程式碼示例,對開發者很有幫助。優點是文件結構清晰、表格對比豐富、指令碼實現完整;不足是內容偏多偏長,部分文件有重複,視覺化示例較少。總體適合需要系統學習或遷移專案的開發者使用。