文章目录
前言
在开发中会为了结合业务要求,多端开发节省开发的时间,为此运用了uniapp 开发出兼容于H5,小程序的多端开发适应。uniapp开发写法好处就是语法上是基于vue写法,写法格式是根据小程序的走,除了基本的vue的格式也引用了小程序特有点的比如onLoad,onReachBottom(触底事件)
等,基本小程序的生命周期执行都能写进去
一、项目搭建
1. 多域名指向考虑
基于vue-uniapp 进行项目创建
- 创建安装vue脚手架工具
npm install --global vue-cli
- 在Hbuilder中创建uni-app项目项目
项目结构 |
---|
app.js |
common |
components |
pages |
untils |
App.js |
config.js |
- 将创建好的nuiapp项目直接拷备到相应的vue-uniapp的src目录当中(如需要在微信开发平台运行需要将src单独拉到Hbuilder里面进行运行)
2. gitlab 自动化构建打包
在服务器中对上传到Git上代码执行相应的命令
命令:cd 存放文件的根目录 && git pull && cd 存放文件根目录下的需要打包文件夹 && npm i && npm run build
根据合并到不同的分支执行相应的命令,派发相应的域名指向
// 创建.gitlab-ci.yml文件,便于上传执行
stages:
- deployDonate
- deploy
deployDonate:
stage: deployDonate
script:
- ssh 服务器ip 'cd 存放文件的根目录 && git pull && cd 存放文件根目录下的需要打包文件 && npm i && npm run build'
only:
- master
deploy:
stage: deploy
script:
- ssh 服务器ip 'cd 存放文件的根目录 && git pull && cd 存放文件根目录下的需要打包文件 && npm i && npm run build'
only:
- production
我们需要vue中创建两个相应的根文件,不同环境下的域名指向
// .env.development
VUE_APP_URL= ''
// .env.production.example
VUE_APP_URL= ''
创建域名指向管理
// config.com
module.exports = {
// 请求域名 格式: https://您的域名
baseURL: process.env.NODE_ENV=="development"? 'http://www.baidu.com' : process.env.VUE_APP_URL, //本地
timeout: '5000',
};
3. 请求接口的封装
封装请求接口是搭建一个项目的核心了,因为接下来项目的可维护性跟团队合作所要遵循的一个环节,每搭建一个项目我都尝试着新的封装方法,一般小项目所用到的请求都是 Post
和Get
,也很少用到其他的方式。我们先来一波如果存在多种的请求方式,我们能怎么封装
import { baseURL, timeout} from '../config.js';
export default function request(api, method, data,loadup) {
let DefaultOpts = {
url: baseURL + api,
method: method || 'GET',
timeout: timeout,
data: data || '',
header: {
'content-type': 'application/json',
//"Content-Type": "multipart/form-data", // 上传图片需要切换的歌声
'Token': uni.getStorageSync('weiju_token'),
}
}
return new Promise((resolve, reject) => {
if(loadup) { // 当前为上传文件接口
let upObj = {
url: baseURL + api,
filePath: data,
name: 'file',
}
uni.uploadFile(upObj).then((res) => {
resolve(JSON.parse(res[1].data))
}).catch((response) => {reject(response)})
} else {
uni.request(DefaultOpts).then((res) => {
if(res[1].data.code == '401' || res[1].data.code == '402' ) {
uni.showToast({
icon:'none',
title: '身份已过期,请重新登录',
duration: 2000
});
uni.navigateTo({
url: '/pages/tabar/login/login'
});
//尚未登录的逻辑处理
return false
}
resolve(res[1].data)
}).catch((response) => {reject(response)})
}
})
}
['options', 'get', 'post', 'put', 'head', 'delete', 'trace', 'connect'].forEach((method) => {
request[method] = (api, data,state) => request(api, method, data,state)
});
创建需要请求的api文件
import request from './request.js'
export function addAddress(data) {
return request.post('xxxx',data)
}
使用则需要直接引入import即可!
4. Vue-uniapp 周期方法
用得比较多的大概就这些方法,如果有其他项目需求能结合 小程序的方法看下
components: {}
data() {return {}}
methods: {}
onLoad() {} // 页面首次加载
onPageScroll(res) {} // 滚动监听
onShow() {} // 显示页面
onReachBottom: function () {} // 页面上拉触底事件的处理函数
onShareAppMessage(res) {} // 页面分享转发
子组件的封装使用 传参之类的则是以Vue的写法为主,比如传值给父级this.$emit('navbarTap',id,index);
二、开发运用技巧
1.分包式项目布局
分包概念是小程序提出,开发者需要将小程序划分成不同的子包,在构建时打包成不同的分包,用户在使用时按需进行加载,其次小程序打包的体积限制,分包无非是在项目体积庞大后能够进行正常发布打包
运行在小程序开发平台上的项目是无法进行用手机预览,当前状态是未压缩状态,需要进行平台进行发布后压缩运行才能预览
实现
"subPackages": [
{
"root": "pages/tabar_page/homeIndex",
"pages": [
{
"path": "business_index",
"style": {
// #ifdef MP
"navigationBarTitleText": "商务合作",
// #endif
// "navigationStyle": "custom",
"app-plus": {
"titleNView": {
"type": "transparent"
}
}
}
},
]
},
{
"root": "pages/apply/",
"name": "apply",
"pages": [
"partner/partner",
"business/business",
"pay/pay"
]
},
]
2. 运用uni封装uniCrazyRouter路由拦截
由于小程序的搭建每次创建都是需要pages.json中配置相应的文件路径,如果我们采用vue-router 无非就是每创建一个文件路径就需要在相应的两个文件都配置一次,故采用封装好的uniCrazyRouter
进行实现路由拦截
- 运用这个在小程序中无法拦截到tabBar的路由(H5除外)
- 如果涉及登录模块要能在跳转到tabBar的路由之前拦截,建议tabBar页面做成未授权登录的占位图
资料: https://github.com/MigoNoSalt/uni-crazy-router
实现: 运用是否需要token的页面进行判断需要拦截登录的页面
uniCrazyRouter.beforeEach((to, from ,next)=>{
next(); // 能符合条件的则继续执行
})
3. 商品多规格实现思路
厂家一件商品多规格,多种颜色和尺码我们该如何实现,以下图便是跟后台约定好的实现格式
思路: 在这当中 我们多规格的实现可以根据 _
进行拼接实现,现在我们需求中有多规格,有N种规格情况,图上可知我们有两种 颜色 + 尺寸,那么我们的规格组装可以是 color_spec
(备注:这里免不了需要对计数器的封装更改)
4. 条件编译:#ifdef #ifndef #endif
a. 格式写法
- vue文件格式
/* #ifndef APP-NVUE */
white-space: nowrap;
/* #endif */
- 其他文件格式
// #ifdef %PLATFORM%
这些代码只在该平台编译
// #endif
b. 命令解读
#ifdef : if defined 仅在某个平台编译
#ifndef : if not defined 在除里该平台的其他编译
#endif : end if 结束条件编译
%PLATFORM% 需要编译的平台,上面的MP就是各个小程序的意思
c. 命令表
APP-PLUS
APP-PLUS-NVUE
H5
MP-WEIXIN
MP-ALIPAY
MP-BAIDU
MP-TOUTIAO
MP-QQ
MP
5. 修改页面保存后返回更新列表数据
在实现地址列表中我们会对列表单个数据编辑修改后保存,能够做到返回就更新列表页的数据,小程序有相应的方法 getCurrentPages()
他能获取到所有你曾经跳转过的页面,继而还能调用到页面里所有的方法
uni.navigateBack({
delta: 1,
success: function(e) {
var page = getCurrentPages();
if (page == undefined || page == null) return;
var pages = page[page.length - 2];
pages.$vm._addressList(); // 调转到列表页重新请求列表数据
}
});
三、微信支付调用
小程序的实现中是只能运用微信支付调用,而微信支付的调用是需要获取相应的openid
,那么在实现中运用静默的方式获取openid
,然而为了避免静默调用微信登录获取不到,故采用在我们需要openid
的地方实行再调用一次,那么我对uni.login
的调用运用了export
抛出整个函数的方法
微信登录
// wxlogin.js
export function wxLogin() {
uni.login({})
}
微信支付
// pay.vue
// #ifndef MP-WEIXIN
// 除了小程序
window.open(res.data.url);
// #endif
// #ifdef MP-WEIXIN
// 微信小程序支付
uni.requestPayment({
provider: 'wxpay',
timeStamp: res.data.info.timeStamp,
nonceStr: res.data.info.nonceStr,
package: res.data.info.package,
signType: res.data.info.signType,
paySign: res.data.info.paySign,
success: function (res) {
uni.redirectTo({
url: '/pages/tabar_page/orderManage/orderManage?type=2'
});
},
fail: function (err) {
uni.showToast({
icon:'none',
title: err,
duration: 2000
});
}
});
// #endif
四、避坑
1. 小程序是没有Dom操作
如果你的项目是需要兼容小程序,那么你在写法上就不能采用js操作dom的事件,比如document.getElementById
和Vue特有的$refs
获取节点的方法,那如果我们不得不获取节点才能实现我们可以用 uni的节点信息方法.
uni.createSelectorQuery().in(this).select("#item-" + index);
2. 复制文案功能
复制文本内容有时我们会直接一波操作猛如虎,殊不知uni已经封装了相应的方法,我们自己实现是可以,但是打包兼容多端后很难避免掉兼容性的问题
uni.setClipboardData({})
3. 图片显示问题
需要遵循小程序的规则,需要将图片写到样式中要转成base64
4.动态添加数据到数组,操作数组数据,数据更新视图未更新
问题描述: 红色框自动生成命名,个数不确定,那么我们在data中声明先需要定义一个空数组专门接收这块(依赖收集机制),未定义空数组接收是会出现只有定义到data的字段数据改变才会改变到添加进去的数据
imgList: {
'billing' : '',
'proveArr':[], //数组
}, // 图片存
改变数组数据是没办法更新视图更新,那么我们需要使用$set
this.$set(this.imgList,url,res.data[0])
以上这部已经可以实现到更改数组数据,让视图更新,那么我在发现如果除了添加,我如果动态删除该数组里的数据置空时还是会出现视图未更新的现象
this.$set(this.imgList.proveArr,url,'')
this.$forceUpdate();
5. 加载更多
<uni-load-more :status="loadingType"></uni-load-more>
import uniLoadMore from '@/components/uni-load-more/uni-load-more.vue'//加载更多
loadingType: 'more', //加载更多状态
this.loadingType = 'loading';
this.loadingType = 'more'
if(res.data.res.length<=0) {
this.loadingType = 'nomore'
}
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
//上拉加载更多
if(this.loadingType === 'nomore') {
return;
}
this.page = this.page+1;
this._GoodArr();
},