为了应对公司业务需求以及我本身技术能力的提升,在得知本组成员已在开发的项目所用的技术栈后,我便开始了对 Taro+React+TypeScript 的学习,因为主要是应对小程序的开发,我本身有过一段时间的原生小程序开发经验,而 Taro 的官方文档组件库都是从微信小程序组件官方文档参考而来,因此对于组件的使用只需注意一下两者之间的一些差异即可。
# React
本次学习用时最多的应当是对 React 的学习,因为在此之前我对于 React 的了解是零,因此我便打开了 React 的官方文档,本以为又要面对一份枯燥而无味的概念学习,没想到 React 官方文档应对不同学习方式的开发者准备了两种学习方式:
- 喜欢边学边做的开发者:用 React 开发一个井字棋(tic-tac-toe);
- 专为喜欢逐步学习概念的开发者:React 核心概念逐步学习指南。
井字棋游戏教程和逐步学习指南是互补的,因此我肯定毫无疑问是先选择学习用 React 开发一个井字棋游戏,我本人是不太喜欢一上来就来一大堆概念知识的轰炸的,边学边做能让我学习更快。
# 井字棋游戏
在跟着 React 官方文档的一步步教程以后,我便完成了一个简单的井字棋游戏,也基本掌握了 React 的使用,理解了 React 的基础知识:组件、props 和 state,还有状态提升、函数式组件等。文档还在最后给出了改进游戏的 6 点想法参考,于是我利用之前所学到的基础知识用自己的方式实现了如下改进并完善后的井字棋游戏 (opens new window):
(opens new window)
# 核心及高级概念学习
后续我还是将 React 核心概念逐步学习指南给啃完了,在有了实践的基础后再去看概念知识还是很容易接受并理解的,于是对 React 又有了更清晰的理解。从JSX (opens new window)到元素渲染 (opens new window),组件 & Props (opens new window)、State & 生命周期 (opens new window)、事件处理 (opens new window)、条件渲染 (opens new window)、列表 & Key (opens new window)、表单 (opens new window)、状态提升 (opens new window)、组合 vs 继承 (opens new window)这一整套概念学习下来,相信应对简单的 React 项目我已经可以有信心应对并上手了! 在后续的高级指引章节里,里面有许多很难理解的点,因为没有实际写过项目,因此在阅读时无法感受到所看到的高级功能能用在哪些地方,解决什么问题,这个我还是得接触到实际的项目遇到问题然后解决问题才能深刻理解了。其中的 Hook 是一个比较吸引我的点,Hook 是 React 16.8 的新增特性,它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。Hook 是一个特殊的函数,它可以让你“钩入”React 的特性。因此使用 Hook 编写 React 就不需要使用 class 来定义组件了,用函数组件即可,而且也不需要再定义 state 了,也不需要再写 this.setState 来更新 state 状态了,其实这种用一个函数来更新变量并更新视图的方式与原生微信小程序的编写方式极其类似。主要还是熟悉了一下State Hook (opens new window)和Effect Hook (opens new window)的使用方式。 到此为止,对于 React 的学习便到此结束了,接下来只需拿到实际项目继续深钻了。
# Taro
# 介绍
Taro 是一个开放式跨端跨框架解决方案,支持使用 React/Vue/Nerv 等框架来开发 微信 (opens new window) / 京东 (opens new window) / 百度 (opens new window) / 支付宝 (opens new window) / 字节跳动 (opens new window) / QQ (opens new window) 小程序 / H5 / RN 等应用。 现如今市面上端的形态多种多样,Web、React Native、微信小程序等各种端大行其道。当业务要求同时在不同的端都要求有所表现的时候,针对不同的端去编写多套代码的成本显然非常高,这时候只编写一套代码就能够适配到多端的能力就显得极为需要。
通俗点讲,就是只需要按照 Taro 的规范编写一套代码就可以转换到 H5、ReactNative 以及任意小程序平台,这对于要开发一个需要应对不同平台的移动端应用来说简直就是福音,只需要花一些时间学习 Taro 的开发方式就能应对所有移动端业务!
# 学习
很开心,Taro 官方文档中也给出了类似 React 的边学边做教程:【教程】5 分钟上手 Taro 开发 (opens new window),甚至还有视频!【视频】5 分钟快速上手 Taro 开发小程序 (opens new window),这对于我这种理论学渣来讲可太 nice 了!话不多说,直接上视频,跟着视频做完了简单的 ToDoList 后再消化渐进式入门教程,学习效率那是相当的可观! 下面是一个简单的 Taro 应用从项目初始化与编译到多端打包的一整套流程:
# 第一步:项目初始化与编译
// 1. 首先全局安装Taro工具:
npm install -g @tarojs/cli
// 2. 项目初始化 按照提示一步步初始化项目即可
// 在创建完项目之后,Taro 会默认开始安装项目所需要的依赖
taro init
// 3. 编译运行
// 使用 Taro 的 build 命令可以把 Taro 代码编译成不同端的代码,然后在对应的开发工具中查看效果
// Taro 编译分为 dev 和 build 模式:
// dev 模式(增加 --watch 参数) 将会监听文件修改。
// build 模式(去掉 --watch 参数) 将不会监听文件修改,并会对代码进行压缩打包。
// 以微信小程序为例:
taro build --type weapp
2
3
4
5
6
7
8
9
10
11
12
13
14
# 第二步:项目预览
下载并打开微信开发者工具 (opens new window),然后选择项目根目录进行预览。(如果是别的平台小程序只需要下载平台对应的开发者工具进行预览即可) 需要注意开发者工具的项目设置:
- 需要设置关闭 ES6 转 ES5 功能,开启可能报错
- 需要设置关闭上传代码时样式自动补全,开启可能报错
- 需要设置关闭代码压缩上传,开启可能报错
# 第三步:编写项目代码
此时就需要根据项目原型图或设计稿并参考 Taro 组件库文档来编写页面代码了。对于组件库代码只需要了解有哪些组件即可,不需要全部记住应该怎么用,要使用的时候直接查阅文档即可。
# 第四步:多端编译
找到 config 文件夹下的 index.js 文件,修改其中的 outputRoot 字段为:dist/${process.env.TARO_ENV}
,这样就可以分开不同平台生成的代码位置了,环境变量 TARO_ENV 可以配置微信、支付宝、百度头条等多种不同平台,使用 taro build type + 平台名称即可在 dist 目录中生成针对不同平台的编译代码。
到这里,使用 Taro 多端开发的流程就走通啦!
接下来便是了解一下关于 Taro 开发的一些细节(以下皆取自Taro 官方文档 (opens new window)):
# Taro 开发基础
# 目录结构
├── dist 编译结果目录
|
├── config 项目编译配置目录
| ├── index.js 默认配置
| ├── dev.js 开发环境配置
| └── prod.js 生产环境配置
|
├── src 源码目录
| ├── pages 页面文件目录
| | └── index index 页面目录
| | ├── index.js index 页面逻辑
| | ├── index.css index 页面样式
| | └── index.config.js index 页面配置
| |
| ├── app.js 项目入口文件
| ├── app.css 项目总通用样式
| └── app.config.js 项目入口配置
|
├── project.config.json 微信小程序项目配置 project.config.json
├── project.tt.json 字节跳动小程序项目配置 project.config.json
├── project.swan.json 百度小程序项目配置 project.swan.json
├── project.qq.json QQ 小程序项目配置 project.config.json
|
├── babel.config.js Babel 配置
├── tsconfig.json TypeScript 配置
├── .eslintrc ESLint 配置
|
└── package.json
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
和小程序规范一样,Taro 包含一个描述整体程序的 app 和多个描述各自页面的 page。
# 编译配置
编译配置存放于项目根目录下的 config 目录中,包含三个文件:
- index.js 是通用配置
- dev.js 是项目预览时的配置
- prod.js 是项目打包时的配置
下面是默认配置中每个字段的中文注释
const config = {
// 项目名称
projectName: 'swzj',
// 项目创建日期
date: '2021-11-30',
// 设计稿尺寸
designWidth: 750,
// 设计稿尺寸换算规则
deviceRatio: {
640: 2.34 / 2,
750: 1,
828: 1.81 / 2,
},
// 项目源码目录
sourceRoot: 'src',
// 项目产出目录
outputRoot: `dist/${process.env.TARO_ENV}`,
// Taro 插件配置
plugins: [],
// 全局变量设置
defineConstants: {},
// 文件 copy 配置
copy: {
patterns: [],
options: {},
},
// 框架,react,nerv,vue, vue3 等
framework: 'react',
// 小程序端专用配置
mini: {
postcss: {
autoprefixer: {
enable: true,
},
// 小程序端样式引用本地资源内联配置
url: {
enable: true,
config: {
limit: 10240,
},
},
cssModules: {
enable: false, // 默认为 false,如需使用 css modules 功能,则设为 true
config: {
namingPattern: 'module', // 转换模式,取值为 global/module
generateScopedName: '[name]__[local]___[hash:base64:5]',
},
},
},
// 自定义 Webpack 配置
webpackChain(chain, webpack) {},
},
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# React 开发注意事项
内置组件使用规范
- 在 React 中使用 Taro 内置组件前,必须从 @tarojs/components 进行引入。
- 组件属性遵从大驼峰式命名规范。
- 事件规范:
- 内置事件名以 on 开头,遵从小驼峰式(camelCase)命名规范。
- React 中点击事件使用 onClick。
事件和 Web 端一样。在事件回调函数中,第一个参数是事件对象,回调中调用 stopPropagation 可以阻止冒泡。
事件
阻止滚动穿透
因为事件都以 bind 的形式进行绑定,因此不能使用 e.stopPropagation() 阻止滚动穿透。 针对滚动穿透,目前总结了两种解决办法:
- 使用样式解决:禁止被穿透的组件滚动 (opens new window)。这也是最推荐的做法。
- 可以为 View 组件增加 catchMove 属性。
<View catchMove></View>
dataset
dataset 是小程序的特性。 dataset 是特别的模版属性,主要作用是可以在事件回调的 event 对象中获取到 dataset 相关数据。这点 Taro 是支持的,在事件回调对象中可以通过 event.target.dataset 或 event.currentTarget.dataset 获取到。 Taro 对于小程序 dataset 的模拟是在小程序的逻辑层实现的。并没有真正在模板设置这个属性。 但在小程序中有一些 API(如:createIntersectionObserver)获取到页面的节点的时候,由于节点上实际没有对应的属性而获取不到。 这时可以考虑使用 taro-plugin-inject (opens new window) 插件注入一些通用属性,如:
const config = {
plugins: [
[
'@tarojs/plugin-inject',
{
components: {
View: {
'data-index': "'dataIndex'",
},
ScrollView: {
'data-observe': "'dataObserve'",
},
},
},
],
],
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
生命周期触发机制
React 组件的生命周期方法在 Taro 中都支持使用。 触发时机:
1. componentWillMount ()
onLoad 之后,页面组件渲染到 Taro 的虚拟 DOM 之前触发。
2. componentDidMount ()
页面组件渲染到 Taro 的虚拟 DOM 之后触发。 此时能访问到 Taro 的虚拟 DOM(使用 React ref、document.getElementById 等手段),并支持对其进行操作(设置 DOM 的 style 等)。 但此时不代表 Taro 的虚拟 DOM 数据已经完成从逻辑层 setData 到视图层。因此这时无法通过 createSelectorQuery 等方法获取小程序渲染层 DOM 节点。 只能在 onReady 生命周期中获取。
Ref
在 Taro 中 ref 的用法和 React 完全一致,但是获取到的 “DOM” 和浏览器环境还有小程序环境都有不同。
React Ref
使用 React Ref 获取到的是 Taro 的虚拟 DOM,和浏览器的 DOM 相似,可以操作它的 style,调用它的 API 等。 但是 Taro 的虚拟 DOM 运行在小程序的逻辑层,并不是真实的小程序渲染层节点,它没有尺寸宽高等信息。
获取小程序的 DOM
获取真实的小程序渲染层节点,需要在 onReady 生命周期中,调用小程序中用于获取 DOM 的 API。
// onReady 触发后才能获取小程序渲染层的节点
Taro.createSelectorQuery()
.select('#only')
.boundingClientRect()
.exec((res) => console.log(res));
2
3
4
5
Hooks
小程序页面的方法,在 Taro 的页面中同样可以使用:在 Class Component 中书写同名方法、在 Functional Component 中使用对应的 Hooks。 Hooks 文档 (opens new window)
# 结语
到此为止,应对 Taro+React 的实际项目我已能上手并做业务迭代了,我还会面对到许多各种各样的实际项目开发问题,面对困难并想办法解决之后相信我对于 React 及 Taro 还有移动端开发的理解将会更上一层楼!