#前端开发技术的调研
标签(空格分隔): 未分类
标签: 移动开发
2018.1.6
名词 | 链接 | 描述 |
---|---|---|
Cordova | http://cordova.apache.org/ | Hybrid App中间件 |
ios-deploy | https://github.com/phonegap/ios-deploy | 使用命令行部署iOS项目 |
PhoneGap | https://phonegap.com/ | 使用Web快速开发、打包Hybrid APP |
Node.js | https://nodejs.org/en/ | 基于 Chrome V8 引擎的 JavaScript 运行环境。 |
npm | https://www.npmjs.com/ | Node.js的包管理器 |
Git | https://git-scm.com/ | 分布式版本控制库 |
ionic | http://ionicframework.com/ | 基于Angular的H5框架 |
Angular | https://angularjs.org/ | 优秀的前端JS框架 |
Weex | https://angularjs.org/ | 一个使用 Web 开发体验来开发高性能原生应用的框架 |
Vue.js | https://vuejs.org/ | 一套用于构建用户界面的渐进式框架 |
Rax | https://alibaba.github.io/rax/ | 一个通用的跨容器的渲染引擎 |
React.js | https://reactjs.org/ | 构建用户界面的JavaScript库 |
Flex (Flexible Box) | https://www.w3.org/TR/css-flexbox-1/ | 简便、完整、响应式地实现各种页面布局方案 |
PhoneGap100 | http://www.phonegap100.com/ | Hybrid App社区 |
本文旨在了解React Native 、Weex和 Cordova 的差异,评估几种种前端技术实现的优缺点。关于Cordova我们在之前有过介绍,本篇再简单介绍ReactNative和Weex。
React.js 不是一个框架,它只是一个库。它只提供 UI(view)层面的解决方案。在实际的项目当中,它并不能解决我们所有的问题,需要结合其它的库,例如 Redux、React-router等来协助提供完整的解决方法。
类似的还有 Vue.js
在web开发中,Facebook认为现有的MVC框架无法满足他们的扩展需求,由于他们非常巨大的代码库和庞大的组织,使得MVC很快变得非常复复杂,每当需要添加一项新的功能或特性时,系统的复杂度就成级数增长,致使代码变得脆弱和不可预测,结果导致他们的MVC正在土崩瓦解。所以认为MVC不适合大规模应用,当系统中有很多的模型和相应的视图时,其复杂度就会迅速扩大,非常难以理解和调试,特别是模型和视图间可能存在的双向数据流动。
于是Facebook推出了Flux 和React.js。
其官方表述:
We built React to solve one problem: building large applications with
data that changes over time.(构建那些数据会随时间改变的大型应用)
React的目的是为了使前端的V层更具组件化,能更好的复用,它能够使用简单的html标签创建更多的自定义组件标签,内部绑定事件,同时可以让你不再需要来回查找某个 DOM 元素,然后再操作 DOM 去更改UI,变成你只需要操作数据就会改变相应的dom。
所以 React 的核心思想是:封装组件。
各个组件维护自己的状态和 UI,当状态变更,自动重新渲染整个组件。
一个简单的例子:
import React, { Component } from 'React';
import { render } from 'React-dom';
class HelloMessage extends Component {
render() {
return Hello {this.props.name};
}
}
// 加载组件到 DOM 元素 mountNode
render( , mountNode);
####组件
React 应用都是构建在组件之上。
上面的 HelloMessage
就是一个 React 构建的组件,最后一句 render
会把这个组件显示到页面上的某个元素 mountNode
里面,显示的内容就是
。
props
是组件包含的两个核心概念之一,另一个是 state
,一个组件就是通过这两个属性的值在 render 方法里面生成这个组件对应的 HTML 结构。
props
props
就是组件的配置属性,由外部通过 JSX 属性传入设置,一旦初始设置完成,就可以认为 this.props
是不可更改的,只是在调用这个组件的时候传入不同的属性(比如这里的 name
)来定制显示这个组件。所以不要轻易更改设置 this.props
里面的值(虽然对于一个 JS 对象你可以做任何事)。
state
state
是组件的当前状态,可以把组件简单看成一个“状态机”,根据状态 state 呈现不同的 UI 展示。
一旦状态(数据)更改,组件就会自动调用 render
重新渲染 UI,这个更改的动作会通过 this.setState
方法来触发。
更多关于组件
从上面的代码可以看到将 HTML 直接嵌入了 JS 代码里面,这个就是 React 提出的一种叫 JSX 的语法,这应该是最开始接触 React 最不能接受的设定之一,因为前端被“表现和逻辑层分离”这种思想“洗脑”太久了。但实际上组件的 HTML 是组成一个组件不可分割的一部分,能够将 HTML 封装起来才是组件的完全体,前端才能实现真正意义上的组件化。
为什么会有JSX
传统的 MVC 是将模板放在其他地方,比如 标签或者模板文件,再在 JS 中通过某种手段引用模板。按照这种思路,四处分散的模板片段会带来更多的纠结,纠结模板引擎,纠结模板存放位置,纠结如何引用模板……
React 官方描述说:
We strongly believe that components are the right way to separate
concerns rather than “templates” and “display logic.” We think that
markup and the code that generates it are intimately tied together.
Additionally, display logic is often very complex and using template
languages to express it becomes cumbersome.
React 认为组件才是王道,而组件是和模板紧密关联的,组件模板和组件逻辑分离让问题复杂化了。
所以就有了 JSX 这种语法,就是为了把 HTML 模板直接嵌入到 JS 代码里面,这样就做到了模板和组件关联,但是 JS 不支持这种包含 HTML 的语法,所以需要通过工具将 JSX 编译输出成 JS 代码才能使用。
因为 JSX 最终是输出成 JS 代码来表达的,所以我们可以直接用 React 提供的这些 DOM 构建方法来写模板,比如一个 JSX 写的一个链接:
Hello!
用 JS 代码来写就成这样了:
React.createElement(‘a’, {href: ‘http://facebook.github.io/React/’}, ‘Hello!’)
更多关于JSX
当组件状态 state 有更改的时候,React 会自动调用组件的 render 方法重新渲染整个组件的 UI。
传统的web应用,操作DOM一般是直接更新操作的,当然如果真的这样大面积的操作 DOM,性能会是一个很大的问题。所以为了尽可能减少对DOM的操作,React 实现了Virtual DOM。
Virtual DOM是一个轻量级的虚拟的DOM,是React抽象出来的一个对象,组件 DOM 结构就是映射到这个 Virtual DOM 上。
React 在这个 Virtual DOM 上实现了一个 diff 算法,当要重新渲染组件的时候,会通过当前新的DOM表述与之前的作比较,计算出最小的步骤更新真实的DOM。由于 Virtual DOM 是一个纯粹的 JS 数据结构,也不是真的渲染整个 DOM 树所以性能会比原生 DOM 快很多。
更多关于Virtual DOM
####Flux
Unidirectional data flow (单向数据流)是Facebook十分推崇的架构方式。
应用架构的方式,也就是数据如何存放,如何更改数据,如何通知数据更改等等,所以它不是 React 提供的额外的什么新功能,可以看成是使用 React 构建大型应用的一种最佳实践。当应用足够复杂时才能体会到它的好处,虽然在一般应用场景下你可能不会意识到它的存在,也不会影响你开始使用 React。
正因为它是这样一种概念,所以涌现了许多实现,比如Facebook官方的 Flux 和更优雅的 Redux。
简单了解下FB官方Flux实现
React 是 MVC 里面 V 的部分,那么 Flux 就相当于添加 M 和 C 的部分。
一个 Flux 应用主要包含四个部分:
名称 | 描述 |
---|---|
the dispatcher | 处理动作分发,维护 Store 之间的依赖关系 |
the stores | 数据和逻辑部分 |
the views | React 组件,这一层可以看作 controller-views,作为视图同时响应用户交互 |
the actions | 提供给 dispatcher 传递数据给 store |
针对上面提到的 Flux 这些概念,需要写一个简单的类库来实现衔接这些功能,市面上有很多种实现,这里简单了解Facebook 官方的一个实现 Dispatcher.js
单向数据流
先来了解一下 Flux 的核心“单向数据流“怎么运作的:
Action -> Dispatcher -> Store -> View
更多时候 View 会通过用户交互触发 Action,所以一个简单完整的数据流类似这样:
整个流程如下:
所有的状态都由 Store 来维护,通过 Action 传递数据,构成了如上所述的单向数据流循环,所以应用中的各部分分工就相当明确,高度解耦了。
这种单向数据流使得整个系统都是透明可预测的。
更多关于Flux
更多关于Redux
React Native其实就是Native版的React,用javascript直接开发原生APP
React 在前端取得突破性成功以后,JavaScript 布道者们开始试图一统三端。移动平台能够运行 JavaScript 代码,并且JavaScript不仅可以传递配置信息,还可以表达逻辑信息的优点。
于是2015年,Facebook推出了基于 JavaScript 的开源框架 React Native。
这是一个面向前端开发者的框架,它结合了 Web 应用和 Native 应用的优势,在 JavaScript 中用 React 抽象操作系统原生的 UI 组件,代替 DOM 元素渲染,它的宗旨是让前端开发者像用 React 写网页那样,用 React Native 写移动端应用。
不同于跨平台语言的 Write once, Run anywhere!
,React Native追求的是Learn once,Write anywhere!
,希望前端开发者学习完 React 后,能够用同样的语法、工具等分别开发安卓和 iOS 平台的应用,而不用再去学习两个平台不同的技术。
当创建完React Native 工程后,app.js文件就是开发者的主要实现文件。
这是一个Hello world的React Native 程序中app.js的内容。
import React, { Component } from 'React';
import { AppRegistry, Text, View } from 'React-Native';
class Greeting extends Component {
render() {
return (
Hello {this.props.name}!
);
}
}
export default class LotsOfGreetings extends Component {
render() {
return (
);
}
}
// skip this line if using Create React Native App
AppRegistry.registerComponent('AwesomeProject', () => LotsOfGreetings);
正如上面所介绍的React.js ,React Native 和前者十分类似,比如Props
和State
。
事实上,对于React-Native开发,仅仅有基础前端开发的知识是不够的,你还需要了解和掌握的有:
因为是用JavaScript来写原生应用,所以要明确的是,即使使用了 React Native,我们依然需要 UIKit 等框架,最终执行的是 Objective-C 代码。总React Native 只不过是以 JavaScript 的形式告诉 Objective-C 该执行什么代码。
所以React Native 能够运行起来,全靠 Objective-C 和 JavaScript 的交互。
React Native用iOS自带的JavaScriptCore作为JS的解析引擎,但并没有用到JavaScriptCore提供的一些可以让JS与OC交互的特性,而是自己实现了一套机制,这套机制可以通用于所有JS引擎上。
React Native详细设计比较复杂,这里简单了解下是如何调用原生代码的。
####React Native如何匹配原生API
先看一下React Native启动流程(iOS)
1.创建RCTRootView -> 设置窗口根控制器的View,把RN的View添加到窗口上显示。
2.创建RCTBridge -> 桥接对象,管理JS和OC交互。
3.创建RCTBatchedBridge -> 批量桥架对象,JS和OC交互具体实现。
4.执行[RCTBatchedBridge loadSource] -> 加载JS源码
5.执行[RCTBatchedBridge initModulesWithDispatchGroup] -> 创建OC模块表
6.执行[RCTJSCExecutor injectJSONText] -> 往JS中插入OC模块表
7.执行完JS代码,回调OC,调用OC中的组件
8.完成UI渲染
可以知道React Native会在一开始生成OC模块配置表,然后把这个模块配置表传入JS中。JS调用OC模块方法时,通过配置表把模块方法转为模块ID和方法ID传给OC,OC通过模块配置表找到对应的方法执行之。
####通讯原理
下面我们简单了解一下iOS端通讯的原理。
1.JS端调用某个OC模块暴露出来的方法。
2.把上一步的调用分解为ModuleName,MethodName,arguments,再扔给MessageQueue处理。
在初始化时模块配置表上的每一个模块都生成了对应的remoteModule对象,对象里也生成了跟模块配置表里一一对应的方法,这些方法里可以拿到自身的模块名,方法名,并对callback进行一些处理,再移交给MessageQueue。
3.在这一步把JS的callback函数缓存在MessageQueue的一个成员变量里,用CallbackID代表callback。在通过保存在MessageQueue的模块配置表把上一步传进来的ModuleName和MethodName转为ModuleID和MethodID。
4.把上述步骤得到的ModuleID,MethodId,CallbackID和其他参数argus传给OC。把ModuleID,MethodID等数据加到一个队列里,等OC过来调JS的任意方法时,再把这个队列返回给OC,此时OC再执行这个队列里要调用的方法。
5.OC接收到消息,通过模块配置表拿到对应的模块和方法。
6.RCTModuleMethod对JS传过来的每一个参数进行处理。
RCTModuleMethod可以拿到OC要调用的目标方法的每个参数类型,处理JS类型到目标类型的转换,所有JS传过来的数字都是NSNumber,这里会转成对应的int/long/double等类型,更重要的是会为block类型参数的生成一个block。参数组装完毕后,通过NSInvocation动态调用相应的OC模块方法。
7.OC模块方法调用完,执行block回调。
8.调用到第6步说明的RCTModuleMethod生成的block。
9.block里带着CallbackID和block传过来的参数去调JS里MessageQueue的方法invokeCallbackAndReturnFlushedQueue。
10.MessageQueue通过CallbackID找到相应的JS callback方法。
11.调用callback方法,并把OC带过来的参数一起传过去,完成回调。
整个流程就是这样,简单概括下:JS函数调用转ModuleID/MethodID -> callback转CallbackID -> OC根据ID拿到方法 -> 处理参数 -> 调用OC方法 -> 回调CallbackID -> JS通过CallbackID拿到callback执行。
###Weex
Weex的官方网站
Weex 是阿里巴巴2016年6月开源的跨平台开发方案。能以 web 的开发体验构建高性能、可扩展的 native 应用,Weex 与 Vue 合作,使用 Vue 作为上层框架,并遵循 W3C 标准实现了统一的 JSEngine 和 DOM API,可以使用其他框架驱动 Weex,打造三端一致的 native 应用。同React Native一样,有人也将WEEX叫做Vue Native。
所以免不了被拿来同React Native“一决高下”的命运。React Native宣称「Learn Once, Write Anywhere」,而Weex宣称「Write Once, Run Everywhere」
对于推出该技术的原因,官方介绍如下
其特点是:
web 开发体验在各端当中是相同的。包括语法设计和工程链路。
Weex 的组件、模块设计都是 iOS、Android、Web 的开发者共同讨论出来的,有一定的通用性和普遍性。
Weex 开发同一份代码,可以在不同的端上分别执行,避免了多端的重复研发成本。
在同构这条路上,Weex比React Native做得更彻底,他“几乎”做到了,“你来使用vue写一个webapp,我顺便给你编译成了ios和android的原生app”
可以认为WEEX其实是阿里巴巴团队提高生产效率的产物,在淘宝这类要求多端统一迭代快速的部门,三端约定一种便于统一的规范,在加上时间的发酵,渐渐的就有了此类脚手架的雏形,同时在脸书ReactNative开源带来的极大轰动后,也有KPI推动的嫌疑
原理
在 Weex 代码中,您可以使用 ,