前言
如何優雅的將專案中的代碼,亦或是你的demo代碼展示到界面上?本文對使用簡單、便于維護且通用的解決方案,進行相關的對比和探究
為了節省大家的時間,把最終解決方案的相關接入和用法寫在前面
預覽代碼
快速開始
- 接入:pub,github
dependencies:
code_preview: ^0.1.5
- 用法:CodePreview,提供需要預覽的className,可自動匹配該類對應的代碼檔案
- 本來想把寫法簡化成傳入物件,但是因為一些原因無奈放棄,改成了
className
- 具體可以參考下面
Flutter Web中的問題
模塊的說明
- 本來想把寫法簡化成傳入物件,但是因為一些原因無奈放棄,改成了
import 'package:code_preview/code_preview.dart';
import 'package:flutter/material.dart';
class Test extends StatelessWidget {
const Test({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const CodePreview(className: 'Test');
}
}
- 使用效果:flutter_smart_dialog
配置代碼檔案
因為原理是遍歷資源檔案,所以必須將需要展示的代碼檔案或者其檔案夾路徑,定義在assets下,這步操作,為大家提供了一個自動化的插件解決
強烈建議需要展示到界面的代碼,都放在統一的檔案夾里管理
- 展示界面的代碼需要在pugspec.yaml中的assets定義
如果代碼預覽的檔案夾,分級復雜,每次都需要定義路徑實在麻煩
提供一個插件:Flutter Code Helper
- 安裝:Plugins中搜索
Flutter Code Helper
- pugspec.yaml中定義下需要自動生成檔案夾的路徑,檔案夾隨便套娃,會自動幫你遞回在assets下生成
- 不需要自動生成,可:不寫該配置,或者配置空陣列(auto_folder: [])
code_helper:
auto_folder: [ "assets/", "lib/widgets/" ]
說明下:上面的插件是基于RayC的FlutterAssetsGenerator插件專案改的
- 看了下RayC的插件代碼和相關功能,和我預想的上面功能實作有一定出入,改動起來變動較大
- 想試下插件專案的各種新配置,直接拉到最新
- 后期如果想到需要什么功能,方便隨時添加
所以沒向其插件里面提pr,就單獨新開了個插件專案
高級使用
主題
提供倆種代碼樣式主題
- 日間模式
CodePreview.config = CodePreviewConfig(codeTheme: CodeTheme.light);
- 夜間模式
CodePreview.config = CodePreviewConfig(codeTheme: CodeTheme.dark);
注釋決議
- 你可以使用如下的格式,在類上添加注釋
- key的前面必須加
@
,舉例(@title,@xxx) - key與value的之間,必須使用
分號
分割,舉例(@xxx: xxx) - value如果需要換行,換行的文案前必須加
中劃線
- key的前面必須加
/// @title:
/// - test title one
/// - test title two
/// @content: test content
/// @description: test description
class OneWidget extends StatelessWidget {
const OneWidget({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const Placeholder();
}
}
- 然后可以從
customBuilder
的回呼獲取param引數,param中擁有parseParam引數- 獲取取得上面注釋的資料:param.parseParam['title']或者param.parseParam['***']
- 獲取的value的型別是List
,可兼容多行value的型別
customBuilder
的用法codeWidget
內置的代碼預覽布局,如果你想定義自己預覽代碼的布局,那就可以不使用codeWidget
- 一般來說,可以根據注釋獲取的資料,結合
codeWidget
嵌套來自定義符合要求的布局 param
中含有多個有用內容,可自行查看
import 'package:code_preview/code_preview.dart';
import 'package:flutter/material.dart';
class Test extends StatelessWidget {
const Test({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return CodePreview(
className: 'OneWidget',
customBuilder: (Widget codeWidget, CustomParam? param) {
debugPrint(param?.parseParam['title'].toString());
debugPrint(param?.parseParam['content'].toString());
debugPrint(param?.parseParam['description'].toString());
return codeWidget;
},
);
}
}
- 目前內部預覽的布局,會自動去掉類上的注釋,如果想保留注釋,可自行匹配下
CodePreview.config = CodePreviewConfig(removeParseComment: false);
幾種代碼預覽方案
FlutterUnit方案
- https://github.com/toly1994328/FlutterUnit
FlutterUnit專案也是自帶代碼預覽方案,這套方案是比較特殊方案
- 大概看了下,整個FlutterUnit的資料都是基于
flutter.db
,該檔案里面就有相關demo的文本資訊 - 所有的demo也是單獨存在一個叫
widgets
的專案中 - 所以大概可以猜測出
- 應該會有個db的輔助工具,會去掃描
widgets
的專案中的demo代碼 - 將他們的文本資訊都掃描出來,然后決議上面的注釋等相關資訊,分類存盤到資料庫中,最后生成db檔案
- 應該會有個db的輔助工具,會去掃描
- 映射表,宿主可以通過db中的組件類名,從這里拿到demo效果實體
總結
整套流程看下來,實作起來的作業量還是有點大的
- db輔助工具的撰寫
- 文本注釋相關決議規則
- 如何便捷的維護db檔案(輔助工具是否支持,生成后自動覆寫宿主db檔案)
- 不同平臺db檔案的讀取和相關適配
優點
- 因為掃描工具不依賴Flutter相關庫,預覽方案可以快速的移植到其它編程語言(compose,SwiftUI等)
- 具備高度自定義,因為是完全獨立的第三方掃描工具,可以隨性所欲的定制化
缺點
- 最明顯的缺點,應該就是稍微改下demo代碼,就需要三方工具重新生成db檔案(如果三方工具實作的是cli工具,可以將掃描生成命令和push等命令集成一起,應該可以比較好的避免該問題)
build_runner方案
- https://pub.dev/packages/build_runner
build_runner是個強大代碼自動生成工具,根據ast語法樹+自定義注解資訊,可以生成很多強大的附屬代碼資訊,例如 json_serializable
等庫
所以,也能利用這點自定義類注解,獲取到對應的整個類的代碼資訊,在對應附屬的xx.g.dart
檔案中,將獲取的代碼內容轉換成字串,然后直接將xx.g.dart
檔案的代碼字串資訊,展示到界面就行了
優點
- 可以通過生成命令,全自動的生成代碼,甚至將整個預覽demo的映射表都可以自動配置完成
- 可以規范的通過注解配置多個引數
缺點
- 因為
build_runner
需要決議整個ast語法樹,一旦專案很大之后,決議生成的時間會非常非常的長! - 因為現在很多的這類別庫都是依賴
build_runner
,所以跑自動生成命令,會導致巨多xx.g.dart
檔案被改動,極大的增加cr作業量
資源檔案方案
這應該最常用的一種方案
- 在
pubspec.yaml
中的assets
中定義下我們代碼檔案路徑
flutter:
assets:
- lib/widgets/show/
- 然后用loadString獲取檔案內容
final code = await rootBundle.loadString('lib/widgets/show/custome_dialog_animation.dart');
優點
- 侵入性非常低,不會像
build_runnner
方案那樣影響到其它模塊 - 便于維護,如果demo預覽代碼被改變了,打包的時候,資源檔案也會生成對應改變后的代碼檔案
缺點
- 使用麻煩,使用的時候需要傳入具體的檔案路徑,才能找到想要的代碼資源檔案
- 需要反復的在
pubspec.yaml
中的assets
里面定義檔案路徑
資源檔案方案優化
上面的三種方案各有優缺點,明確當前的訴求
-
目前是想寫個簡單的,通用的,僅在Flutter中實作代碼預覽方案
-
要求使用簡單,高效
-
維護簡單,多人開發的時候不會有很大成本
FlutterUnit方案:實作起來成本較大,且多人開發對單個db檔案的維護很可能會有點問題,例如:更新代碼的時候,db檔案忘記更新
build_runner方案:生成時間是個問題,還有很對其他型別xx.g.dart
檔案產生影響也比較麻煩
資源檔案方案:整體是符合預期的,但是使用時候,需要傳入路徑和pubspec.yaml
中反復定義檔案路徑,這是倆個很大痛點
結合實作成本和訴求,選擇資源檔案方案
,下面對其痛點進行優化
使用優化
Flutter的編譯產物中,有個相當有用的檔案:AssetManifest.json
AssetManifest.json檔案里面,有所有的資源檔案的路徑,然后就簡單了,我們只需要讀取該檔案內容
final manifestContent = await rootBundle.loadString('AssetManifest.json');
獲取到所有的路徑之后,再結合傳入的類名,讀取所有路徑的檔案內容,然后和傳入的類名做正則匹配就行了
稍微優化
- 將傳入的類名,轉換為下劃線名稱和所有路徑名稱做匹配,如果能匹配上,再進行內容匹配,匹配成功后就回傳該檔案的代碼內容
- 如果上述匹配失敗,就進行兜底的全量匹配
優化前
import 'package:code_preview/code_preview.dart';
import 'package:flutter/material.dart';
class Test extends StatelessWidget {
const Test({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const CodePreview(path: 'lib/widgets/show/custome_dialog_animation.dart');
}
}
優化后
import 'package:code_preview/code_preview.dart';
import 'package:flutter/material.dart';
class Test extends StatelessWidget {
const Test({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const CodePreview(className: 'CustomDialogAnimation');
}
}
- 一般來說,我是統一配置預覽demo和className,這樣比較好對照
路徑定義優化
本來是想在pubspec.yaml
的assets
里面直接寫通配符定義全路徑,然后悲劇了,它不支持這種寫法
flutter:
assets:
- lib/widgets/**/*.dart
GG,只能想其他辦法了,想了很多方法都不行,只能從外部入手,用idea插件的形式,實作自動化掃描生成路徑
- 安裝:Plugins中搜索
Flutter Code Helper
- pugspec.yaml中定義下需要自動生成檔案夾的目錄,檔案夾隨便套娃,會自動幫你遞回在assets下生成
- 不需要自動生成,可:不寫該配置,或者配置空陣列(auto_folder: [])
code_helper:
auto_folder: [ "assets/", "lib/widgets/" ]
Flutter Web中的問題
魔幻的runtimeType
flutter web的release模式中
- dart2js 會壓縮 JS,這樣會使得型別名被改變
- 例如:dart中的
TestWidgetFunction
類的runtimeType,可能會變成minified:Ah
,而不是TestWidgetFunction
!
為啥需要壓縮呢?壓縮名稱可以使得編譯器將 JavaScript體積縮小 3 倍+;精確等效語意和性能/代碼大小之間的權衡,Dart明顯是選擇了后者
這種情況只會在Flutter Web的release模式下發生,其他平臺和Flutter web的Debug | Profile模式都不會有這種問題;所以說Xxx.runtimeType.toString
,并不一定會得到預期內的資料,,,
具體討論可參考
- https://github.com/dart-lang/sdk/issues/35052
- https://github.com/flutter/flutter/issues/78041
解決思路
- 將壓縮型別
minified:Ah
恢復成Test
- 將獲取的
Test
字串使用相同演算法壓縮成minified:Ah
如有知道如何實作的,務必告訴鄙人
下面從壓縮級別調整的角度,探究是否可解決該問題
dart2js壓縮說明
注:flutter build web默認的是O4優化級別
- O0: 禁用許多優化,
- O1: 啟用默認優化(僅是dart2js該命令的默認級別)
- O2: 在O1優化基礎上,尊重語言語意且對所有程式安全的其他優化(例如縮小)
- 備注:使用-O2,使用開發JavaScript編譯器編譯時,型別的字串表示不再與Dart VM中的字串表示相同
- O3: 在O2優化基礎上,并省略隱式型別檢查,
- 注意:省略型別檢查可能會導致應用程式因型別錯誤而崩潰
- O4: 在O3優化基礎上,啟用更積極的優化
- 注意:O4優化容易受到輸入資料變化的影響,在依賴O4之前,需測驗用戶輸入中的邊緣情況
下面是flutter新建專案,未做任何改動,不同壓縮級別的js產物體積
# main.dart.js: 7.379MB
flutter build web --dart2js-optimization O0
# main.dart.js: 5.073MB
flutter build web --dart2js-optimization O1
# main.dart.js: 1.776MB
flutter build web --dart2js-optimization O2
# main.dart.js: 1.716MB
flutter build web --dart2js-optimization O3
# main.dart.js: 1.687MB
flutter build web --dart2js-optimization O4
總結
- 預期用法
- 為什么想使用物件?因為當物件名稱改變時,對應使用的地方,可以便捷觀察到需要改變
- 可以使用傳入的物件實體,在內部使用runtimeType獲取型別名,再進行相關匹配
CodePreview(code: Test());
但是
綜上可知,使用flutter build web --dart2js-optimization O1
編譯的flutter web release產物,能夠使得runtimeType的語意和Dart VM中字串保持一致
但是該壓縮級別下的,js體積過于夸張,務必會對加載速度產生極大影響,可想而知,在復雜專案中的體積增漲肯定更加離譜
對于想要用法更加簡單,使用低級別壓縮命令打包的想法需要舍棄
- 用法不得已做妥協
CodePreview(className: "Test");
這是個讓我非常糾結的思路歷程
最后
到這里也結束了,自我感覺,對大家應該能有一些幫助
一般來說,大部分團隊,都會有個自己的內部組件庫,因為Flutter強大的跨平臺特性,所以就能很輕松的發布到web平臺,可以方便的體驗各種組件的效果,結合文章中的代碼預覽方案,就可以更加快速的上手各種組件用法了~
好了,下次再見了,靚仔們!
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/551683.html
標籤:其他
上一篇:當我第一次通過Kotlin和Compose來實作一個Canvas時, 我識訓了什么?
下一篇:返回列表