最近写一个微信小程序的项目,由于是协同开发,前期的搭建工作由另一个妹子完成,现在项目阶段一完成了,为了备忘回顾,做一个阶段性小结。
在写小程序之前经过对比最后采用了京东凹凸实验室开发的类react
框架Taro
,用框架的好处就不多说了,比直接写原生小程序方便太多。数据管理采用的是封装了redux
的dva
框架,如果没有学过的同学可以去看看文档。先声明篇幅比较长,如果你需要,还请看完,相信一定有帮助,不想看的同学文末放了GitHub地址,自己去下。
附上文档链接:
taro文档:https://nervjs.github.io/taro/docs/README.html
dva文档:https://dvajs.com/guide/
1.基础步骤
// 全局安装taro (cnpm为淘宝镜像)
cnpm install -g @tarojs/cli
// 创建项目
taro init taro-demo
如下配置(推荐为项目配上ts
):
![](http://upload-images.jianshu.io/upload_images/13691847-57b136640e831f17.jpg?imageMogr2/auto-orient/strip|imageView2/2/w/1200/format/webp)
安装与 react-redux API 几乎一致的包 @tarojs/redux
cnpm install --save redux @tarojs/redux @tarojs/redux-h5 redux-thunk redux-logger
安装dva
cnpm install --save dva-core dva-loading
-
dva-core
:封装了 redux 和 redux-saga的一个插件 -
dva-loading
:管理页面的loading状态
2.整理项目文件
删除
- 删除
./src/page
文件夹下的index
文件夹
添加
- 在
./src
文件夹下添加如下文件夹(根据自己实际情况和项目需求进行配置,只罗列一些必要的):
assets
:静态资源,如images、scss、iconfont...
components
:编写共用组件
config
:项目配置文件
models
:dva插件model
函数引用或者共用的js
types
:公共typescript类型申明
utils
:封装的插件
3.编写插件(主要且常用的)
1.在./src/config
下创建index.ts
,添加项目配置信息,例如:
/**
* 线上环境
* 为了方便测试,使用的是聚合数据免费接口
* 网址:https://www.juhe.cn/
*/
export const ONLINEHOST = 'http://api.juheapi.com'
/**
- 测试环境
*/
export const QAHOST = ‘http://xxx.cn’
/**
- 线上mock
*/
export const MOCKHOST = ‘http://xxx/mock’
/**
- 是否mock
*/
export const ISMOCK = false
/**
- 当前的host ONLINEHOST | QAHOST | MOCKHOST
*/
export const MAINHOST = ONLINEHOST
/**
- 全局的分享信息 不用每一个都去写
*/
export const SHAREINFO = {
‘title’: ‘分享标题’,
‘path’: ‘路径’,
‘imageUrl’: ‘图片’
}
2.在./src/utils
下创建dva.ts
,配置dva,内容如下:
import { create } from 'dva-core';
import { createLogger } from 'redux-logger';
import createLoading from 'dva-loading';
let app
let store
let dispatch
let registered
function createApp(opt) {
// redux日志
opt.onAction = [createLogger()]
app = create(opt)
app.use(createLoading({}))
if (!registered) opt.models.forEach(model => app.model(model))
registered = true
app.start()
store = app._store
app.getStore = () => store
app.use({
onError(err) {
console.log(err)
},
})
dispatch = store.dispatch
app.dispatch = dispatch
return app
}
export default {
createApp,
getDispatch() {
return app.dispatch
}
}
3.在./src/config
下创建requestConfig.ts
,统一配置请求接口,内容如下:
/**
* 请求的公共参数
*/
export const commonParame = {}
/**
- 请求映射文件
*/
export const requestConfig = {
loginUrl: ‘/api/user/wechat-auth’, // 微信登录接口
}
5.在./src/utils
下创建tips.ts
,整合封装微信原生弹窗,内容如下:
import Taro from '@tarojs/taro'
/**
* 提示与加载工具类
*/
export default class Tips {
static isLoading = false
/**
- 信息提示
/
static toast(title: string, onHide?: () => void) {
Taro.showToast({
title: title,
icon: ‘none’,
mask: true,
duration: 1500
});
// 隐藏结束回调
if (onHide) {
setTimeout(() => {
onHide();
}, 500);
}
}
/* - 弹出加载提示
*/
static loading(title = ‘加载中’, force = false) {
if (this.isLoading && !force) {
return
}
this.isLoading = true
if (Taro.showLoading) {
Taro.showLoading({
title: title,
mask: true
})
} else {
Taro.showNavigationBarLoading()
}
}
/**
- 加载完毕
*/
static loaded() {
let duration = 0
if (this.isLoading) {
this.isLoading = false
if (Taro.hideLoading) {
Taro.hideLoading()
} else {
Taro.hideNavigationBarLoading()
}
duration = 500
}
// 隐藏动画大约500ms,避免后面直接toast时的显示bug
return new Promise(resolve => setTimeout(resolve, duration))
}
/**
- 弹出提示框
*/
static success(title, duration = 1500) {
Taro.showToast({
title: title,
icon: ‘success’,
mask: true,
duration: duration
});
if (duration > 0) {
return new Promise(resolve => setTimeout(resolve, duration));
}
}
}
5.在./src/utils
下创建common.ts
,共用函数,内容如下:
/** 时间格式的转换 */
export const formatTime = time => {
`${pad(time.getHours())}:${pad(time.getMinutes())}:${pad(time.getSeconds())}.${pad(time.getMilliseconds(), 3)}`
}
export var globalData: any = {} // 全局公共变量
6.在./src/utils
下创建logger.ts
,封装log函数,内容如下:
import {
formatTime
} from './common'
const defaults = {
level: ‘log’,
logger: console,
logErrors: true,
colors: {
title: ‘inherit’,
req: ‘#9E9E9E’,
res: ‘#4CAF50’,
error: ‘#F20404’,
}
}
function printBuffer(logEntry, options) {
const {
logger,
colors
} = options;
let {
title,
started,
req,
res
} = logEntry
// Message
const headerCSS = [‘color: gray; font-weight: lighter;’]
const styles = s => </span><span class="token string">color: </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>s<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">; font-weight: bold</span><span class="token template-punctuation string">
// render
logger.group(</span><span class="token string">%c </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>title<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> @</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token function">formatTime</span><span class="token punctuation">(</span>started<span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">
, …headerCSS)
logger.log(’%c req’, styles(colors.req), req)
logger.log(’%c res’, styles(colors.res), res)
logger.groupEnd()
}
interface LogEntry {
started?: object // 触发时间
}
function createLogger(options: LogEntry = {}) {
const loggerOptions = Object.assign({}, defaults, options)
const logEntry = options
logEntry.started = new Date()
printBuffer(logEntry, Object.assign({}, loggerOptions))
}
export {
defaults,
createLogger,
}
7.在./src/utils
下创建request.ts
,封装http请求,内容如下:
import Taro, { Component } from '@tarojs/taro'
import {
ISMOCK,
MAINHOST
} from '../config'
import {
commonParame,
requestConfig
} from '../config/requestConfig'
import Tips from './tips'
// import { createLogger } from ‘./logger’
declare type Methods = “GET” | “OPTIONS” | “HEAD” | “POST” | “PUT” | “DELETE” | “TRACE” | “CONNECT”;
declare type Headers = { [key: string]: string };
declare type Datas = { method: Methods;[key: string]: any; };
interface Options {
url: string;
host?: string;
method?: Methods;
data?: Datas;
header?: Headers;
}
export class Request {
//登陆的promise
static loginReadyPromise: Promise<any> = Promise.resolve()
// 正在登陆
static isLogining: boolean = false
// 导出的api对象
static apiLists: { [key: string]: () => any; } = {}
// token
static token: string = ‘’
// constructor(setting) {
// }
/**
- @static 处理options
- @param {Options | string} opts
- @param {Datas} data
- @returns {Options}
- @memberof Request
*/
static conbineOptions(opts, data: Datas, method: Methods): Options {
typeof opts === ‘string’ && (opts = { url: opts })
return {
data: { ….conmomPrams, ….opts.data, ….data },
method: opts.method || data.method || method || ‘GET’,
url:$<span class="token punctuation">{</span>opts<span class="token punctuation">.</span>host <span class="token operator">||</span> MAINHOST<span class="token punctuation">}</span>$<span class="token punctuation">{</span>opts<span class="token punctuation">.</span>url<span class="token punctuation">}</span>
}
}
static getToken() {
!this.token && (this.token = Taro.getStorageSync(‘token’))
return this.token
}
/**
*
- @static request请求 基于 Taro.request
- @param {Options} opts
*/
static async request(opts: Options) {
// token不存在
// if (!this.getToken()) { await this.login() }
<span class="token comment">// token存在</span>
<span class="token comment">// let options = Object.assign(opts, { header: { 'token': this.getToken() } })</span>
<span class="token comment">// Taro.request 请求</span>
<span class="token keyword">const</span> res <span class="token operator">=</span> await Taro<span class="token punctuation">.</span><span class="token function">request</span><span class="token punctuation">(</span>opts<span class="token punctuation">)</span>
<span class="token comment">// 是否mock</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>ISMOCK<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> res<span class="token punctuation">.</span>data <span class="token punctuation">}</span>
<span class="token comment">// 登陆失效 </span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>res<span class="token punctuation">.</span>data<span class="token punctuation">.</span>code <span class="token operator">===</span> <span class="token number">99999</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> await <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">login</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">request</span><span class="token punctuation">(</span>opts<span class="token punctuation">)</span> <span class="token punctuation">}</span>
<span class="token comment">// 请求成功</span>
<span class="token comment">// if (res.data && res.data.code === 0 || res.data.succ === 0) { return res.data }</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>res<span class="token punctuation">.</span>data<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> res<span class="token punctuation">.</span>data <span class="token punctuation">}</span>
<span class="token comment">// 请求错误</span>
<span class="token keyword">const</span> d <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token operator">..</span><span class="token punctuation">.</span>res<span class="token punctuation">.</span>data<span class="token punctuation">,</span> err<span class="token operator">:</span> <span class="token punctuation">(</span>res<span class="token punctuation">.</span>data <span class="token operator">&&</span> res<span class="token punctuation">.</span>data<span class="token punctuation">.</span>msg<span class="token punctuation">)</span> <span class="token operator">||</span> `网络错误~` <span class="token punctuation">}</span>
Tips<span class="token punctuation">.</span><span class="token function">toast</span><span class="token punctuation">(</span>d<span class="token punctuation">.</span>err<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">throw</span> new <span class="token function">Error</span><span class="token punctuation">(</span>d<span class="token punctuation">.</span>err<span class="token punctuation">)</span>
}
/**
*
- @static 登陆
- @returns promise
- @memberof Request
*/
static login() {
if (!this.isLogining) { this.loginReadyPromise = this.onLogining() }
return this.loginReadyPromise
}
/**
*
-
@static 登陆的具体方法
-
@returns
-
@memberof Request
*/
static onLogining() {
this.isLogining = true
return new Promise(async (resolve, reject) => {
// 获取code
const { code } = await Taro.login()// 请求登录
const { data } = await Taro.request({
url:$<span class="token punctuation">{</span>MAINHOST<span class="token punctuation">}</span>$<span class="token punctuation">{</span>requestConfig<span class="token punctuation">.</span>loginUrl<span class="token punctuation">}</span>
,
data: { code: code }
})if (data.code !== 0 || !data.data || !data.data.token) {
reject()
return
}Taro.setStorageSync(‘token’, data.data.token)
this.isLogining = false
resolve()
})
}
/**
*
- @static 创建请求函数
- @param {(Options | string)} opts
- @returns
- @memberof Request
*/
static creatRequests(opts: Options | string): () => {} {
return async (data = {}, method: Methods = “GET”) => {
const _opts = this.conbineOptions(opts, data, method)
const res = await this.request(_opts)
// createLogger({ title: ‘request’, req: _opts, res: res })
return res
}
}
/**
*
- @static 抛出整个项目的api方法
- @returns
- @memberof Request
*/
static getApiList(requestConfig) {
if (!Object.keys(requestConfig).length) return {}
Object<span class="token punctuation">.</span><span class="token function">keys</span><span class="token punctuation">(</span>requestConfig<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span>key<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token punctuation">{</span>
<span class="token keyword">this</span><span class="token punctuation">.</span>apiLists<span class="token punctuation">[</span>key<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">creatRequests</span><span class="token punctuation">(</span>requestConfig<span class="token punctuation">[</span>key<span class="token punctuation">]</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">.</span>apiLists
}
}
// 导出
const Api = Request.getApiList(requestConfig)
Component.prototype.$api = Api
export default Api as any
Tip
:这时候tslint会报这样的错:类型“Component<any, any>”上不存在属性“$api”。
,因为我们没有添加声明,我们可以这样解决,在./src
目录下创建app-shim.d.ts
,内容如下:
/**
*
* @static 添加taro等自定义类型
* @interface Component
*/
import Taro, { Component } from '@tarojs/taro'
// 在Component上定义自定义方法类型
declare module ‘@tarojs/taro’ {
interface Component {
$api: any
}
}
//声明
declare var require: any
declare var dispach: any
这时候应该不报错了。
8.在./src/config
下创建taroConfig.ts
,封装taro小程序的一些方法,内容如下:
/**
* 进行taro的处理
* 1.方法的改写
* 2.utils的挂载
*
*/
import Taro, { Component } from "@tarojs/taro";
import { SHAREINFO } from '../config/index'
/**
- navigateTo 超过8次之后 强行进行redirectTo 否则会造成页面卡死
*/
const nav = Taro.navigateTo
Taro.navigateTo = (data) => {
if (Taro.getCurrentPages().length > 8) {
return Taro.redirectTo(data)
}
return nav(data)
}
/**
- Component挂载分享方法
*/
Component.prototype.onShareAppMessage = function () {
return SHAREINFO
}
4.编写node命令快速创建page
和component
先来看一张图,就明白为什么需要编写这样一个命令了
![](http://upload-images.jianshu.io/upload_images/13691847-742ac123f246f15d.png?imageMogr2/auto-orient/strip|imageView2/2/w/714/format/webp)
当你每次需要创建一个页面的时候需要不断的创建,这样太麻烦了,而且容易出错,所以写个node命令快速生成如图中index文件夹下的5个文件,一条命令的事情,下面上代码:
首先在
根目录
下创建
scripts
文件夹,在该文件夹下添加如下文件:
- 添加
./scripts/template.js
,内容如下:
/**
* pages页面快速生成脚本
* 用法:npm run tep `文件名`
*/
const fs = require(‘fs’);
const dirName = process.argv[2];
const capPirName = dirName.substring(0, 1).toUpperCase() + dirName.substring(1);
if (!dirName) {
console.log(‘文件夹名称不能为空!’);
console.log(‘示例:npm run tep test’);
process.exit(0);
}
//页面模板
const indexTep = `
import Taro, { Component, Config } from ‘@tarojs/taro’
import { View } from ‘@tarojs/components’
// import { connect } from ‘@tarojs/redux’
// import Api from ‘…/…/utils/request’
// import Tips from ‘…/…/utils/tips’
import { ${capPirName}Props, KaTeX parse error: Expected 'EOF', got '}' at position 129: …n punctuation">}̲</span> from <s…{dirName}.interface’
import ‘./${dirName}.scss’
// import { } from ‘…/…/components’
// @connect(({ KaTeX parse error: Expected 'EOF', got '}' at position 11: {dirName} }̲) => ({</spa…{dirName},
// }))
class KaTeX parse error: Expected 'EOF', got '&' at position 211: …oken operator">&̲lt;</span>{capPirName}Props,${capPirName}State > {
config:Config = {
navigationBarTitleText: ‘标题’
}
constructor(props: ${capPirName}Props) {
super(props)
this.state = {}
}
componentDidMount() {
}
render() {
return (
<View className=’${dirName}-wrap’>
<span class="token operator"><</span><span class="token operator">/</span>View<span class="token operator">></span>
<span class="token punctuation">)</span>
}
}
export default ${capPirName}
`
// scss文件模版
const scssTep = $<span class="token punctuation">{</span>dirName<span class="token punctuation">}</span><span class="token operator">-</span>wrap <span class="token punctuation">{</span> width<span class="token punctuation">:</span> <span class="token number">100</span><span class="token operator">%</span><span class="token punctuation">;</span> min<span class="token operator">-</span>height<span class="token punctuation">:</span> <span class="token number">100</span>vh<span class="token punctuation">;</span> <span class="token punctuation">}</span>
// config 接口地址配置模板
const configTep = <span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span> test<span class="token punctuation">:</span> <span class="token string">'/wechat/perfect-info'</span><span class="token punctuation">,</span> <span class="token comment">//xxx接口</span> <span class="token punctuation">}</span>
// 接口请求模板
const serviceTep = `
import Api from ‘…/…/utils/request’
export const testApi = data => Api.test(
data
)
`
//model模板
const modelTep = `
// import Taro from ‘@tarojs/taro’;
import * as ${dirName}Api from ‘./service’;
export default {
namespace: ‘${dirName}’,
state: {
},
effects: {},
reducers: {}
}
`
const interfaceTep = `
/**
- ${dirName}.state 参数类型
- @export
- @interface ${capPirName}State
*/
export interface ${capPirName}State {}
/**
- ${dirName}.props 参数类型
- @export
- @interface ${capPirName}Props
*/
export interface ${capPirName}Props {}
`
fs.mkdirSync(<span class="token punctuation">.</span><span class="token operator">/</span>src<span class="token operator">/</span>pages<span class="token operator">/</span>$<span class="token punctuation">{</span>dirName<span class="token punctuation">}</span>
); // mkdir
1
<
/
s
p
a
n
>
p
r
o
c
e
s
s
<
s
p
a
n
c
l
a
s
s
=
"
t
o
k
e
n
p
u
n
c
t
u
a
t
i
o
n
"
>
.
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
t
o
k
e
n
f
u
n
c
t
i
o
n
"
>
c
h
d
i
r
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
t
o
k
e
n
p
u
n
c
t
u
a
t
i
o
n
"
>
(
<
/
s
p
a
n
>
‘
<
s
p
a
n
c
l
a
s
s
=
"
t
o
k
e
n
p
u
n
c
t
u
a
t
i
o
n
"
>
.
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
t
o
k
e
n
o
p
e
r
a
t
o
r
"
>
/
<
/
s
p
a
n
>
s
r
c
<
s
p
a
n
c
l
a
s
s
=
"
t
o
k
e
n
o
p
e
r
a
t
o
r
"
>
/
<
/
s
p
a
n
>
p
a
g
e
s
<
s
p
a
n
c
l
a
s
s
=
"
t
o
k
e
n
o
p
e
r
a
t
o
r
"
>
/
<
/
s
p
a
n
>
1</span> process<span class="token punctuation">.</span><span class="token function">chdir</span><span class="token punctuation">(</span>`<span class="token punctuation">.</span><span class="token operator">/</span>src<span class="token operator">/</span>pages<span class="token operator">/</span>
1</span>process<spanclass="tokenpunctuation">.</span><spanclass="tokenfunction">chdir</span><spanclass="tokenpunctuation">(</span>‘<spanclass="tokenpunctuation">.</span><spanclass="tokenoperator">/</span>src<spanclass="tokenoperator">/</span>pages<spanclass="tokenoperator">/</span>{dirName}`); // cd $1
fs.writeFileSync($<span class="token punctuation">{</span>dirName<span class="token punctuation">}</span><span class="token punctuation">.</span>tsx
, indexTep); //tsx
fs.writeFileSync($<span class="token punctuation">{</span>dirName<span class="token punctuation">}</span><span class="token punctuation">.</span>scss
, scssTep); // scss
fs.writeFileSync(‘config.ts’, configTep); // config
fs.writeFileSync(‘service.ts’, serviceTep); // service
fs.writeFileSync(‘model.ts’, modelTep); // model
fs.writeFileSync($<span class="token punctuation">{</span>dirName<span class="token punctuation">}</span><span class="token punctuation">.</span>interface<span class="token punctuation">.</span>ts
, interfaceTep); // interface
process.exit(0);
- 添加
./scripts/component.js
,内容如下:
/**
* pages页面快速生成脚本
* 用法:npm run com `文件名`
*/
const fs = require(‘fs’);
const dirName = process.argv[2];
const capPirName = dirName.substring(0,1).toUpperCase() + dirName.substring(1);
if (!dirName) {
console.log(‘文件夹名称不能为空!’);
console.log(‘示例:npm run com test’);
process.exit(0);
}
//页面模板
const indexTep = `import Taro, { Component } from ‘@tarojs/taro’
import { View } from ‘@tarojs/components’
import { ${capPirName}Props, KaTeX parse error: Expected 'EOF', got '}' at position 129: …n punctuation">}̲</span> from <s…{dirName}.interface’
import ‘./${dirName}.scss’
class KaTeX parse error: Expected 'EOF', got '&' at position 211: …oken operator">&̲lt;</span>{capPirName}Props,${capPirName}State > {
constructor(props:
<
s
p
a
n
c
l
a
s
s
=
"
t
o
k
e
n
p
u
n
c
t
u
a
t
i
o
n
"
>
<
/
s
p
a
n
>
c
a
p
P
i
r
N
a
m
e
<
s
p
a
n
c
l
a
s
s
=
"
t
o
k
e
n
p
u
n
c
t
u
a
t
i
o
n
"
>
<
/
s
p
a
n
>
P
r
o
p
s
<
s
p
a
n
c
l
a
s
s
=
"
t
o
k
e
n
p
u
n
c
t
u
a
t
i
o
n
"
>
)
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
t
o
k
e
n
p
u
n
c
t
u
a
t
i
o
n
"
>
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
t
o
k
e
n
k
e
y
w
o
r
d
"
>
s
u
p
e
r
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
t
o
k
e
n
p
u
n
c
t
u
a
t
i
o
n
"
>
(
<
/
s
p
a
n
>
p
r
o
p
s
<
s
p
a
n
c
l
a
s
s
=
"
t
o
k
e
n
p
u
n
c
t
u
a
t
i
o
n
"
>
)
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
t
o
k
e
n
k
e
y
w
o
r
d
"
>
t
h
i
s
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
t
o
k
e
n
p
u
n
c
t
u
a
t
i
o
n
"
>
.
<
/
s
p
a
n
>
s
t
a
t
e
<
s
p
a
n
c
l
a
s
s
=
"
t
o
k
e
n
o
p
e
r
a
t
o
r
"
>
=
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
t
o
k
e
n
p
u
n
c
t
u
a
t
i
o
n
"
>
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
t
o
k
e
n
p
u
n
c
t
u
a
t
i
o
n
"
>
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
t
o
k
e
n
p
u
n
c
t
u
a
t
i
o
n
"
>
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
t
o
k
e
n
k
e
y
w
o
r
d
"
>
s
t
a
t
i
c
<
/
s
p
a
n
>
o
p
t
i
o
n
s
<
s
p
a
n
c
l
a
s
s
=
"
t
o
k
e
n
o
p
e
r
a
t
o
r
"
>
=
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
t
o
k
e
n
p
u
n
c
t
u
a
t
i
o
n
"
>
<
/
s
p
a
n
>
a
d
d
G
l
o
b
a
l
C
l
a
s
s
<
s
p
a
n
c
l
a
s
s
=
"
t
o
k
e
n
p
u
n
c
t
u
a
t
i
o
n
"
>
:
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
t
o
k
e
n
b
o
o
l
e
a
n
"
>
t
r
u
e
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
t
o
k
e
n
p
u
n
c
t
u
a
t
i
o
n
"
>
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
t
o
k
e
n
k
e
y
w
o
r
d
"
>
s
t
a
t
i
c
<
/
s
p
a
n
>
d
e
f
a
u
l
t
P
r
o
p
s
<
s
p
a
n
c
l
a
s
s
=
"
t
o
k
e
n
p
u
n
c
t
u
a
t
i
o
n
"
>
:
<
/
s
p
a
n
>
<span class="token punctuation">{</span>capPirName<span class="token punctuation">}</span>Props<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">super</span><span class="token punctuation">(</span>props<span class="token punctuation">)</span> <span class="token keyword">this</span><span class="token punctuation">.</span>state <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">static</span> options <span class="token operator">=</span> <span class="token punctuation">{</span> addGlobalClass<span class="token punctuation">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span> <span class="token keyword">static</span> defaultProps<span class="token punctuation">:</span>
<spanclass="tokenpunctuation"></span>capPirName<spanclass="tokenpunctuation"></span>Props<spanclass="tokenpunctuation">)</span><spanclass="tokenpunctuation"></span><spanclass="tokenkeyword">super</span><spanclass="tokenpunctuation">(</span>props<spanclass="tokenpunctuation">)</span><spanclass="tokenkeyword">this</span><spanclass="tokenpunctuation">.</span>state<spanclass="tokenoperator">=</span><spanclass="tokenpunctuation"></span><spanclass="tokenpunctuation"></span><spanclass="tokenpunctuation"></span><spanclass="tokenkeyword">static</span>options<spanclass="tokenoperator">=</span><spanclass="tokenpunctuation"></span>addGlobalClass<spanclass="tokenpunctuation">:</span><spanclass="tokenboolean">true</span><spanclass="tokenpunctuation"></span><spanclass="tokenkeyword">static</span>defaultProps<spanclass="tokenpunctuation">:</span>{capPirName}Props = {}
render() {
return (
<View className=‘fx-${dirName}-wrap’>
<span class="token operator"><</span><span class="token operator">/</span>View<span class="token operator">></span>
<span class="token punctuation">)</span>
}
}
export default ${capPirName}
`
// scss文件模版
const scssTep = $<span class="token punctuation">{</span>dirName<span class="token punctuation">}</span><span class="token operator">-</span>wrap <span class="token punctuation">{</span> width<span class="token punctuation">:</span> <span class="token number">100</span><span class="token operator">%</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
const interfaceTep = `
/**
- ${dirName}.state 参数类型
- @export
- @interface ${capPirName}State
*/
export interface ${capPirName}State {}
/**
- ${dirName}.props 参数类型
- @export
- @interface ${capPirName}Props
*/
export interface ${capPirName}Props {}
`
fs.mkdirSync(<span class="token punctuation">.</span><span class="token operator">/</span>src<span class="token operator">/</span>components<span class="token operator">/</span>$<span class="token punctuation">{</span>dirName<span class="token punctuation">}</span>
); // mkdir
1
<
/
s
p
a
n
>
p
r
o
c
e
s
s
<
s
p
a
n
c
l
a
s
s
=
"
t
o
k
e
n
p
u
n
c
t
u
a
t
i
o
n
"
>
.
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
t
o
k
e
n
f
u
n
c
t
i
o
n
"
>
c
h
d
i
r
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
t
o
k
e
n
p
u
n
c
t
u
a
t
i
o
n
"
>
(
<
/
s
p
a
n
>
‘
<
s
p
a
n
c
l
a
s
s
=
"
t
o
k
e
n
p
u
n
c
t
u
a
t
i
o
n
"
>
.
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
t
o
k
e
n
o
p
e
r
a
t
o
r
"
>
/
<
/
s
p
a
n
>
s
r
c
<
s
p
a
n
c
l
a
s
s
=
"
t
o
k
e
n
o
p
e
r
a
t
o
r
"
>
/
<
/
s
p
a
n
>
c
o
m
p
o
n
e
n
t
s
<
s
p
a
n
c
l
a
s
s
=
"
t
o
k
e
n
o
p
e
r
a
t
o
r
"
>
/
<
/
s
p
a
n
>
1</span> process<span class="token punctuation">.</span><span class="token function">chdir</span><span class="token punctuation">(</span>`<span class="token punctuation">.</span><span class="token operator">/</span>src<span class="token operator">/</span>components<span class="token operator">/</span>
1</span>process<spanclass="tokenpunctuation">.</span><spanclass="tokenfunction">chdir</span><spanclass="tokenpunctuation">(</span>‘<spanclass="tokenpunctuation">.</span><spanclass="tokenoperator">/</span>src<spanclass="tokenoperator">/</span>components<spanclass="tokenoperator">/</span>{dirName}`); // cd $1
fs.writeFileSync($<span class="token punctuation">{</span>dirName<span class="token punctuation">}</span><span class="token punctuation">.</span>tsx
, indexTep); //tsx
fs.writeFileSync($<span class="token punctuation">{</span>dirName<span class="token punctuation">}</span><span class="token punctuation">.</span>scss
, scssTep); // scss
fs.writeFileSync($<span class="token punctuation">{</span>dirName<span class="token punctuation">}</span><span class="token punctuation">.</span>interface<span class="token punctuation">.</span>ts
, interfaceTep); // interface
Tip
:最后也是重点,记得在根目录的package.json
的scripts
里加上如下内容:
"scripts": {
...
"tep": "node scripts/template",
"com": "node scripts/component"
}
5.编写业务代码
上面4个步骤基本已经配置完了,接下去进入正题,可以愉快的撸代码了。
运行我们上面写的快速生成脚本,在命令行里输入:
cnpm run tep index
ok,这时候tslint应该不报找不到index的错了,可以看到我们page
文件夹下生成了一个index
的文件夹,里面包含config.ts
、index.interface.ts
、index.scss
、index.tsx
、model.ts
、service.ts
1.改写./src/app.tsx
首先先下载taro的@tarojs/async-await
,在命令行输入如下:
cnpm i --save @tarojs/async-await
下载完了之后,按照如下改写app.tsx
import Taro, { Component, Config } from "@tarojs/taro";
import "@tarojs/async-await";
import { Provider } from "@tarojs/redux";
import "./utils/request";
import Index from "./pages/index";
import dva from './utils/dva'
import models from './models'
import './app.scss'
import { globalData } from "./utils/common";
const dvaApp = dva.createApp({
initialState: {},
models: models,
});
const store = dvaApp.getStore();
class App extends Component {
config: Config = {
pages: [
‘pages/index/index’
],
window: {
backgroundTextStyle: ‘light’,
navigationBarBackgroundColor: ‘#fff’,
navigationBarTitleText: ‘WeChat’,
navigationBarTextStyle: ‘black’
}
}
/**
*
- 1.小程序打开的参数 globalData.extraData.xx
- 2.从二维码进入的参数 globalData.extraData.xx
- 3.获取小程序的设备信息 globalData.systemInfo
- @memberof App
*/
async componentDidMount() {
// 获取参数
const referrerInfo = this. r o u t e r < s p a n c l a s s = " t o k e n p u n c t u a t i o n " > . < / s p a n > p a r a m s < s p a n c l a s s = " t o k e n p u n c t u a t i o n " > . < / s p a n > r e f e r r e r I n f o < s p a n c l a s s = " t o k e n p u n c t u a t i o n " > ; < / s p a n > < s p a n c l a s s = " t o k e n k e y w o r d " > c o n s t < / s p a n > q u e r y < s p a n c l a s s = " t o k e n o p e r a t o r " > = < / s p a n > < s p a n c l a s s = " t o k e n k e y w o r d " > t h i s < / s p a n > < s p a n c l a s s = " t o k e n p u n c t u a t i o n " > . < / s p a n > router<span class="token punctuation">.</span>params<span class="token punctuation">.</span>referrerInfo<span class="token punctuation">;</span> <span class="token keyword">const</span> query <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span> router<spanclass="tokenpunctuation">.</span>params<spanclass="tokenpunctuation">.</span>referrerInfo<spanclass="tokenpunctuation">;</span><spanclass="tokenkeyword">const</span>query<spanclass="tokenoperator">=</span><spanclass="tokenkeyword">this</span><spanclass="tokenpunctuation">.</span>router.params.query;
!globalData.extraData && (globalData.extraData = {});
if (referrerInfo && referrerInfo.extraData) {
globalData.extraData = referrerInfo.extraData;
}
if (query) {
globalData.extraData = {
…globalData.extraData,
…query
};
}
<span class="token comment">// 获取设备信息</span>
<span class="token keyword">const</span> sys <span class="token operator">=</span> <span class="token keyword">await</span> Taro<span class="token punctuation">.</span><span class="token function">getSystemInfo</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
sys <span class="token operator">&&</span> <span class="token punctuation">(</span>globalData<span class="token punctuation">.</span>systemInfo <span class="token operator">=</span> sys<span class="token punctuation">)</span><span class="token punctuation">;</span>
}
componentDidShow() { }
componentDidHide() { }
componentDidCatchError() { }
// 在 App 类中的 render() 函数没有实际作用
// 请勿修改此函数
render() {
return (
<Provider store={store}>
<Index />
</Provider>
)
}
}
Taro.render(<App />, document.getElementById(‘app’))
发现tslint
报找不到模块“./models”。
这样的错,不要急,我们在./models
文件夹下创建index.ts
这样一个文件夹,内容如下:
import index from '../pages/index/model';// index 页面的model
// 这里记得export的是数组,不是对象
export default [
index
]
可以发现,tslint
已经不报错了。
2.改写./src/pages/index/config.ts
export default {
getLists: '/japi/toh', // 获取历史上的今天接口
}
3.在./src/config/requestConfig.ts
引入上面配置的接口,如下修改:
import index from '../pages/index/config' // index接口
/**
* 请求的公共参数
*/
export const commonParame = {}
/**
- 请求映射文件
*/
export const requestConfig = {
loginUrl: ‘/api/user/wechat-auth’, // 微信登录接口
…index
}
4.改写./src/pages/index/service.ts
,如下:
import Api from '../../utils/request'
export const getLists = (data) => {
return Api.getLists(data)
}
5.改写./src/pages/index/index.interface.ts
,如下:
/**
* index.state 参数类型
*
* @export
* @interface IndexState
*/
export interface IndexState {
month: number
day: number
}
/**
- index.props 参数类型
- @export
- @interface IndexProps
*/
export interface IndexProps {
dispatch?: any,
data?: Array<DataInterface>
}
export interface DataInterface {
day: number
des: string
lunar: string
month: number
pic: string
title: string
year: number
_id: string
}
6.改写./src/pages/index/model.ts
,如下:
// import Taro from '@tarojs/taro';
import * as indexApi from './service';
export default {
namespace: ‘index’,
state: {
data: [],
key: ‘72eed010c976e448971655b56fc2324e’,
v: ‘1.0’
},
effects: {
* getLists({ payload }, { select, call, put }) {
const { key, v } = yield select(state => state.index)
const { error, result } = yield call(indexApi.getLists, {
key,
v,
…payload
})
if (!error) {
yield put({
type: ‘updateState’,
payload: {
data: result
}
})
}
}
},
reducers: {
updateState(state, { payload: data }) {
return { …state, …data }
}
}
}
7.改写./src/pages/index/index.tsx
,如下:
import Taro, { Component, Config } from '@tarojs/taro'
import { View, Text } from '@tarojs/components'
import { connect } from '@tarojs/redux'
// import Api from '../../utils/request'
// import Tips from '../../utils/tips'
import { IndexProps, IndexState } from './index.interface'
import './index.scss'
// import { } from '../../components'
@connect(({ index }) => ({
…index,
}))
class Index extends Component<IndexProps, IndexState> {
config: Config = {
navigationBarTitleText: ‘Taro + dva demo’
}
constructor(props: IndexProps) {
super(props)
this.state = {
month: 0,
day: 0
}
}
// 获取今日数据
async getData(month: number, day: number) {
await this.props.dispatch({
type: ‘index/getLists’,
payload: {
month: month,
day: day
}
})
}
// 获取系统当前时间并请求参数
getDate() {
const myDate = new Date()
const m = myDate.getMonth() + 1
const d = myDate.getDate()
this.setState({
month: m,
day: d
})
this.getData(m, d)
}
componentDidMount() {
this.getDate()
}
render() {
const { month, day } = this.state
const { data } = this.props
return (
<View className=‘fx-index-wrap’>
<View className=‘index-topbar’>
<Text>{</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>month<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">月</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>day<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">日</span><span class="token template-punctuation string">
}</Text>
<View>历史上的今天都发生了这些大事</View>
</View>
<View className=‘index-list’>
{
data && data.map((item, index) => {
return <View className=‘index-li’ key={index}>
<View className=‘index-bg’ style={</span><span class="token string">background-image: url(</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>item<span class="token punctuation">.</span>pic<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">)</span><span class="token template-punctuation string">
}></View>
<View className=‘index-info’>
<View className=‘index-title’>{item.title}</View>
<View className=‘index-des’>{item.des}</View>
</View>
</View>
})
}
</View>
</View>
)
}
}
export default Index
5.接下来写样式./src/pages/index/index.scss
,如下:
.index {
&-wrap {
width: 100%;
min-height: 100vh;
}
&-topbar {
padding: 10rpx 50rpx;
text-align: center;
font-weight: bold;
Text {
color: rgb(248, 122, 3);
font-size: 40rpx;
}
View {
color: #333;
font-size: 30rpx;
}
}
&-list {
padding: 50rpx;
}
&-li {
box-shadow: 0 4rpx 20rpx rgba($color: #000000, $alpha: 0.1);
margin-bottom: 50rpx;
border-radius: 8rpx;
overflow: hidden;
}
&-bg {
width: 100%;
height: 300rpx;
background-repeat: no-repeat;
background-size: contain;
background-position: center center;
background-color: #f5f5f5;
}
&-info {
padding: 15rpx;
}
&-title {
font-size: 30rpx;
font-weight: bold;
margin-bottom: 10rpx;
}
&-des {
font-size: 26rpx;
color: #666;
}
}
这时候基本结束了,在命令行运行:
cnpm run dev:weapp
如下显示,说明编译成功(tip
:以后记得先编译,我是之前写好了的,不然很有可能一堆报错,那时候估计你会绝望的)
![](http://upload-images.jianshu.io/upload_images/13691847-47dbd90fe90f67dd.jpg?imageMogr2/auto-orient/strip|imageView2/2/w/1200/format/webp)
最后的最后,打开微信开发者工具,选择微信小程序,选择taro-demo文件夹下编译成功的
dist
,appid就用微信提供给你测试的,名字随便输入一个,点击确定,之前步骤都没问题的话,最后显示的结果如下图:
![](http://upload-images.jianshu.io/upload_images/13691847-87e8b78814891401.jpg?imageMogr2/auto-orient/strip|imageView2/2/w/668/format/webp)
最后,恭喜你,配置完了,可以满足基本开发和需求了,如果有什么错误还望指出
。