mpvue開發小程序總結

前期準備

1.框架選型

原生小程序開發方式與vue有些相似,因此用過vue的前端er會很容易上手。可是原生的開發體驗實在糟糕,在前端組件化的今天用原生開發組件顯得很無力。對於習慣vue開發方式的前端er來講mpvue再合適不過了。mpvue能夠將H5代碼打包成小程序代碼,目前mpvue還作不到一套代碼多端運行(畢竟各個端有本身的差別性,小程序沒有document和window,因此那些第三方移動端組件庫並不能適用於小程序),可是已經大大減小了開發的工做量。css

2.項目的搭建


3.項目結構

project 
└───build 
└───config 
└───dist 
└───node_modules 
└───server//mock服務器 
└───src 
    └───assets
        └───sass
        |    common.scss   // 全局樣式
    └───components
    └───pages
    └───plugins //存放封裝的插件
    └───services
    |    Api.js   // 封裝請求
    |    WxApi.js   // 對小程序api二次封裝    

    └───store
        └───modules // vuex模塊文件夾
        |    index.js // vuex處理文件
        |    action.js
        |    state.js
        |    mutations.js 
        |    type.js複製代碼
|   App.vue
    |   config.js//配置信息,如請求地址等
    |   main.js
└───static    //靜態資源
    └───images//圖片
    └───font//字體圖標
│   README.md
│   package.json  
│   package-lock.json  
複製代碼

3.其餘

小程序網絡請求只能是https協議且不能有端口號html

登陸流程

1.關於OpenId和UnionId

OpenId 是一個用戶對於一個小程序/公衆號的標識,開發者能夠經過這個標識識別出用戶。前端

UnionId 是一個用戶對於同主體微信小程序/公衆號/APP的標識,開發者須要在微信開放平臺下綁定相同帳號的主體。開發者可經過UnionId,實現多個小程序、公衆號、甚至APP 之間的數據互通了。vue

同一個用戶的這兩個 ID 對於同一個小程序來講是永久不變的,就算用戶刪了小程序,下次用戶進入小程序,開發者依舊能夠經過後臺的記錄標識出來。node

2.官方給出的最佳實踐

新版的小程序對獲取用戶信息受權進行了改動,用戶在小程序中須要點擊組件後,才能夠觸發登陸受權彈窗、受權本身的暱稱頭像等數據。所以官方給出的最佳實踐是react

1.調用 wx.login 獲取 code,而後從微信後端換取到 session_key,用於解密 getUserInfo返回的敏感數據。
2.使用 wx.getSetting 獲取用戶的受權狀況
1) 若是用戶已經受權,直接調用 API wx.getUserInfo 獲取用戶最新的信息;
2) 用戶未受權,在界面中顯示一個按鈕提示用戶登入,當用戶點擊並受權後就獲取到用戶的最新信息。
3.獲取到用戶數據後能夠進行展現或者發送給本身的後端。


3.實現

  1. 製做一個登陸頁面
  2. 在App.vue裏的onLaunch生命週期中判斷Storage中是否存在,如不存在跳轉到登陸頁並把當前頁面的路由看成參數傳遞過去,如存在再調用wx.checkSession()檢查session_key 的有效性不然跳到登陸頁並把當前頁面的路由看成參數傳遞過去
  3. 在ajax請求response攔截器裏判斷狀態碼爲401表示token已過時,跳轉到登陸頁從新登錄並把當前頁面的路由看成參數傳遞過去
  4. 登陸按鈕加上open-type="getUserInfo"屬性,並監聽getuserinfo事件,用戶點擊後會返回加密後的用戶信息,此時執行wx.login()獲取到code,將code和用戶信息發送到後臺換取token,並把token存儲到Storage

附官方的最佳實踐mp.weixin.qq.com/s/JBdC-G9Mw…ios

vuex模塊化

在開發時有時會遇到一些變量須要跨兩三個頁面傳遞公用,因此引入vuex是比較好的解決方案。當一個項目比較大時,全部的狀態都集中在一塊兒會獲得一個比較大的對象,進而顯得臃腫,難以維護。爲了解決這個問題,Vuex容許咱們將store分割成模塊(module),每一個module有本身的state,mutation,action,getter。新建如下目錄git

store
        └───modules // vuex模塊文件夾
               └───  kx //一級模塊
                 └───  jkdbb//二級模塊
                    | index.js  
                    ...
        |    index.js // vuex處理文件
        |    action.js
        |    state.js
        |    mutations.js 
        |    type.js複製代碼

action,state,mutations,type做爲公共。github

在index.js中設置namespaced爲trueajax

export default {
    namespaced: true,
    state: {
        ywmkid: '1',
        formData:{}
    },
    mutations:{
        SET_YWMKID (state,ywmkid) {
            state.ywmkid = ywmkid
        },
        SET_FORMDATA (state,formData) {
            state.formData = formData
        }
    }
    
}複製代碼

在store下的index.js

import Vue from 'vue'
import Vuex from 'vuex'
import state from './state'
import mutations from './mutations'
import actions from './actions'
import jkdbb from './module/kx/jkdbb/index'

Vue.use(Vuex)

const isDebug = process.env.NODE_ENV !== 'production'

export default new Vuex.Store({
    state,
    mutations,
    actions,
    modules:{
        jkdbb
    },
    strict: isDebug
})
複製代碼

訪問模塊裏的state

this.$store.state.jkdbb.ywmkid複製代碼

修改模塊裏的state

this.$store.commit('jkdbb/SET_YWMKID',this.$route.query.id)複製代碼

mpvue-router-patch

vue-router不兼容小程序,因此爲了和h5代碼保持一致選用mpvue-router-patch,其和vue-router相似的使用方式,默認對應小程序的navigateTo。附小程序的三種跳轉方式的區別

wx.navigateTo:跳轉到某個頁面。會產生一個新的頁面
wx.redirectTo:重定向到某個頁面。替換掉當前頁面
wx.switchTab:切換tab
wx.reLaunch:從新啓動。清空以前的全部頁面
在小程序中有層級的限制,只能打開十個頁面,所以要合理利用跳轉的方式。

在須要重啓應用時只須要設置reLaunch爲true便可

this.$router.push({path:'/pages/index/main',reLaunch: true})
複製代碼

在須要切換tab時只須要設置switchTab爲true便可

this.$router.push({path:'/pages/index/main',switchTab: true})
複製代碼

附完整api地址github.com/F-loat/mpvu…

flyio

axios不兼容小程序,flyio兼容Node.js微信小程序WeexReact NativeQuick App 和瀏覽器。如下的代碼是對flyio的使用進行封裝

import Config from '@/config'
import {CacheUtil} from '@/services/WxApi' 
let Fly=require("flyio/dist/npm/wx") 
let fly=new Fly;

class Response {

    constructor (res) {
        this.rawData = res
        this.code = res.code
        this.messages = res.messages
        this.data = res.data
        this.wwRequestEntity = res.wwRequestEntity
    }

    resolve () {
        if (this.isSuccess()) {
            return Promise.resolve(this.rawData)
        }
        if (this.isError()) {
            let message = Config.defErrorMessage
        }
        return Promise.reject(this.messages)
    }

    isSuccess () {
        return this.code === 1
    }

    isError () {
        return this.code === 0
    }
}


class ApiManager {

  constructor (apiPrefix) {
      fly.config.baseURL = apiPrefix || Config.apiPrefix;
      fly.config.timeout = 120000
      fly.interceptors.request.use(request => {
          const token = CacheUtil.getStorage('token');
          if (token) {  // 判斷是否存在token,若是存在的話,則每一個http header都加上token
              request.headers.Authorization = `Bearer ${token}`;
          }
          return request
      })


      fly.interceptors.response.use(res => {
          if (res.status >= 200 && res.status < 300) {
              let response = new Response(res.data)
              return response.resolve()
          }
          return Promise.reject(res)
      }, error => {
          const { response } = error;
          if (!response) return Promise.reject(error)
          if (response.status === 401) {
              //沒有權限跳轉到受權頁面
              return Promise.reject(null)
          }
          return Promise.reject(error)
      })
  }
  post(uri, data, config){
    return fly.post(uri, data, config);
  }

  get(uri, data){
    return fly.get(uri, data);
  }

  put (uri, data) {
      return fly.put(uri, data)
  }

  delete (uri, data) {
      return fly.delete(uri, data)
  }
}

export function httpManager(baseURL){
    return new ApiManager(baseURL)
}

export let apiManager = httpManager()複製代碼

在main.js引用並掛載

Vue.prototype.apiManager = apiManager;
複製代碼

以後在頁面中就能夠這樣使用

this.apiManager.post複製代碼


二次封裝Storage

小程序的Storage沒有設置過時時間的參數,咱們能夠對他進行二次封裝

export const CacheUtil = {
  getStorage(key){
    let data = wx.getStorageSync(key);
    if (!data) {
      return null;
    }else{
      let now = new Date().getTime()
      if (now >= parseInt(data.expired)) {
        wx.removeStorageSync(key)
        return null;
      }else{
        return data.data;
      }
    }
    return 
  },
  setStorage(key,data,expired){
    let time = new Date().getTime() + expired;
    wx.setStorageSync(key,{data:data,expired:time})
  },
  removeStorage(key){
    wx.removeStorageSync(key)
  }
}
複製代碼

表單驗證

小程序的表單驗證彷佛很少,經常使用的就是WxValidate。可是我以爲不是那麼好用,提示判斷還要本身寫。因此採用we-validator,它支持微信小程序、支付寶小程序、瀏覽器以及Nodejs端使用。咱們能夠在plugins文件夾中新建一個js用於添加自定義的校驗規則

import WeValidator from 'we-validator'
WeValidator.addRule('Mobile', function(value, param){
     const reg = 11 && /^(((13[0-9])|(14[5,7])|(15[0-3,5-9])|(17[0,3,5-8])|(18[0-9])|166|198|199|(147))\d{8})$/;
    return reg.test(value) 
})

export default{
    WeValidator
}
複製代碼

而後再main.js中引用並掛載

Vue.prototype.$WeValidator = WeValidator.WeValidator;
複製代碼

在頁面中

mounted(){
      this.oValidator = new this.$WeValidator({
        rules: {
          sbmc: {
            required: true
          },
          sbxh: {
            required: true
          },
          fx: {
            required: true
          },
          yxqk: {
            required: true
          },
          azrq: {
            required: true
          },
          ssqx: {
            required: true
          },
          azdz: {
            required: true
          }
        },
        messages: {
          sbmc: {
            required: '請選擇設備名稱'
          },
          fx: {
            required: '請選擇方向'
          },
          sbmc: {
            required: '請選擇設備名稱'
          },
          yxqk: {
            required: '請選擇運行狀況'
          },
          azrq: {
            required: '請選擇安裝日期'
          },
          ssqx: {
            required: '請選擇所屬區縣'
          },
          azdz: {
            required: '請輸入安裝地址'
          }
        }
      })
    },
    methods:{
     submit(){ 
        if(this.oValidator.checkData(this.form)){
          
        } 
      }
    }複製代碼

獲取當前位置

小程序的api獲取當前位置返回的信息比較少,若有須要獲取到地址須要引入地圖的地址解析。我用的是騰訊地圖,在引用的時候會報錯,是由於它不是用export導出的,因此能夠在最後修改成export default QQMapWX。

import QQMapWX from '../plugins/qqmap-wx-jssdk.js'
let qqmapsdk = new QQMapWX({
                  key: config.txMapKey
              });
export function getPosition(){
  return new Promise((resolve,reject)=>{
    wx.showLoading({
      title:'正在定位',
      mask:true
    })
    wx.getLocation({
      success:function(res){
        qqmapsdk.reverseGeocoder({
            coord_type:0,
            location: {
                latitude: res.latitude,
                longitude: res.longitude
            },
            success: function(data) {
                resolve({
                  latitude: res.latitude,
                  longitude: res.longitude,
                  adcode: data.result.ad_info.adcode,
                  addr:data.result.address
                })
            },
            fail: function(err) {
                reject(err);
            },
            complete: function(err) {
              wx.hideLoading()
            }
        });
      },
      fail:function(err){
        reject(err)
      }
    })
  }) 
}複製代碼

封裝input組件

咱們能夠將input作成一個組件,能夠去配置輸入框的標題,清空輸入框等功能。isShowArea以下一條所說的做用。在向父頁面傳遞值時須要一個settimeout,若是不添加會有閃爍的bug。

<template>
  <div class="yg-input">
    <label :placeholder="placeholder"  :style="{width:labelWidth}">{{label}}</label>
    <div class="yg-input-control yg-textarea-control" v-if="type=='textarea'">
      <textarea v-if="isShowArea" :maxlength="maxlength" v-bind="$attrs" :cursor-spacing="100" :placeholder="placeholder" :disabled="disabled||readonly" :value="currentVal" @input="updateVal($event.target.value)"></textarea>
      <div v-if="!isShowArea">{{currentVal}}</div>
    </div>
    <div class="yg-input-control" v-else>
      <input :placeholder="placeholder" :maxlength="maxlength" :cursor-spacing="100" :disabled="disabled||readonly" :value="currentVal" v-bind="$attrs" @input="updateVal($event.target.value)" :type="type" />
      <i v-if="value && $attrs.readonly!==!!'true'" @click="clear" class="iconfont iconfont-close"></i> 
    </div>
  </div>
</template>
<script>
  export default {
    inheritAttrs: false,
    data() {
      return {
        currentVal:this.value
      }
    },
    computed:{
      isShowArea(){
        return this.$store.state.mask;
      }
    },
    methods:{
      clear(){
        this.currentVal = '';
        this.$emit('input', '');
      },
      updateVal(val) {
        this.currentVal = val;
        setTimeout(()=>{
          this.$emit('input', val);
        },0)  
      }
    },
    watch:{
      value: {
    handler(newValue, oldValue){
          this.currentVal = newValue;   
    }
   }
    },
    props: {
      label:{
        default:'',
        type:String
      },
      labelWidth:{
        default:'75px',
        type:String
      },
      type:{
        default:'text',
        type:String
      },
      value:{
        default:'',
        type:String
      },
      placeholder:{
        default:'',
        type:String
      },
      maxlength:{
        default:'-1',
        type:String
      },
      disabled:{
        default:false,
        type:Boolean
      },
      readonly:{
        default:false,
        type:Boolean
      }
    }
  }
</script>
<style lang="scss">
  
</style>
複製代碼

能夠將它定義爲全局組件,在main.js中

Vue.component('yg-input',YgInput)
複製代碼

在父頁面使用

<yg-input :readonly="status==3" v-model="form.sbxh" label="設備型號" placeholder="請輸入設備型號"></yg-input>
複製代碼

textarea的bug

textarea是原生組件所以層級最高,所以在頁面上自定義的彈窗會被textarea的內容覆蓋。所以咱們能夠在彈窗是將它隱藏,在關閉彈窗時顯示。能夠在vuex中定義一個變量控制,而後在彈窗組件中對這個變量賦值。

<textarea v-if="isShowArea"></textarea>
<div v-if="!isShowArea">{{currentVal}}</div>//在textarea隱藏時顯示textarea填寫的內容複製代碼

還有一個問題是textarea在ios真機上會有一個默認的padding致使和其餘表單元素不對齊。這個官方仍是沒修復。

封裝select組件

原生的picker不能自定義樣式,而卻選項的文字多了會顯示...,項目中還要顯示中英文因此本身寫了一個

<template>
  <div class="yg-input yg-select">
    <label :style="{width:labelWidth}">{{label}}</label>
    <div class="yg-input-control" @click="showPicker">
      <input :value="selectVal" :placeholder="placeholder" v-bind="$attrs" :disabled="true"/>
      <i class="iconfont iconfont-arrow-r"></i>
    </div>
    <div class="popup-mark" v-if="toggle">
      <ul class="yg-popup">
        <li class="yg-popup-head">
          <span @click.stop="cancel">取消</span>
          <span @click.stop="confirm">肯定</span>
        </li>
        <li v-for="(item,$index) in selectOption" :class="{'active':item.active}" :key="index" @click.stop="itemClick(item)">{{item.text}}</li>
      </ul>
    </div>
  </div>
</template>
<script>
  export default {
    inheritAttrs: false,
    data() {
      return {
        selectVal:'',
        toggle:false,
        cache_selectVal:'',
        cache_selectTxt:'',
        isFirstEnter:true,
        selectOption:[]
      }
    },
    methods:{
      cancel(){
        this.selectOption = [...this.setActive(this.value,this.option)];
        this.hide();
      },
      confirm(){
        if(this.selectVal != this.cache_selectTxt&&this.cache_selectTxt){
          this.selectVal = this.cache_selectTxt;
          this.$emit('input', this.cache_selectVal);
          this.$emit('change', this.cache_selectVal,this.cache_selectTxt);
        }
        this.hide();
      },
      itemClick(item){
        this.cache_selectVal = item.value;
        this.cache_selectTxt = item.text;
        this.selectOption = [...this.setActive(item.value,this.option)];
      },
      setActive(value,arr){
        for(let item of arr){
          if(value==item.value){
            item.active=true;
          }else{
            item.active=false;
          }
        }
        return arr;
      },
      hide(){
        this.$store.commit('SET_MARK',true);
        this.toggle = false;
      },
      showPicker(){
        if(this.disabled||this.readonly){
          return false;
        }

        if(!this.option.length){
          this.$message('沒有相關選項');
          return false;
        }
        this.$store.commit('SET_MARK',false);
        this.toggle = true;
      }
    },
    props: {
      label:{
        default:'',
        type:String
      },
      disabled:{
        default:false,
        type:Boolean
      },
      readonly:{
        default:false,
        type:Boolean
      },
      labelWidth:{
        default:'75px',
        type:String
      },
      value:{
        default:'',
        type:String
      },
      placeholder:{
        default:'',
        type:String
      },
      option:{//
        default:function(){
          return []
        },
        type:Array
      },
      defaultFirst:{
        default:false,
        type:Boolean
      }
    },
    watch:{
      value: {
    handler(newValue, oldValue){
          this.selectOption = this.selectOption.map((item)=>{
            if(this.value==item.value){
              item.active=true;
              this.selectVal=item.text;
            }else{
              item.active=false;
            }
            return item;
          })
    },
        immediate: true
    },
      option: {
    handler(newValue, oldValue){
          this.selectOption = [...newValue];

          let _value = '';
          for(let item of newValue){
            if(this.value==item.value){
              _value = item.value;
              break;
            }
          }

          if(_value){
            this.$emit('input', _value);
          }

          if(this.option.length!==0&&!_value&&this.defaultFirst){
            this.$emit('input', this.option[0].value);
          }
          
          if(this.option.length==0&&!_value&&!this.isFirstEnter){
            this.$emit('input', '');
            this.isFirstEnter = false;
          }          
    },
    deep: true,
        immediate: true
    }
    }
  }
</script>
<style lang="scss">
  @keyframes popup{
    0%{
      transform:translate(0,100%);
    }
    100%{
      transform:translate(0,0)
    }
  }
  .popup-mark{
    position:fixed;
    z-index:9999;
    background:rgba(0,0,0,0.4);
    top:0;
    left:0;
    right:0;
    bottom:0;
    display:flex;
    align-items:flex-end;
    .yg-popup{
      animation:popup .3s;
      flex:1;
      max-height:800px;
      min-height:500px;
      overflow:auto;
      background:#fff;
      li{
        &.yg-popup-head{
          background:#fbf9fe;
          color:#999;
          line-height: 1;
          padding:0;
          overflow:hidden;
          padding:30px;
          span:nth-of-type(1){
            float: left;
          }
          span:nth-of-type(2){
            float: right;
            color:#2b79fb;
          }
        }
        background:#fff;
        border-bottom:1px solid #eee;
        line-height: 1;
        padding:30px 50px 30px 30px;
        font-size:32px;/*no*/
        color:#666;
        position:relative;
        &.active{
          color:#2b79fb;
          &:before{
            content:'';
            position:absolute;
            width:8PX;
            height:8PX;
            border-radius:50%;
            top:50%;
            background:#2b79fb;
            margin-top: -4PX;
            right: 40px;
          }
        }
      }
    }
  }
</style>複製代碼

下拉刷新上拉加載

1.上拉加載更多

在列表的下面添加如下節點,loadTip默認爲空,用於加載時的提示

<div class="page-list-loading">{{loadTip}}</div>
複製代碼

定義maxPage表示最後一頁

在onReachBottom鉤子里加載下一頁

onReachBottom(){
      this.loadTip = '正在加載中';
      this.form.page++;
      this.loadData();
    },
複製代碼

在loadData的回調裏

this.list = this.list.concat(res.data);
this.loadTip = '';
this.maxPage = Math.ceil(res.total/this.form.size);//算出最後一頁
複製代碼

2.下拉刷新

在須要下拉刷新的頁面同級新建main.json

{
  "enablePullDownRefresh": true
}
複製代碼

在onPullDownRefresh鉤子里加載數據

this.maxPage = null;
this.form.page = 1;
this.list = [];
this.loadTip = '';
wx.showNavigationBarLoading();//顯示標題欄的加載動畫

//請求成功後
wx.hideNavigationBarLoading();
wx.stopPullDownRefresh();//中止下拉複製代碼

截至2018-10-15,下拉刷新還存在bug,ios上頭部head設置position爲fixed時會擋住加載動畫,在安卓上頭部head又會跟隨下滑

粘性導航

在h5中能夠方便地利用window對象獲取滾動距離,元素距離頂部距離等。可是在小程序中並無window對象只能使用小程序的api獲取相關數據。

<template>
  <div class="Home">
      <div class="com-part-class">
        //導航
        <div class="affix">
          <div :class="{'fixed':isfixed,'nav-list-wrap':true}">
           <div class="nav-list">
              <div v-for="(item,index) in affixArr" @click="changeSt(index)" :class="{'active':navActive==index,'nav-item':true}" :key="index">
               {{item}}
             </div>
           </div>
         </div>
        //內容
         <div class="affix-pane-wrap">
           <div v-for="(item, index) in indexDataLists" :key="index" :class="item.style">
              <div class="com-bar-ti">
                {{item.label}}
              </div>
              <app-list @jump="toPage" :parent-index="index" @loadMore="loadMore" :more="true" :data="item.data"></app-list>
            </div>
         </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
function getNavActiveIndex(num,arr){//返回第幾個高亮
  if(num===0){
    return 0;
  }
  for(var i=0;i<arr.length;i++){
    if(num>=arr[i]){
      if (!arr[i + 1]) {
        return arr.length - 1;
      } else if (num < arr[i + 1]) {
        return i;
      }
    }else if (num < arr[i]) {
      if (!arr[i - 1]) {
        return 0;
      } else if (num >= arr[arr - 1]) {
        return i-1;
      }
    }
  }
}
import AppList from './AppList.vue'
export default {
  data () {
    return {
      offsetTopArr:[],
      indexDataLists:[],
      affixArr:[],
      navActive:0,
      isfixed:false
    }
  },
  components: {
    AppList
  },
  methods: {
    changeSt(index){//點擊導航跳轉對應內容區
      this.navActive = index;
      wx.pageScrollTo({
        scrollTop: this.offsetTopArr[index]
      })
    }
  },
  onPageScroll(e){
    const query = wx.createSelectorQuery();
    query.select('.affix').boundingClientRect((res)=>{//判斷相對於顯示區域的距離是否小於0即頁面滾動到導航的位置
            if(res){
              if(res.top<0){
                this.isfixed = true;
              }else{
                this.isfixed = false;
                this.navActive = 0;
              }
            }
    }).exec();
    this.navActive = getNavActiveIndex(e.scrollTop,this.offsetTopArr);
  },
  mounted () {
    this.$showLoading();
    Promise.all([this.apiManager.post('/home/indexLists'),this.apiManager.post('/home/frequentlyUsed'),this.apiManager.post('/gaywtz/wrapNotice')]).then(res=>{
      this.affixArr = res[0].data[0].indexLabelLists
      this.indexDataLists=res[0].data[0].indexDataLists;
      setTimeout(()=>{
        const query = wx.createSelectorQuery();
        query.selectAll('.affix-pane').boundingClientRect((res)=>{
            res.map((item)=>{
              this.offsetTopArr.push(item.top-45);//45是導航的高度,用px佈局
            })
        }).exec();
        this.$hideLoading();
      }, 200)
    })
  }
}
</script>


複製代碼

px2rpx

rpx 是微信小程序解決自適應屏幕尺寸的尺寸單位。以iPhone6爲例,屏幕寬度爲375px,把它分爲750rpx後, 1rpx = 0.5px。px2rpx能夠很方便地把px轉成rpx。文檔中說和px2rem使用同樣,可是當某個屬性並不須要將px轉成rpx時,設置/*no*/並無用,解決辦法是把px改成PX。

mpvue-wxparse

後臺返回的富文本編輯器中的內容是html節點,這在小程序中是解析不出來的。所以須要mpvue-wxparse去轉換

js
import wxParse from 'mpvue-wxparse'
components:{
  wxParse
}

html
<wxParse :content="content" @preview="preview" @navigate="navigate" />
複製代碼

全部頁面的created鉤子函數在小程序打開時就所有執行

改用mouted鉤子

從新進入頁面會保留以前的數據

假設有三個頁面:列表頁A,表單頁一B,表單頁二C。當填寫完表單頁C後跳轉到列表頁,這時若是再從列表頁進入表單頁B會發現B仍是以前填寫的數據。這是由於mpvue在離開頁面時並無調用destroyed鉤子,所以目前的解決方案是在小程序的onUnload中重置data函數裏的數據。能夠將它封裝成一個插件

const resetPageData = {
  install: function (Vue) {
    Vue.mixin({
      onUnload() {
        if (this.$options.data) {
            Object.assign(this.$data, this.$options.data())
        }
      }
    })
  }
}

export default resetPageData;
複製代碼

而後在main.js中使用

Vue.use(resetPageData);
複製代碼

其餘須要注意的

1.background不支持本地路徑

2.不能使用v-show需替換成v-if

3.在map中使用cover-view須要直接使用cover-view,如使用div會有問題,文檔中寫到目前cover-view支持動畫,開發者工具中有效實際在真機無效,且不支持單邊border,rotate等

4.solt不支持動態渲染,在封裝業務組件時非常蛋疼

5.不支持自定義指令

最後吐槽

小程序還很年輕,還存在不少不足(bug超多),開發體驗說實話目前爲止感受不太好。可是什麼技術都不可能一出來就作得很完美,做爲前端只能跟着潮流掌握這些新技術了。

相關文章
相關標籤/搜索