這段時間一直忙于公司業(yè)務中听诸,不可自拔坐求,好在通過零零星星的點滴時間,慢慢的還是完成了腳手架搭建晌梨。微信小程序發(fā)展到現(xiàn)在桥嗤,已經(jīng)不再像以前只是簡簡單單的應用,業(yè)務愈發(fā)的臃腫了起來仔蝌,同時也催生出了許多框架泛领,mpvue,wepy以及后起之秀taro等敛惊。
搭建這次腳手架的目的主要是為了滿足后期小程序快速開發(fā)的需求渊鞋,首先看看該腳手架搭建的簡單的todo演示應用效果
最初選擇taro,主要的原因是習慣于react開發(fā)方式,在taro還沒發(fā)布正式版之前锡宋,上一個項目選擇了wepy作為開發(fā)框架儡湾,深陷苦擾,我們不得不游走切換于wepy語法及原生小程序組件語法之間
特性
- 簡化redux的引入和配置执俩,只需簡單幾步即可快速開發(fā)
- 簡易的環(huán)境配置徐钠,支持配置多環(huán)境開發(fā)
- 對于業(yè)務錯誤進行了全局捕獲,即可統(tǒng)一提示役首,又可定制性處理
- 引入資源自動上傳阿里云oss服務器尝丐,并自動替換成最終服務器路徑
- 封裝request,支持restful api
快速開始
我們使用yarn工具代替npm進行依賴管理衡奥,沒有安裝yarn的摊崭,請先安裝,實在不想安裝杰赛,可以用npm代替
綁定oss配置信息
首先我們克隆腳手架到本地服務器
$ git clone git@github.com:FaureWu/ztaro.git
安裝依賴包
$ cd ./ztaro
$ yarn # 如果沒有安裝yarn 則可以使用npm install
接下來我們需要配置阿里云oss服務器呢簸,打開文件./ztaro/config/config.js
module.exports = {
// 阿里云oss插件配置
oss: {
dev: {
accessKeyId: '************',
accessKeySecret: '***************',
endpoint: 'https://************.aliyuncs.com',
region: '*************',
bucket: '*********',
},
prod: {
accessKeyId: '************',
accessKeySecret: '***************',
endpoint: 'https://************.aliyuncs.com',
region: '*************',
bucket: '*********',
},
path: 'src/assets/',
prefix: '@oss',
formats: ['png', 'jpeg', 'jpg', 'svg'],
},
}
可以看到oss配置項,該配置項支持區(qū)分編譯環(huán)境BUILD_ENV乏屯,dev為開發(fā)環(huán)境下的配置根时,prod為線上環(huán)境的配置,要是沒有區(qū)分辰晕,那就都配置成一樣的嘛蛤迎,其中accessKeyId,accessKeySecret含友,endpoint替裆,region,bucket都是阿里云oss基本配置窘问,這里就不多說了辆童,說一下其他參數(shù)吧
- path 這個路徑用于指定需要上傳到阿里云oss資源的搜索路徑,這個路徑下的資源并不會所有的全部都上傳到阿里云惠赫,只會上傳在代碼中使用的并且以prefix開頭的資源
- prefix 需要上傳到阿里云oss的前綴把鉴,也類似path的路徑別名
- formats 需要上傳資源格式
以下代碼僅用于展示如何使用,需要把資源放入上面配置的path路徑下儿咱,
無法使用require, import導入庭砍,無法在tabbar中配置
<Image src="@oss/logo.jpeg" /> // 可以這樣用
或者
const activeHomeIcon = '@oss/home-active.png' // 或者這樣用
或者在樣式文件中
.app {
background: url('@oss/logo.jpeg') // 還可以這樣用
}
又或者在json文件中
{
"logo": "@oss/logo.jpeg" // 又或者這樣用
}
讓微信小程序跑起來
執(zhí)行如下命令,該命令會做三件事
- 編譯taro語法為微信小程序語法混埠,并啟動監(jiān)聽文件修改
- 啟動本地mock服務器
- 啟動gulp任務怠缸,上傳圖片資源,并監(jiān)聽文件修改
$ yarn mock:weapp
等待命令執(zhí)行完成钳宪,會在根目錄下生成dist目錄揭北,打開微信開發(fā)者工具概耻,選擇編譯后dist預覽最終效果
編寫todo應用
當我們準備開始編寫一個功能前,我們希望能數(shù)據(jù)驅(qū)動開發(fā)罐呼,所以首先我們編寫模擬api請求,該腳手架主要是采用express搭建一個簡易的node api服務器侦高,通過faker進行模擬數(shù)據(jù)嫉柴,如果習慣其他生成模擬數(shù)據(jù)的庫,可以替換faker
編寫獲取todo列表的接口奉呛,新建文件ztaro/mock/todos.js
const faker = require('faker')
function createTodos(number) {
const todos = []
for (let i = 0; i < number; i += 1) {
todos.push({
id: faker.random.uuid(),
text: faker.random.words(10),
})
}
return todos
}
let todos = createTodos(faker.random.number({ min: 3, max: 6 }))
function getTodos(req, res) {
res.status(200).json({
code: 'success',
message: '獲取待辦列表成功',
data: todos,
})
}
module.exports = {
'GET /v1/todos': getTodos,
}
編寫getTodos的request請求计螺,新建/ztaro/src/requests/todos.js
import request from '../utils/request'
export function getTodos() {
return request({
url: '/v1/todos',
})
}
編寫用于todos model,在ztaro/src/models/todos.js
import { getTodos } from '../requests/todos'
export default {
namespace: 'todos',
state: {
lists: [],
},
// 這里的配置用于擴展model瞧壮,model之前共用邏輯
// common mixins定義于ztaro/src/mixins/common.js
// 主要提供共用的update action
mixins: ['common'],
effects: {
async getTodos(action, { put }) {
const { data } = await getTodos()
// 這里的update是由于上面引入了common mixins
// 如果沒有引入打開下方的reducers注釋
put({ type: 'update', payload: { lists: data } })
},
},
// reducers: {
// update({ payload }, state) {
// return { ...state, ...payload }
// },
// },
},
將todos model注入到應用中登馒,引入到ztaro/src/models/index.js
import todos from './todos'
export default [todos]
該文件會被引入到到app.js中
數(shù)據(jù)準備已經(jīng)完成,我們可以開始編寫界面咆槽,ztaro/src/pages/todos/*
import Taro, { Component } from '@tarojs/taro'
import { View } from '@tarojs/components'
import { connect } from '@tarojs/redux'
import { dispatcher } from '@opcjs/zoro'
import ComponentSpin from '../../components/spin/spin'
import './todos.scss'
// 從redux中綁定數(shù)據(jù)到界面
@connect(({ todos }) => ({
todos: todos.lists,
}))
class PageTodos extends Component {
config = {
navigationBarTitleText: '待辦事項',
}
state = {
loading: false,
}
componentWillMount() {
this.showLoading()
dispatcher.todos
.getTodos()
// 獲取成功之后執(zhí)行.then
.then(this.hideLoading)
// 獲取失敗之后執(zhí)行.catch
.catch(this.hideLoading)
}
showLoading = () => this.setState({ loading: true })
hideLoading = () => this.setState({ loading: false })
render() {
const { todos } = this.props
const { value, loading } = this.state
return (
<View className="todos">
<ComponentSpin loading={loading} />
<View className="logo" />
{todos.map(todo => (
<View className="todo" key={todo.id}>
<Text>{todo.text}</Text>
</View>
))}
</View>
)
}
}
export default PageTodos
最后我們需要編寫我們的樣式
.todos {
position: relative;
counter-reset: count;
.logo {
display: block;
height: 160px;
// 這里以@oss前墜開頭陈轿,因此編譯時會被上傳至阿里云oss,并替換成最終路徑
background-image: url("@oss/logo.jpeg");
background-size: auto 160px;
background-repeat: no-repeat;
background-origin: center;
}
.todo {
font-size: 28px;
word-break: break-all;
counter-increment: count;
padding: 10px;
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: flex-start;
&::before {
content: counter(count);
display: inline-block;
border: 2px solid #e9e9e9;
padding: 0 10px;
margin: 0 10px 0 0;
border-radius: 10px;
}
}
}
全局錯誤提示
在zoro框架中秦忿,可以注冊一個全局錯誤函數(shù)onError麦射,該函數(shù)注冊于ztaro/src/app.js
import zoro from '@opcjs/zoro'
const app = zoro({
onError(error) {
if (error.message) {
Taro.showToast({
icon: 'none',
title: error.message,
duration: 2000,
})
}
},
})
zoro框架會對于每一個effect外層做了try/catch,捕獲在執(zhí)行effect過程中拋出的一切錯誤灯谣,并回調(diào)到onError函數(shù)里
那我們?nèi)绾尾东@異步請求的錯誤呢潜秋,我們可以移步ztaro/src/utils/request.js
export default function request(options) {
const { url } = options
return Taro.request(
resolveParams({
...options,
url: `${CONFIG.SERVER}${url}`,
mode: 'cors',
header: {
'content-type': 'application/json',
...options.header,
},
}),
)
.then(checkHttpStatus)
.then(checkSuccess)
.catch(throwError)
}
封裝的request函數(shù)中有checkHttpStatus,checkSuccess兩個函數(shù)胎许,我們分別看下它們做了什么
function checkHttpStatus(response) {
// 當http狀態(tài)在200到300之間時峻呛,說明請求是成功的
// 我們只需返回響應的數(shù)據(jù)即可
if (response.statusCode >= 200 && response.statusCode < 300) {
return response.data
}
// 當狀態(tài)出現(xiàn)錯誤時,我們需要拋出相關信息
// 這樣錯誤被一層層往上拋出辜窑,最終被zoro捕獲钩述,回調(diào)onError函數(shù)
const message =
HTTP_ERROR[response.statusCode] || `ERROR CODE: ${response.statusCode}`
const error = new Error(message)
error.response = response
throw error
}
function checkSuccess(data) {
// 當數(shù)據(jù)是個字符串,或者是ArrayBuffer時穆碎,我們認為業(yè)務是成功的
// 返回數(shù)據(jù)即可
if (typeof data === 'string' && data instanceof ArrayBuffer) {
return data
}
// 當業(yè)務響應數(shù)據(jù)中的code值返回SUCCESS時切距,我們認為業(yè)務是成功的
// 這里主要根據(jù)與后端約定好的格式,你可以根據(jù)實際情況進行更改
if (
typeof data.code === 'string' &&
data.code.toLocaleUpperCase() === 'SUCCESS'
) {
return data
}
// 當業(yè)務出現(xiàn)錯誤時惨远,我們依舊需要獲取后臺拋出的錯誤谜悟,
// 像上一層拋出錯誤,最終被zoro捕獲北秽,回調(diào)onError
const error = new Error(data.message)
error.data = data
throw error
}
這樣處理過后葡幸,當后臺接口報錯,或者業(yè)務錯誤時贺氓,就會看到彈出toast錯誤提示了蔚叨,無需額外的處理
那當我們想要屏蔽某些不那么重要的接口,錯誤提示,我們該怎么辦呢蔑水?就那getTodos來舉例邢锯,我們只需修改它
async getTodos(action, { put }) {
try {
const { data } = await getTodos()
put({ type: 'update', payload: { lists: data } })
} catch (error) {
// 這里僅僅是為了可以知道該接口是否錯誤
return { isError: true, error }
}
}
這樣即使是getTodos接口拋出錯誤也不會觸發(fā)全局錯誤提示了
接下來我們可以自定義該接口的錯誤邏輯了
dispatcher.todos.getTodos().then((data = {}) => {
if (data.isError) {
// 執(zhí)行一些相關的錯誤
}
})
假如我們想要在dispatcher.todos.getTodos()的.then函數(shù)中獲取到某些數(shù)據(jù),我們又該如何呢搀别?依舊那getTodos舉例
async getTodos(action, { put }) {
const { data } = await getTodos()
put({ type: 'update', payload: { lists: data } })
return data
}
然后我們在使用的時候
dispatcher.todos.getTodos().then(data => console.log(data))
與服務器進行接口聯(lián)調(diào)
在進行接口聯(lián)調(diào)之前丹擎,我們首先需要配置開發(fā)環(huán)境下的api地址,打開./ztaro/config/config.js
module.exports = {
server: {
// 修改下面這一行為你的開發(fā)環(huán)境下的api服務器
dev: 'https://devapiserver',
},
}
配置完成后執(zhí)行如下命令
yarn dev:weapp
這個所有的api便會指向開發(fā)環(huán)境下的api服務器地址了
打包測試包
執(zhí)行如下命令即可
yarn build:weapp-dev
打包線上包
首先配置生產(chǎn)環(huán)境下的api地址歇父,打開./ztaro/config/config.js
module.exports = {
server: {
// 修改下面這一行為你的生產(chǎn)環(huán)境下的api服務器
prod: 'https://devapiserver',
},
}
配置完成后執(zhí)行
yarn build:weapp
以上教程僅列出了微信小程序端蒂培,h5端也基本是一致的,只需執(zhí)行的命令中榜苫,將weapp替換成h5即可
其他更詳細的使用方式护戳,請查看對應github倉庫
歡迎star,歡迎加我咨詢相關問題