小程序第三方框架對比 ( wepy / mpvue / taro )

   

 

     衆所周知現在市面上端的形態多種多樣,手機Web、ReactNative、微信小程序, 支付寶小程序, 快應用等,每一端都是巨大的流量入口,當業務要求同時在不一樣的端都要求有所表現的時候,針對不一樣的端去編寫多套代碼的成本顯然很是高,這時候只編寫一套代碼就可以適配到多端的能力就顯得極爲須要。但面對目前市面上成熟的小程序第三方框架如何針對本身的需求進行選擇也是一個麻煩事,本文針對當前市面上的三大轉譯框架進行一個綜合對比,但願能對你們的技術選擇有所幫助,若有哪裏不妥的地方但願指正;css

 小程序開發有哪些痛點?

  • 頻繁調用 setData及 setData過程當中頁面跳閃
  • 組件化支持能力太弱(幾乎沒有)
  • 不能使用 less、scss 等預編譯器
  • request 併發次數限制

 爲何使用第三方框架? 

  • 只要熟悉vue或react便可快速上手,學習成本低 
  • 一套代碼可在多端編譯運行(微信,支付寶,h5,RN)  支付寶小程序暫不完善
  • 組件化開發,完美解決組件隔離,組件嵌套,組件通訊等問題 
  • 支持使用第三方 npm 資源
  • 使小程序可支持 Promise,解決回調煩惱
  • 可以使用 Generator Fu-nction / Class / Async Function 等特性,提高開發效率 
  • 對小程序自己的優化,如生命週期的補充,性能的優化等等
  • 支持樣式編譯器: Scss/Less,模板編譯器,代碼編譯器:Babel/Typescript。

    第三方框架對比 wepy mpvue taro

  在這裏我經過對目前已開源的三種經常使用小程序框架作一個綜合對比, 還有一個叫nanchi的基於react的小程序轉譯框架,因爲沒來的及研究暫不作比較;html

  • WEPY https://tencent.github.io/wepy/document.html

  騰訊團隊開源的一款類vue語法規範的小程序框架,借鑑了Vue的語法風格和功能特性,支持了Vue的諸多特徵,好比父子組件、組件之間的通訊、computed屬性計算、wathcer監聽器、props傳值、slot槽分發,還有不少高級的特徵支持:Mixin混合、攔截器等;WePY發佈的第一個版本是2016年12月份,也就是小程序剛剛推出的時候,到目前爲止,WePY已經發布了52個版本, 最新版本爲1.7.2; 前端

  • MpVue http://mpvue.com/mpvue/#-html

  美團團隊開源的一款使用 Vue.js 開發微信小程序的前端框架。使用此框架,開發者將獲得完整的 Vue.js 開發體驗,同時爲 H5 和小程序提供了代碼複用的能力。mpvue在發佈後的幾天間得到2.7k的star,上升速度飛起,截至目前爲止已經有13.7k的star;vue

  • Taro https://taro.aotu.io/

   京東凹凸實驗室開源的一款使用 React.js 開發微信小程序的前端框架。它採用與 React 一致的組件化思想,組件生命週期與 React 保持一致,同時支持使用 JSX 語法,讓代碼具備更豐富的表現力,使用 Taro 進行開發能夠得到和 React 一致的開發體驗。,同時由於使用了react的緣由因此除了能編譯h5, 小程序外還能夠編譯爲ReactNative;node

Star 

  

           

 生命週期

  同爲vue規範的mpvue和wepy的生命週期和各類方法不盡相同react

  wepy

  wepy生命週期基本與原生小程序相同,再此基礎上糅合了一些vue的特性; 對於WePY中的methods屬性,由於與Vue中的使用習慣不一致,很是容易形成誤解,這裏須要特別強調一下:WePY中的methods屬性只能聲明頁面wxml標籤的bind、catch事件,不能聲明自定義方法,這與Vue中的用法是不一致的。  git

import wepy from 'wepy';

export default class MyPage extends wepy.page {
// export default class MyComponent extends wepy.component {
    customData = {}  // 自定義數據

    customFunction () {}  //自定義方法

    onLoad () {}  // 在Page和Component共用的生命週期函數

    onShow () {}  // 只在Page中存在的頁面生命週期函數

    config = {};  // 只在Page實例中存在的配置數據,對應於原生的page.json文件

    data = {};  // 頁面所需數據均需在這裏聲明,可用於模板數據綁定

    components = {};  // 聲明頁面中所引用的組件,或聲明組件中所引用的子組件

    mixins = [];  // 聲明頁面所引用的Mixin實例

    computed = {};  // 聲明計算屬性(詳見後文介紹)

    watch = {};  // 聲明數據watcher(詳見後文介紹)

    methods = {};  // 聲明頁面wxml中標籤的事件處理函數。注意,此處只用於聲明頁面wxml中標籤的bind、catch事件,自定義方法需以自定義方法的方式聲明

    events = {};  // 聲明組件之間的事件處理函數
}

  mpvuegithub

  mpvue 除了 Vue 自己的生命週期外,還兼容了小程序生命週期,這部分生命週期鉤子的來源於微信小程序的 Page, 除特殊狀況外,不建議使用小程序的生命週期 鉤子。web

 1Vue
 3 beforeCreate
 4 created
 5 beforeMount
 6 mounted
 7 beforeUpdate
 8 updated
 9 activated
10 deactivated
11 beforeDestroy
12 destroyed
13 app 部分 15 onLaunch,初始化 16 onShow,當小程序啓動,或從後臺進入前臺顯示 17 onHide,當小程序從前臺進入後臺
18 page 部分 20 onLoad,監聽頁面加載 21 onShow,監聽頁面顯示 22 onReady,監聽頁面初次渲染完成 23 onHide,監聽頁面隱藏 24 onUnload,監聽頁面卸載 25 onPullDownRefresh,監聽用戶下拉動做 26 onReachBottom,頁面上拉觸底事件的處理函數 27 onShareAppMessage,用戶點擊右上角分享 28 onPageScroll,頁面滾動 29 onTabItemTap, 當前是 tab 頁時,點擊 tab 時觸發 (mpvue 0.0.16 支持)

簡單示例vuex

new Vue({
  data: {
    a: 1
  },
  created () {
    // `this` 指向 vm 實例
    console.log('a is: ' + this.a)
  },
  onShow () {
    // `this` 指向 vm 實例
    console.log('a is: ' + this.a, '小程序觸發的 onshow')
  }
})
// => "a is: 1"

 taro與react生命週期徹底相同

class Clock extends Component {
  constructor (props) {
    super(props)
    this.state = { date: new Date() }
  }

  componentDidMount() {

  }

  componentWillUnmount() {

  }

  render () {
    return (
      <View>
        <Text>Hello, world!</Text>
        <Text>如今的時間是 {this.state.date.toLocaleTimeString()}.</Text>
      </View>
    )
  }
}

列表渲染

在列表渲染上三者也分別有不一樣的應用方法

wepy當須要循環渲染WePY組件時(相似於經過wx:for循環渲染原生的wxml標籤),必須使用WePY定義的輔助標籤<repeat>

<template>
    <!-- 注意,使用for屬性,而不是使用wx:for屬性 -->
    <repeat for="{{list}}" key="index" index="index" item="item">
        <!-- 插入<script>腳本部分所聲明的child組件,同時傳入item -->
        <child :item="item"></child>
    </repeat>
</template>

<script>
    import wepy from 'wepy';
    // 引入child組件文件
    import Child from '../components/child';

    export default class Index extends wepy.component {
        components = {
            // 聲明頁面中要使用到的Child組件的ID爲child
            child: Child
        }

        data = {
            list: [{id: 1, title: 'title1'}, {id: 2, title: 'title2'}]
        }
    }
</script>

mpvue使用v-for與vue一致,只是須要注意一點,嵌套列表渲染,必須指定不一樣的索引

<!-- 在這種嵌套循環的時候, index 和 itemIndex 這種索引是必須指定,且別名不能相同,正確的寫法以下 -->
<template>
    <ul v-for="(card, index) in list">
        <li v-for="(item, itemIndex) in card">
            {{item.value}}
        </li>
    </ul>
</template>

taro的列表循環用法基本與react相同,有一點須要注意,在 React 中,JSX 是會編譯成普通的 JS 的執行,每個 JSX 元素,其實會經過 createElement 函數建立成一個 JavaScript 對象(React Element),所以實際上你能夠這樣寫代碼 React 也是徹底能渲染的:

const list = this.state.list.map(l => {
  if (l.selected) {
    return <li>{l.text}</li>
  }
}).filter(React.isValidElement)

可是 Taro 中,JSX 會編譯成微信小程序模板字符串,所以你不能把 map 函數生成的模板當作一個數組來處理。當你須要這麼作時,應該先處理須要循環的數組,再用處理好的數組來調用 map 函數。例如上例應該寫成:

const list = this.state.list
  .filter(l => l.selected)
  .map(l => {
    return <li>{l.text}</li>
  })

事件處理

mpvue目前全支持小程序的事件處理器,引入了 Vue.js 的虛擬 DOM ,在前文模版中綁定的事件會被掛在到 vnode 上,同時 compiler 在 wxml 上綁定了小程序的事件,並作了相應的映射,因此你在真實點擊的時候經過 runtime 中 handleProxy 經過事件類型分發到 vnode 的事件上,同 Vue 在 WEB 的機制同樣,因此能夠作到無損支持。同時還順便支持了自定義事件和 $emit 機制

// 事件映射表,左側爲 WEB 事件,右側爲 小程序 對應事件
{
    click: 'tap',
    touchstart: 'touchstart',
    touchmove: 'touchmove',
    touchcancel: 'touchcancel',
    touchend: 'touchend',
    tap: 'tap',
    longtap: 'longtap',
    input: 'input',
    change: 'change',
    submit: 'submit',
    blur: 'blur',
    focus: 'focus',
    reset: 'reset',
    confirm: 'confirm',
    columnchange: 'columnchange',
    linechange: 'linechange',
    error: 'error',
    scrolltoupper: 'scrolltoupper',
    scrolltolower: 'scrolltolower',
    scroll: 'scroll'
}

踩坑注意(官方文檔):

  • 列表中沒有的原生事件也可使用例如 bindregionchange 事件直接在 dom 上將bind改成@ @regionchange,同時這個事件也很是特殊,它的 event type 有 begin 和 end 兩個,致使咱們沒法在handleProxy 中區分究竟是什麼事件,因此你在監聽此類事件的時候同時監聽事件名和事件類型既 <map @regionchange="functionName" @end="functionName" @begin="functionName"><map>
  • 小程序能力所致,bind 和 catch 事件同時綁定時候,只會觸發 bind ,catch 不會被觸發,要避免踩坑。
  • 事件修飾符
    • .stop 的使用會阻止冒泡,可是同時綁定了一個非冒泡事件,會致使該元素上的 catchEventName 失效!
    • .prevent 能夠直接幹掉,由於小程序裏沒有什麼默認事件,好比submit並不會跳轉頁面
    • .capture 支持 1.0.9
    • .self 沒有能夠判斷的標識
    • .once 也不能作,由於小程序沒有 removeEventListener, 雖然能夠直接在 handleProxy 中處理,但很是的不優雅,違背了原意,暫不考慮
  • 其餘 鍵值修飾符 等在小程序中壓根沒鍵盤,因此。。。

wepy事件綁定區別於vue,根據原生小程序事件提供了語法優化

綁定事件
 bindtap="click" 替換爲 @tap="click",
取消冒泡
原catchtap="click"替換爲@tap.stop="click"。

捕獲監聽事件
 capture-bind:tap="click" 替換爲 @tap.capture="click",
中斷捕獲監聽
capture-catch:tap=「click"替換爲 @tap.capture.stop="click"。

Taro 元素的事件處理和 DOM 元素的很類似。可是有一點語法上的不一樣:

Taro 事件綁定屬性的命名採用駝峯式寫法,而不是小寫。 若是採用 JSX 的語法你須要傳入一個函數做爲事件處理函數,而不是一個字符串 (DOM 元素的寫法)。 例如,傳統的微信小程序模板:

<button onclick="activateLasers">
  Activate Lasers
</button>

Taro 中稍稍有點不一樣:

<button onClick={this.activateLasers}>
  Activate Lasers
</button>

在 Taro 中另外一個不一樣是你不能使用 catchEvent 的方式阻止事件冒泡。你必須明確的使用 stopPropagation。例如,阻止事件冒泡你能夠這樣寫:

class Toggle extends React.Component {
  constructor (props) {
    super(props)
    this.state = {isToggleOn: true}
  }

  onClick = (e) => {
    e.stopPropagation()
    this.setState(prevState => ({
      isToggleOn: !prevState.isToggleOn
    }))
  }

  render () {
    return (
      <button onClick={this.onClick}>
        {this.state.isToggleOn ? 'ON' : 'OFF'}
      </button>
    )
  }
}

 request請求

wepy對wx.request作了接受參數的修改,值得一提的是它提供了針對全局的intercapter攔截器
// 原生代碼:

wx.request({
    url: 'xxx',
    success: function (data) {
        console.log(data);
    }
});

// WePY 使用方式, 須要開啓 Promise 支持,參考開發規範章節
wepy.request('xxxx').then((d) => console.log(d));

// async/await 的使用方式, 須要開啓 Promise 和 async/await 支持,參考 WIKI
async function request () {
   let d = await wepy.request('xxxxx');
   console.log(d);
}

攔截器

import wepy from 'wepy';

export default class extends wepy.app {
    constructor () {
        // this is not allowed before super()
        super();
        // 攔截request請求
        this.intercept('request', {
            // 發出請求時的回調函數
            config (p) {
                // 對全部request請求中的OBJECT參數對象統一附加時間戳屬性
                p.timestamp = +new Date();
                console.log('config request: ', p);
                // 必須返回OBJECT參數對象,不然沒法發送請求到服務端
                return p;
            },

            // 請求成功後的回調函數
            success (p) {
                // 能夠在這裏對收到的響應數據對象進行加工處理
                console.log('request success: ', p);
                // 必須返回響應數據對象,不然後續沒法對響應數據進行處理
                return p;
            },

            //請求失敗後的回調函數
            fail (p) {
                console.log('request fail: ', p);
                // 必須返回響應數據對象,不然後續沒法對響應數據進行處理
                return p;
            },

            // 請求完成時的回調函數(請求成功或失敗都會被執行)
            complete (p) {
                console.log('request complete: ', p);
            }
        });
    }
}

taro對request進行了二次封裝,可使用Taro.request(OBJECT)發起網絡請求,支持 Promise 化使用。

 

import Taro from '@tarojs/taro'

Taro.request({
  url: 'http://localhost:8080/test',
  data: {
    foo: 'foo',
    bar: 10
  },
  header: {
    'content-type': 'application/json'
  }
})
  .then(res => console.log(res.data))

mpvue沒有對request作特殊優化,與原生相同,能夠本身根據須要進行封裝

狀態管理

wepy 可引用Redux和Mbox,目前wepy的腳手架內已經集成了redux,選擇須要便可;

mpVue使用vuex

taro使用Redux

如何選擇適合本身的項目

  • 若是隻須要作一個微信小程序則根據本身的擅長框架選擇mpvue或taro
  • 若是是當前老項目想像向程序遷移同時老項目又是使用vue開發,建議使用mpvue或wepy
  • 若是是老項目使用react開發且須要部分遷移小程序,建議使用taro
  • 若是是新項目且新項目須要同時支持微信小程序和支付寶小程序, 建議使用原生開發,由於目前框架的轉譯支付寶小程序支持並非很好,且出了問題很差定位修改, 但若是是小demo不涉及太多邏輯的項目均可以使用框架做爲嚐鮮; 但若是是涉及太多交互邏輯的則不建議使用框架轉譯,因爲支付寶小程序在視圖層基本與小程序一致因此建議手動更改替換部分方法和全局替換一些屬性或文件名,如wxml替換爲axml這種, 手動轉換時間比大概是四比一; 固然若是人手足夠一端開發一個是最好的...

時刻前端新鮮技術推送,按期前端精品文章分享,歡迎關注公衆號前端小苑。

 

相關文章
相關標籤/搜索