小程式折騰記 – Taro(1.2.x)開發一個微信小程式下來的要點梳理及爬坑姿勢(篇幅有點長)

CRPER發表於2019-01-16

前言

Taro 0.x的時候就寫過一個小玩意,雖然沒什麼人玩. 最近正好看到Taro 1.2.x已經發布了

React風格的小程式框架,部分地方針對小程式做了妥協.找點東西試試水,看看改進如何了.

剛好公司有個需求做個手機端的舉報管理的程式, 開會上攬了過來;

對於這個框架,你除了需要有React的基本功外, 還需要這兩方面的知識;

雖這個專案(僅微信端)不是很大,梳理下還是有挺多東東的,其他廢話不多說,直入主題


問題彙總

生命週期及JSX{
}
不支援箭頭函式

用箭頭函式會出現不可控的結果;

最常見的就是報錯;

  • JSX裡面僅支援onClick={this.xxxx.bind(this,args)這種寫法
  • 生命週期用了會導致,store亦或者render的結果異常(比如執行順序和值的錯誤)
  • 至於其他常規函式則支援箭頭函式的寫法

動態樣式

雖說Taro官方支援CSS Module,若是你考慮多平臺的話..還是建議用常規的命名規劃來維護好一些

至於className的動態css class處理..我傾向於使用classnames這個庫

classname: 最普通的用法如下

// 在Taro的用法跟在React的用法差不多..Taro上需要放在{
} ,不然會直接報錯(就是= classnames物件這種寫法)
import classnames from 'classnames' <
View className={classnames({
"tab-operator": true, "show": userIdList.length>
0, "hide": userIdList.length === 0
})
}>
<
View className="tab-content-top-counter">
<
View className="left-text">
{userIdList.length === 0 ?'如需操作條目請勾選下面條目!':`選中了${userIdList.length
}條`
}<
/View>
{userIdList.length === 0 ? null : <
View className="unselect" onClick={this.unselect.bind(this)
}
>
取消<
/View>

} <
/View>
複製程式碼

自己封裝的元件提示型別缺失的(TS)

比如你封裝的元件裡面依賴了Taro封裝的一些元件,這時候暴露這個元件,

就會缺失你自己加進去的特性,導致編輯器會提示有錯誤資訊..

最簡便的就是用type或者interface,這樣就不會報錯了.比如下面

//方式一type staticPropsSearchPanel={ 
open: boolean, onClose?: () =>
void
} // 也可以用interface ,與上面的區別,比較明顯的是這個可以繼承其他的// 方式二interface staticPropsSearchPanel {
open: boolean, onClose?: () =>
void
}class SearchPanel extends Component<
staticPropsSearchPanel>
{
}複製程式碼

元件支援程度

  • 不支援函式式元件:具體看官方說法截止1.2.x依舊不支援,只能寫成 class xx extends Component這種

  • 不支援同個檔案內直接多個class xx extends且被引用


允許幾種狀態管理器的接入?

dva,mobx,redux 都有對應taro接入方案,後兩者是taro官方維護


是否支援alias

最新版是支援的(可用),在config目錄暴露了配置檔案,當然很多其他webpack的配置也有一些直接暴露

小程式折騰記 – Taro(1.2.x)開發一個微信小程式下來的要點梳理及爬坑姿勢(篇幅有點長)

至於eslint不識別alias符號的,這個暫時無解,我試過社群的一些方案,好像沒啥用!


路由跳轉註意點

  • 中劃線的坑跳轉的路由不支援中劃線(目前),以後未知

開發模式和真機除錯可以正常編譯,打包上傳就不能識別了…浪費我很多時間..

  • 路徑的坑

跳轉的url必須全路徑!!!!!,比如

// 重定向,會提供返回按鈕Taro.redirectTo({ 
url: '/pages/list/index'
})// 過載整個程式,關閉其他所有頁面(清除堆疊的頁面),然後開啟你指定的頁面// 這個非常適合鑑權失敗或者過期的時候.只開啟註冊頁面Taro.reLaunch({
url:'/pages/login/index'
})//還有其他`navigate`這些,基本都是微信文件寫到的東西,taro封裝了下複製程式碼

鑑權頁面渲染突兀的改善姿勢!

若是你在第一個頁面做鑑權跳轉,很容易就遇到渲染部分再跳轉的

給人的視覺反饋不是很好,對於此,寫一箇中間鑑權頁面作為第一頁,跳轉會改善很多(視覺上)

因為效果可以定製,而不渲染很多沒必要的元件

比如我的,我的入口頁面就是auth

import './index.less';
import {
View
} from '@tarojs/components';
import Taro, {
Component, Config
} from '@tarojs/taro';
class Auth extends Component {
/** * 指定config的型別宣告為: Taro.Config * * 由於 typescript 對於 object 型別推導只能推出 Key 的基本型別 * 對於像 navigationBarTextStyle: 'black' 這樣的推匯出的型別是 string * 提示和宣告 navigationBarTextStyle: 'black' | 'white' 型別衝突, 需要顯示宣告型別 */ config: Config = {
navigationBarTitleText: 'xx小助手'
} static options = {
addGlobalClass: true
} // 有token就可以進入內容區域,至於token是否有效是在裡面去判斷的;
// 沒有token乖乖去登入 componentDidShow() {
const token = Taro.getStorageSync('token');
if (!!token) {
Taro.redirectTo({
url: '/pages/list/index'
}) return
} Taro.redirectTo({
url: '/pages/login/index'
})
} render() {
return ( <
View className='auth-page'>
loading....<
/View>
)
}
}export default Auth複製程式碼

componentDidShow的注意點

previewImage(圖片的點選全屏預覽),在關掉後會再次觸發該生命週期..

所以把請求放這裡的需要自己權衡下..比如我的列表展開後,點選圖片關閉後導致列表重刷;

挪到了componentWillMount就不會受previewImage的影響


mobx的接入及資料觀察?

mobx的接入和常規的接入差不多,用法基本也一致..

就是從mobx-react變成@tarojsw/mobx,由taro封裝來提供

至於devtools這種.小程式目前只能從開發者工具看到,

雖然沒專業的devtools那麼清晰,但是總體上可以看到資料的組織和響應,如圖

小程式折騰記 – Taro(1.2.x)開發一個微信小程式下來的要點梳理及爬坑姿勢(篇幅有點長)

結合mobx在跳轉前預請求?

比如詳情頁,展示類的頁面,我們一般都是通過typeId去拿到具體的詳情,再來展示

常規做法都是進到頁面後在componentDidMount去觸發請求,然後把結果集渲染到頁面,

但這樣一進去就會展示預設資料再替換,有點突兀;
我們肯定想改善使用者體驗,那就把資料預請求

我們可以根據實際場景在跳轉之前的生命週期入手,比如redirecTo可以在componentDidHide內呼叫函式dispatch

reLuanch可以在componentWillUnmount內觸發;

跳轉過去的頁面,可以直接從props拿到渲染,不會那麼突兀


時間戳及常見日期格式轉換

對於日期的處理,我們最常用的是兩種姿勢的傳遞的時候用時間戳,展示的時候用可讀性較強的YYYY-MM-DD這種

所以就沒必要引入moment這個大庫了用的是dayjs,很小功能比較全面的庫,apimoment,用過都說好.

當然,你自己用函式封裝一個轉換也行,就不用引入多一個庫了,見仁見智了.


獲取結點資訊注意點

若是要指定元件自身內的結點,this必須為this.$scope

微信小程式官方的this代表例項,在tarothis.$scope代表元件自身(例項)

  componentDidMount() { 
const query = Taro.createSelectorQuery().in(this.$scope);
query.select('#list-card').boundingClientRect((res) =>
{
console.log('res: ', res);

}).exec()
}複製程式碼

變動專案基礎資訊(微信小程式)

直接在開發者工具的選項裡面勾選不會儲存到專案內,比如基礎庫的切換;

有效的是直接操作根目錄下的project.config.json

// 這份配置的引數可以具體看微信官方給出的解釋,會更加全面// https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html?search-key=%E9%A1%B9%E7%9B%AE%E9%85%8D%E7%BD%AE{ 
"miniprogramRoot": "打包輸出路徑", "projectname": "專案名稱", "description": "聲兮管理後臺小程式", "appid": "xxxx", "setting": {
"urlCheck": true, // 是否檢查安全域名和 TLS 版本 "es6": false, // 是否啟用es6轉換 "postcss": true, // 啟用postcss的支援 "minified": false, // 是否壓縮程式碼 "newFeature": true // 是否啟用新特性的支援
}, "compileType": "miniprogram", // 編譯型別 "libVersion": "2.5.0", // 基礎庫版本的指定 "condition": {
}
}複製程式碼

其他小程式有對應的配置檔案,看官方連結


封裝的一些小玩意

請求封裝(TS)

  • request.tsx
    • 支援路由prefix
    • header的合併
    • 響應的攔截
/* * @Author: CRPER * @LastEditors: CRPER * @Github: https://github.com/crper * @Motto: 折騰是一種樂趣,求知是一種追求。不懂就學,懂則分享。 * @Description:請求介面封裝 */import Taro from '@tarojs/taro';
import '@tarojs/async-await';
interface options {
header: any, method?: string, dataType?: string, responseType?: string, success?: Function, fail?: Function, complete?:Function
}/** * * @param url : 介面路徑 * @param method : 請求方法(RESTFUL,但是沒有PATCH,看微信文件支援) * @param data : 傳遞的資料 * @param options : 可以覆蓋header這些 * @param prefix : 介面額外的字首 */export default async function(url: string, method?:string,data?: string | [any] | Object, options?: options, prefix?: string){
// 不支援patch!!!!!微信自家的請求本身就不支援patch!!! // 微信端自己快取token const wxToken:string|void =await Taro.getStorage({
key: 'token'
}) .then(res =>
res.data).catch(err =>
{
if(err) return
} ) // 預設值 const defaultOtions: options = {
method: 'GET', header:{
}
} // 若是存在token則賦予 if (wxToken) {
defaultOtions.header.Authorization = wxToken
} const baseUrl: string = process.env.NODE_ENV === 'development' ? 'https://xxx.com/api/web' : 'https://xxx.com/api/web';
const newUrl = prefix ? `${baseUrl
}
${prefix
}
${url
}
`
: `${baseUrl
}
${url
}
`
const requestObject: any = {
url: newUrl, ...defaultOtions, ...options, method, data
} const codeMessage: Object = {
200: '伺服器成功返回請求的資料。', 201: '新建或修改資料成功。', 202: '一個請求已經進入後臺排隊(非同步任務)。', 204: '刪除資料成功。', 400: '發出的請求有錯誤,伺服器沒有進行新建或修改資料的操作。', 401: '使用者沒有許可權(令牌、使用者名稱、密碼錯誤)。', 403: '使用者得到授權,但是訪問是被禁止的。', 404: '發出的請求針對的是不存在的記錄,伺服器沒有進行操作。', 406: '請求的格式不可得。', 410: '請求的資源被永久刪除,且不會再得到的。', 412: '訪問被拒絕,請重新登入', 422: '當建立一個物件時,發生一個驗證錯誤。', 500: '伺服器發生錯誤,請檢查伺服器。', 502: '閘道器錯誤。', 503: '服務不可用,伺服器暫時過載或維護。', 504: '閘道器超時。',
};
// 檢測請求狀態 const checkStatusAndFilter = (response):Promise<
any>
| undefined =>
{
if (response.statusCode >
= 200 &
&
response.statusCode <
300) {
if (response.statusCode === 200 || response.statusCode === 304) {
return response.data
} return response;

} // 除此之外的錯所有遍歷上面的錯誤資訊丟擲異常 const errortext = codeMessage[response.statusCode] || response.errMsg;
Taro.showToast({
title: errortext, mask: true, icon: 'none', duration: 2000
}) return Promise.reject(response)
};
try {
return await Taro.request(requestObject) .then(checkStatusAndFilter) .then(res =>
{
// 這一塊是我和後端協商的,介面內部為1則出錯的,為0才有資料回來 if (res.code === 1) {
const errMsg = res.msg ? res.msg : '介面錯誤了';
Taro.showToast({
title: errMsg, mask: true, icon: 'none', duration: 2000
}) Promise.reject(errMsg)
} if (res.code === 0) {
if (res.data) {
return res.data
} return null
} return res
}).catch(errRes =>
{
if (errRes.statusCode === 412) {
Taro.reLaunch({
url:'/pages/login/index'
})
}
})
} catch (err) {
Taro.showToast({
title: '程式碼執行異常', mask: true, icon: 'none', duration: 2000
})
}
}複製程式碼
  • 用法
//  我配置了aliasimport wxfetch from '@utils/request';
// 比如我程式碼中的其中一個請求,處理行為 // 切割列表資料 spliceList = (dataIdArr: Array<
string | number>
= []
) =>
{
const {list, paginate: {total
}
} = this.state;
// 若是隻有一條,幹掉後嘗試請求列表判斷是否還有新的資料 if (list.length <
= 1) {
this.getList()
} let tempArr: Array<
Object>
= list.filter((item) =>
{
for (let i = 0;
i <
dataIdArr.length;
i++) {
let innerItemId = Number(dataIdArr[i]);
if (item.id !== innerItemId) {
return item
}
}
}) this.setState({
list: tempArr, paginate: {
total: total - dataIdArr.length
}, dataIdArr: []
})
} // 處理行為 handleActionSheetClick = async (e: number): Promise<
any>
=>
{
try {
const actionParam = {operationType: e
};
const {dataIdArr, operationNote
} = this.state;
const isActionNoValid: boolean = !e || e === 0 || (Array.isArray(dataIdArr) &
&
dataIdArr.length === 0);
if (isActionNoValid) {
Taro.atMessage({
'message': '請再次您的行為是否正常,比如勾選資料!', 'type': 'error', 'duration': 1000
}) return false;

} await wxfetch('/suspiciousDatas', 'POST', {
dataIdArr, operationNote, ...actionParam
});
// 切割陣列且關閉遮罩層 this.spliceList(dataIdArr);
this.handleActionSheetClose();

} catch (err) {
console.log(err);

}
}複製程式碼

簡化版的節流器(TS)

  • throttle.tsx
/* * @Author: CRPER * @LastEditors: CRPER * @Github: https://github.com/crper * @Motto: 折騰是一種樂趣,求知是一種追求。不懂就學,懂則分享。 * @Description: 簡易版的節流函式 */ /** * @param fn : 回撥函式 * @param threshold : 時間,單位毫秒 */export default function throttle(fn: Function, threshold: number = 1500) { 
if (threshold === null) {
threshold = 1500
} let _lastExecTime: null | number = null;
let context = this return function (...args: any[]): void {
let _nowTime: number = new Date().getTime();
if (_nowTime - Number(_lastExecTime) >
threshold || !_lastExecTime) {
fn.apply(context, args);
_lastExecTime = _nowTime
}
}
}複製程式碼
  • 用法

this.xxx.bind的基礎上

import throttle from '@utils/throttle';
// 滾動到頂部觸發onScrolltoupper = throttle(() =>
{
console.log('1111');

},3000)複製程式碼

下拉重新整理顯示內建的loading.

小程式折騰記 – Taro(1.2.x)開發一個微信小程式下來的要點梳理及爬坑姿勢(篇幅有點長)

就是微信自家的三個小點, 這個需要配置下頁面的一些自有屬性.

Taro只要引入Config,即可在元件內宣告頁面屬性

import Taro, { 
Component, Config
} from '@tarojs/taro';
class ReportList extends Component {
/** * 指定config的型別宣告為: Taro.Config * * 由於 typescript 對於 object 型別推導只能推出 Key 的基本型別 * 對於像 navigationBarTextStyle: 'black' 這樣的推匯出的型別是 string * 提示和宣告 navigationBarTextStyle: 'black' | 'white' 型別衝突, 需要顯示宣告型別 */ config: Config = {
navigationBarTitleText: '可疑資料彙總', enablePullDownRefresh: true, // 這個是啟用下拉重新整理特性 backgroundTextStyle: "dark", // 把顯示的文字顏色改成暗色調,亮色的話.你背景不改看不到,因為同色 backgroundColor:'#f7f7f7' // 頁面的背景色
}
}// 啟用後,記得加對應的條件關閉,不然會一直顯示 // 下拉重新整理 onPullDownRefresh = () :void =>
{
// 這個loading是 導航欄,頁面標題那塊顯示一個loading , 微信內建的 Taro.showLoading({
title: 'loading....'
}) // 因為我的介面請求都是 async await的姿勢,所以可以佇列執行 this.getList();
this.unselect();
// 介面請求完畢後隱藏兩個loading , 標題和下拉區域 Taro.hideLoading();
Taro.stopPullDownRefresh();

}複製程式碼

實現元件樣式過渡?

實現一個元件過渡可以一定程度上增強體驗,本質就是CSS3來寫過渡,

比如看我這邊實現的一個效果,自己感覺還看得過去

小程式折騰記 – Taro(1.2.x)開發一個微信小程式下來的要點梳理及爬坑姿勢(篇幅有點長)
  • 樣式
//若是要產生視覺效應,那元素有偏移才能看出來,所以一般被作用的元素都不會在預設位置// 這個專案用了less ,主要過渡.testdiv{ 
opacity: 0;
transform: translateY(100vh) rotate(270deg) scale(0.5);
&
.fadeIn{
opacity: 1;
transform: translateY(0) rotate(0deg);
transition:all 0.3s ease-in-out;

} &
.fadeOut{
opacity: 0;
transform: rotate(-270deg) scale(0.2) translateX(-100vw);
transition:all 0.3s ease-in-out;

}
}複製程式碼
  • 作用區域

這邊用了classnames來動態追加class

<
View className={classnames({
"search-panel": true, 'fadeIn': open, 'fadeOut': !open
})
} >
<
/View>
複製程式碼

節點元素高度的過渡(CSS3)

就是讓展開和收起有個過渡效果,

經過N多次的嘗試(不能給元素設定height!!), 把元素初始化的高度設定max-height:0,

其他過渡設定合適的max-height即可解決

小程式折騰記 – Taro(1.2.x)開發一個微信小程式下來的要點梳理及爬坑姿勢(篇幅有點長)

Taro裡面對事件的支援

有些文件沒說到,只能去翻原始碼…看common.d.ts一目瞭然,比如長按事件這些

github.com/NervJS/taro…


css3 loading 引入

小程式折騰記 – Taro(1.2.x)開發一個微信小程式下來的要點梳理及爬坑姿勢(篇幅有點長)

其實跟在普通開發模式上寫法差不,基本還是CSS3的功能,DIV換成能識別的節點而已..比如Taro

// 樣式部分 .no-data-text { 
background-color: rgba(233, 228, 228, 0.726);
color: #333;
height: 100vh;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
font-size: 50px;
font-weight: 700;
.loading-text{
font-size:28px;
color:#555;

}
}.spinner {
width: 200px;
height: 70px;
text-align: center;
font-size: 10px;

} .spinner .rect {
background-color: rgb(123, 176, 225);
height: 100%;
width: 10px;
margin:0 5px;
display: inline-block;
-webkit-animation: stretchdelay 1.2s infinite ease-in-out;
animation: stretchdelay 1.2s infinite ease-in-out;

} .spinner .rect2 {
-webkit-animation-delay: -1.1s;
animation-delay: -1.1s;

} .spinner .rect3 {
-webkit-animation-delay: -1.0s;
animation-delay: -1.0s;

} .spinner .rect4 {
-webkit-animation-delay: -0.9s;
animation-delay: -0.9s;

} .spinner .rect5 {
-webkit-animation-delay: -0.8s;
animation-delay: -0.8s;

} @-webkit-keyframes stretchdelay {
0%, 40%, 100% {
-webkit-transform: scaleY(0.4)
} 20% {
-webkit-transform: scaleY(1.0)
}
} @keyframes stretchdelay {
0%, 40%, 100% {
transform: scaleY(0.4);
-webkit-transform: scaleY(0.4);

} 20% {
transform: scaleY(1.0);
-webkit-transform: scaleY(1.0);

}
}複製程式碼
<
!--節點部分-->
<
View className="no-data-text">
<
View className="spinner">
<
View className="rect rect1">
<
/View>
<
View className="rect rect2">
<
/View>
<
View className="rect rect3">
<
/View>
<
View className="rect rect4">
<
/View>
<
View className="rect rect5">
<
/View>
<
/View>
<
View className="loading-text">
正在載入中......<
/View>
<
/View>
複製程式碼

總結

截止該文章輸出的時候,Taro的版本

? Taro v1.2.7  Taro CLI 1.2.7 environment info:    System:      OS: macOS 10.14.2      Shell: 5.3 - /bin/zsh    Binaries:      Node: 10.14.2 - /usr/local/bin/node      Yarn: 1.13.0 - /usr/local/bin/yarn      npm: 6.5.0 - /usr/local/bin/npm    npmPackages:      @tarojs/async-await: 1.2.7 =>
1.2.7 @tarojs/components: 1.2.7 =>
1.2.7 @tarojs/mobx: 1.2.7 =>
1.2.7 @tarojs/mobx-h5: 1.2.7 =>
1.2.7 @tarojs/mobx-rn: 1.2.7 =>
1.2.7 @tarojs/plugin-babel: 1.2.7 =>
1.2.7 @tarojs/plugin-csso: 1.2.7 =>
1.2.7 @tarojs/plugin-less: 1.2.7 =>
1.2.7 @tarojs/plugin-sass: 1.2.7 =>
1.2.7 @tarojs/plugin-uglifyjs: 1.2.7 =>
1.2.7 @tarojs/rn-runner: 1.2.7 =>
1.2.7 @tarojs/router: 1.2.7 =>
1.2.7 @tarojs/taro: 1.2.7 =>
1.2.7 @tarojs/taro-alipay: 1.2.7 =>
1.2.7 @tarojs/taro-h5: 1.2.7 =>
1.2.7 @tarojs/taro-swan: 1.2.7 =>
1.2.7 @tarojs/taro-tt: 1.2.7 =>
1.2.7 @tarojs/taro-weapp: 1.2.7 =>
1.2.7 @tarojs/webpack-runner: 1.2.7 =>
1.2.7 eslint-config-taro: 1.2.7 =>
1.2.7 eslint-plugin-taro: 1.2.7 =>
1.2.7 複製程式碼

TaroTaro UI目前版本對ts的支援還有待提高,會偶爾碰到缺少types

若是專案不大,對於想省心的,建議直接擼JS版本;

Taro社群目前還是很活躍的, 照這樣的情況下去,再迭代兩三個X.Y.Z(Y位)版本應該會好用很多.

ts的好處很明顯,編輯器可以直接懸浮顯示推斷的型別,很多錯誤可以在開發過程避免了;

水文到此結束,有不對之處請留言,會及時修正,謝謝閱讀.

來源:https://juejin.im/post/5c236868e51d45351c4f35a3

相關文章