百度360必应搜狗淘宝本站头条
当前位置:网站首页 > IT知识 > 正文

写给前端工程师的Flutter详细教程

liuian 2025-01-01 21:35 20 浏览

最爱折腾的就是前端工程师了,从 jQuery 折腾到 AngularJs,再折腾到 Vue、React。最爱跨屏的也是前端工程师,从 phonegap,折腾到 React Native,这不又折腾到了 Flutter。

作者:hicc;来源:腾讯技术工程


低成本地为用户带来更优秀的用户体验。

目前来说Flutter可能是其中最优秀的一种方案了。

Flutter 是什么?

Flutter is Google’s UI toolkit for building beautiful, natively compiled applications for mobile, web, and desktop from a single codebase.

Flutter是由原 Google Chrome 团队成员,利用 Chrome 2D 渲染引擎,然后精简 CSS 布局演变而来。

或者更详细的版本

  • Flutter 在各个原生的平台中,使用自己的 C++的引擎渲染界面,没有使用 webview,也不像 RN、NativeScript 一样使用系统的组件。简单来说平台只是给 Flutter 提供一个画布。
  • 界面使用 Dart 语言开发,貌似唯一支持 JIT,和 AOT 模式的强类型语言。
  • 写法非常的现代,声明式,组件化,Composition > inheritance,响应式……就是现在前端流行的这一套
  • 一套代码搞定所有平台。

Flutter 为什么快?Flutter 相比 RN 的优势在哪里?

从架构中实际上已经能看出 Flutter 为什么快,至少相比之前的当红炸子鸡 React Native 快的原因了。

  • Skia 引擎,Chrome, Chrome OS,Android,Firefox,Firefox OS 都以此作为渲染引擎。
  • Dart 语言可以 AOT 编译成 ARM Code,让布局以及业务代码运行的最快,而且 Dart 的 GC 针对 Flutter 频繁销毁创建 Widget 做了专门的优化。
  • CSS 的的子集 Flex like 的布局方式,保留强大表现能力的同时,也保留了性能。
  • Flutter 业务书写的 Widget 在渲染之前 diff 转化成 Render Object,对,就像 React 中的 Virtual DOM,以此来确保开发体验和性能。

而相比 React Native:

  • RN 使用 JavaScript 来运行业务代码,然后 JS Bridge 的方式调用平台相关组件,性能比有损失,甚至平台不同 js 引擎都不一样。
  • RN 使用平台组件,行为一致性会有打折,或者说,开发者需要处理更多平台相关的问题。

而具体两者的性能测试,可以看这里,结论是 Flutter,在 CPU,FPS,内存稳定上均优于 ReactNative。

Dart 语言

在开始 Flutter 之前,我们需要先了解下 Dart 语言……

Dart 是由 Google 开发,最初是想作为 JavaScript 替代语言,但是失败沉寂之后,作为 Flutter 独有开发语言又焕发了第二春 。

实际上即使到了 2.0,Dart 语法和 JavaScriptFlutter非常的相像。单线程,Event Loop……

当然作为一篇写给前端工程师的教程,我在这里只想写写 JavaScript 中暂时没有的,Dart 中更为省心,也更“甜”的东西。

  • 不会飘的this
  • 强类型,当然前端现在有了 TypeScript
  • 强大方便的操作符号:
  • ?. 方便安全的foo?.bar取值,如果 foo 为null,那么取值为null
  • ?? condition ? expr1 : expr2 可以简写为expr1 ?? expr2
  • =和其他符号的组合: *=、~/=、&=、|= ……
  • 级联操作符(Cascade notation ..)
//?想想这样省了多少变量声明??querySelect('#button')???..text?="Confirm"???..classes.add('important')???..onClick.listen((e)?=>?window.alert('Confirmed'))??

甚至可以重写操作符

class?Vector?{???final?int?x,?y;???Vector(this.x,?this.y);????Vector?operator?+(Vector?v)?=>?Vector(x?+?v.x,?y?+?v.y);???Vector?operator?-(Vector?v)?=>?Vector(x?-?v.x,?y?-?v.y);????//?Operator?==?and?hashCode?not?shown.?For?details,?see?note?below.???//?···?}??void?main()?{???final?v?=?Vector(2,?3);???final?w?=?Vector(2,?2);????assert(v?+?w?==?Vector(4,?5));???assert(v?-?w?==?Vector(0,?1));?}?

注:重写==,也需要重写 Object hashCodegetter

class?Person?{???final?String?firstName,?lastName;???Person(this.firstName,?this.lastName);????//?Override?hashCode?using?strategy?from?Effective?Java,???//?Chapter?11.???@override???int?get?hashCode?{?????int?result?=?17;?????result?=?37?*?result?+?firstName.hashCode;?????result?=?37?*?result?+?lastName.hashCode;?????return?result;???}????//?You?should?generally?implement?operator?==?if?you???//?override?hashCode.???@override???bool?operator?==(dynamic?other)?{?????if?(other?is!?Person)?return?false;?????Person?person?=?other;?????return?(person.firstName?==?firstName?&&?????????person.lastName?==?lastName);???}?}??void?main()?{???var?p1?=?Person('Bob',?'Smith');???var?p2?=?Person('Bob',?'Smith');???var?p3?=?'not?a?person';???assert(p1.hashCode?==?p2.hashCode);???assert(p1?==?p2);???assert(p1?!=?p3);?}?

这点在 diff 对象的时候尤其有用。

lsolate

Dart 运行在独立隔离的 iSolate 中就类似 JavaScript 一样,单线程事件驱动,但是 Dart 也开放了创建其他 isolate,充分利用 CPU 的多和能力。

loadData()?async?{????//?通过spawn新建一个isolate,并绑定静态方法????ReceivePort?receivePort?=ReceivePort();????await?Isolate.spawn(dataLoader,?receivePort.sendPort);????//?获取新isolate的监听port????SendPort?sendPort?=?await?receivePort.first;????//?调用sendReceive自定义方法????List?dataList?=?await?sendReceive(sendPort,?'https://hicc.me/posts');????print('dataList?$dataList');?}??//?isolate的绑定方法?static?dataLoader(SendPort?sendPort)?async{????//?创建监听port,并将sendPort传给外界用来调用????ReceivePort?receivePort?=ReceivePort();????sendPort.send(receivePort.sendPort);?????//?监听外界调用????await?for?(var?msg?in?receivePort)?{??????String?requestURL?=msg[0];??????SendPort?callbackPort?=msg[1];???????Client?client?=?Client();??????Response?response?=?await?client.get(requestURL);??????List?dataList?=?json.decode(response.body);??????//?回调返回值给调用者??????callbackPort.send(dataList);???}?????}??//?创建自己的监听port,并且向新isolate发送消息?Future?sendReceive(SendPort?sendPort,?String?url)?{????ReceivePort?receivePort?=ReceivePort();????sendPort.send([url,?receivePort.sendPort]);????//?接收到返回值,返回给调用者????return?receivePort.first;?}?

当然 Flutter 中封装了compute,可以方便的使用,譬如在其它 isolate 中解析大的 json。

Dart UI as Code

在这里单独提出来的意义在于,从 React 开始,到 Flutter,到最近的 Apple SwiftUI,Android Jetpack Compose 声明式组件写法越发流行,Web 前端使用 JSX 来让开发者更方便的书写,而 Flutter,SwiftUI 则直接从优化语言本身着手。

函数类的命名参数

void?test({@required?int?age,String?name})?{???print(name);???print(age);?}?//?解决函数调用时候,参数不明确的问题?test(name:"hicc",age:?30)?//?这样对于组件的使用尤为方便?class?MyApp?extends?StatelessWidget?{???@override???Widget?build(BuildContext?context)?{???return?Scaffold(???????appBar:?AppBar(),???????body:?Container(),???????floatingActionButton:FloatingActionButton()?????);???}?}?

大杀器:Collection If 和 Collection For

//?collection?If?Widget?build(BuildContext?context)?{???return?Row(?????children:?[???????IconButton(icon:?Icon(Icons.menu)),???????Expanded(child:?title),???????if?(!isAndroid)?????????IconButton(icon:?Icon(Icons.search)),?????],???);?}?//?Collect?For?var?command?=?[???engineDartPath,???frontendServer,???for?(var?root?in?fileSystemRoots)?"--filesystem-root=$root",???for?(var?entryPoint?in?entryPoints)?????if?(fileExists("lib/$entryPoint.json"))?"lib/$entryPoint",???mainPath?];?

更多 Dart 2.3 对此的优化看这里。

Flutter 怎么写

到这里终于到正题了,如果熟悉 web 前端,熟悉 React 的话,你会对下面要讲的异常的熟悉。

Flutter App 的一切从lib/main.dart文件的 main 函数开始:

import?'package:flutter/material.dart';?void?main()?=>?runApp(MyApp());??class?MyApp?extends?StatelessWidget?{???@override???Widget?build(BuildContext?context)?{?????return?MaterialApp(???????title:?'Welcome?to?Flutter',???????home:?Scaffold(?????????appBar:?AppBar(???????????title:?Text('Welcome?to?Flutter'),?????????),?????????body:?Center(???????????child:?Text('Hello?World'),?????????),???????),?????);???}?}?

Dart 类 build 方法返回的便是 Widget,在 Flutter 中一切都是 Widget,包括但不限于

  • 结构性元素,menu,button 等
  • 样式类元素,font,color 等
  • 布局类元素,padding,margin 等
  • 导航
  • 手势

Widget 是 Dart 中特殊的类,通过实例化(Dart 中new 是可选的)相互嵌套,你的这个 App 就是形如下图的一颗组件树(Dart 入口函数的概念,main.dart -> main())。

Widget 布局

上说过 Flutter 布局思路来自 CSS,而 Flutter 中一切皆 Widget,因此整体布局也很简单:

  • 容器组件 Container
  • decoration 装饰属性,设置背景色,背景图,边框,圆角,阴影和渐变等
  • margin
  • padding
  • alignment
  • width
  • height
  • Padding,Center
  • Row,Column,Flex
  • Wrap, Flow 流式布局
  • Stack, Z 轴布局
  • ……

Flutter 中 Widget 可以分为三类,形如 React 中“展示组件”、“容器组件”,“context”。

StatelessWidget

这个就是 Flutter 中的“展示组件”,自身不保存状态,外部参数变化就销毁重新创建。Flutter 建议尽量使用无状态的组件。

StatefulWidget

状态组件就是类似于 React 中的“容器组件”了,Flutter 中状态组件写法会稍微不一样。

class?Counter?extends?StatefulWidget?{????//?This?class?is?the?configuration?for?the?state.?It?holds?the????//?values?(in?this?case?nothing)?provided?by?the?parent?and?used?by?the?build????//?method?of?the?State.?Fields?in?a?Widget?subclass?are?always?marked?"final".????@override????_CounterState?createState()?=>?_CounterState();??}????class?_CounterState?extends?State<Counter>?{????int?_counter?=?0;??????void?_increment()?{??????setState(()?{????????//?This?call?to?setState?tells?the?Flutter?framework?that????????//?something?has?changed?in?this?State,?which?causes?it?to?rerun????????//?the?build?method?below?so?that?the?display?can?reflect?the????????//?updated?values.?If?you?change?_counter?without?calling????????//?setState(),?then?the?build?method?won't?be?called?again,????????//?and?so?nothing?would?appear?to?happen.????????_counter++;??????});????}??????@override????Widget?build(BuildContext?context)?{??????//?This?method?is?rerun?every?time?setState?is?called,?for?instance??????//?as?done?by?the?_increment?method?above.??????//?The?Flutter?framework?has?been?optimized?to?make?rerunning??????//?build?methods?fast,?so?that?you?can?just?rebuild?anything?that??????//?needs?updating?rather?than?having?to?individually?change??????//?instances?of?widgets.??????return?Row(????????children:?<Widget>[??????????RaisedButton(????????????onPressed:?_increment,????????????child:?Text('Increment'),??????????),??????????Text('Count:?$_counter'),????????],??????);????}??}??

可以看到 Flutter 中直接使用了和 React 中同名的setState方法,不过不会有变量合并的东西,当然也有生命周期。

可以看到一个有状态的组件需要两个 Class,这样写的原因在于,Flutter 中 Widget 都是 immmutable 的,状态组件的状态保存在 State 中,组件仍然每次重新创建,Widget 在这里只是一种对组件的描述,Flutter 会 diff 转换成 Element,然后转换成 RenderObject 才渲染。

Flutter Widget 更多的渲染流程可以看这里。

实际上 Widget 只是作为组件结构一种描述,还可以带来的好处是,你可以更方便的做一些主题性的组件, Flutter 官方提供的Material Components widgets和Cupertino (iOS-style) widgets质量就相当高,再配合 Flutter 亚秒级的Hot Reload,开发体验可以说挺不错的。

State Management

setState()可以很方便的管理组件内的数据,但是 Flutter 中状态同样是从上往下流转的,因此也会遇到和 React 中同样的问题,如果组件树太深,逐层状态创建就显得很麻烦了,更不要说代码的易读和易维护性了。

InheritedWidget

同样 Flutter 也有个context一样的东西,那就是InheritedWidget,使用起来也很简单。

class?GlobalData?extends?InheritedWidget?{???final?int?count;???GlobalData({Key?key,?this.count,Widget?child}):super(key:key,child:child);???@override???bool?updateShouldNotify(GlobalData?oldWidget)?{?????return?oldWidget.count?!=?count;???}????static?GlobalData?of(BuildContext?context)?=>?context.inheritFromWidgetOfExactType(GlobalData);?}??class?MyApp?extends?StatelessWidget?{???//?This?widget?is?the?root?of?your?application.???@override???Widget?build(BuildContext?context)?{?????return?MaterialApp(???????title:?'Flutter?Demo',???????theme:?ThemeData(?????????primarySwatch:?Colors.blue,???????),???????home:?MyHomePage(title:?'Flutter?Demo?Home?Page'),?????);???}?}??class?MyHomePage?extends?StatefulWidget?{???MyHomePage({Key?key,?this.title})?:?super(key:?key);????final?String?title;????@override???_MyHomePageState?createState()?=>?_MyHomePageState();?}??class?_MyHomePageState?extends?State<MyHomePage>?{???int?_counter?=?0;????void?_incrementCounter()?{???????_counter++;?????});???}????@override???Widget?build(BuildContext?context)?{?????return?Scaffold(???????appBar:?AppBar(?????????title:?Text(widget.title),???????),???????body:?GlobalData(?????????count:?_counter,?????????child:?Center(???????????child:?Column(?????????????mainAxisAlignment:?MainAxisAlignment.center,?????????????children:?<Widget>[???????????????Text(?????????????????'You?have?pushed?the?button?this?many?times:',???????????????),???????????????Text(?????????????????'$_counter',?????????????????style:?Theme.of(context).textTheme.display1,???????????????),???????????????Body(),???????????????Body2()?????????????],???????????),?????????),???????),???????floatingActionButton:?FloatingActionButton(?????????onPressed:?_incrementCounter,?????????tooltip:?'Increment',?????????child:?Icon(Icons.add),???????),?????);???}?}??class?Body?extends?StatelessWidget?{???@override???Widget?build(BuildContext?context)?{?????GlobalData?globalData?=?GlobalData.of(context);?????return?Text(globalData.count.toString());???}?}??class?Body2?extends?StatelessWidget?{???@override???Widget?build(BuildContext?context)?{?????//?TODO:?implement?build?????GlobalData?globalData?=?GlobalData.of(context);?????return?Text(globalData.count.toString());???}?

具体实现原理可以参考这里,不过 Google 封装了一个更为上层的库provider,具体使用可以看这里。

BlOC

BlOC是 Flutter team 提出建议的另一种更高级的数据组织方式,也是我最中意的方式。简单来说:

Bloc = InheritedWidget + RxDart(Stream)

Dart 语言中内置了 Steam,Stream ~= Observable,配合RxDart, 然后加上StreamBuilder会是一种异常强大和自由的模式。

class?GlobalData?extends?InheritedWidget?{???final?int?count;???final?Stream<String>?timeInterval$?=?new?Stream.periodic(Duration(seconds:?10)).map((time)?=>?new?DateTime.now().toString());???GlobalData({Key?key,?this.count,Widget?child}):super(key:key,child:child);???@override???bool?updateShouldNotify(GlobalData?oldWidget)?{?????return?oldWidget.count?!=?count;???}????static?GlobalData?of(BuildContext?context)?=>?context.inheritFromWidgetOfExactType(GlobalData);??}??class?TimerView?extends?StatelessWidget?{????@override???Widget?build(BuildContext?context)?{?????GlobalData?globalData?=?GlobalData.of(context);?????return?StreamBuilder(?????????stream:?globalData.timeInterval$,?????????builder:?(context,?snapshot)?{???????????return?Text(snapshot?.data????'');?????????}?????);???}?}?

当然 Bloc 的问题在于

  • 学习成本略高,Rx 的概念要吃透,不然你会抓狂
  • 自由带来的问题是,可能代码不如 Redux 类的规整。

顺便,今年 Apple 也拥抱了响应式,Combine(Rx like) + SwiftUI 也基本等于 Bloc 了。

所以,Rx 还是要赶紧学起来

除去 Bloc,Flutter 中还是可以使用其他的方案,譬如:

  • Flutter Redux
  • 阿里闲鱼的Fish Redux,据说性能很好。
  • Mobx
  • ……

展开来说现在的前端开发使用强大的框架页面组装已经不是难点了。开发的难点在于如何组合富交互所需的数据,也就是上面图中的state部分。

更具体来说,是怎么优雅,高效,易维护地处理短暂数据(ephemeral state)setState()和需要共享的 App State 的问题,这是个工程性的问题,但往往也是日常开发最难的事情了,引用 Redux 作者 Dan 的一句:

“The rule of thumb is:Do whatever is less awkward.”

到这里,主要的部分已经讲完了,有这些已经可以开发出一个不错的 App 了。剩下的就当成一个 bonus 吧。

测试

Flutter debugger,测试都是出场自带,用起来也不难。

//?测试在/test/目录下面?void?main()?{???testWidgets('Counter?increments?smoke?test',?(WidgetTester?tester)?async?{?????//?Build?our?app?and?trigger?a?frame.?????await?tester.pumpWidget(MyApp());??????//?Verify?that?our?counter?starts?at?0.?????expect(find.text('0'),?findsOneWidget);?????expect(find.text('1'),?findsNothing);??????//?Tap?the?'+'?icon?and?trigger?a?frame.?????await?tester.tap(find.byIcon(Icons.add));?????await?tester.pump();??????//?Verify?that?our?counter?has?incremented.?????expect(find.text('0'),?findsNothing);?????expect(find.text('1'),?findsOneWidget);???});?}?

包管理,资源管理

类似与 JavaScript 的 npm,Flutter,也就是 Dart 也有自己的包仓库。不过项目包的依赖使用 yaml 文件来描述:

name:?app?description:?A?new?Flutter?project.?version:?1.0.0+1?environment:???sdk:?">=2.1.0?<3.0.0"??dependencies:???flutter:?????sdk:?flutter????cupertino_icons:?^0.1.2?

生命周期

移动应用总归需要应用级别的生命周期,flutter 中使用生命周期钩子,也非常的简单:

class?MyApp?extends?StatefulWidget?{???@override???_MyAppState?createState()?=>?new?_MyAppState();?}?class?_MyAppState?extends?State<MyApp>?with?WidgetsBindingObserver?{???@override???void?initState()?{?????super.initState();?????WidgetsBinding.instance.addObserver(this);???}????@override???void?dispose()?{?????WidgetsBinding.instance.removeObserver(this);?????super.dispose();???}????@override???void?didChangeAppLifecycleState(AppLifecycleState?state)?{?????switch?(state)?{???????case?AppLifecycleState.inactive:?????????print('AppLifecycleState.inactive');?????????break;???????case?AppLifecycleState.paused:?????????print('AppLifecycleState.paused');?????????break;???????case?AppLifecycleState.resumed:?????????print('AppLifecycleState.resumed');?????????break;???????case?AppLifecycleState.suspending:?????????print('AppLifecycleState.suspending');?????????break;?????}?????super.didChangeAppLifecycleState(state);???}????@override???Widget?build(BuildContext?context)?{???????return?Container();???}?}?

使用原生能力

和 ReactNative 类似,Flutter 也是使用类似事件的机制来使用平台相关能力。

Flutter Web, Flutter Desktop

这些还在开发当中,鉴于对 Dart 喜欢,以及对 Flutter 性能的乐观,这些倒是很值得期待。

相关推荐

2023年最新微信小程序抓包教程(微信小程序 抓包)

声明:本公众号大部分文章来自作者日常学习笔记,部分文章经作者授权及其他公众号白名单转载。未经授权严禁转载。如需转载,请联系开百。请不要利用文章中的相关技术从事非法测试。由此产生的任何不良后果与文...

测试人员必看的软件测试面试文档(软件测试面试怎么说)

前言又到了毕业季,我们将会迎来许多需要面试的小伙伴,在这里呢笔者给从事软件测试的小伙伴准备了一份顶级的面试文档。1、什么是bug?bug由哪些字段(要素)组成?1)将在电脑系统或程序中,隐藏着的...

复活,视频号一键下载,有手就会,长期更新(2023-12-21)

视频号下载的话题,也算是流量密码了。但也是比较麻烦的问题,频频失效不说,使用方法也难以入手。今天,奶酪就来讲讲视频号下载的新方案,更关键的是,它们有手就会有用,最后一个方法万能。实测2023-12-...

新款HTTP代理抓包工具Proxyman(界面美观、功能强大)

不论是普通的前后端开发人员,还是做爬虫、逆向的爬虫工程师和安全逆向工程,必不可少会使用的一种工具就是HTTP抓包工具。说到抓包工具,脱口而出的肯定是浏览器F12开发者调试界面、Charles(青花瓷)...

使用Charles工具对手机进行HTTPS抓包

本次用到的工具:Charles、雷电模拟器。比较常用的抓包工具有fiddler和Charles,今天讲Charles如何对手机端的HTTS包进行抓包。fiddler抓包工具不做讲解,网上有很多fidd...

苹果手机下载 TikTok 旧版本安装包教程

目前苹果手机能在国内免拔卡使用的TikTok版本只有21.1.0版本,而AppStore是高于21.1.0版本,本次教程就是解决如何下载TikTok旧版本安装包。前期准备准备美区...

【0基础学爬虫】爬虫基础之抓包工具的使用

大数据时代,各行各业对数据采集的需求日益增多,网络爬虫的运用也更为广泛,越来越多的人开始学习网络爬虫这项技术,K哥爬虫此前已经推出不少爬虫进阶、逆向相关文章,为实现从易到难全方位覆盖,特设【0基础学爬...

防止应用调试分析IP被扫描加固实战教程

防止应用调试分析IP被扫描加固实战教程一、概述在当今数字化时代,应用程序的安全性已成为开发者关注的焦点。特别是在应用调试过程中,保护应用的网络安全显得尤为重要。为了防止应用调试过程中IP被扫描和潜在的...

一文了解 Telerik Test Studio 测试神器

1.简介TelerikTestStudio(以下称TestStudio)是一个易于使用的自动化测试工具,可用于Web、WPF应用的界面功能测试,也可以用于API测试,以及负载和性能测试。Te...

HLS实战之Wireshark抓包分析(wireshark抓包总结)

0.引言Wireshark(前称Ethereal)是一个网络封包分析软件。网络封包分析软件的功能是撷取网络封包,并尽可能显示出最为详细的网络封包资料。Wireshark使用WinPCAP作为接口,直接...

信息安全之HTTPS协议详解(加密方式、证书原理、中间人攻击 )

HTTPS协议详解(加密方式、证书原理、中间人攻击)HTTPS协议的加密方式有哪些?HTTPS证书的原理是什么?如何防止中间人攻击?一:HTTPS基本介绍:1.HTTPS是什么:HTTPS也是一个...

Fiddler 怎么抓取手机APP:抖音、小程序、小红书数据接口

使用Fiddler抓取移动应用程序(APP)的数据接口需要进行以下步骤:首先,确保手机与计算机连接在同一网络下。在计算机上安装Fiddler工具,并打开它。将手机的代理设置为Fiddler代理。具体方...

python爬虫教程:教你通过 Fiddler 进行手机抓包

今天要说说怎么在我们的手机抓包有时候我们想对请求的数据或者响应的数据进行篡改怎么做呢?我们经常在用的手机手机里面的数据怎么对它抓包呢?那么...接下来就是学习python的正确姿势我们要用到一款强...

Fiddler入门教程全家桶,建议收藏

学习Fiddler工具之前,我们先了解一下Fiddler工具的特点,Fiddler能做什么?如何使用Fidder捕获数据包、修改请求、模拟客户端向服务端发送请求、实施越权的安全性测试等相关知识。本章节...

fiddler如何抓取https请求实现手机抓包(100%成功解决)

一、HTTP协议和HTTPS协议。(1)HTTPS协议=HTTP协议+SSL协议,默认端口:443(2)HTTP协议(HyperTextTransferProtocol):超文本传输协议。默认...