Alibaba vendor fluent for Web Engineering Practice

Author: Ma Kunle (Kunwu)

Since its debut in 2015, after years of development, fluent has become quite mature and has been widely used in Internet companies such as Alibaba, meituan and pinduoduo. In the ICBU Ali seller, 90 +% of the new businesses are developed with Flutter, and the ICBU client development group has a large number of Flutter developers.

The early trial version of fluent for web (FFW) was released in 2019. At that time, many interested students had conducted research on it. At that time, due to many problems just released, it was not suitable for use in the production environment. In March this year (2021), Flutter 2.0 was released and FFW officially entered the stable branch.

Alibaba seller's foreign trade information section is mainly developed by Flutter. In the goal of this fiscal year, the external promotion of foreign trade information App is an important part of open source drainage. Information promotion outside the App requires a Web page carrying content. The requirements for this Web page are as follows:

  • Reproduce the UI and functions of relevant pages on the App side (mainly including a custom html parsing and rendering engine written by dart) [main workload]
  • Fast online
  • App side function synchronization

Due to the lack of front-end students' support, the students on the App side can only invest in completing this page. After some consideration, we chose FFW for the following reasons:

  • The cost of switching to the front-end technology stack, such as Rax, is slightly higher, and the function reproduction of the target page takes more time
  • Use most of the codes on the FFW target page to reuse the ready-made dart codes on the end
  • The fluent technology stack on the App side covers a wide range of students

After the above thinking, the FFW pit filling trip was officially started.

Demo

At present, the relevant pages of Alibaba seller FFW have been online. Since the release of FFW, the problem of large product js files has always existed, which will affect the page loading experience in theory. In the actual test, it is observed that the loading experience on PC and mobile devices is fair and runs smoothly. The relevant demos are as follows:

Problem overview

Creating an FFW project is relatively simple. Switch to the stable version of fluent, and then run the command fluent create xxxproject. After entering the project, click Run a Demo project to run it. To apply FFW to practical projects, we need to consider the problems of the project and how to integrate into Alibaba's system, such as how to publish, how to control the development process, how to request interfaces, etc. it is summarized as follows:

The above are the problems encountered in the practice of open source drainage and minimum closed loop of FFW by Alibaba sellers. In addition, the problems to be built by FFW include:

Engineering Foundation

Next is the explanation of the causes and solutions of engineering basic problems in the minimum closed-loop practice.

Environment and reuse

Referring to the development of Flutter on the App side, FFW should first consider what version of Flutter to choose, and then consider how to reuse the existing Flutter code.

Flutter version selection

The version selection problem is caused by the fact that the Flutter versions of FFW and Flutter for App (FFA) cannot be unified. FFW requires a 2.0 + version of the Flutter, while the current version of the Flutter in our App is 1.0 + X +, to upgrade to version 2.0 +, you still need to wait for an uncertain time. After some consideration, the versions we choose for FFW and FFA are as follows:

FFA: hummer/release/v1.22.6.3          -- UC of Hummer branch
FFW: origin/flutter-2.8-candidate.9     -- Official branch

The reason why FFW does not choose the stable version is that the FFW page will get stuck due to webGL problems on the recently released iOS 15. The problem repair scheme has been integrated into the candidate version. (the latest stable version is 2.10.0, and the problem has been solved)

code reuse

The problems to be considered in reusing FFA code into FFW are divided into two parts:

  • Dart code reuse
  • Reuse of platform related plug-in capabilities

Dart code reuse

FFW requires dart version 2.0 + corresponding to dart version 2.12. This version of dart introduces the feature of sound null safety. The version of Flutter used on FFA is 1+ The dart corresponding to the version has not introduced empty security. At the same time, dart library codes of new and old versions in fluent cannot be mixed compiled, so the existing App side code library cannot be reused seamlessly at present. It can only be reused by modifying the existing code. The main points of code modification are as follows:

  • Variables that can be null are added after the type?
User? nullableUser;
  • Use when operating a variable that can be empty? Or!
nullableUser?.toString();   // If it is empty, NPE will not appear
nullableUser!.toString();   // Mandatory designation is not empty. If it is empty, an error will be reported
  • The optional parameter @ required annotation is replaced by the required reserved word
///Old version
User({
  @required this.name,
  @required this.age,
});

///New version
User({
  required this.name,
  required this.age,
});

After these three steps of modification, the low version code can basically be compiled in the new version. In addition, some API s will be changed due to version upgrade and need to be modified accordingly, such as:

///Old version
typedef ValueWidgetBuilder<T> 
  = Widget Function(BuildContext context, T value, Widget child);

///New version
typedef ValueWidgetBuilder<T> 
  = Widget Function(BuildContext context, T value, Widget? child);

In API changes, such problems account for the majority, and it is easy to modify. In addition, there is another kind of change. For example, in the abstract class TextSelectionControls, the number of method parameters such as handleCut has changed:

///Old version
void handleCut(TextSelectionDelegate delegate) {...}

///New version
void handleCut(TextSelectionDelegate delegate, 
               ClipboardStatusNotifier? clipboardStatus) {...}

Such changes need to be modified according to the actual situation, and the difficulty is medium. The probability of newly added parameters can not be used.

Platform related plug-ins

Platform related plug-ins will call Native capabilities. To use the plug-ins in FFA on FFW, you need to implement the corresponding capabilities for the plug-ins on the Web platform, which will be described in the js call section below. If you use pub If the library in dev meets the following conditions, the corresponding version can be used directly:

  • The code base has a Web version
  • Among the released versions, there are versions that support Null safety (Web support will also support this)
Support Web version Support empty security

Release system

After the local Demo project is created and run successfully, the following issues should be considered:

  • How to control the process from development to release
  • How to publish the page to the online public network for access
    • How to package and build
    • How to publish

For the management and control of the development to release process, refer to the DEF platform (Alibaba's internal front-end development and release platform) selected by the front-end and managed by creating WebApp, which will not be described in detail here. The contents involved in page publishing are as follows:

Engineering construction

There are two ways to build FFW. Not all products need to be simplified in application; In addition, to release products on the DEF platform, some additional processing needs to be carried out on the products. How to build is mainly considered in the construction. The optional commands for FFW compilation and construction are as follows:

///Canvas it rendering
flutter build web --web-renderer canvaskit
  
///html rendering
flutter build web --web-renderer html

The difference between the two commands is how the target page is rendered. The official explanation of the two commands is as follows:

The summary is as follows:

  • Html mode: the page is rendered using the basic elements of Html, which has the advantage of small page resource files;
  • CanvasKit mode: it uses the WebAssembly technology and has better performance. However, it needs to load 2.5 more wasm files related to WebAssembly+ MB resource file, which is more suitable for scenes with high requirements for page performance.

We choose the following two ways to load resources based on the performance of the html page and the empty page.

Html mode CanvasKit mode

Product simplification and treatment

For the newly created project, the compiled product is located in/ Under the build/web directory, the structure is:

build
└── [ 384]  web
    ├── [ 224]  assets
    │   ├── [   2]  AssetManifest.json
    │   ├── [  82]  FontManifest.json
    │   ├── [740K]  NOTICES
    │   └── [  96]  fonts
    │       └── [1.2M]  MaterialIcons-Regular.otf
    ├── [ 917]  favicon.png
    ├── [6.5K]  flutter_service_worker.js
    ├── [ 128]  icons
    │   ├── [5.2K]  Icon-192.png
    │   └── [8.1K]  Icon-512.png
    ├── [3.6K]  index.html           [[release reserved]
    ├── [1.2M]  main.dart.js         [[release reserved]
    ├── [ 570]  manifest.json
    └── [  59]  version.json

The functions and descriptions of each directory and file are as follows:

  • Assets: image, font and other resource files, corresponding to the assets configured in yaml file. In FFW, if the image is configured on TPS and IconFont is not used, this directory may not be required;
  • favicon.png: the icon of the page, which is not required when using TPS resources;
  • flutter_service_worker.js: control page loading, reload, closing, etc. during local debug ging, which is not required during publishing;
  • icons: icon resource. It is unnecessary to publish to TPS;
  • index.html: page entry file. The main work is to introduce main dart. JS also has some other resources, such as the shell project of App, which needs to be;
  • main.dart.js: the product of dart compilation in the project, which needs to be;
  • manifest.json: the page is used as the configuration of webapp, which is unnecessary;
  • version.json: build information, not required.

In the actual release, the only required build product is index HTML and main dart. JS, for each iteration, only main is needed when the "shell Engineering" change is not involved dart. JS.

After the required products are selected, the two files need to be processed before the DEF platform is released:

Page Publishing

On the DEF platform, js and html files will be published to the corresponding cdn after the product files are processed, and html will be deployed to a specific address:

Advance:

on-line:

For online environment, index The HTML content is as follows:

<!DOCTYPE html>
<html>
 <head> 
  <!-- Used when publishing to the secondary directory of the domain name --> 
  <base href="/content_page/" /> 
 </head> 
 <body> 
  <!-- Replace with main.dart.js Corresponding cdn address --> 
  <script type="text/javascript" src="https://g.alicdn.com/algernon/alisupplier_content_web/1.0.10/main.dart.js"></script>  
 </body>
</html>

At this point, we can access our target page by using the page deployment address. If the page is opened at one time and there is no need to jump multiple pages internally, the publishing work is completed at this step. If multi page Jump is involved, you also need to publish relevant contents to your own domain name. A simpler way is to configure redirection. In addition, you can also directly reference products:

Code debugging

After the basic link runs through, the requirements can be developed. The more important environment in the development process is code debugging. FFW can be debugged in chrome in an App like manner and has a good experience. After setting breakpoints in IDE in Debug environment, you can debug breakpoints in IDE, see breakpoints in Chrome, and even dart code in Chrome. Taking VSCode as an example, the debug process and experience are as follows:

Start fluent debugging

Breakpoints visible in VSCode and Chrome

Capability support

After entering the actual development, we need the support of capabilities such as routing and interface request. The first is page routing and address.

Page routing and address

When multiple pages appear in FFW applications or parameters need to be transmitted through Http links, corresponding routing configuration is required. Similar to FFA, you can configure the corresponding Route in the root MaterialApp and then use navigator Push jump or directly open the page through the page address. The following code can realize name jump and page address jump:

///MaterialApp configuration
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      onGenerateRoute: RouteConfiguration.onGenerateRoute,
      onGenerateInitialRoutes: (settings) {
        return [RouteConfiguration.onGenerateRoute(RouteSettings(name: settings))];
      },
    );
  }
}
///Named routing configuration
class RouteConfiguration {
  static Map<String, RouteWidgetBuilder?> builders = {
    ///Page A
    '/page_a': (context, params) {
      return PageA(title: params?['title']);
    },

    ///Page B
    '/page_b': (context, params) {
      return PageB(param1: params?['param1'], param2: params?['param2']);
    },
  };

  static Route<dynamic> onGenerateRoute(RouteSettings settings) {
    return NoAnimationMaterialPageRoute(
      settings: settings,
      builder: (context) {
        var uri = Uri.parse(settings.name ?? '');
        var route = builders[uri.path];

        if (route != null) {
          return route(context, uri.queryParameters);
        } else {
          return CommonPageNotFound(routeSettings: settings);
        }
      },
    );
  }
}

After the configuration is completed, you can jump on the page, or directly jump through the Champions League browser address:

  • Jump in application: after configuration is completed, you can jump to the target page through Navigator inside the application
///Navigator jumps to page B
Navigator.of(context).restorablePushNamed('/page_b?param1=123¶m2=abc');
  • Address jump: enter the address of the page in the browser address bar to jump to the page
///Page B access address
https://xxx.xx/#/page_b?param1=123¶m2=abc

Note: the above address jump mode requires that the UrlStrategy of FFW is hash tag mode (the default UrlStrategy).

Native of Web Platform -- JS call

By using pub Dev and other warehouses can easily use various capabilities in FFW. For the capabilities that are not available in the warehouse, it is necessary to consider extending them. The ability of native can be used through plug-ins on FFA, and the ability of js can be extended on FFW. Through the ability of calling js, a large amount of technology accumulation at the front end can be applied to FFW.

Dart in FFW will eventually be compiled into js, which should be used naturally in FFW. In order to support js's call in dart, dart official released the js library. By using annotations in the library, it is convenient to call js in dart.

For example, when the alert method needs to be called, the following definitions are made:

///File: js_interface.dart

///The tool class library for calling js method needs to introduce js library into dependencies
@JS()
library lib.content;

import 'package:js/js.dart';

///alert Popup
@JS('alert')
external void jsAlert(String msg);

Then introduce JS where alert is needed_ interface. Dart and call jsAlert method:

import 'package:mtest/js_interface.dart';

...
jsAlert('Test message');
...

For more usage, see pub Description of js Library in dev: https://pub.dev/packages/js . Got through After the ability of js, many problems will be solved.

Mtop interface

In view of the construction of the existing App Mtop (a gateway used by Ali App), it can reduce a lot of work if the existing Mtop can be invoked in FFW. To do this, you need to add the ability of MTop call to FFW. To complete this work, you need two parts:

  • The FFW side supports Mtop calls
  • The server supports Mtop call in H5 mode

FFW supports Mtop

By calling mtop JS can introduce the ability of mtop into FFW. The overall process is as follows:

1. In index Introducing mtop.html js

<script src="//g.alicdn.com/mtb/lib-mtop/2.6.1/mtop.js"></script>

2. Define interface file js_mtop_interface.dart

@JS()
library lib.mtop;

import 'package:js/js.dart';
import 'package:js/js_util.dart';
import 'dart:convert';
import 'dart:js';

///Parameters of mtop request
@anonymous
@JS()
class MtopParams {
  external String get api;
  external String get v;
  external dynamic get data;
  external String get ecode;
  external String get dataType;
  external String get type;
  external factory MtopParams({
    required String api,
    required String v,
    required dynamic data,
    required String ecode,
    required String dataType,
    required String type,
  });
}

/// lib.mtop request function
@JS('lib.mtop.request')
external dynamic _jsMtopRequest(MtopParams params);

///Convert dart map to js object
Object mapToJSObj(Map<String, dynamic> a) {
  var object = newObject();
  a.forEach((k, v) {
    var key = k;
    var value = v;
    setProperty(object, key, value);
  });
  return object;
}

///mtop js request interface
Future mtopRequest(String api, Map<String, dynamic> params, String version, String method) {
  var jsPromise = _jsMtopRequest(
    MtopParams(
      api: api,
      v: version,
      data: mapToJSObj(params),
      ecode: '0',
      type: method,
      dataType: 'json',
    ),
  );
  return promiseToFuture(jsPromise);
}

///The returned result is used for parsing
@JS('JSON.stringify')
external String stringify(Object obj);

3. Call mtop interface

import 'package:mtest/mtop/js_mtop_interface.dart';

...
try {
   var res = await mtopRequest(apiName, params, version, method);
   print('res $res');
} catch (err) {
   data = stringify(err);
}

4. Parsing result: the result returned by the interface request is a jsObject, which can be json through the js method After stringify is converted to json, it is used at the dart level

String jsonStr = stringify(res);

Server H5 Mtop configuration

Access mtop.com in FFW JS, the target MTop interface needs to be processed accordingly before calling:

  • mtop release h5 version
  • Apply for configuration of CORS domain name white list

Manage

As requested by mtop, the js Library of golden arrow can be introduced into FFW for management. The process is as follows:

1,index.html import js file

<script type="text/javascript" src="https://g.alicdn.com/alilog/mlog/aplus_v2.js"></script>

2. Define interface file js_goldlog_interface.dart

@JS()
library lib.goldlog;

import 'package:js/js.dart';

///record function
@JS('goldlog.record')
external dynamic _jsGoldlogRecord(String t, String e, String n, String o, String a);

void goldlogRecord(String logkey, String eventType, String queryParams) {
  _jsGoldlogRecord(logkey, eventType, queryParams, 'GET', '');
}

3. Dot call

import 'package:mtest/track/js_goldlog_interface.dart';

...
goldlogRecord(logkey, eventType, queryParams);
...

Then, configure the corresponding points on the log platform.

monitor

The access of monitoring capability is relatively simple. Here, select arms (application real-time monitoring service) and directly in index Just introduce arms into HTML. The process is as follows:

1. In index Introducing related libraries into HTML

<script type="text/javascript">
    var trace = window.TraceSdk({
      pid: 'as-content-web',
      plugins: [
        [window.TraceApiPlugin, { sampling: 1 }],
        [window.TracePerfPlugin],
        [window.TraceResourceErrorPlugin]
      ],
    });
    // Start trace and listen for global JS exceptions. The problem will be commented temporarily during debug
    trace.install();
    trace.logPv();
</script>

2. Configure on the arms platform

Note: trace Install() will cause the page not to be displayed in the Debug environment, which can be disabled in the Debug environment.

Optimization and compatibility

After completing the above basic capacity-building, FFW can basically meet the development of simple needs. Besides requirements development, experience, compatibility and other issues need to be considered.

Loading optimization

One problem with FFW since its release is the package size. For an empty helloworld project, the size of a single js package is 1.2 MB (before uncompressed). It may take several seconds to load when the network on the mobile device is bad. In order to improve the experience of page loading, consider the following things:

Waiting process optimization

FFW pages are always blank before js loading, which gives people a feeling that the page is stuck. Therefore, you can add loading animation before js loading, so as not to keep the page blank all the time. Referring to the effective practices on the App, a bone screen can be inserted between data loading, which is realized as follows:

 <iframe src="https://g.alicdn.com/algernon/alisupplier_content_web/0.9.1/skeleton/index.html" id="iFrame" frameborder="0" scrolling="no"></iframe>
  <script>
    function setIframeSize() {
      <!-- Bone screen size setting, full screen -->
    }
    function removeIFrame() {
      var iframe = document.getElementById("iFrame");
      iframe.parentNode.removeChild(iframe);
    }
    setIframeSize();
</script>

  <!-- load Remove the bone screen when finished -->
  <script type="text/javascript" src="https://g.alicdn.com/algernon/alisupplier_content_web/1.0.10/main.dart.js" 
    onload="removeIFrame()"></script>

TODO JS unpacking & Optimization

The optimization of waiting process can improve the waiting experience to a certain extent. If you want to load fast, you have to make the loaded resources small. For multi page applications, you can use the whole main dart. JS is divided into several small packages and loaded gradually in the process of use. At present, it is understood that meituan has corresponding technology, but the implementation details are unknown and need to be studied. Can refer to https://github.com/flutter/flutter/issues/46589

Compatibility issues

Similar apps have experience problems on different devices, and FFW has compatibility problems in different H5 container pages. In our practice, the records of stepping on pits of different H5 containers are as follows:

White screen problem in nail H5 container:

  • I won't support it?? Syntax, solve after replacement
  • FFW product js contains a large number of try{}finally {} no catch operations, and an error will be reported in the nail H5 container. The script will be used for unified replacement during packaging

White screen problem in wechat H5 container:

  • Remove MaterialIcons and replace with pictures

The page on iOS 15 is stuck:

iOS compatibility issues:

  • For clickable RichText, after the underline property is set, the link immediately following the picture will be blocked. No solution has been found yet, so we can only not use the underline provided by RichText first
  • Clickable RichText will scroll automatically after clicking. The verification is caused by the InteractiveSelectionu property. When set to false, the performance is consistent with that of Android

Other issues

In addition to the compatibility problem of H5 container, some problems of FFW itself are encountered in practice. The records are as follows:

provider library problem:

  • In the provider library, / lib / SRC / provider The toString() method of dart providernotfoundexception class contains a huge error description String. The js syntax of this String after compilation will be wrong. Delete it

JsonConverter problem:

  • JsonConverter().convert will report an error when running. Use it with caution. Convert dart array to js array can be converted manually

Contents of TODO

At present, only a small closed-loop construction available for business has been completed in practice, and there are still many contents of TODO in FFW, as follows:

Engineering Construction:

  • DEF cloud construction: after trying to install the Flutter environment on the DEF cloud construction platform, all requests for content outside Alibaba will 403, and there are many contents in Flutter that need to be pulled online, such as the contents in packages under the root directory of Flutter, which are currently built locally and need to be solved;
  • mtop access during local debugging: the mtop request needs to be configured with CORS white list and the port must be 80. The ip and port used in local debugging are a random number. When setting it forcibly, you have no right to operate. At present, you can only run the http server locally and debug in chrome after setting the host. The breakpoint debug needs to be solved.

Basic functions:

  • Video and audio playback ability to be studied

Compatibility and optimization

  • js package split loading to be studied
  • Custom font file optimization to be studied

Imagination:

  • Flutter dynamic in App: replace the flutter page in App with FFW to make a dynamic scheme similar to weex
  • WebApp: the App implemented by Flutter can be converted into WebApp at low cost through FFW, which solves the problem that there is no Mac version of App

Focus on Alibaba mobile technology WeChat official account, 3 mobile technology practices dry cargo per week to give you thought!

Keywords: Javascript dart

Added by joshbb on Fri, 25 Feb 2022 09:03:10 +0200