Merge branch 'dev' into happi/dev/database-update
This commit is contained in:
@@ -1400,7 +1400,7 @@ flowchart LR
|
||||
<li>无法精确控制重建范围</li>
|
||||
</ul>
|
||||
|
||||
<p><strong>实际案例(UUTalk):</strong></p>
|
||||
<p><strong>实际案例:</strong></p>
|
||||
|
||||
<pre><code class="language-dart">// 整个 Container 都会重建
|
||||
Obx(() => Container(
|
||||
@@ -1636,7 +1636,7 @@ Get.put<BaseController>(ChatController());
|
||||
final controller = Get.find<UserController>(); // 找到 ChatController,类型不匹配!
|
||||
</code></pre>
|
||||
|
||||
<p><strong>运行时错误案例(UUTalk 实际问题):</strong></p>
|
||||
<p><strong>运行时错误案例:</strong></p>
|
||||
<ul>
|
||||
<li>Controller 未初始化</li>
|
||||
<li>类型转换错误</li>
|
||||
@@ -1683,16 +1683,16 @@ ChatViewModel
|
||||
<li>重构安全:IDE 自动提示依赖变化</li>
|
||||
<li>团队协作:依赖关系明确,不会互相影响</li>
|
||||
</ul>
|
||||
<h4>GetX + Obx vs Riverpod:来自 UUTalk 项目的实践教训</h4>
|
||||
<h4>GetX + Obx vs Riverpod:实践教训</h4>
|
||||
|
||||
<div style="background-color: #fff3cd; border-left: 4px solid #ffc107; padding: 15px; margin: 20px 0;">
|
||||
<p><strong>真实案例警示</strong></p>
|
||||
<p>以下内容基于 <strong>UUTalk 项目</strong>的实际经验,展示了 GetX + Obx 在大型项目中暴露的严重问题。</p>
|
||||
<p><strong>典型问题示例</strong></p>
|
||||
<p>以下内容展示了 GetX + Obx 在大型项目中暴露的典型问题,作为选型参考。</p>
|
||||
</div>
|
||||
|
||||
<h5>1. 状态管理混乱</h5>
|
||||
|
||||
<p><strong>UUTalk 项目的 GetX + Obx 问题代码(chat_list_controller.dart):</strong></p>
|
||||
<p><strong>GetX + Obx 问题代码示例(chat_list_controller.dart):</strong></p>
|
||||
|
||||
<pre><code class="language-dart">class ChatListController extends GetxController {
|
||||
var lastClickedSpecialChatId = (-1).obs;
|
||||
@@ -1728,7 +1728,7 @@ ChatViewModel
|
||||
|
||||
<h5>2. 过度嵌套和性能问题</h5>
|
||||
|
||||
<p><strong>UUTalk 项目的 Obx 嵌套地狱(home_view.dart):</strong></p>
|
||||
<p><strong>Obx 嵌套地狱示例(home_view.dart):</strong></p>
|
||||
|
||||
<pre><code class="language-dart">body: Obx(() => AnimatedPadding(
|
||||
child: GetBuilder(
|
||||
@@ -1765,7 +1765,7 @@ ChatViewModel
|
||||
|
||||
<h5>3. Controller 过于臃肿</h5>
|
||||
|
||||
<p><strong>UUTalk 项目的巨型 Controller:</strong></p>
|
||||
<p><strong>巨型 Controller 反模式示例:</strong></p>
|
||||
|
||||
<pre><code class="language-dart">// chat_list_controller.dart
|
||||
import 'dart:async';
|
||||
@@ -1811,7 +1811,7 @@ class ChatListController extends GetxController
|
||||
|
||||
<h5>4. 没有编译时安全</h5>
|
||||
|
||||
<p><strong>UUTalk 项目的运行时陷阱:</strong></p>
|
||||
<p><strong>GetX 运行时陷阱:</strong></p>
|
||||
|
||||
<pre><code class="language-dart">// 通过字符串查找 Controller,运行时才知道对错
|
||||
Get.find<ChatListController>();
|
||||
@@ -1839,7 +1839,7 @@ class ChatListController extends GetxController {
|
||||
|
||||
<h5>5. 难以测试</h5>
|
||||
|
||||
<p><strong>UUTalk 项目的测试困境:</strong></p>
|
||||
<p><strong>GetX 测试困境:</strong></p>
|
||||
|
||||
<pre><code class="language-dart">// 测试时必须初始化整个 GetX 框架
|
||||
testWidgets('test chat list', (tester) async {
|
||||
@@ -1865,7 +1865,7 @@ testWidgets('test chat list', (tester) async {
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<h5>GetX + Obx vs Riverpod 实际对比(基于 UUTalk 项目经验)</h5>
|
||||
<h5>GetX + Obx vs Riverpod 实际对比</h5>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
@@ -1933,7 +1933,7 @@ testWidgets('test chat list', (tester) async {
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin: 20px 0;">
|
||||
|
||||
<div style="background-color: #f8d7da; border: 2px solid #dc3545; padding: 15px; border-radius: 8px;">
|
||||
<p><strong>GetX + Obx(UUTalk 现状)</strong></p>
|
||||
<p><strong>GetX + Obx</strong></p>
|
||||
|
||||
<p><strong>状态混乱:</strong></p>
|
||||
<pre><code class="language-dart">class ChatListController extends GetxController {
|
||||
@@ -1999,10 +1999,10 @@ class ChatListViewModel extends StateNotifier<ChatListState> {
|
||||
|
||||
</div>
|
||||
|
||||
<h5>UUTalk 项目的痛点总结</h5>
|
||||
<h5>GetX + Obx 痛点总结</h5>
|
||||
|
||||
<div style="background-color: #fff3cd; border-left: 4px solid #ffc107; padding: 15px; margin: 20px 0;">
|
||||
<p>从 UUTalk 项目的实际经验中,我们总结出 GetX + Obx 的核心问题:</p>
|
||||
<p>从实践经验中,我们总结出 GetX + Obx 的核心问题:</p>
|
||||
<ol>
|
||||
<li><strong>"快速开发"变成"技术债"</strong>:初期确实快,但 6 个月后代码无法维护</li>
|
||||
<li><strong>"响应式"变成"性能杀手"</strong>:Obx 嵌套导致过度重建,页面卡顿</li>
|
||||
@@ -2014,7 +2014,7 @@ class ChatListViewModel extends StateNotifier<ChatListState> {
|
||||
|
||||
<div style="background-color: #d4edda; border-left: 4px solid #28a745; padding: 15px; margin: 20px 0;">
|
||||
<p><strong>Riverpod 的技术优势</strong></p>
|
||||
<p>基于 UUTalk 项目的实践教训,我们选择 Riverpod 作为新架构的状态管理方案,因为它从根本上解决了 GetX 的所有问题:</p>
|
||||
<p>基于以上实践教训,我们选择 Riverpod 作为新架构的状态管理方案,因为它从根本上解决了 GetX 的所有问题:</p>
|
||||
<ul>
|
||||
<li><strong>编译时安全</strong>:不会再有运行时崩溃</li>
|
||||
<li><strong>结构化状态</strong>:@freezed 强制统一状态结构</li>
|
||||
|
||||
@@ -47,6 +47,7 @@ if [ "$FORMAT" = "aab" ]; then
|
||||
cd "$APP_DIR"
|
||||
flutter build appbundle \
|
||||
--release \
|
||||
--no-pub \
|
||||
--dart-define-from-file=config/config.json \
|
||||
--split-debug-info="$DEBUG_INFO_DIR" \
|
||||
--obfuscate
|
||||
@@ -57,6 +58,7 @@ else
|
||||
cd "$APP_DIR"
|
||||
flutter build apk \
|
||||
--release \
|
||||
--no-pub \
|
||||
--dart-define-from-file=config/config.json \
|
||||
--split-debug-info="$DEBUG_INFO_DIR" \
|
||||
--obfuscate
|
||||
|
||||
@@ -41,6 +41,7 @@ cd "$APP_DIR"
|
||||
|
||||
flutter build ipa \
|
||||
--release \
|
||||
--no-pub \
|
||||
--dart-define-from-file=config/config.json \
|
||||
--split-debug-info="$DEBUG_INFO_DIR" \
|
||||
--obfuscate
|
||||
|
||||
@@ -37,6 +37,7 @@ cd "$APP_DIR"
|
||||
|
||||
flutter build macos \
|
||||
--release \
|
||||
--no-pub \
|
||||
--dart-define-from-file=config/config.json \
|
||||
--split-debug-info="$DEBUG_INFO_DIR" \
|
||||
--obfuscate
|
||||
|
||||
@@ -39,6 +39,7 @@ cd "$APP_DIR"
|
||||
|
||||
flutter build windows \
|
||||
--release \
|
||||
--no-pub \
|
||||
--dart-define-from-file=config/config.json \
|
||||
--split-debug-info="$DEBUG_INFO_DIR" \
|
||||
--obfuscate
|
||||
|
||||
60
scripts/pre-commit
Normal file
60
scripts/pre-commit
Normal file
@@ -0,0 +1,60 @@
|
||||
#!/bin/bash
|
||||
# Git pre-commit hook:提交前自动 format,analyze 有 error 则拦截提交
|
||||
#
|
||||
# 安装:bash scripts/setup.sh(setup.sh 自动把本文件复制到 .git/hooks/pre-commit)
|
||||
# 手动安装:cp scripts/pre-commit .git/hooks/pre-commit && chmod +x .git/hooks/pre-commit
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(git rev-parse --show-toplevel)"
|
||||
DART="$(command -v dart 2>/dev/null || echo "")"
|
||||
|
||||
if [[ -z "$DART" ]]; then
|
||||
echo "[pre-commit] dart not found, skipping checks."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# ── pubspec.lock 保护 ────────────────────────────────────────────────────────
|
||||
# flutter run / flutter build 会在未经授权的情况下触发 pub get 并修改 lock。
|
||||
# 规则:lock 有未暂存的改动(意外修改)→ 自动还原;
|
||||
# 开发者主动 git add pubspec.lock 后(暂存)→ 正常放行。
|
||||
LOCK_UNSTAGED=$(git diff --name-only pubspec.lock 2>/dev/null || true)
|
||||
LOCK_STAGED=$(git diff --cached --name-only pubspec.lock 2>/dev/null || true)
|
||||
|
||||
if [[ -n "$LOCK_UNSTAGED" && -z "$LOCK_STAGED" ]]; then
|
||||
git checkout HEAD -- pubspec.lock
|
||||
echo "[pre-commit] pubspec.lock was auto-modified by flutter, restored to HEAD."
|
||||
echo " To intentionally update: run 'dart pub get' or 'dart pub upgrade',"
|
||||
echo " then 'git add pubspec.lock' before committing."
|
||||
fi
|
||||
|
||||
# ── dart format + analyze ────────────────────────────────────────────────────
|
||||
STAGED_DART=$(git diff --cached --name-only --diff-filter=ACM | grep '\.dart$' || true)
|
||||
|
||||
if [[ -z "$STAGED_DART" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "[pre-commit] formatting staged dart files..."
|
||||
echo "$STAGED_DART" | xargs dart format --line-length=80
|
||||
echo "$STAGED_DART" | xargs git add
|
||||
|
||||
echo "[pre-commit] running dart analyze..."
|
||||
cd "$ROOT_DIR"
|
||||
|
||||
ANALYZE_OUTPUT=$(dart analyze 2>&1)
|
||||
|
||||
if echo "$ANALYZE_OUTPUT" | grep -q "^ error"; then
|
||||
echo "[pre-commit] dart analyze found errors, commit blocked."
|
||||
echo "$ANALYZE_OUTPUT" | grep "^ error"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if echo "$ANALYZE_OUTPUT" | grep -q "^ warning"; then
|
||||
echo "[pre-commit] dart analyze found warnings, commit blocked."
|
||||
echo "$ANALYZE_OUTPUT" | grep "^ warning"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "[pre-commit] all checks passed."
|
||||
exit 0
|
||||
@@ -28,13 +28,13 @@ echo "=== customer-im-client 环境初始化 ==="
|
||||
echo ""
|
||||
|
||||
# ---- Step 1: 安装全局工具 ----
|
||||
echo "[1/6] 安装全局工具 (melos + mason_cli)..."
|
||||
echo "[1/7] 安装全局工具 (melos + mason_cli)..."
|
||||
dart pub global activate melos
|
||||
dart pub global activate mason_cli
|
||||
echo ""
|
||||
|
||||
# ---- Step 2: 配置 PATH ----
|
||||
echo "[2/6] 配置 PATH..."
|
||||
echo "[2/7] 配置 PATH..."
|
||||
if [[ "$SHELL" == */zsh ]]; then
|
||||
SHELL_CONFIG="$HOME/.zshrc"
|
||||
elif [[ "$SHELL" == */bash ]]; then
|
||||
@@ -78,12 +78,19 @@ if [ -f "$ROOT_DIR/mason-lock.json" ]; then
|
||||
fi
|
||||
|
||||
# ---- Step 5: 注册 mason bricks ----
|
||||
echo "[5/6] mason get (注册本地 bricks)..."
|
||||
echo "[5/7] mason get (注册本地 bricks)..."
|
||||
cd "$ROOT_DIR" && "$PUB_CACHE_BIN/mason" get
|
||||
echo ""
|
||||
|
||||
echo "[6/7] mason add clean_plugin_sdk bricks..."
|
||||
if ! grep -qE '^\s*clean_plugin_sdk:' "$ROOT_DIR/mason.yaml" 2>/dev/null; then
|
||||
cd "$ROOT_DIR" && "$PUB_CACHE_BIN/mason" add clean_plugin_sdk --path ./mason/bricks/clean_plugin_sdk
|
||||
else
|
||||
echo " clean_plugin_sdk 已在 mason.yaml 中,跳過"
|
||||
fi
|
||||
|
||||
# ---- Step 6: 首次代码生成 ----
|
||||
echo "[6/6] melos run gen (首次代码生成)..."
|
||||
echo "[7/7] melos run gen (首次代码生成)..."
|
||||
cd "$ROOT_DIR" && "$PUB_CACHE_BIN/melos" run gen
|
||||
echo ""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user