Flutter For Web:人人都是大前端开发
今年 9 月,作为腾讯 Flutter 实践团队之一的我们,有幸参与了 GDD 大会上 Flutter 应用视频的录制,感受到国内众多开发者对 Flutter 的热情。两天时间,讲道理,其实没有太多的干货,但收获还是满满的。有那么点空闲的时间,也关注了一下其他的同学,大家讨论的重心还是第一天主会场的内容。我想吸引大家的一直是 Flutter 新版本 stable1.9 的重磅发布。
新版本一个重要功能就是 Flutter For Web 仓库合入 Flutter master 主仓库,意味着我们可以真正地使用一套代码、一套资源部署大前端。辅导团队经过一段时间的准备,使用 Flutter 开发的 Web 页面也即将发布,希望和大家分享下实践过程和踩坑实例,欢迎一起交流探讨。
iOS 中打开 Flutter Web 页面
浏览器中打开 Flutter Web 页面
index.html:h5 访问入口; main.dart.js:dart 代码转化成 js 后的产物; assets:静态资源,包括图片、字体,以及我们定制的 js 文件。
Flutter For Web 谷歌官方一直建议暂时不要在生产环境使用。但如果不尝试,我们永远不知道这里会藏有什么样的秘密,什么样的场景适合。对现有 Flutter 改造,增加对浏览器的支持,突破而不失稳重。
我们原先的版本使用 Flutter 1.5.4,增加 Web 支持,我们需要切换到最新的 master 分支,改造运行。
如果我们只是新建一个新的项目,只需切换到最新的 master 分支,创建一个新的项目即可。创建完成之后,根目录下会多出一个 web 文件夹,里面只有一个 web 入口文件 index.html。
原有项目中如果直接运行 flutter run -d chrome,会发现控制器中 import 报错,原因是 dart: io 库不支持 web。io 库是 Flutter 中非常常用的库,主要是平台相关的一些 api,改造的第二步我们就需要屏蔽 dart:io 的引入。
Flutter For Web 最终运行的是在浏览器中的 js 代码,Flutter For Phone 使用,Platform 引擎与 Native 通信。js 在平台上是通过其他的系统支持(iOS 中的 JavaScriptCore) 与 Native 通信,二者是完全不同的方式,所以 dart: io 无法继续支持。
import 'main_web.dart' if (dart.library.io) "main_io.dart"; //dart.library.io
UI 侧我们仍然使用同一套代码,逻辑侧通过 library 库分区 native 还是 web,逻辑侧对应不同的平台实现。这几天,我看到一个新的区分方法,简单而且实用:web 侧我们可以判断 0 和 0.0 是否是一个对象。
//init
Config.inWeb = identical(0, 0.0);
/////////////////////////////////////
enum K12Platform {iPhone, iPad, android}
class Config {
static bool inWeb = false;
static K12Platform k12platform = K12Platform.iPhone;
static bool inProduction = bool.fromEnvironment("dart.vm.product");
}
原有 Flutter 项目我们通过 MJFlutter 调用 Native 的网络接口,实现数据请求。辅导 web 侧是通过 CGI 请求获得后台数据,现在 web 需要另一种方式,给大家推荐几种方式:
a. package:http/http.dart
import 'dart:convert' as convert;
import 'package:http/http.dart' as http;
main(List<String> arguments) async {
// This example uses the Google Books API to search for books about http.
// https://developers.google.com/books/docs/overview
var url = "https://www.googleapis.com/books/v1/volumes?q={http}";
// Await the http get response, then decode the json-formatted responce.
var response = await http.get(url);
if (response.statusCode == 200) {
var jsonResponse = convert.jsonDecode(response.body);
var itemCount = jsonResponse['totalItems'];
print("Number of books about http: $itemCount.");
} else {
print("Request failed with status: ${response.statusCode}.");
}
}
import 'dart:html' as html;
var req = html.HttpRequest.getString("https://www.googleapis.com/books/v1/volumes?q={http}");
c. package:dio/dio.dart
pub 链接:https://pub.dev/packages/dio#-installing-tab-
dio 这个网络库,大家之前可能用过,需要注意一点,3.0 之前的版本 Flutter For Web 不支持 (dart:io)。3.0 之后的版本,这个库经过官方大改造,现已支持 Flutter For Web 开发。
var url = 'https://fudao.qq.com/xxx';
BaseOptions options = BaseOptions(
method: 'get',
baseUrl: url,
connectTimeout: 5000,
receiveTimeout: 100000,
contentType: 'json',
responseType: ResponseType.plain,
headers: {
'host' : "fudao.qq.com", // 一般就是 webview 的 url 的 host
'cookieHeader': getWebCookie(),
'accept': "*",
'referer': "https://fudao.qq.com/"
}
);
Dio dio = Dio(options);
try {
Response response = await dio.get(url);
if (callback != null) {
callback(response.statusCode, response.statusMessage, response.data);
}
} on DioError catch(e) {
if(e.response != null) {
print(e.response.data);
print(e.response.headers);
print(e.response.request);
} else{
print(e.request);
print(e.message);
}
}
有了基本的网路请求,配置完 CGI,接下来就是界面的展示。但是你会发现,所有的业务请求都没有回包,无法得到数据。如果作为移动端开发,没有经常接触到 http 请求,一下子发蒙,我觉得可以理解。这个是 Web 开发中经常遇到的 跨域问题。前端开发们这里可以举手了,这个问题我会。简单的处理方式就是开发过程中挂上一个代理,具体就不详细展开了。
网路问题解决,进入发布调试,上文我们介绍过 Flutter For Web 的最终产物是三个文件。如何发布,前端同学又可以举手了。有一个简单的方式:github pages(配置方法:https://pages.github.com/)。
与使用 Flutter 实现的页面不同,Flutter For Web 的页面使用 Http 请求获取后台数据。Http 请求中很重要的一点是 header 中的 cookie,这里就需要通过 dart 与 js 之间的交互,还是要称赞下 Flutter 团队的实力。
官方说明:https://dart.dev/web/js-interop
pub 地址:https://pub.dev/packages/js
新建 native_api.js 文件
dart 封装 js 方法
index 入口文件中引入 js 文件
添加完 js 支持后,dart 侧调用 getStringFromJS 方法,并打印结果。
通过 js.dart 这个库,可以实现 web 侧的主要功能。例如 cookie、localStorage。
native_api.js 中声明基本方法
dart 封装 js 方法
处理完 Web 侧的基本能力后,需要部署 dart 转成 js 后,与 native 交互的能力。我们知道,目前 app 中 native 与 web 交互的方式主要通过 jsBridge。Native 侧我们已经有了成熟的体系主动调用和接收调用 web 的能力,这部分我们可以不需要修改,减少开发工作量。在 Flutter 侧我们需要添加交互支持,也就是需要在 native_api.js 中添加与 native 交互的能力。
JavaScript 调用 Native 的方式,主要有两种:注入 API 和 拦截 URL SCHEME,你可以根据自己的业务能力,选择合适的方式。相较于 JavaScript 调用 Native,Native 调用 JavaScript 比较简单,不管是 iOS 的 UIWebView 还是 WKWebView,还是 Android 的 WebView 组件,都以子组件的形式存在于 View/Activity 中,直接调用相应的 API 即可。
function callNativeFunction() {
var url = 'jsbridge://edu/setCenterTitle?p=%7B%22text%22%3A%22%E9%B1%BC%E9%A5%BC%22%7D#2?title=aaa&desc=bbb&link=http%3A%2F%2Fwww.baidu.com';
var jsbridgeNode = document.createElement('iframe');
var removeTimeStamp;
jsbridgeNode.style.cssText = 'display:none;width:0px;height:0px;';
jsbridgeNode.onerror = function(e) {
// 在 android 4.0-4.3 中,script 节点的 src 赋值成 jsbridge://ui/showDialog 的形式会报错
e.stopPropagation();
}
/*
ios 必须先赋值, 然后 append, 否者连续的 api 调用会间隔着失败
也就是 api1(); api2(); api3(); api4(); 的连续调用,
只有 api1 和 api3 会真正调用到客户端
*/
jsbridgeNode.src = url;
var root = document.body || document.documentElement;
root.appendChild && root.appendChild(jsbridgeNode);
setTimeout(function() {
jsbridgeNode &&
jsbridgeNode.parentNode &&
jsbridgeNode.parentNode.removeChild(jsbridgeNode);
}, 500);
}
整体来说,Flutter For Web 达到成熟还有一段时间。在这段时间里,尽早布局,未免不是一件好事。Flutter For Web 现阶段的使用场景并不多,不如直接使用 Web 开发便捷、稳定。但我们可以通过 Flutter,拉近与大前端的距离,感受大前端的魅力,做一个真正的大前端开发。
腾讯企鹅辅导团队会继续实践,不管是 Flutter 实现的页面,还是 Flutter 转 Web 的页面。Flutter For Web 一个复杂的场景,我们会尝试降级及热更新能力,与 Web 同学通力合作,封装 App 和 Web 的基础 API、集成 CI 等,欢迎与我们一起交流。
技术服务于业务,业务推动技术,二者并行,提升用户体验,Flutter 道路上期待大家的反馈。
涂金林,来自江苏苏州。2014 年和 2017 年分别获得东南大学本科和硕士学位。2017 年加入腾讯在线教育部,主要从事移动客户端开发。入职以来,参与腾讯企鹅辅导、腾讯课堂 iOS 和 Android 开发,腾讯企鹅辅导 iOS 负责人。目前主要担任腾讯在线教育 Flutter 项目技术负责人。
近年来,随着 jQuery 的落幕,三大框架的割据以及小程序的爆发,大前端的发展也经历了从静态页面到 JavaScript 跨时代的诞生,再从 PC 端到移动端的转向,以及由依赖后端到前后端分离的架构演变。
腾讯在线教育前端团队,近年来在大前端技术架构演进方面也有了不少突破,如 Hybird 方案、离线包方案、PWA 结合 SSR 方案、以及 RN 动态化方案的落地和执行等。
这次 GMTC 全球大前端技术大会(深圳站)2019,我们专门请到了 腾讯的前端高级工程师曹海歌,希望可以从腾讯在线教育前端团队的实操案例中,深入了解腾讯为提升研发效率,进行的前端工程化体系建设过程。扫描下方二维码或点击阅读原文,查看详情。
- WEB前端开发规范文档(for: mrthink.net)
- 这本小书的目的是引导你进入 React 和 Webpack 的世界。他们两个都是非常有用的技术,如果同时使用他们,前端开发会更加有趣。
- Web前端开发人员和设计师必读文章推荐【系列十】
- 百度2010校园招聘 web前端开发笔试题(大概)
- Web前端开发人员和设计师必读文章推荐【系列十】
- web前端开发教程系列-1 - 前端开发编辑器介绍
- 使用Eclipse进行web前端开发
- JavaScript & JQuery 交互式Web前端开发
- 女孩子零基础学习web前端开发怎么…
- Web前端开发基础 第四课(颜色值)
- web前端PC端 百度地图的开发指导
- 独家分享——大牛教你如何学习Web前端开发
- Web前端开发实用在线工具
- 【前端福利】用grunt搭建自动化的web前端开发环境-完整教程
- web前端及混合开发
- Web前端开发技术 HTML、CSS、JavaScript pdf
- web前端开发分享-css,js工具篇
- 2018年20种最佳前端Web开发工具
- Web前端开发——JS技术大梳理
- 指尖下的js —— 多触式web前端开发之三:处理复杂手势