第一次使用mpVue遇见的坑

963 阅读3分钟

mpvue

记录个人从 0 开始使用 mpvue 开发小程序项目中踩到的坑

这个文档只是记录个人开发时遇到的坑,并不是非常专业,希望能对各位读者带来一些帮助,减少遇到相同问题时查找资料消耗的时间成本

1. 文件目录

// 文件目录
build
  ├------- build.js
  ├----- check-version.js
  ······
  └----- webpack.prod.config.js
config
  ├----- dev.env.js
  ├----- index.js
  └----- prod.env.js
dist    // 打包后的小程序代码文件夹
  |
  ·····
  |
src
  ├----- commponents    // 组件
  |        └----- card.vue
  ├----- pages
  |        └----- index     // 页面文件夹
  |                 ├----- index.vue    // main.wxml & index.vue.wxml & main.wxss
  |                 ├----- main.json    // main.json
  |                 └----- main.js      // main.js
  ├----- app.json   // 路由配置
  ├----- App.vue    // app.wxss & app.wxml
  ├----- main.js    // app.js
  ├----- static     // 静态资源
  |        ├----- images
  |        └----- tabs
  ·····
  └----- project.config.json // 小程序配置文件

2. 异常 bug 已找到解决方案

<template>
    <div class="login">
        <div class="input-group flex">
            <div class="input-row flex-item">
                <input 
                    type="number" 
                    maxlength="11" 
                    class="block" 
                    placeholder="登录帐号" 
                    v-model="account.value"
                    @blur="hideClear('account')"
                    @focus="handleShowClear('account')"
                    @input="handleShowClear('account')"
                >
                <div 
                    class="clear" 
                    v-show="account.is_clear"
                    @tap="clearInput('account')"
                >
                    <img src="/static/icons/icons_chongzhi@2x.png">
                </div>
            </div>
        </div>
        <div class="input-group flex">
            <div class="input-row flex-item">
                <input 
                    type="text" 
                    class="block" 
                    placeholder="验证码" 
                    v-model="password.value"
                    @blur="hideClear('password')"
                    @focus="handleShowClear('password')"
                    @input="handleShowClear('password')"
                >
                <div 
                    class="clear" 
                    v-show="password.is_clear"
                    @tap="clearInput('password')"
                >
                    <img src="/static/icons/icons_chongzhi@2x.png">
                </div>
            </div>
            <div class="input-row get-vertifly" @click="timeCountDown">
                <span>获取验证码</span>
            </div>
        </div>
        <button class="login-btn">登录</button>
    </div>
</template>
<script>
export default {
    data() {
        // 如果 data 内对象层级太深会造成一个无法更新数据的bug
        return {
            // 如果在该格式的情况 methods 中的方法修改 data 将不会同步
            // 并且 console 打印的调试结果会很奇怪
            // form: {
            //     account: {
            //         value: '',
            //         is_clear: false
            //     },
            //     password: {
            //         value: '',
            //         is_clear: false
            //     },
            // },
            account: {
                value: '',
                is_clear: false
            },
            password: {
                value: '',
                is_clear: false
            },
            timer: null,
            timeCount: 60,
            text: defaultText,
            isAjax: false,
        }
    },
    methods: {
        // 隐藏 clear 图标
        hideClear(type) {
            this[type].is_clear = false
        },
        // 显示 clear 图标
        handleShowClear(type) {
            let $that = this[type]
            let is_clear;
            if ($that.value) {
                is_clear = true
            } else {
                is_clear = false
            }
            // 该判断减少 data 数据修改次数,减少性能消耗
            if ($that.is_clear !== is_clear) {
                this[type].is_clear = is_clear
            }
            // 下面这句将会失效
            // this.form[type].is_clear = is_clear
            // 以下是执行上方注释的代码之后的打印结果
            // 怪异的表现,感觉像是修改成功之后又修改回去了
            // console.log(this.form.account)                   // { __ob__ } => { value: '', is_clear: false }
            // console.log(JSON.stringify(this.form.account))   // "{ 'value': '', 'is_clear': true }"
        },
        // 清空对应输入框
        clearInput(type) {
            this[type].value = '';
        },
    }
}
</script>
<style>
/**
* 在 css 中无法使用 background-image 属性,
* 编译成小程序代码之后将会找不到对应文件图片,查看编译后的代码会看到一串奇怪的路径
*/
.login {
    margin-top: .7rem;
    padding: 0 .56rem;
}
.input-group {
    border-bottom: 1px solid #d8d8d8;
    line-height: .56rem;
    margin-bottom: .28rem;
}
.input-group .input-row {
    position: relative;
    padding: .3rem 0;
}
.input-row .clear {
    position: absolute;
    top: 0;
    right: 6rpx;
    bottom: 0;
    margin: auto 0;
    width: .36rem;
    height: .36rem;
    font-size: 0;
    line-height: 0;
    z-index: 3;
}
.clear img {
    display: block;
    width: 100%;
    height: 100%;
}
.input-row input {
    font-size: .4rem;
}
.get-vertifly{
    font-size: .28rem;
    color: #888;
    width: 2rem;
    text-align: center;
}
.login-btn {
    margin: .6rem .2rem 0;
    line-height: .88rem;
    height: .88rem;
    background: linear-gradient(90deg,#f22320 0%,#ff5b79 100%);
    box-shadow: 0 5px 8px 0 #ee0809;
    border-radius: .56rem;
    font-size: .34rem;
    color: #fff;
    font-weight: 500;
}
</style>

3. 如何导入外部 css 文件

// 在 scr 文件夹新建一个 app.css 文件
// 打开要导入 css 的 .vue 文件,比如:App.vue 
<script>
// 可多尝试几种导入方式,'@/src/app.css' 的导入方式还未尝试过,未知结果如何
import './app.css'

export default {
    data() {
        return {
            // code
        }
    }
}
</script>

4. 在项目内使用 flyaxios

  1. 使用 flyio
    先安装模块 npm install flyio or yarn add flyio
    开始配置:
    // 打开 ...\build\webpack.base.conf.js 文件
    
    alias: {
      'vue': 'mpvue',
      '@': resolve('src')
      // 新增该配置只是为了方便引入 flyio 模块, 末尾的 wx 表示引入的是小程序相关模块
      'wxFly': 'flyio/dist/npm/wx'
    }
    
    // 在 src 目录下新建 fly.js 文件
    
    import wxFly from 'wxFly' // 这里的 wxFly 是之前在 alias 自定义的模块名称
    // const Fly = require("flyio/dist/npm/wx"); // 也可以用 require 导入模块
    const fly = new wxFly() // 实例化 flyio
    
    const url = 'http://xxx.xxx.com/xxx/xx'
    
    //添加拦截器
    fly.interceptors.request.use((config,promise)=>{
        // 给所有请求添加自定义header
        // code
    
        return config;
    })
    
    //添加响应拦截器,响应拦截器会在then/catch处理之前执行
    fly.interceptors.response.use(
        (response) => {
            if (response.status === 200) { 
                // 只将请求结果的data字段返回
                return response.data
            } else {
            
            }
        },
        (err) => {
            //发生网络错误后会走到这里
        }
    )
    
    
    fly.config.baseURL = url
    
    export default fly
    
    // 打开 src/main.js
    
    import Vue from 'vue'
    import App from './App'
    
    import './app.css' // 这里可以顺便导入全局 css
    
    import fly from './fly.js' // 导入之前配置的 fly 文件
    
    Vue.config.productionTip = false
    App.mpType = 'app'
    
    const app = new Vue(App)
    app.$mount()
    
    Vue.prototype.$http = fly // 最好在 app.$mount() 代码之后将 fly 挂载到 Vue 原型
    
  2. 使用 axios
    老规矩, 安装模块 npm install axios or yarn add axios
    配置方式和 flyio 类似, 只需要查 api 按照文档做自定义配置就好
    // 唯一的差异在 ...\build\webpack.base.conf.js 文件
    alias: {
      // ...
      'axios': 'axios/dist/axios'
    }
    

5. 使用小程序内的全局变量 globalData

查了一下资料, 有文章说使用 `this.$root.$mp.app`, 有可能是版本原因, mpvue 的过去版本可能可以实现吧. 该方式实现的全局变量在执行小程序路由方法跳转 `page` 之后就获取不到之前缓存的数据了. 想做到在整个项目中都能自由获取的全局变量用以下实现就好.

如果是大型项目需求全局变量的话建议使用 Vuex, 以下方式只适用于小项目

// ./src/main.js 文件

app.$mount()
// 一定要放在 app.$mount() 代码之下, 不然 webpack 自动编译不会动过, 会抛出错误

// 查了很多文章, 只说了要在 const app = new Vue(App) 代码之下, 可能版本的原因, 之前查的资料可能对应 mpvue 老版本适用吧

Vue.prototype.globalData = getApp().globalData  // 将 globalData 挂载给 Vue 原型

// 挂载给 Vue 原型之后使用如下代码就可调用与配置了
// this.globalData.xxx = xxx

6. 小程序 page 之间的传参

// 小程序相关的方法都在 mpvue 这个全局变量

let url = '../setting/main?id=xxx'
mpvue.navigateTo({ url })

// 在 setting.vue 文件内
console.log(this.$root.$mp.query) // => { id: 'xxx' }

写在最后

这是本人目前使用 mpvue 中的所有相关依赖, 该文章内所有相关配置只适用下面的依赖配置版本, 其它版本暂未试过, 各位读者可自行测试

  "dependencies": {
    "flyio": "^0.6.14",
    "mpvue": "^2.0.0"
  },
  "devDependencies": {
    "babel-core": "^6.22.1",
    "babel-loader": "^7.1.1",
    "babel-plugin-transform-runtime": "^6.22.0",
    "babel-preset-env": "^1.3.2",
    "babel-preset-stage-2": "^6.22.0",
    "babel-register": "^6.22.0",
    "chalk": "^2.4.0",
    "connect-history-api-fallback": "^1.3.0",
    "copy-webpack-plugin": "^4.5.1",
    "css-loader": "^0.28.11",
    "cssnano": "^3.10.0",
    "eventsource-polyfill": "^0.9.6",
    "express": "^4.16.3",
    "extract-text-webpack-plugin": "^3.0.2",
    "file-loader": "^1.1.11",
    "friendly-errors-webpack-plugin": "^1.7.0",
    "glob": "^7.1.2",
    "html-webpack-plugin": "^3.2.0",
    "http-proxy-middleware": "^0.18.0",
    "mkdirp": "^0.5.1",
    "mpvue-loader": "^2.0.0",
    "mpvue-template-compiler": "^2.0.0",
    "mpvue-webpack-target": "^1.0.3",
    "optimize-css-assets-webpack-plugin": "^3.2.0",
    "ora": "^2.0.0",
    "portfinder": "^1.0.13",
    "postcss-loader": "^2.1.4",
    "postcss-mpvue-wxss": "^1.0.0",
    "prettier": "~1.12.1",
    "px2rpx-loader": "^0.1.10",
    "relative": "^3.0.2",
    "rimraf": "^2.6.0",
    "semver": "^5.3.0",
    "shelljs": "^0.8.1",
    "uglifyjs-webpack-plugin": "^1.2.5",
    "url-loader": "^1.0.1",
    "vue-style-loader": "^4.1.0",
    "webpack": "^3.11.0",
    "webpack-bundle-analyzer": "^2.2.1",
    "webpack-dev-middleware-hard-disk": "^1.12.0",
    "webpack-merge": "^4.1.0",
    "webpack-mpvue-asset-plugin": "^2.0.0",
    "webpack-mpvue-vendor-plugin": "^2.0.0"
  },

未完待续...