在Taro 0.x
的時候就寫過一個小玩意,雖然沒什麼人玩. 最近正好看到Taro 1.2.x
已經發布了javascript
類React
風格的小程序框架,部分地方針對小程序作了妥協.找點東西試試水,看看改進如何了.css
恰好公司有個需求作個手機端的舉報管理的程序, 開會上攬了過來;html
對於這個框架,你除了須要有React
的基本功外, 還須要這兩方面的知識;java
雖這個項目(僅微信端)不是很大,梳理下仍是有挺多東東的,其餘廢話很少說,直入主題node
JSX
的{}
不支持箭頭函數用箭頭函數會出現不可控的結果;react
最多見的就是報錯;webpack
JSX
裏面僅支持onClick={this.xxxx.bind(this,args)
這種寫法store
亦或者render
的結果異常(好比執行順序和值的錯誤)雖然說Taro官方支持CSS Module
,如果你考慮多平臺的話..仍是建議用常規的命名規劃來維護好一些css3
至於className
的動態css class
處理..我傾向於使用classnames
這個庫git
classname: 最普通的用法以下es6
// 在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>
複製代碼
好比你封裝的組件裏面依賴了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
的配置也有一些直接暴露
至於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
那麼清晰,可是整體上能夠看到數據的組織和響應,如圖
mobx
在跳轉前預請求?好比詳情頁,展現類的頁面,咱們通常都是經過typeId
去拿到具體的詳情,再來展現
常規作法都是進到頁面後在componentDidMount
去觸發請求,而後把結果集渲染到頁面,
但這樣一進去就會展現默認數據再替換,有點突兀;咱們確定想改善用戶體驗,那就把數據預請求
咱們能夠根據實際場景在跳轉以前的生命週期入手,好比redirecTo
能夠在componentDidHide
內調用函數dispatch
reLuanch
能夠在componentWillUnmount
內觸發;
跳轉過去的頁面,能夠直接從props
拿到渲染,不會那麼突兀
對於日期的處理,咱們最經常使用的是兩種姿式的傳遞的時候用時間戳,展現的時候用可讀性較強的YYYY-MM-DD
這種
因此就不必引入moment
這個大庫了用的是dayjs
,很小功能比較全面的庫,api
類moment
,用過都說好.
固然,你本身用函數封裝一個轉換也行,就不用引入多一個庫了,見仁見智了.
如果要指定組件自身內的結點,this
必須爲this.$scope
微信小程序官方的this
表明實例,在taro
中this.$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": {}
}
複製代碼
其餘小程序有對應的配置文件,看官方連接
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
})
}
}
複製代碼
// 我配置了alias
import 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);
}
}
複製代碼
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
只要引入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
來寫過渡,
好比看我這邊實現的一個效果,本身感受還看得過去
//如果要產生視覺效應,那元素有偏移才能看出來,因此通常被做用的元素都不會在默認位置
// 這個項目用了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>
複製代碼
就是讓展開和收起有個過渡效果,
通過N屢次的嘗試(不能給元素設置height
!!), 把元素初始化的高度設置max-height:0
,
其餘過渡設置合適的max-height
便可解決
有些文檔沒說到,只能去翻源碼...看common.d.ts
一目瞭然,好比長按事件這些
其實跟在普通開發模式上寫法差不,基本仍是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
複製代碼
Taro
和Taro UI
目前版本對ts
的支持還有待提升,會偶爾碰到缺乏types
的
如果項目不大,對於想省心的,建議直接擼JS
版本;
Taro
社區目前仍是很活躍的, 照這樣的狀況下去,再迭代兩三個X.Y.Z
(Y位)版本應該會好用不少.
ts
的好處很明顯,編輯器能夠直接懸浮顯示推斷的類型,不少錯誤能夠在開發過程避免了;
水文到此結束,有不對之處請留言,會及時修正,謝謝閱讀.