Mpvue采坑之路

宋乐
宋乐 7月25日

在不同页面使用时通过组件 method 改变组件 data 失效

  • 源代码
data() {
        _this = this
        return {
            show: false
        }
    },
    methods: {
        select(item) {
            if (_this.show) {//展开状态

                _this.list = _.concat([], _.sortBy(_this.list, tItem => tItem.val != item.val))
                _this.show = false
            } else {
                _this.show = true
            }
        },
        close() {
            _this.show = false
        }
    }

原因是 mpvue 编译的时候 data 里赋值的_this 和 methods 中不是同一个上下文。

  • 新代码
data() {
        _this = this
        return {
            show: false
        }
    },
    methods: {
        select(item) {
            if (this.show) {//展开状态

                this.list = _.concat([], _.sortBy(this.list, tItem => tItem.val != item.val))
                this.show = false
            } else {
                this.show = true
            }
        },
        close() {
            this.show = false
        }
    }

配置 page.json

  • 页面的 main.js
import Vue from 'vue'
import App from './index'

const app = new Vue(App)
app.$mount()
export default {
    // 这个字段走 page.json
    config: {
        enablePullDownRefresh: true,
        backgroundTextStyle: 'dark'
    }
}

配置页面的事件 (如下拉刷新等)

mounted() {
        _this.getLocation().then(() => {
        })
        _this.getShops()
    },
    async onReachBottom() {

    },
    async onPullDownRefresh() {
        // to doing..
        // 停止下拉刷新
        wx.stopPullDownRefresh();

    },
    async onPageScroll(e) {
        //    _this.headerActive = false
    }

获取用户信息权限的事件回调

<button class="btn" open-type="getUserInfo" @getuserinfo="getUserAuthCallback">
                <img class="btn-icon" :src="imgs[1]" alt="">
            </button>
methods: {
        getUserAuthCallback(e) {
            wx.showLoading({ title: '登录中...', mask: true })
            if (e.mp.detail.errMsg == 'getUserInfo:ok') {//授权成功
                _this.getUserAuth()
            } else {
                wx.hideLoading()
                wx.showToast({ title: '授权失败,请重新授权', icon: 'none' })
            }
        }

    }

微信小程序原生组件的事件

<swiper duration="100" class="inter-body" @animationfinish="changeSwiper"></swiper>


methods: {
        changeSwiper(e) {
            _this.activeBody = e.mp.detail.current
        },
}

微信异步请求封装

/*
    Author:宋乐
    Time:2018-07-13

    fetch 用法

    fetch进行请求有两种格式,一种是参数形式,一种是对象形式

    1.fetch(url,【data,token,method,header】)       【】代表可选项
        (1) fetch(url)   Get请求
        (2) fetch(url,{id:12},true)   Post请求且需要login_token
    2.fetch({
        url:url,
        【
            data:data(Json格式),
            token:true|false(是否需要login_token,默认true),
            method:'GET'|'POST'(请求方式,默认post)
            header:request header
        】
    })
        (1)fetch({
            url:url,
            token:false,
            method:'GET'
        })   Get请求且不需要login_token
*/

let CryptoJS = require('crypto-js')
let md5 = require('js-md5')

// 加密
var encryption = function(word, key3) {
    var key = CryptoJS.enc.Utf8.parse(key3)
    var srcs = CryptoJS.enc.Utf8.parse(word)
    var encrypted = CryptoJS.AES.encrypt(srcs, key, {
        mode: CryptoJS.mode.ECB,
        padding: CryptoJS.pad.Pkcs7
    })
    return encrypted.toString()
}

// 解密
var decryption = function(word, key3) {
    var key = CryptoJS.enc.Utf8.parse(key3)
    var decrypt = CryptoJS.AES.decrypt(word, key, {
        mode: CryptoJS.mode.ECB,
        padding: CryptoJS.pad.Pkcs7
    })
    return CryptoJS.enc.Utf8.stringify(decrypt).toString()
}

// 处理data数据
var paramsData = function(data, key1, key2) {
    var paramsStr = key1
    for (var p in data) {
        paramsStr += data[p]
    }
    paramsStr += key2
    console.log('Prams Plus Key2:', paramsStr)
    data['key'] = md5(paramsStr)
    return data
}

export default function fetch(obj) {
    let tmpObj = null
    if (arguments.length > 1) {
        tmpObj = {
            url: arguments['0'],
            data: arguments['1'],
            token: arguments['2'],
            method: arguments['3'] || null,
            header: arguments['4'] || null
        }
        if (tmpObj.token == null) tmpObj.token = true
    } else {
        if (obj instanceof Object) {
            tmpObj = obj
            if (tmpObj.token == null) tmpObj.token = true
        } else {
            tmpObj = {
                url: arguments[0],
                method: 'GET'
            }
        }
    }

    //做排序
    let sortKeys = _.sortBy(_.keys(tmpObj.data), item => !item)
    let sortObj = {}
    _.forEach(sortKeys, key => {
        sortObj[key] = tmpObj.data[key]
    })
    tmpObj.data = sortObj
    //排序完成
    console.log('Request Param Object:', tmpObj.data)
    let MA11111 = ''
    let MA22222 = ''
    let MA33333 = ''
    let userObj = null
    if (tmpObj.token) {
        userObj = wx.getStorageSync('userInfo')
        MA11111 = userObj.key1
        MA22222 = userObj.key2
        MA33333 = userObj.key3
    } else {
        MA11111 = 'iqpqdVLawksP0wW2Wfdsfdsfdstjh9l1awePMHGmAog'
        MA22222 = 'w2w22DFHYUojWJuxQMfdsfdsf30VPduy4ewV2Tjg8'
        MA33333 = 'vowGIuiUdsfdssjhddjeq'
    }

    let withKeyParams = paramsData(tmpObj.data || {}, MA11111, MA22222)
    var paramsJson = JSON.stringify(withKeyParams)
    var reqData = {
        data: encryption(paramsJson, MA33333)
    }
    if (tmpObj.token) {
        reqData['login_token'] = userObj.login_token
    }
    console.log('Request Encrypt Param Object:', reqData)
    let params = {
        url: 'https://www.maiyia.cn/appserver_test/public/' + tmpObj.url,
        data: reqData,
        method: tmpObj.method || 'POST',
        header: {
            'content-type': 'application/json',
            Accept: 'application/json',
            //'Authorization': 'Bearer ' + token,
            ...tmpObj.header
        }
    }

    return new Promise((res, rej) => {
        wx.request(
            Object.assign(params, {
                success(resp) {
                    let result = null
                    try {
                        result = JSON.parse(decryption(resp.data.data, MA222222))
                        console.log('Request Return Object:', result)
                    } catch (error) {
                        rej(result)
                    }
                    if (resp.data.statusCode !== 1) {
                        console.log(resp.data.message)
                        console.log('Request Error Object:', resp.data)
                        rej(result)
                    } else {
                        res(result)
                    }
                },
                fail(e) {
                    rej(e)
                }
            })
        )
    })
}

mpvue 开发删除 node_modules, 重新安装报错

  • 错误:未找到 app.json
    删除 package.json 文件中 "mpvue-loader": "^1.0.13", 里面的‘^’,再重新 npm install、npm run dev 即可正常。原因:如果不锁定版本安装,默认会安装最新的版本,造成新版本不兼容而编译不成功。

mpvue 组件式开发,组件的状态在组件内部维护的时候,当小程序从 A --B--A-- B 的时候,组件自己的状态不会改变,而网页可以。

  • 首页来到搜索页,切换为附近以后,返回首页,再进搜索,搜索状态不会刷新为热门
    因为网页是会重新加载,重新创建组件实例,而小程序不会重新创建,所以组件状态没有刷新

小程序组件 scroll-view 不能高度超过屏幕的范围,否则下拉的时候不会有空白的回弹

position 为 fixed 时候,v-show 不起作用,需要用 v -if 才起作用

深度数组改变值时候视图需要变化的时候

小程序(mpvue 开发)弹窗组件式滑动穿透

之前参照网上还是能滑动,是因为在组件内部使用了 position:fixed, 如果组件内部使用 absolute, 将 fixed 提出到 vue page 页面, 就能够禁止滑动了

  • 当弹窗内 scroll-view 内容不足以滑动的时候,还是会穿透,解决办法是在 scroll-view 内套一个 div, 让这个 div 最小高度超过 scroll-view 的高度
  • 弹窗组件
<template>
    <div class="body">
        <div class="panel" :class="{'show':isShow}">
            <div class="close">
                <img :src="imgs[0]" alt="" class="close-icon">
            </div>
            <scroll-view scroll-y="true" class="scroll-body">
            </scroll-view>
        </div>

    </div>
</template>

<script>
import config from '../utils/config'
let _this = null

export default {
    props: [],
    data() {
        _this = this
        return {
            isShow: false,
            imgs: [
                config.imgServer + 'ic_ly_guanbi@2x.png'
            ]
        }
    },
    mounted() {
        this.isShow = true
    },
    methods: {

    }
}
</script>

<style lang="scss" scoped>
@import '../../static/css/base.scss';
.scroll-body {
    height: 74vh;
    margin-top: 6px;
}
.body {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: rgba(0, 0, 0, 0.5) fixed;
    z-index: 999;
    height: 100vh;
    width: 100vw;
    overflow: hidden;
    .panel {
        position: absolute;
        height: 80vh;
        width: 100%;
        bottom: -80vh;
        transition: bottom 0.6s;
        background: rgba(255, 255, 255, 0.96);
        &.show {
            bottom: 0;
        }
        .close{
            .close-icon{
                
            }
        }
    }

}
</style>
  • vue 页面应用
<div class="stop-modal-scroll" @touchmove.stop="">
    <div class="wapper">
        <comments-panel></comments-panel>
    </div>
</div>


.stop-modal-scroll {
    height: 100vh;
    width: 100vw;
    position: fixed;
    overflow: hidden;
    background: rgba(204, 204, 204, 0.9);
    bottom: 0;
    left: 0;
    z-index: 999;
    .wapper {
        position: relative;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
    }
}

使用 scroll-view 的时候调用页面的 下拉刷新

<scroll-view scroll-y="true" class="inter-body recommend"  @scroll="scrollBody">
</scroll-view>
data() {
    _this = this
    return {
        timer: null,
        topScrollFirstTime: 0,
        topScrollEndTime: 0,
    }
},
methods:{
    scrollBody(e) {
        if (e.mp.detail.scrollTop < -15) {
            if (this.topScrollFirstTime == 0 && this.topScrollEndTime == 0) {
                let time = new Date().getTime()
                this.topScrollFirstTime = time
                this.topScrollEndTime = time
                this.timer = setInterval(() => {
                    this.topScrollEndTime = new Date().getTime()
                    if (this.topScrollEndTime - this.topScrollFirstTime >= 450) {
                        clearInterval(this.timer)
                        this.timer = null
                        this.topScrollFirstTime = 0
                        this.topScrollEndTime = 0
                        wx.startPullDownRefresh({
                            success: function () {
                                _this.regetCurrentPageData().then(() => {
                                    wx.stopPullDownRefresh()
                                })
                            }
                        })
                    }
                }, 100)
            }

        } else {
            clearInterval(this.timer)
            this.timer = null
            this.topScrollFirstTime = 0
            this.topScrollEndTime = 0
        }
    },
}