说明:当前仓库长期维护基于 Taro2.x 的模版,同时还提供了 1.x 和 3.x 版本(基于React)可供选择。
3.x 版本为独立仓库,点此前往
基于这个模版,开发了 taro-xui 这个 UI 库,而在这个模版中又引用了 taro-xui 作为首页的演示 demo,emm...
禁止套娃...
- 基础功能支持
- TypeScript
- Sass,全局注入公用样式文件
- UI 库(taro-ui)
- 状态管理(mobx)
- 异步编程(async/await)
- 引入字体(iconfont)
- 接口请求
- request 类
- 拦截器
- url 拦截器
- header 拦截器
- param 拦截器
- data 拦截器
- 开发环境本地代理(h5 端)
- jsonp 支持(h5 端)
- 调试
- vconsole(h5 端)
- 工程化
- 全局变量
- 插件
- 通过 plop 插件一键生成模版文件(页面、组件、样式、服务类、mobx 状态管理)
- 底层组件,用于页面和组件继承,实现类似 vue 原型绑定的功能
- git hooks
- eslint
- stylelint
- prettier
- commit lint
- 引入自建组件库(taro-xui)
- 引入自建工具类库(wtils)
- 接入 Taro 模版源 http://taro-docs.jd.com/taro/docs/template.html
- 提交 Taro 物料市场 https://taro-ext.jd.com/
- 组件
- Card 卡片组件 提供圆角、阴影功能,可自定义类名、样式(圆角及内外边距)
- Countdown 倒计时组件,可自定义结束时间、自定义倒计时长、是否展示天,自定义 item 样式
- Divider 分割线,可自定义高度
- Nodata 缺省组件 可自定义图片、文字、宽高
- Paging 分页提示组件 将 scrollerLoader, scrollerEndMessage 合并成一个组件,减少判断
- Modal 基础弹窗组件,可选择弹窗位置,包括中间弹窗、底部弹窗,抛出关闭回调
- Tabs 标签页
- TButton 按钮组件,可自定义类名、自定义宽高、背景色、圆角、positionType
- TImage 图片组件 提供错误处理、loading 过渡、查看大图等功能
- TImageUploader 图片上传组件 基于 image 提供上传图片、图片数量限制、删除图片、查看大图等功能
- 工具类
- img.ts 图片处理类(如拼接 url、预览等)
- mp.ts 小程序独有 api 封装(如检查更新)
- page.ts 页面工具类,实现获取页面路由、跳转等功能
- toast.ts loading/toast api 封装简化
- validator.ts 表单验证
- meta.ts meta 相关功能
- 体验工程
- 骨架屏
以下是项目结构的缩略图
# 获取模版
git clone https://github.com/lexmin0412/taro-template.git
# 进入项目文件夹
cd taro_template
# 安装依赖
yarn
# 本地浏览器运行
yarn dev:h5
# 本地小程序运行
yarn dev:mp
为了提高开发体验、调整了默认 taro 模版的部分编译命令,也为不同服务器环境(包括 dev/sit/uat/pro)、不同编译模式(开发/打包)、不同运行环境(h5/小程序)提供了统一的命令。
新增的服务器环境参数,主要是考虑到在处理线上问题时,为了复现问题,经常需要在本地请求非开发环境的接口,这时候习惯的操作是去更改配置文件,而更改配置文件的风险是很高的,不仅操作繁琐,更容易在多人开发时造成冲突,甚至可能将测试的变量提交到生产环境,造成不必要的线上问题。
编译命令格式如下:
yarn <mode>:<platform>-<env>
mode
,编译模式:
- dev 本地开发
- build 服务器部署
platform
,运行环境
- mp 微信小程序
- h5 h5
env
,服务器环境标识,不同标识对应着不同的配置项,如接口 host
- sit 测试环境
- uat 预发环境
- pro 生产环境
- 空 开发环境
示例:
yarn dev:mp # 本地开发 小程序 开发环境
yarn dev:mp-sit # 本地开发 小程序 测试环境
yarn build:mp # 部署 小程序 开发环境
yarn build-mo-pro # 部署 小程序 生产环境
完整的编译命令列表详见 package.json 中的 scripts
配置项。
启动本地调试模式需要新建 config/local.js,参考如下内容:
/**
* 本地调试配置
* 默认使用开发环境
* 运行 yarn dev:h5命令
*/
module.exports = {
defineConstants: {
APP_CONF: {
API_HOST: JSON.stringify('https://xx.com/'),
APPID: JSON.stringify('this_is_my_tourist_appid'),
API_MAP_QQ: JSON.stringify('https://apis.map.qq.com'),
KEY_MAP_QQ: JSON.stringify('UQPBZ-RCU36-K2YS3-EMV6Y-JI6JJ-3WBUM'),
},
},
}
在开发阶段,为了减少一些重复且枯燥,还有可能造成报错的代码,做了以下几个工作:
- 通过命令生成文件
- pages 和 components 文件夹的扫描
- 公用 sass 文件的全局注册
- 通过命令生成文件
对于文件的新建操作,在项目中也预置了命令,开发者只需在命令行中输入 yarn template
,然后根据相关提示输入对应的配置项,即可生成对应的文件,目前支持以下四种文件的快捷创建:
- 页面(同时生成对应的 scss 和 ts 类型生命文件)
- 组件(同时生成对应的 scss 文件)
- mobx 模块
- service 类
- pages 和 components 文件夹的扫描
在平常的项目开发中,存在以下问题:
- 每新建一个页面文件,就需要在 app.tsx 中的 pages 配置项中追加一行;
- 每一个组件的引用,都需要另起一行,引用到具体的组件存放路径
pages 文件夹的扫描,是基于 pages 目录及其所有文件夹及文件夹下的文件名,生成一个路由文件 routes.js,再读取这个路由文件,追加到 app.tsx 模版文件的 pages 配置项中去。
components 文件夹的扫描,跟 pages 目录同理,但生成的是一个 index.ts 文件,自动引入了 components 文件夹下的所有组件并导出,这样做的目的是在多个组件引用时,不需要每一个组件的引用都另起一行,而可以通过如下方式书写:
import { Line, TImage } from '~/components
- 公用 sass 文件的全局注册
在 config/index.js 中预置了如下内容:
// config/index.js
{
sass: {
// 全局注入scss文件
resource: [
'src/styles/classes.scss',
'src/styles/mixin.scss',
'src/styles/theme.scss',
'src/styles/var.scss'
],
// 指定项目根目录,这样在resource字段中就不需要重复书写path.resolve了
projectDirectory: path.resolve(__dirname, '..')
},
}
作用是全局注入了 mixin.scss 和 theme.scss,这样做之后,在项目内的所有 scss 文件中,可以直接使用这两个文件中的所有特性而不需要引入对应的文件,如果有更多的公用文件注入,只需要修改这里的配置项即可(注意:修改后需要重启项目才能生效)。(TODO:后续需要在编译插件中扫描 styles 文件夹,省去配置项追加的操作)
// pages/index/index.scss
.index-page {
background-color: $body-bg; // 变量来自styles/theme.scss
.block-title {
@include textOrient(1); // textOrient来自styles/
}
}
在页面中请求数据,需要先做一个判断:当前这个接口的数据需不需要跨页面共享,如果不需要,那么就没有必要经过 dva,直接调用 service 即可;反之则需要定义 model , 在页面上发起 action, 走 dva 的流程。
service, 也就是我们的服务模块,用于统一存放后端接口定义,供页面调用。
** service
文件设计规范**
由于同一个接口被不同页面调用调用的可能性非常高,服务模块的结构需要依照后端接口来设计,如项目内既包含了公司后端项目的接口请求,又需要请求第三方接口,那么 service 模块就要分成两个大的模块,大的模块下面再根据接口模块划分来划分小的 service 文件。
如一个接口路径为 https://xxx.normal.com/webapi/account/queryBalanceAccount
, 用途是查询用户账户余额,那么这个接口在 service 模块的结构就应该表现为:
首先分为两个大的模块,下一层是后台的项目,最后根据后台接口模块命名一个 xxx.service.ts
, xxx 是后台的模块名称。只要一个接口是在后台接口项目中的这个子模块,那么在前端就应该定义在相应的 service 文件下。
上面的示例接口设计结构如下:
├── services 服务根文件夹
| ├── inside 内部服务
| ├── qqMap 腾讯地图api接口
| ├── ws.service.ts webservice服务
import QQMapWSService from '~/services/qqMap/ws.service'
class Index extends Component {
state = {}
componentDidMount() {
this.handleJSONPTest()
}
// 直接调用service
async handleJSONPTest() {
const result = await QQMapWSService.geocoder({
location: `28.2532,112.87887`,
get_poi: 0,
})
this.setState({
locationData: result.data,
})
console.log('result', result)
}
render() {
const { locationData } = this.state
return (
<View className='home-index-page'>
{locationData && (
<View>
当前位置:{locationData.latitude},{locationData.longitude}
</View>
)}
</Vew>
)
}
}
在业务开发的过程中,我们常需要复用一些相同的结构,如商品轮播图,订单 item 等,如果每个页面都复制粘贴一遍,不仅不美观,更难以维护,这时候就需要开发组件了。
组件分为展示型组件和容器型组件。展示型组件只需要接收父组件传递的属性并渲染页面,容器型组件则会涉及到数据处理等复杂的逻辑,难以重用,所以平常我们开发的一般都是展示型的组件。
在项目模板中已经包含了数个常用的基础组件,可直接使用,引用方式:
import { Card, TImage } from '~/components'
在编译前已经进行了 components 文件夹的扫描操作,自动生成了 components/index.ts,而 ~/components
会指向 src/components/index.ts 文件,所以可以直接通过以上方式引用。
通过 yarn template 命令新建组件,会生成如下模版:
/**
* ComponentDesc
*/
import { ComponentClass } from 'react'
import Taro, { Component } from '@tarojs/taro'
import { View } from '@tarojs/components'
import './ComponentName.scss'
/**
* props属性
*/
interface IProps {
/**
* 子元素
*/
children?: any
}
/**
* 组件内部属性
*/
interface IState {}
interface ComponentName {
props: IProps
state: IState
}
class ComponentName extends Component {
static defaultProps: IProps = {}
render() {
return <View className='ComponentName-comp'>ComponentDesc</View>
}
}
export default ComponentName as ComponentClass<IProps, IState>
基于以上模版,我们就可以开始组件的具体逻辑开发了。
import { ComponentName } from '~/components'
代码书写规范请遵循 Taro 规范,后续会有更完善的规范补充。
一个页面文件导入模块时应该按照如下规范:
1. 先导入第三方模块,如第三方UI库等
2. 再导入项目内部模块,如组件、工具类等
3. 导入静态文件,图片在前,其他资源次之,样式文件最后
示例:
// 导入第三方库
import Taro, { Component, Config } from '@tarojs/taro'
import { View, Text } from '@tarojs/components'
import { connect } from '@tarojs/redux'
import { ComponentClass } from 'react'
// 导入项目内部模块
import Line from '~/components/Line'
import Toast from '~/utils/toast'
// 导入静态文件和样式
import './index.scss'
-
页面容器应以模块-文件名-容器类型命名,如 home-index-page, line-comp 等
-
样式
.index-page {
background: $theme-color;
}
请基于以下文档优化:
根据 package.json 文件中的 scripts 配置项,在对应的自动化部署工具(如 jenkins)中进行相应的配置即可。编译命令说明 详见
原因:没有 async await 支持
解决方案:
- https://nervjs.github.io/taro/docs/async-await.html#docsNav
- https://nervjs.github.io/taro/docs/migrate-to-2.html
解决方案:不将类型声明独立文件,此问题需要后续观察。
解决方案:loading 放在 success 回调中 上传之前开始展示
// app.scss
// 方式1: 一次性引入所有样式
@import '~taro-ui/dist/style/index.scss';
// 方式2: 在使用到新的组件时才引入
@import '~taro-ui/dist/style/components/noticeBar.scss';
@import '~taro-ui/dist/style/components/tag.scss';
对于上面的情况,如果在项目中只使用到了 taro-ui 中的 Button 和 Tag 组件,打包后的 app.css 体积从 210kb 减少到 53kb,只要打包后生成的 app.css 文件小于 210kb,那么这种引入方式就是值得的。