指尖前端重构React技术调研分析

243次阅读

共计 5444 个字符,预计需要花费 14 分钟才能阅读完成。

一、为什么选择 React

React 是当前前端应用最广泛的框架。三大 SPA 框架 Angular、React、Vue 比较。

  • Angular 出现最早,但其在原理上并没有 React 创新的性能优化,且自身相对来说显得笨重。
  • Vue 出现最晚,其核心原理学习了 React,只是语法形式的变化,关系上来说 React 是开拓者,Vue 是学习者。
  • React 社区有强大活力与创新能力,不断涌现革命性的创新产品,其中包括可以使用 JS 操作原生控件的 React Native,Vue 后来跟进学习出了类似的 Weex,但两者成熟度差很多。

目前来看 React 的生态系统要比 Vue 大的多,在 github、stackoverflow 等最大的技术社区搜索两者,React 的搜索结果是 Vue 的十倍左右,另外据近期统计使用 React 的站点是 Vue 的几百倍以上。更大的生态意味着更多可用的资源,以及遇到问题可以得到更多的有效参考与帮助,这也是除了性能之外选择 React 的核心原因。

选择 React 之后,应用会在以下几个方面有提升。

  • 第一,原先的 html 间跳转会有短暂的白屏现象,这一点在安卓性能较差的机器上尤为明显,而 React 作为单页应用没有这个问题。
  • 第二,React 提供的虚拟 DOM 包含 Diff 算法,即将原 dom copy 一份,与改动后的 dom 对比,只渲染不同的 dom 节点,实现最小代价渲染,vdom 创新的性能优化方式对性能的提升毋庸置疑。
  • 第三,React 中核心组件化技术,更加容易的绑定事件行为,动态更新特定的 dom,代码更加模块化,重用代码更容易,结构清晰易维护。

二、在移动端使用 React

三大框架在移动端分别有自己的东西。Angular 的 ionic,React 的 React Native,Vue 的 Weex。其中 ionic 是基于 cordova 技术,依然是浏览器应用。而后两者已上升到操作原生控件的层面,做出来的是原生界面,其中 React Native 的成熟度远高于 Weex,已经被很多公司使用,而 Weex 使用者很少。

综合来看选择 React 生态明显最佳,由当前的 cordova 过渡为 cordova+Reactjs,然后可以平滑地过渡到 React Native,媲美原生性能的最优混合开发方式。之所以说平滑是因为 React Native 中近 90% 的代码(JS)可以在 IOS 和 Android 端使用,剩余的涉及原生的代码也基本可以找到可用的资源,就像 cordova 的插件一样。毕竟如果需要同时掌握 JS, OC(或 swift),java(或 kotlin)才能开发 React Native 的话,那这门技术不会有人用;当然反过来如果有原生开发知识的话会对开发 React Native 有一定帮助。

直接转型为 React native 的话涉及了应用底层架构的变动,有比较大的跨度,而转为 cordova+Reactjs 相对容易,而由 cordova+Reactjs 到 React Native 同样容易不少,因为其中大部分 Reactjs 代码可以重用。

三、Reactjs 开发工具的选择

首先开发脚手架官方出了 Create-react-app,集成了 webpack- 当前最流行的打包工具,babel- 提高 js 版本兼容性的转码器,以及 ESLint- 代码检测工具和其它一些常用工具,同时对这些工具进行了比较优的配置。值得一提的是该脚手架将这些工具的配置文件进行了隐藏,本意是让使用者专注于编码即可,但实际使用时通常会有自己配置的需求,此时执行 npm run eject 即可出现被隐藏配置文件。

React-router 是官方推荐的路由管理工具,由于是单页应用区别于原先的 html 界面间跳转,跳转实质是在组件间进行,所以需要有路由管理工具来统一化管理。这里值得一提的是,React-router 配合 webpack 可以实现代码的按需加载。

一般来说,webpack 打包后会在生成一个压缩的 js 文件,在单页应用打开会整体加载这个文件,由于该 js 文件包含之前所有的 js 代码,虽然进行了压缩,一般仍至少有几百 kb,当应用稍微复杂点,打包后文件会相应变大。而加载的时候,不管那些代码有没有执行到,都会下载下来并进行加载,造成性能浪费,这一点在显然在 web 端很重要,而在 cordova 中是将 js 代码直接打包在本地,等于跳过了下载步骤但仍然会有加载过程。通过在 router 中写 require.ensure 代码并在 webpack 中相应地修改配置即可将 js 分成多个文件,在需要时加载对应的 js 文件,实现按需加载。

Redux 是应用最广泛的第三方状态管理工具,其作用是当应用中多数据状态交互时,可以更有方便且代码结构清晰地统一管理状态,下图给出了形象的阐释。由于在实际开发中一般是分人员 / 分功能模块独立开发,考虑引入 redux 的成本(redux 本身略复杂),可以在没有多数据交互的模块不使用 redux,而在类似涉及增删改查的表单以及即时通讯 websocket 等契合 redux 的模块使用。

为项目选取合适 UI 组件库,一定程度上简化 UI 样式的开发并且考虑使用其提供的过渡动画效果。这方面有比较多的选择,Google Material Design 风格的 Material-UI 在 github 上最受欢迎,但其设计语言与我们当前 APP 截然不同,腾讯的 weui 和阿里的 antd-mobile 较为相近,其中 antd-mobile 与 create-react-app 脚手架配合使用时配置项比较繁杂,因为阿里本意是用来配合自己的脚手架 dva(封装了 react-router 和 redux),因此暂时选择 weui,后期开发有特定组件需求可结合其他 ui 库使用。

至于页面跳转时的过渡动画,有些 UI 库给出了一些过渡样式,比如 touchstone。但该库已不再维护,文档不佳,且与新版本的 react-router 配合使用有不兼容情况。后来浏览官方文档发现官方有动画库 react-addons-css-transition-group,使用该库结合 css3 的动画三件套 animation,transition,transform 即可实现各种动画效果包括基本的过渡效果,比如渐进平移等。

另外关于 css,因为是单页应用,所以如果不加处理,直接 import css 文件的话最终打包生成一个 css 文件会导致样式应用到全局,造成同类名样式相互污染影响。解决这个问题有很多种方案。Facebook 积极探索 css in js 方式,但直接写内联样式代码可读性太差。目前解决方案中应用最广泛的是 css-modules,即在 webpack 配置中开启 module 选项,使用 styles 对象来写样式。

解决的原理是将 css 类名在打包后编译成哈希字符串,保持其唯一性。但当想要使用全局样式时要再配置,稍显繁杂,且它类名编写的方式为对象的方式,需要整体修改,另外在使用它时,发现不支持 - 横线的类命名方式,支持下划线方式,推荐驼峰式,而我们之前 html 中的样式类名大多是横线命名,这意味着原 html 和 css 中的类名都要对应修改,考虑到样式类名非常多,这一方式舍弃。

另外有基于 css-modules 使用高阶组件的 react-css-modules 使用人数也比较多,允许横线命名方式且全局本地样式区分简单,但有 benchmark 测试表明其会较大程度拖累性能,所以也舍弃。解决这个问题要最大程度兼容原先 css 的写法,即改动最小,因为之前的 css 类样式数量庞大。

Webpack css-loader 有个属性 :local 加上之后类会变成局部作用域,即 webpack 会对该类型的类进行自动哈希转码处理,但显然类名一个个加:local 是有些呆板的工作,于是想到可以利用 scss 的嵌套属性将:local 在一个 css 文件中统一加到类名前。这里涉及到在脚手架 create-react-app 添加对 scss 的支持,在命令行执行安装,并在 package.json 的 scripts 中添加 watch-css 指令,将原 css 文件改为 scss 文件,然后在最外层添加:local,执行 watch-css 命令,即可在 scss 文件旁自动产生 css 文件,且类名前自动添加:local 前缀,这种方法实践中发现并非所有类的样式都与:local 兼容良好,相应的可以使用文件名代替:local,要做的就是保持文件名的唯一性,这一点原工程下的文件名已满足。这样原先文件中引入 css 的方式,全局 css 引入的方式都不需要变化,做到最小代价。

scss 是 sass 3 引入新的语法,其语法完全兼容 css3,并且继承了 sass 的强大功能,sass 和 less 是前端扩充 css 常用的方式,添加了嵌套,变量,继承等语法,但需要编译成 css 来最终使用(稳定性考虑)。

四、Reactjs 和 cordova 结合有哪些需要注意的

开发 Reactjs 使用官方提供的脚手架 Create-react-app,最终通过 npm run build 生成一个单页网页应用,放入 cordova 的 www 目录下即可。由于这两部分开发时独立,而原先开发是在含 www 目录的 cordova 工程目录下直接开发,这种不同会产生一些问题。比如 cordova 中某些插件安装后 export 函数或者变量供引入使用,因为一开始是分离的,在 create-react-app 中并找不到这些变量,就造成在 build 的时候产生变量 undefined 的错误,尽管最终放到 cordova 工程中后可以找到变量并正常运行,但在第一步 react 开发时控制台报一堆 error 会妨碍调试,影响开发体验。

在 github 上有一些 react cordova 库,但实质上它们都需要通过 npm run build 来打包,所以并没有解决引入插件变量的问题,且会与 create-react-app 有相斥的地方。所以要想办法使插件提供的变量在 React 中不报错,这里在不影响 ESLint 检错机制的情况下可以采取迂回的方式。Build 时控制台报错仅针对 src 文件夹下的代码,而在 public 文件夹下还有个 index.html 这个文件会最终被打包放到 www 目录下,因此可以在这个文件中 deviceready 时添加全局的插件变量(注意该类全局变量的唯一性,可以添加 plugin 前缀或使用命名空间等方式保证),并将值传给 src 目录下的代码中,这样即可绕过控制台 build 以及调试时的报错。

另外一个小技巧可以将 react 工程直接放在 cordova 工程目录下,指定最终 build 生成的文件放入 www 目录下,省掉手动转移文件的过程。

还有需要注意的一点是由于 React 中默认配置的公共路径是绝对路径,当放在 cordova 中时需要使用 file 协议放本地,需要在 webpack 的 production 配置的 public 路径前加 ”.”,或者在 package.json 文件增加一行 ”homepage”: “../www” 或 ”homepage”: “.” 改为相对路径,否则会出现找不到文件的情况,这里推荐最后一种方式。

五、React 项目的目录结构

首先 IDE 选取 webstorm,功能强大,之前项目组在用可以沿用下来,但需要注意的一点是当目录中包含了安装的依赖 node_modules 时,由于该文件夹下文件数量非常多,webstorm 在智能建立代码关联时会占用大量资源,在某些电脑上会偶尔会出现卡死现象,这一现象在我配置比较高(固态硬盘加 8g 运存)的电脑上同样出现了,解决办法是在 file-setting-File types 中配置 ignore node_modules 文件夹。

上图是 create-react-app 项目的目录,主要代码放在 src 目录下。Components 中包含所有组件。React 严格地执行组件技术,组件化不仅方便重用,同样可以将一个页面清晰地分割为几个部分最后放入一个父组件展示,因为 jsx 技术将 js 和 html 放在了一起,分割后每个部分有自己的功能逻辑与页面展示,这样更加清晰易维护。事实上 react 提出了一切皆组件的思想,只是有的组件 render 了部分界面,而有的没有 render。

上图中 components 下有 common 文件用来放项目成员自己写的公用组件比如公共请求方法等,external 放外部引入的组件,work_log 里放的是我写的工作日志模块的组件,各个功能模块都各自创建一个文件夹,命名规则统一使用下划线方式,这也是之前使用的方式。具体功能模块的划分与层级关系可以参考之前的.

值得一提的,以前 html 的层级关系必须严格为两层(涉及到跳转路径的逻辑),导致最后出现没有把一个功能模块放到一个文件夹里的情况,比如上面的工作日志之前所包含的各个文件直接和其它的一些功能模块一起放到了 setting 文件夹内。而现在只要在 React-router 统一配置好路由,实质上是往某个组件跳转,不存在跳转路径的限制。

Constants 文件夹下存放各种常量,比如各种接口路径。Fonts 存放字体图标文件,images 存放图片,redux 文件夹下是 redux 的几个组成部分,styles 下放 scss/css 样式文件。Index.js 是入口也是最顶层的父组件,router.js 则维护了整个应用的路由关系。

上面就是本次调研的技术分析文档,浏览大量技术文档,开源社区以及技术论坛并结合实践摸索得出的选型思路和理由,可能依然会有一些点没有添加进去,以后会结合新知识和实践继续完善。

作者:梁鑫

来源:宜信技术学院

正文完
 0