在Taro官方文档的基础上做了一些修改,以下只讲和官方默认配置不同的部分

一 核心库的选择

1.1 官方脚手架@tarojs/cli版本

2.x的RN端不支持样式表达式的语法,导致classnames库不能使用,动态样式的写法会异常繁琐。
2.x的RN端读取无法读取本地图片,因此放弃使用2.x,待上述bug解决后再升级。
目前工程以1.x最后一个版本,1.3.34为基础进行开发,对RN的兼容性最全面

1.2 状态管理框架选型

由于会员中台项目中大量使用了mobx框架,可供参考代码很多,而且mobx书写简约,学习成本低,因此选择mobx作为工程的状态管理框架
工程的所有mobx数据模型放置在src/store目录下

1.3 css预处理器选型

由于scss已在公司多个项目中有成熟的使用,因此继续选择scss作为css预处理器

1.4 图标库

统一使用阿里的iconfont图标库
引入方式:https://github.com/iconfont-cli/taro-iconfont-cli

1.5 网络请求库

尝试过接入taro-axios库,但是RN端在Android上无法加载bundle包,因此放弃,使用官方的request API即可

二 项目组织规范

D2125F4C-5372-4368-80ED-2D92313565FD.png
入口文件 app.js 里面引入的样式就是全局样式,本地样式会覆盖全局样式

三 工程编译配置

3.1 配置SCSS代码编译

为了支持在 scss 中通过别名(@ 或 ~)引用需要指定路径,需要在编译插件中设置

  1. //根据引入scss文件的方式的不同,获取对应文件的绝对地址
  2. const sassImporter = function(url) {
  3. if (url[0] === '~' && url[1] !== '/') {
  4. return {
  5. //引入以~/开头的地址,路径对应 ../node_modules/xxx
  6. file: path.resolve(__dirname, '..', 'node_modules', url.substr(1))
  7. }
  8. }
  9. //如果引入以@styles开头的地址,路径对应 ../styles/xxx,否则照原路径引入
  10. const reg = /^@styles\/(.*)/
  11. return {
  12. file: reg.test(url) ? path.resolve(__dirname, '..', 'src/styles', url.match(reg)[1]) : url
  13. }
  14. }
  15. //编译插件中配置scss文件的导入规则
  16. plugins: {
  17. sass: {
  18. importer: sassImporter
  19. }
  20. }

3.2 关键目录配置别名

  1. alias: {
  2. '@pages': path.resolve(__dirname, '..', 'src/pages'),
  3. '@utils': path.resolve(__dirname, '..', 'src/utils'),
  4. '@store': path.resolve(__dirname, '..', 'src/store'),
  5. '@assets': path.resolve(__dirname, '..', 'src/assets'),
  6. '@styles': path.resolve(__dirname, '..', 'src/styles'),
  7. '@constants': path.resolve(__dirname, '..', 'src/constants'),
  8. '@components': path.resolve(__dirname, '..', 'src/components')
  9. },

3.3 h5端开发时的跨域配置

  1. h5: {
  2. devServer: {
  3. proxy: {
  4. '/api':{
  5. target: 'https://xxx.com',//后台接口地址
  6. changeOrigin: true
  7. }
  8. }
  9. }
  10. }

3.4 自定义RN模块名称

  1. rn: {
  2. appJson: {
  3. // NOTE taro-native-shell 中默认用的是 taroDemo,所以这里必须与之对应
  4. name: 'taroDemo'
  5. }
  6. }

四 环境分离配置

4.1 分离配置文件

脚手架默认提供了两个环境,开发环境和生产环境,对应编译配置在config文件夹下
index为通用配置,其他两个为环境专属配置

4.2 定义全局变量

  1. defineConstants: {
  2. HOST:'"https://api.apiopen.top"' //必须是json字符串,注意写法
  3. }

不同环境配置文件定义相同的全局变量时,系统会自动读取想对应的变量
全局变量在代码中不需要引入,直接使用

  1. options.url = HOST + options.url

五 代码规范

根据官方推荐规范做了简化:

5.1 命名规范

文件夹和文件的命名全部一律小写字母短杆连接 。
组件文件在类中被导入引用时,用Pascal 命名

  1. import Item from '../item'

5.2 JS书写规范

根据官方文档摘录几条重要的必须遵守的规范:
不要在句末使用分号
字符串统一使用单引号
当前作用域不需要改变的变量使用 const,反之则使用 let
每个 const/let 关键字单独声明一个变量
对于变量和函数名统一使用驼峰命名法
检查 NaN 的正确姿势是使用 isNaN()

  1. if (price === NaN) { } // ✗ 错误
  2. if (isNaN(price)) { } // ✓ 正确

使用数组字面量而不是构造器

  1. const nums = new Array(1, 2, 3) // ✗ 错误
  2. const nums = [1, 2, 3] // ✓ 正确

不要扩展原生对象

  1. Object.prototype.age = 21 // ✗ 错误

不要使用 eval()

  1. eval( "var result = user." + propName ) // ✗ 错误
  2. const result = user[propName] // ✓ 正确

嵌套的代码块中禁止再定义函数

  1. if (authenticated) {
  2. function setAuthUser () {} // ✗ 错误
  3. }

禁止使用 Object 构造器
禁止使用 Function 构造器
不使用 Generator 函数语法,使用 Promise 或者 async functions 来实现异步编程
始终使用 === 替代 ==
三元运算符 ? 和 : 与他们所负责的代码处于同一行

5.3 组件及 JSX 书写规范

组件以类的形式进行创建,并且单个文件中只能存在单个组件
JSX 属性均使用单引号:
属性名称始终使用驼峰命名法
用括号包裹多行 JSX 标签
使用对象解构的方式来使用 state、props

  1. const { isEnable } = this.props
  2. const { myTime } = this.state

类成员变量书写顺序:

  1. static 静态方法
  2. constructor
  3. componentWillMount
  4. componentDidMount
  5. componentWillReceiveProps
  6. shouldComponentUpdate
  7. componentWillUpdate
  8. componentDidUpdate
  9. componentWillUnmount
  10. 点击回调或者事件回调 比如 onClickSubmit() 或者 onChangeDescription()
  11. render

map 循环时请给元素加上 key 属性
不要在 componentDidMount 中调用 this.setState,会导致触发更新
不要在 componentWillUpdate/componentDidUpdate/render 中调用 this.setState,会导致循环更新
由于Taro 编译到小程序端后,每个组件的constructor首先会被调用一次(即使没有实例化),因此自定义组件必须定义 defaultProps,否则页面加载时小程序端会报属性undefined,在微信小程序端的自定义组件中,只有在 properties 中指定的属性,才能从父组件传入并接收
值为 true 的属性可以省略书写值
事件绑定均以 on 开头
子组件传入函数时属性名需要以 on 开头

5.4 Taro 自身限制规范

不能使用 Array#map 之外的方法操作 JSX 数组
解决方案:

  1. for (let index = 0; index < array.length; index++) {
  2. // do you thing with array
  3. }
  4. const element = array.map(item => {
  5. return <View />
  6. })