MPVUE - 使用vue.js開發微信小程序
什么是mpvue?
mpvue 是美團點評前端團隊開源的一款使用 Vue.js 開發微信小程序的前端框架。框架提供了完整的 Vue.js 開發體驗,開發者編寫 Vue.js 代碼,mpvue 將其解析轉換為小程序並確保其正確運行。
簡單上手mpvue
官方提供了一套quickstart模板。
vue init mpvue/mpvue-quickstart my-project
安裝好依賴之后,執行npm run dev,將會將小程序文件打包進dist文件夾,之后使用微信開發者工具將目錄指向dist文件夾即可。
差異化情況
-
生命周期
支持vue.js的生命周期部分,並且兼容了小程序的生命周期。
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"
-
模板語法
不支持 v-html,因為小程序里沒有dom和bom的概念。
-
不支持部分復雜的 JavaScript 渲染表達式
mpvue會把 template 中的 {{}} 雙花括號的部分,直接編碼到 wxml 文件中,由於微信小程序的能力限制(數據綁定),所以無法支持復雜的 JavaScript 表達式
目前可以使用的有 + - * % ?: ! == === > < [] .。
以下這幾種情況都不支持:
// 基本類型的方法調用
<p>{{ message.split('').reverse().join('') }}</p>
// 實例方法調用
<p>{{ strDecode(message) }}</p>
// 類型判斷
<p>{{ typeof message }}</p>
// 過濾器
<p>{{ message | strDecode }}</p>
第一種可以使用計算屬性來解決,第二種和第四種都是經常使用的,可以數據初始化的時候用js處理,只不過遍歷的時候會稍微有點繁瑣。也可以讓后端支持,直接返回處理過的數據。
支持短路和斷路:
<p>{{ false || '默認值' }}</p>
<p>{{ true && '默認值' }}</p>
-
Class 與 Style 綁定
基本全支持,不支持classObj和styleObj形式,例如:
<p :class="classObj"></p>
data () {
return {
classObj: {
active: true
}
}
}
mpvue會解析成:
<p class="object Object"></p>
styleObj 類似,另外,暫不支持在組件上使用 Class 與 Style 綁定。
-
條件渲染、列表渲染、計算屬性全支持
-
組件
不支持列表:
- 暫不支持在組件引用時,在組件上定義 click 等原生事件、v-show(可用 v-if 代替)和 class style 等樣式屬性(例:
<card class="class-name"> </card>樣式是不會生效的) - Slot(scoped 暫時還沒做支持)
- 動態組件
- 異步組件
- inline-template
- X-Templates
- keep-alive
- transition
不支持復雜的slot,組件化會受限,不支持transition,過渡動畫可能要用css3手寫。
支持小程序原生組件,原生組件上的事件綁定,需要以 vue 的事件綁定語法來綁定,如 bindchange="eventName" 事件,需要寫成 @change="eventName",例如:
<picker mode="date" :value="date" start="2015-09-01" end="2017-09-01" @change="bindDateChange">
<view class="picker">
當前選擇: {{date}}
</view>
</picker>
注意事項及踩坑
-
模板中的
img標簽url指向相對路徑時不能正確解析例如:
<img src="~assets/images/home/header.png" />
<img src="./assets/images/home/header.png" />
在當前mpvue版本(1.0.8)中,圖片相對url不能正確解析,官方已列入待修復bug(27 days ago),https://github.com/Meituan-Dianping/mpvue/issues/152,暫時的解決方法是可以將靜態資源放入static文件夾中,然后img標簽用絕對路徑引入:
<img src="/static/images/home/header.png" />
帶來的副作用是不能享受url-loader的處理(小圖片轉base64)。
另外,css中卻是可以通過相對路徑引入圖片的,例如:
.icon {
background-image: url('./assets/images/home/header.png');
}
.icon1 {
background-image: url('~assets/images/home/header.png');
}
url-loader 會將小於limit數值的圖片轉成base64,但是小程序中好像規定了css中不能引入本地靜態資源,可以通過配置url-laoder、nginx別名、host解決:
host配置:
127.0.0.1 sns-mp.guahao-inc.com
nginx配置:
server {
listen 80;
server_name sns-mp.guahao-inc.com;
location /static/src/assets/ {
alias /Users/lavyun/Code/FE/vue/vue-mp-demo/src/assets/;
}
}
url-loader配置:
const imageRule = {
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10 * 1024,
publicPath: 'http://sns-mp.guahao-inc.com/',
name: utils.assetsPath('[path][name].[ext]')
}
}
// 生產環境下指向靜態資源服務器
if (isProd) {
imageRule.options.publicPath = 'http://static.gauhao-inc.com'
}
module.export = {
...,
module: {
rules: [
imageRule,
...
]
}
}
組件和小程序系統組件重名時的BUG
比如:
<loading></loading>
小程序文檔里沒有提到有loading這個組件,之前直接使用loading做組件名,產生了很多莫名其妙的bug,框架層面也沒做錯誤提示。所以大家開發時最好避免下命名沖突。
slot的各種問題
之前mpvue版本1.0.6時,使用slot生成的小程序代碼少了import命令。
https://github.com/Meituan-Dianping/mpvue/issues/217
組件的代碼:
<template>
<div class="scroll-wrapper">
<slot></slot>
<loading v-if="loading"></loading>
</div>
</template>
使用組件時的代碼:
<scroll-wrapper :loading="loading">
<top-entries></top-entries>
</scroll-wrapper>
生成的wxml:
<template name="data-v-7d8efa60-default-1">
<template data="{{...$root[$kk+'0'], $root}}" is="home$69cf8ee0"></template>
</template>
少了一行:<import src="home$69cf8ee0" />
現版本 1.0.8 使用slot時:
<scroll-wrapper :loading="loading">
<circle-list v-if="circleList.length" :list="circleList"></circle-list>
</scroll-wrapper>
circleList狀態被更新時不能正確的觸發視圖更新,官方的倉庫中也有大部分issue也是關於slot的。
使用vuex管理狀態
雖然小程序是多頁的,但小程序的多頁主要是指 視圖層 是多個 webview,相互獨立,但是 js 都是在同一個執行環境中的,所以在mpvue中可以直接使用vuex來管理狀態。
初始化一個vuex實例,然后在需要使用的組件里引入。
import Vue from 'vue'
import Vuex from 'vuex'
import actions from './actions'
import mutations from './mutations'
import getters from './getters'
import state from './state'
Vue.use(Vuex)
const store = new Vuex.Store({
state,
actions,
mutations,
getters
})
export default store
在組件里使用:
import store from '../../store'
import * as types from '../../store/types'
export default {
data () {
return {
animate: false
}
},
computed: {
popup () {
return store.getters.postEntryPopup
}
},
methods: {
close () {
this.animate = false
setTimeout(() => {
store.commit(types.POST_ENTRY_POPUP)
}, this.popup.animateTime)
}
}
}
app.json 和 page.json
框架規定在 src/main.js 中導出app.json配置,
import Vue from 'vue'
import App from './App'
Vue.config.productionTip = false
App.mpType = 'app'
const app = new Vue(App)
app.$mount()
export default {
// 這個字段走 app.json
config: {
// 頁面前帶有 ^ 符號的,會被編譯成首頁,其他頁面可以選填,我們會自動把 webpack entry 里面的入口頁面加進去
pages: ['^pages/home/main'],
window: {
onReachBottomDistance: 10
}
}
}
在 src/pages/**/main.js 中導出page.json配置:
import Vue from 'vue'
import App from './index'
const app = new Vue(App)
app.$mount()
export default {
config: {
navigationBarBackgroundColor: '#3f86ff',
navigationBarTitleText: '首頁',
backgroundTextStyle: 'light',
navigationBarTextStyle: 'light',
enablePullDownRefresh: true,
backgroundColor: '#3f86ff'
}
}
使用總結
使用mpvue可以很好降低開發人員的學習小程序語法的成本,可以很大程度上的實現h5和小程序代碼復用(使用vuejs框架),在遷移一些vue組件到小程序時,可能都不需要改動代碼或者改動少量代碼就可以直接使用。官方對待框架的態度是很不錯的,開源一個多月(3月9日 - 4月16日),一共有300個issue,處理了250個,獲得7800star,社區比較活躍。
