//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)前提是项目代码体积已经超过2M的项目才可以使用此方法 小程序单包最大支持2M,分包后最大可支持8M,故此需要对其进行分包处理 将单个页面的配置单独存储于pages.js内(有点像路由的配置文件) 然后根据功能性来划分相应模块,只有用户触及到某些模块的时候才会去加载 相应的也就提高了进入小程序的加载速度 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
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: {} }// 版本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": "1.0.18", "mpvue-loader": "1.1.4", "mpvue-template-compiler": "1.0.18", }{ "mpvue": "1.4.7", "mpvue-loader": "1.4.0", "mpvue-template-compiler": "1.4.7", }多端支持从mpvue2.0开始
在添加v-show指令的元素上,不要使用 display:flex;
安装依赖 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编译就不会报错了
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; 安装 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 ]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: '当前微信版本过低,无法使用该功能,请升级到最新微信版本后重试。' }) } }先看数据请求代码
//数据请求地址 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的怀抱吧。。。 // 引用数据请求 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绘制 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) })/** * 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}` }) } }跟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); } }) }) }只看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 :show-confirm-bar="false"></textarea>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解决 方法1:使用button组件来使用此功能,示例代码如下: <button open-type="openSetting" bindopensetting="callback">打开设置页</button> 方法2:由点击行为触发wx.openSetting接口的调用,示例代码如下: <button bindtap="openSetting">打开设置页</button> => openSetting(){wx.openSetting()} 其他方法:在点击中调用showModal,showModal的回调再调用openSetting也可以 this.$forceUpdate();Object.assign(this.$data, this.$options.data())canvas的绘制函数为异步函数,故作延时处理之后导出裁剪区域图片,再做图片上传
胡成全mpvue-platform-sample项目示例,请戳我
// ./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' }) } }// ./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。 隐藏导航栏,获取手机状态栏高度wx.getSystemInfoSync()['statusBarHeight']),自定义顶部状态栏即可达到适配 针对自定义状态栏以组件的形式要考虑以下内容
| 返回按钮 | 导航栏背景,中间标题文字、颜色 | 是否固定顶部 |
|---|---|---|
| 是否展示,展示箭头颜色 | 父组件传入 | 布尔值 |
以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,感谢以上朋友,十分感谢!!!