温故知新,整理一下知识点,顺便理清思路,找到发力点
Category Archives: 前端
Flutter Web初探
Flutter Web 框架
Flutter build && run
1. flutter build web
2. flutter build apk
3. flutter build ios
4. flutter run -d chrome
flutter build web
生成物示例:
│ .last_build_id
│ favicon.png
│ flutter_service_worker.js
│ index.html
│ main.dart.js
│ manifest.json
│ version.json
│
├─assets
│ │ AssetManifest.json
│ │ FontManifest.json
│ │ NOTICES
│ │
│ ├─fonts
│ │ MaterialIcons-Regular.otf
│ │
│ └─packages
│ └─cupertino_icons
│ └─assets
│ CupertinoIcons.ttf
│
└─icons
Icon-192.png
Icon-512.png
Icon-maskable-192.png
Icon-maskable-512.png
main.dart.js
代码片段示例:
(function dartProgram(){function copyProperties(a,b){var s=Object.keys(a)
for(var r=0;r<s.length;r++){var q=s[r]
b[q]=a[q]}}function mixinProperties(a,b){var s=Object.keys(a)
for(var r=0;r<s.length;r++){var q=s[r]
if(!b.hasOwnProperty(q))b[q]=a[q]}}var z=function(){var s=function(){}
s.prototype={p:{}}
var r=new s()
if(!(r.__proto__&&r.__proto__.p===s.prototype.p))return false
try{if(typeof navigator!="undefined"&&typeof navigator.userAgent=="string"&&navigator.userAgent.indexOf("Chrome/")>=0)return true
if(typeof version=="function"&&version.length==0){var q=version()
if(/^\d+\.\d+\.\d+\.\d+$/.test(q))return true}}catch(p){}return false}()
function setFunctionNamesIfNecessary(a){function t(){};if(typeof t.name=="string")return
for(var s=0;s<a.length;s++){var r=a[s]
var q=Object.keys(r)
for(var p=0;p<q.length;p++){var o=q[p]
var n=r[o]
if(typeof n=="function")n.name=o}}}function inherit(a,b){a.prototype.constructor=a
a.prototype["$i"+a.name]=a
if(b!=null){if(z){a.prototype.__proto__=b.prototype
return}var s=Object.create(b.prototype)
copyProperties(a.prototype,s)
a.prototype=s}}function inheritMany(a,b){for(var s=0;s<b.length;s++)inherit(b[s],a)}function mixin(a,b){mixinProperties(b.prototype,a.prototype)
a.prototype.constructor=a}function lazyOld(a,b,c,d){var s=a
a[b]=s
a[c]=function(){a[c]=function(){H.xF(b)}
图标资源:
Web Renderer
HTML renderer
(default for app)CanvasKit renderer
(default for desktop)
命令行参数
--web-renderer
命令行参数三选一 auto
, html
, or canvaskit
.
auto
(default)html
canvaskit
这个参数可以和 run
或者 build
子命令组合起来. 例如:
flutter run -d chrome --web-renderer html
flutter build web --web-renderer canvaskit
如果没有识别的设备连接,这个参数会被忽略
代码压缩
1. -O(0,1,2,3,4)
dart2js的一个参数,控制代码压缩力度(dartdevc)
-O1产物示例:
// Generated by dart2js (NullSafetyMode.sound, trust primitives, omit checks, lax runtime type, no-legacy-javascript), the Dart to JavaScript compiler version: 2.14.2.
// The code supports the following hooks:
// dartPrint(message):
// if this function is defined it is called instead of the Dart [print]
// method.
//
// dartMainRunner(main, args):
// if this function is defined, the Dart [main] method will not be invoked
// directly. Instead, a closure that will invoke [main], and its arguments
// [args] is passed to [dartMainRunner].
//
// dartDeferredLibraryLoader(uri, successCallback, errorCallback, loadId):
// if this function is defined, it will be called when a deferred library
// is loaded. It should load and eval the javascript of `uri`, and call
// successCallback. If it fails to do so, it should call errorCallback with
// an error. The loadId argument is the deferred import that resulted in
// this uri being loaded.
//
// dartCallInstrumentation(id, qualifiedName):
// if this function is defined, it will be called at each entry of a
// method or constructor. Used only when compiling programs with
// --experiment-call-instrumentation.
(function dartProgram() {
function copyProperties(from, to) {
var keys = Object.keys(from);
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
to[key] = from[key];
}
}
function mixinProperties(from, to) {
var keys = Object.keys(from);
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
if (!to.hasOwnProperty(key))
to[key] = from[key];
}
}
2. Tree shaking
Tree Shaking是一种死代码消除(Dead Code Elimination)技术,这一想法起源于20世纪90年代的LISP。其思想是:一个程序所有可能的执行流程都可以用函数调用的树来表示,这样就可以消除那些从未被调用的函数。
示例:
class MyHomePage extends StatefulWidget {
_unusedMethod() { // 没有调用过的函数
print("hello");
}
const MyHomePage({Key? key, required this.title}) : super(key: key);
// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.
// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
- debug
- profile
- release
Dart2js
lib/src/build_system/web.dart
片段
final ProcessResult javaScriptResult =
await environment.processManager.run(<String>[
...sharedCommandOptions,
if (dart2jsOptimization != null) '-$dart2jsOptimization' else '-O4',
if (buildMode == BuildMode.profile) '--no-minify',
if (csp) '--csp',
'-o',
outputJSFile.path,
environment.buildDir.childFile('app.dill').path, // dartfile
]);
中间编译产物(路径在project/.dart_tool/flutter_build/****/
.filecache
app.dill
app.dill.deps
dart2js.d
dart2js.stamp
flutter_assets.d
gen_localizations.stamp
main.dart
main.dart.js
main.dart.js.deps
outputs.json
service_worker.d
web_entrypoint.stamp
web_release_bundle.stamp
web_resources.d
web_service_worker.stamp
app.dill.deps
和main.dart.js.deps
分别是app.dill
和main.dart.js
的依赖项
app.dill.deps
片段
file:///C:/Users/chao.bai/AndroidStudioProjects/flutter-demos/for-web/build-demo/build_demo/.dart_tool/flutter_build/27a4a6ceccb8fdc624e4eaa983588bba/main.dart
file:///C:/Users/chao.bai/AndroidStudioProjects/flutter-demos/for-web/build-demo/build_demo/.dart_tool/package_config.json
file:///C:/Users/chao.bai/AndroidStudioProjects/flutter-demos/for-web/build-demo/build_demo/lib/main.dart
file:///C:/src/flutter/.pub-cache/hosted/pub.flutter-io.cn/characters-1.1.0/lib/characters.dart
file:///C:/src/flutter/.pub-cache/hosted/pub.flutter-io.cn/characters-1.1.0/lib/src/characters.dart
file:///C:/src/flutter/.pub-cache/hosted/pub.flutter-io.cn/characters-1.1.0/lib/src/characters_impl.dart
file:///C:/src/flutter/.pub-cache/hosted/pub.flutter-io.cn/characters-1.1.0/lib/src/extensions.dart
file:///C:/src/flutter/.pub-cache/hosted/pub.flutter-io.cn/characters-1.1.0/lib/src/grapheme_clusters/breaks.dart
file:///C:/src/flutter/.pub-cache/hosted/pub.flutter-io.cn/characters-1.1.0/lib/src/grapheme_clusters/constants.dart
file:///C:/src/flutter/.pub-cache/hosted/pub.flutter-io.cn/characters-1.1.0/lib/src/grapheme_clusters/table.dart
file:///C:/src/flutter/.pub-cache/hosted/pub.flutter-io.cn/collection-1.15.0/lib/collection.dart
file:///C:/src/flutter/.pub-cache/hosted/pub.flutter-io.cn/collection-1.15.0/lib/src/algorithms.dart
file:///C:/src/flutter/.pub-cache/hosted/pub.flutter-io.cn/collection-1.15.0/lib/src/canonicalized_map.dart
file:///C:/src/flutter/.pub-cache/hosted/pub.flutter-io.cn/collection-1.15.0/lib/src/combined_wrappers/combined_iterable.dart
file:///C:/src/flutter/.pub-cache/hosted/pub.flutter-io.cn/collection-1.15.0/lib/src/combined_wrappers/combined_iterator.dart
file:///C:/src/flutter/.pub-cache/hosted/pub.flutter-io.cn/collection-1.15.0/lib/src/combined_wrappers/combined_list.dart
file:///C:/src/flutter/.pub-cache/hosted/pub.flutter-io.cn/collection-1.15.0/lib/src/combined_wrappers/combined_map.dart
file:///C:/src/flutter/.pub-cache/hosted/pub.flutter-io.cn/collection-1.15.0/lib/src/comparators.dart
file:///C:/src/flutter/.pub-cache/hosted/pub.flutter-io.cn/collection-1.15.0/lib/src/empty_unmodifiable_set.dart
file:///C:/src/flutter/.pub-cache/hosted/pub.flutter-io.cn/collection-1.15.0/lib/src/equality.dart
file:///C:/src/flutter/.pub-cache/hosted/pub.flutter-io.cn/collection-1.15.0/lib/src/equality_map.dart
file:///C:/src/flutter/.pub-cache/hosted/pub.flutter-io.cn/collection-1.15.0/lib/src/equality_set.dart
file:///C:/src/flutter/.pub-cache/hosted/pub.flutter-io.cn/collection-1.15.0/lib/src/functions.dart
file:///C:/src/flutter/.pub-cache/hosted/pub.flutter-io.cn/collection-1.15.0/lib/src/iterable_extensions.dart
file:///C:/src/flutter/.pub-cache/hosted/pub.flutter-io.cn/collection-1.15.0/lib/src/iterable_zip.dart
file:///C:/src/flutter/.pub-cache/hosted/pub.flutter-io.cn/collection-1.15.0/lib/src/list_extensions.dart
产物比对
Flutter Native混编
Flutter Native混编调研
0. 背景
由于公司现在有多个移动应用已经在线上运行,比如易销售、智慧仓库等等,虽然Flutter拥有一次编写多端使用的优势,但是推倒用Flutter重新实现一遍代价也比较大,最好的折衷方案是在现有App的基础上进行混合开发编译,在拥有了成熟的Flutter技术沉淀以后可以在新应用上快速接入使用,提升开发效率。
1. 混编接入方式
- 在宿主项目中增加ndk支持
android {
//...
defaultConfig {
ndk {
// Filter for architectures supported by Flutter.
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86_64'
}
}
}
-
添加Java8支持
android { //... compileOptions { sourceCompatibility 1.8 targetCompatibility 1.8 } }
-
然后使用Android Studio新建一个Flutter Module项目
- 在这个Flutter Module项目中进行相关业务开发
- 选择以下三种接入方式之一完成混合开发
1. 本地级联编辑
- 在宿主项目的setting.gradle中增加本地Flutter Module依赖(假设Flutter Module鱼宿主项目同级)
// Include the host app project.
include ':app' // assumed existing content
setBinding(new Binding([gradle: this])) // new
evaluate(new File( // new
settingsDir.parentFile, // new
'my_flutter/.android/include_flutter.groovy' // new
))
- 在宿主项目的app中的build.gradle中增加module依赖
dependencies {
implementation project(':flutter')
}
2. 本地Maven仓库依赖
在Flutter Module中运行
cd some/path/my_flutter
$ flutter build aar
C:\Users\chao.bai\AndroidStudioProjects\flutter-demos\native-module\flutter_module_pure>flutter build aar
Building with sound null safety
Running Gradle task 'assembleAarDebug'... 12.3s
√ Built build\host\outputs\repo.
Running Gradle task 'assembleAarProfile'... 29.6s
√ Built build\host\outputs\repo.
Running Gradle task 'assembleAarRelease'... 30.6s
√ Built build\host\outputs\repo.
Consuming the Module
1. Open <host>\app\build.gradle
2. Ensure you have the repositories configured, otherwise add them:
String storageUrl = System.env.FLUTTER_STORAGE_BASE_URL ?: "https://storage.googleapis.com"
repositories {
maven {
url 'C:\Users\chao.bai\AndroidStudioProjects\flutter-demos\native-module\flutter_module_pure\build\host\outputs\repo'
}
maven {
url "$storageUrl/download.flutter.io"
}
}
3. Make the host app depend on the Flutter module:
dependencies {
debugImplementation 'com.fc.mobile.dms.flutter_module_pure:flutter_debug:1.0'
profileImplementation 'com.fc.mobile.dms.flutter_module_pure:flutter_profile:1.0'
releaseImplementation 'com.fc.mobile.dms.flutter_module_pure:flutter_release:1.0'
}
4. Add the `profile` build type:
android {
buildTypes {
profile {
initWith debug
}
}
}
这个命令会生成一个本地仓库(初始位置为有your flutter module/build/host/outputs/repo)
build/host/outputs/repo
└── com
└── example
└── my_flutter
├── flutter_release
│ ├── 1.0
│ │ ├── flutter_release-1.0.aar
│ │ ├── flutter_release-1.0.aar.md5
│ │ ├── flutter_release-1.0.aar.sha1
│ │ ├── flutter_release-1.0.pom
│ │ ├── flutter_release-1.0.pom.md5
│ │ └── flutter_release-1.0.pom.sha1
│ ├── maven-metadata.xml
│ ├── maven-metadata.xml.md5
│ └── maven-metadata.xml.sha1
├── flutter_profile
│ ├── ...
└── flutter_debug
└── ...
为了依赖生成的AAR,宿主项目必须找到这些文件
String storageUrl = System.env.FLUTTER_STORAGE_BASE_URL ?: "https://storage.googleapis.com"
repositories {
maven {
url 'C:\Users\chao.bai\AndroidStudioProjects\flutter-demos\native-module\flutter_module_pure\build\host\outputs\repo'
}
maven {
url "$storageUrl/download.flutter.io"
}
}
dependencies {
debugImplementation 'com.fc.mobile.dms.flutter_module_pure:flutter_debug:1.0'
profileImplementation 'com.fc.mobile.dms.flutter_module_pure:flutter_profile:1.0'
releaseImplementation 'com.fc.mobile.dms.flutter_module_pure:flutter_release:1.0'
}
3. 远程Maven仓库依赖
远程仓库依赖可以使用fat-aar三方库将Flutter Module库一起打包生成aar,然后上传至公司maven仓库
2. Native调用Flutter页面
最终我们的目的就是需要在存在的应用上调起Flutter页面,一套代码跨端使用
- 对于Android应用,需要在宿主的清单文件中进行注册
<activity
android:name="io.flutter.embedding.android.FlutterActivity"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize"
/>
-
在Application中初始化Flutter引擎
// 实例化 flutterEngine = new FlutterEngine(this); // 引擎预热,这么做可以在初次调用Flutter时有更快的相应速度 flutterEngine.getDartExecutor().executeDartEntrypoint( DartEntrypoint.createDefault() ); // 根据id缓存对应的引擎 FlutterEngineCache .getInstance() .put("my_engine_id", flutterEngine);
-
启动页面
这里使用Activity最为例子,Fragment以及View粒度的实现这里暂不做介绍
myButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
startActivity(
FlutterActivity.createDefaultIntent(currentActivity)
);
}
});