Skip to content

zyh9/mpvue-apps

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

mpvue 踩坑之旅

单包demo

单包demo(已加入分包配置),请戳我

//main.json配置(单个页面的配置) { navigationBarTitleText: '',//导航文字 navigationBarBackgroundColor: '',//导航颜色 backgroundTextStyle: "dark",//下拉loading样式 enablePullDownRefresh: true,//启用下拉刷新 onReachBottomDistance: 60//触底距离 }

webpack分包配置(需要修改配置文件),如有错误之处还请指出

mpvue-loader 1.1.2-rc.2 之后的版本已支持分包,请戳我

//webpack.base.conf.js文件 const appEntry = { app: resolve('./src/main.js') } function getEntry (rootSrc, path) { var map = {}; glob.sync(rootSrc + '/' + path + '/**/main.js') .forEach(file => { var key = relative(rootSrc, file).replace('.js', ''); map[key] = file; }) return map; } let entry; const pagesEntry = getEntry(resolve('./src'), 'pages') // 判断app.json文件中是否包含subpackages配置来判断单包、分包 let appJson = require('../src/app.json') let subpackages = appJson.subpackages || appJson.subPackages || []; if(subpackages.length){ let entryPath = subpackages.map(({root})=>({root})) let entryArray = []; entryPath.forEach( e =>{ entryArray.push(getEntry(resolve('./src'), e['root'])) }) entry = Object.assign({}, appEntry, pagesEntry, ...entryArray) }else entry = Object.assign({}, appEntry, pagesEntry)

分包demo

分包demo,请戳我

前提是项目代码体积已经超过2M的项目才可以使用此方法	小程序单包最大支持2M,分包后最大可支持8M,故此需要对其进行分包处理	将单个页面的配置单独存储于pages.js内(有点像路由的配置文件)	然后根据功能性来划分相应模块,只有用户触及到某些模块的时候才会去加载	相应的也就提高了进入小程序的加载速度 

mpvue-entry,请戳我

mpvue-entry1.x.x版本的配置

//pages.js配置(单个页面的配置以及路径) module.exports = [ { path: 'pages/index',//主包页面所在路径 config: { navigationBarTitleText: '',//导航文字 navigationBarBackgroundColor: '',//导航颜色 backgroundTextStyle: "dark",//下拉loading样式 enablePullDownRefresh: true,//启用下拉刷新 onReachBottomDistance: 60//触底距离 } }, { path: 'pagesOther/other',//分包页面所在路径 subPackage: true,//是否分包,主包可不用配置此项 config: { navigationBarTitleText: '',//导航文字 navigationBarBackgroundColor: '',//导航颜色 backgroundTextStyle: "dark",//下拉loading样式 enablePullDownRefresh: true,//启用下拉刷新 onReachBottomDistance: 60//触底距离 } } ]

mpvue-entry2.0之后的配置,已加入mpvue-config-loader

demo地址,请戳我

mpvue-entry好搭档mpvue-config-loader

mpvue-config-loader

app.json的路径以及config配置(不需要mpvue-config-loader依赖)

"pages":[	{ "path": "pages/index", "config": { "navigationBarTitleText": "首页"	}	},	{ "path": "pages/order", "config": { "navigationBarTitleText": "订单"	}	},	{ "path": "pages/user", "config": { "navigationBarTitleText": "我的"	}	},	{ "path": "pagesOther/other", "subPackage": true, "config": { "navigationBarTitleText": "其它"	}	}	]

app.json的路径以及单个vue文件的config配置(需要mpvue-config-loader依赖)

//app.json的路径 "pages":[ "pages/index", "pages/order", "pages/user", { "path": "pagesOther/other", "subPackage": true } ] //单个vue文件的config配置 export default { config: { navigationBarTitleText: '首页' }, data() { return {} }, onLoad() {}, onReady() {}, onShow() {}, methods: {}, computed: {}, watch: {}, components: {} }

CopyWebpackPlugin配置

// 版本1 new CopyWebpackPlugin([ { from: path.resolve(__dirname, '../static/tabBar'), to: path.resolve(__dirname, '../dist/static/tabBar') }, { from: path.resolve(__dirname, '../static'), to: path.resolve(__dirname, '../dist/static'), ignore: ['*.png'] } ]), // 版本2 new CopyWebpackPlugin([ { from: path.resolve(__dirname, '../static/tabBar'), to: path.resolve(config.build.assetsRoot, './static/tabBar') }, { from: path.resolve(__dirname, '../static'), to: path.resolve(config.build.assetsRoot, './static'), ignore: ['*.png'] } ]),

注意:针对三目引入静态资源做require处理,否则不能copy进打包文件或者转为base64

mpvue微信小程序锁定依赖版本

仅支持微信小程序demo,请戳我

{ "mpvue": "1.0.18", "mpvue-loader": "1.1.4", "mpvue-template-compiler": "1.0.18", }

支持微信、百度小程序demo,请戳我

{ "mpvue": "1.4.7", "mpvue-loader": "1.4.0", "mpvue-template-compiler": "1.4.7", }

多端支持从mpvue2.0开始

v-show的使用

在添加v-show指令的元素上,不要使用 display:flex;

v-for、v-if同时使用

vue文档,请戳我

gulp压缩zip

安装依赖 npm i gulp gulp-zip gulp-vsftp gulp-ignore dayjs -D

//只针对压缩zip const fs = require('fs'); const path = require('path'); const gulp = require('gulp'); const vsftp = require('gulp-vsftp'); const gulpIgnore = require('gulp-ignore');//过滤文件插件 const zip = require('gulp-zip');//生成zip插件 const dayjs = require('dayjs'); const distFile = 'dist';//打包目录 const packageInfo = require("./package.json"); let platform = process.argv[process.argv.length - 1] || 'wx'; const gulpZip = () => gulp.src(path.resolve(distFile + '/' + platform + '/**')) .pipe(gulpIgnore.exclude('*.map')) .pipe(zip('名称' + platform + '-' + packageInfo.version + '-' + dayjs().format('YYYY-MM-DD HH-mm-ss') + '.zip')) .pipe(gulp.dest('./')) //压缩打包文件 gulp.task(platform, gulpZip) // gulp.task--定义任务 // gulp.src--找到需要执行任务的文件 // gulp.dest--执行任务的文件的去处 // gulp.watch--观察文件是否发生变化

package.json添加指令

"scripts": { "zip": "npm run build:wx && gulp wx", "zip:wx": "npm run zip", "zip:swan": "npm run build:swan && gulp swan", "zip:tt": "npm run build:tt && gulp tt", "zip:my": "npm run build:my && gulp my" }

地理位置获取

引入腾讯的微信小程序JavaScript SDK	因为微信小程序wx.getLocation API 返回的是地理位置坐标	所以要用到地址逆解析,然后就是一顿复制	var QQMapWX = require('xxx/qqmap-wx.js')...	然后就出问题了,貌似SDK最后的代码是这样导出的module.exports = QQMapWX;	改为export default QQMapWX; 引入改为import QQMapWX from 'XXX/qqmap-wx-jssdk.js'; 即可	百度的微信小程序JavaScript SDK和其类似,故此不再赘述 

注:另一种解决方法,把地图SDK放到static下,别让它被webpack编译就不会报错了

腾讯 微信小程序 SDK

百度 微信小程序 SDK

高德 微信小程序 SDK

距离计算

distance(lat1, lng1, lat2, lng2) { var radLat1 = lat1 * Math.PI / 180.0; var radLat2 = lat2 * Math.PI / 180.0; var a = radLat1 - radLat2; var b = lng1 * Math.PI / 180.0 - lng2 * Math.PI / 180.0; var s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) + Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2))); s = s * 6378.137; s = Math.round(s * 10000) / 10000; return s; //返回数值单位:公里 }

取小数点后两位

Math.floor(this.distance(lat1, lng1, lat2, lng2)*100)/100; 

一个处理地理坐标系的js库

安装 and 引入

npm install gcoord --save <script src="https://unpkg.com/gcoord/dist/gcoord.js"></script>	CommonJS:	const gcoord = require( 'gcoord' );	const { transform, WGS84, GCJ02 } = require( 'gcoord' );	ES Module:	import gcoord from 'gcoord'	import { transform, WGS84, GCJ02 } from 'gcoord';

小例子

var result = gcoord.transform( [ 116.403988, 39.914266 ], // 经纬度坐标 gcoord.WGS84, // 当前坐标系 gcoord.BD09 // 目标坐标系 ) console.log( result ); // [ 116.41661560068297, 39.92196580126834 ]

gcoord github地址,请戳我

autoprefixer配置

npm i autoprefixer -D

在文件.postcssrc.js里面添加"autoprefixer":{}

module.exports = { "plugins": { "postcss-mpvue-wxss": {}, "autoprefixer":{} } }

小程序之一键回到顶部和获取滚动条当前位置

1.获取滚动条当前位置

onPageScroll(e){ // 获取滚动条当前位置 console.log(e) }

2.回到顶部

goTop: function (e) { // 一键回到顶部 if (wx.pageScrollTo) { wx.pageScrollTo({ scrollTop: 0 }) } else { wx.showModal({ title: '提示', content: '当前微信版本过低,无法使用该功能,请升级到最新微信版本后重试。' }) } }

ES7 async/await 使用

先看数据请求代码

//数据请求地址 const baseUrl = 'localhost:8080'; const commonHeader = _ => { //headers每次必传数据存放位置 return { //... } } //get数据请求 const get = (opt = {}) => { let time = new Date().getTime(); const str = Object.entries(opt.params).map(e => `${e[0]}=${e[1]}`).join("&").replace(/\s/g, ''); let editHeaders = Object.assign({}, { 'content-type': 'application/json' }, commonHeader()) opt.headers && (editHeaders = Object.assign({}, editHeaders, opt.headers)) return new Promise((resolve, reject) => { let address = str ? `${opt.url}?${str}&t=${time}` : `${opt.url}?t=${time}`; wx.request({ url: baseUrl + address, header: editHeaders, method: "GET", success: res => { setTimeout(_ => { opt['success'] && opt['success'](res.data); resolve(res.data) }, 0) }, fail: err => { opt['fail'] && opt['fail'](err); reject(err) } }) }) } //post数据请求 const post = (opt = {}) => { let time = new Date().getTime(); let editHeaders = Object.assign({}, { 'content-type': 'application/json' }, commonHeader()) opt.headers && (editHeaders = Object.assign({}, editHeaders, opt.headers)) return new Promise((resolve, reject) => { wx.request({ url: `${baseUrl}${opt.url}?t=${time}`, data: opt.data || {}, header: editHeaders, method: "POST", success: res => { setTimeout(_ => { opt['success'] && opt['success'](res.data); resolve(res.data) }, 0) }, fail: err => { opt['fail'] && opt['fail'](err); reject(err) } }) }) } export default { get, post };
// 代码示例 let shopInfo = async _ =>{ let data1 = await this.util.post({ url:'http://XXXXXX', data:{ demo:'111' } }) console.log(data1) let data2 = await this.util.post({ url:'http://XXXXXX', data:{ demo:'111' } }) console.log(data2) } //async函数内部抛出错误,会导致返回的 Promise 对象变为reject状态 //抛出的错误对象会被catch方法回调函数接收到 shopInfo().then(res=>{console.log(res)}).catch(err=>{console.log(err)})

vuex的加入

 // 小程序内部是不能手动去刷新页面的,这就为状态管理的实现提供了可能性 // 某些状态不需要长期存储,索性投入vuex的怀抱吧。。。 // 引用数据请求 import util from './utils/index'; Vue.prototype.util = util; // 引用toast提示 import msg from './utils/toast'; Vue.prototype.msg = msg; // 引用vuex import store from './store/index'; Vue.prototype.$store = store; // 发起action =>  this.$store.dispatch('code',{a:1,b:2}) // 获取state数据 => this.$store.state.mutations

示例代码

// store/index.js import Vue from 'vue'; import Vuex from 'vuex'; import mutations from './mutations'; import actions from './actions'; Vue.use(Vuex) export default new Vuex.Store({ modules:{	mutations },	actions }) // store/types.js import keymirror from 'keymirror' let types = keymirror({ COMMIT_CODE:null, }) export {types}; // store/actions.js import {types} from './types.js' const actions = { code({commit},val){ commit(types.COMMIT_CODE,val) }, } export default actions; // store/mutations.js import {types} from './types.js' // 定义state的值 const state = { qrcode:null } // 每个action的提交引发state的改变 const mutations = { [types.COMMIT_CODE](state,val){ state.qrcode = val }, } // 获取到变化的值 const getters = { qrcode(state){ return state.qrcode; } } // 导出 export default{	state,	mutations,	getters }

关于canvas请求网络图片绘制

网络图片的绘制在真机实测的时候是不会显示的,可以调用以下方法先获取本地图片路径,再进行canvas绘制	wx.downloadFile(OBJECT) || wx.getImageInfo(OBJECT)	针对多个图片获取地址的解决方法,将上述两个方法用Promise包装一下	再使用async/await来获取所有的图片本地地址,用catch来抛出图片地址获取异常的情况 
//获取线上图片生成本地临时路径 const downImg = val => { return new Promise((resolve, reject) => { //判断本地图片路径是否存在 if (val.indexOf('wxfile://') == -1) { wx.downloadFile({ url: val, success: res => { resolve(res.tempFilePath) }, fail: err => { reject(err) } }) } else { resolve(val) } }) }

优化图片请求方式,采用异步加载

Promise.all([this.downImg(this.QrCodeUrl), this.downImg(this.Logo) ]).then(res=>{ console.log(res,111) }).catch(err=>{ console.log(err,222) })

canvas文字截取

/**  * ctx,画布对象  * str,需要绘制的文字  * splitLen,切割的长度字符串  * strHeight,每行文字之间的高度  * x,位置  * y,位置  */ fontLineFeed(ctx, str, splitLen, strHeight, x, y) { let strArr = []; for (let i = 0, len = str.length / splitLen; i < len; i++) { strArr.push(str.substring(i * splitLen, i * splitLen + splitLen)); } if (str.length > splitLen) { strArr[0] = strArr[0] + '...'; } // console.log(strArr[0]) // let s = 0; // for (let j = 0, len = strArr.length; j < len; j++) { // s = s + strHeight; // ctx.fillText(strArr[j], x, y + s); // } ctx.fillText(strArr[0], x, y); }

虚拟导航层级处理

灵感源自于有赞对虚拟导航的处理方式,判断当前路径是否在路径数组中,存在即回退,不存在则导向新的路径,可解决层级过深的问题

let index = getCurrentPages().findIndex(e => e.route == 'pages/index/main'); if (index > -1) { wx.navigateBack({ delta: getCurrentPages().length - 1 - index }) } else { wx.navigateTo({ url: '/pages/index/main' }) }

页面层级过深,采取折回处理

//path路径 示例:'pages/index/main' 如果路径带参数,需要indexOf处理一下 const goPath = path => { let index = getCurrentPages().findIndex(e => path.indexOf(e.route) > -1); if (index > -1) { getCurrentPages()[index].onUnload(); wx.navigateBack({ delta: getCurrentPages().length - (index > 0 ? index + 1 : index), success: res => { setTimeout(_ => { wx.redirectTo({ url: `/${path}` }) }, 500) }, fail: err => {} }) } else { wx.navigateTo({ url: `/${path}` }) } }

小程序0.5像素边框

跟1px边框实现方式大同小异,使用伪类来实现

element:before { content: ''; position: absolute; top: -50%; bottom: -50%; left: -50%; right: -50%; -webkit-transform: scale(0.5); transform: scale(0.5); border: 1px solid #999; border-radius: 6rpx;	}

检测是否授权

const isAuth = (name) => { return new Promise((resolve, reject) => { wx.getSetting({ success: (res) => { if (res.authSetting[name]) { resolve(); } else { reject(); } }, fail: (err) => { reject(err); } }) }) }

微信APP定位权限关闭以及小程序定位权限关闭

只看getLocation API fail的处理方式

fail: err => { wx.hideLoading(); //无定位判断 if(wx.getStorageSync('QQmap')&&!wx.getStorageSync('QQmap').mapGet){ reject('位置信息获取失败,启用无定位搜索'); }else{ wx.getSetting({ success: ok => { if(!(ok.authSetting['scope.userLocation'])){ //小程序位置信息权限关闭 console.log('小程序定位未开启') model(1); }else{ console.log('手机定位未开启') model(2); } }, fail: error => { console.log('权限获取失败') } }) reject('位置信息获取失败'); } }

地理位置授权

const model = val => { wx.showModal({ title: '定位失败', content: `未获取到你的地理位置,请检查${val==1?'小程序':'微信APP'}是否已关闭定位权限,或尝试重新打开小程序`, // showCancel:false, success: res => { if (res.confirm) { console.log('用户点击确定') if(val==1){ wx.redirectTo({ url: '/pages/wx-auth/main?type=1' }) } //调用wxLogin接口 } else if (res.cancel) { console.log('用户点击取消') // model(val); //调用wxLogin接口  } } }) }

小程序跳转另一个小程序

注:小程序基础库 2.1.2 开始支持 wx.getLaunchOptionsSync() 方法

可使用navigator标签,但想要在另外一个小程序来接受参数的话就需要使用到extra-data属性	但在跳转过去的onShow(options){}里,并未获取到referrerInfo信息	解决方法:使用小游戏的 wx.getLaunchOptionsSync() 方法 

textarea去除输入法上方完成栏

<textarea :show-confirm-bar="false"></textarea>

scroll-view左右滑动失效

1.scroll-view 中的需要滑动的元素不可以用 float 浮动;	2.scroll-view 中的包裹需要滑动的元素的大盒子用 display:flex; 是没有作用的;	3.scroll-view 中的需要滑动的元素要用 dislay:inline-block; 进行元素的横向编排;	4.包裹 scroll-view 的大盒子有明确的宽和加上样式--> overflow:hidden;white-space:nowrap; 

关于滚动吸顶的实践

滚动吸顶利用onPageScroll和createIntersectionObserver均有操作延迟,暂时用position: sticky解决 

参考链接,请戳我

打开小程序设置页(wx.openSetting)接口调整

微信开放社区,请戳我

方法1:使用button组件来使用此功能,示例代码如下:	<button open-type="openSetting" bindopensetting="callback">打开设置页</button> 方法2:由点击行为触发wx.openSetting接口的调用,示例代码如下:	<button bindtap="openSetting">打开设置页</button> => openSetting(){wx.openSetting()} 其他方法:在点击中调用showModal,showModal的回调再调用openSetting也可以 

强制数据更新(数据层级太多,render函数没有自动更新,需手动强制刷新)

 this.$forceUpdate();

vuejs文档,请戳我

mpvue多个页面公用一个vm对象的问题

Object.assign(this.$data, this.$options.data())

github issues地址,请戳我

函数节流防抖参考链接

30-seconds-of-code,请戳我

vue 与 throttle 的坑

参考链接,请戳我

小程序图片裁剪上传插件

we-cropper地址,请戳我

图片裁剪根据像素比来提高裁剪图片清晰度

canvas的绘制函数为异步函数,故作延时处理之后导出裁剪区域图片,再做图片上传

we-cropper在mpvue中截图模糊问题

getCropperImage参数,v1.3.3支持

优化setState的数据频繁更新

github issues地址,请戳我

热更新失效问题以及文件拷贝出错问题

github issues地址,请戳我

mpvue-loader升级指南

更改日志,请戳我

升级指南以及webpack配置,请戳我

获取用户位置信息时需填写用途说明

permission 属性配置,请戳我

支持多端的http请求库

Fly.js地址,请戳我

多端支持更新文档

github issues地址,请戳我

胡成全mpvue-platform-sample项目示例,请戳我

mpvue2.0升级指南

github releases地址,请戳我

mpvue重要更新,页面更新机制进行全面升级

github issues地址,请戳我

sentry的加入

npm库,请戳我

掘金食用链接,请戳我

小程序开发错误收集,请戳我

// ./src/app.vue import Raven from 'sentry-weapp' export default { onLaunch() { Raven.config('https://xxx@your.example.com/x', { release: '1.0.0', //版本号 environment: 'production', allowDuplicates: true, // 允许相同错误重复上报 sampleRate: 0.5 // 采样率 }).install() }, onError(msg) { // Raven.captureException(msg) Raven.captureException(msg, { level: 'error' }) } }

map文件生成,请戳我

UglifyJsPlugin压缩配置

// ./build/webpack.prod.conf.js new UglifyJsPlugin({ sourceMap: true }) // ./config/index.js文件	build: { // ... productionSourceMap: true, // 生产环境开启map文件生成 // ... }

引申一些小插曲

webpack中开启了map映射,但是打包一直没有map文件。	经检查webpack的配置都没有问题,那为什么没有生成对应的map文件呢?	最终找到的原因是使用uglifyjs-webpack-plugin这个插件导致的。	在webpack的devtool文档中,有一块不起眼的小字: 

When using the uglifyjs-webpack-plugin you must provide the sourceMap: true option to enable SourceMap support.

也就是当使用了uglifyjs-webpack-plugin 插件时,sourceMap这个值的默认值是false,不开启map。	如果要启用map,需要在插件中配置sourceMap值为true。 

webpack的devtool文档,请戳我

全屏适配

隐藏导航栏,获取手机状态栏高度wx.getSystemInfoSync()['statusBarHeight']),自定义顶部状态栏即可达到适配 

针对自定义状态栏以组件的形式要考虑以下内容

返回按钮 导航栏背景,中间标题文字、颜色 是否固定顶部
是否展示,展示箭头颜色 父组件传入 布尔值

内嵌webview跳转小程序

以vue为例,需要安装weixin-js-sdk依赖

//src/main.js const wx = require('weixin-js-sdk'); //挂载原型 Vue.prototype.wx = wx; //页面跳转小程序 this.wx.miniProgram.reLaunch({ url: "/pages/index/index" })
点击收起/打开赞赏名单

赞赏名单 🎨

昵称 赞赏时间 赞赏方式 赞赏金额
**丽 2019-12-26 微信 ¥28.00

个人微信号:zyh941109,感谢以上朋友,十分感谢!!!

About

使用 Vue.js 开发小程序的前端框架

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published