Taro v1.2.7開發一個微信小程序下來的要點梳理及爬坑姿式

前言

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>

複製代碼

本身封裝的組件提示類型缺失的(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的配置也有一些直接暴露

至於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,很小功能比較全面的庫,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
    })
  }
 }


複製代碼
  • 用法
// 我配置了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);
    }
  }

複製代碼

簡化版的節流器(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只要引入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>
複製代碼

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

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

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

其餘過渡設置合適的max-height便可解決

Taro裏面對事件的支持

有些文檔沒說到,只能去翻源碼...看common.d.ts一目瞭然,好比長按事件這些

github.com/NervJS/taro…

css3 loading 引入

其實跟在普通開發模式上寫法差不,基本仍是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的好處很明顯,編輯器能夠直接懸浮顯示推斷的類型,不少錯誤能夠在開發過程避免了;

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

相關文章
相關標籤/搜索