背景:嵌入app的H5页面需要变成支付宝小程序版本。
使用框架:"vue": "^2.6.11"
使用依赖:
"vue-router": "^3.2.0",
"vuex": "^3.4.0",
"axios": "^0.19.2",
"crypto-js": "^4.0.0", // 加密解密
"exif-js": "^2.3.0", // 获取图片元信息
"vant": "^2.10.0",
"vue-cli-plugin-svg-sprite": "^1.0.0", // svg引入库
下方详解这些框架和组件在uniapp的可用或替换方案。
目录总结:
(1)vue-router
=> uni-simple-router
(2)axios
=> luch-request
(3)exif-js
=> uni.getImageInfo(OBJECT)
(4) vant
=> vant-ailapp(其实我是没有用,因为我需要的常用组件基本都报错了,如果你用的vant组件不多,可以试试。还是换成了官方的基础组件和官方的扩展组件(uni-ui)
(5)vue-cli-plugin-svg-sprite
=> 作为图片引入:background-image/
uniapp版本:
// package.json
{
...
"dependencies": {
"@dcloudio/uni-app-plus": "^2.0.0-28920200907001",
"@dcloudio/uni-h5": "^2.0.0-28920200907001",
"@dcloudio/uni-helper-json": "^1.0.6",
"@dcloudio/uni-mp-alipay": "^2.0.0-28920200907001",
"@dcloudio/uni-mp-baidu": "^2.0.0-28920200907001",
"@dcloudio/uni-mp-qq": "^2.0.0-28920200907001",
"@dcloudio/uni-mp-toutiao": "^2.0.0-28920200907001",
"@dcloudio/uni-mp-weixin": "^2.0.0-28920200907001",
"@dcloudio/uni-quickapp-native": "^2.0.0-28620200814005",
"@dcloudio/uni-quickapp-webview": "^2.0.0-28920200907001",
"@dcloudio/uni-stat": "^2.0.0-28920200907001",
"copy-webpack-plugin": "5.0.0",
"core-js": "^3.6.4",
"crypto-js": "^4.0.0",
"exif-js": "^2.3.0",
"flyio": "^0.6.2",
"luch-request": "^3.0.4",
"mp-storage": "^1.0.3",
"regenerator-runtime": "^0.12.1",
"sass": "^1.26.10",
"sass-loader": "^10.0.2",
"uni-simple-router": "^1.5.5",
"vue": "^2.6.12",
"vuex": "^3.5.1"
},
"devDependencies": {
"@dcloudio/types": "^2.0.6",
"@dcloudio/uni-automator": "^2.0.0-28920200907001",
"@dcloudio/uni-cli-shared": "^2.0.0-28920200907001",
"@dcloudio/uni-migration": "^2.0.0-28920200907001",
"@dcloudio/uni-template-compiler": "^2.0.0-28920200907001",
"@dcloudio/vue-cli-plugin-hbuilderx": "^2.0.0-28920200907001",
"@dcloudio/vue-cli-plugin-uni": "^2.0.0-28920200907001",
"@dcloudio/vue-cli-plugin-uni-optimize": "^2.0.0-28920200907001",
"@dcloudio/webpack-uni-mp-loader": "^2.0.0-28920200907001",
"@dcloudio/webpack-uni-pages-loader": "^2.0.0-28920200907001",
"@vue/cli-plugin-babel": "~4.3.0",
"@vue/cli-service": "~4.3.0",
"babel-plugin-import": "^1.11.0",
"cross-env": "^7.0.2",
"jest": "^25.4.0",
"mini-types": "^0.1.4",
"miniprogram-api-typings": "^3.0.2",
"postcss-comment": "^2.0.0",
"vue-template-compiler": "^2.6.12"
},
"browserslist": [
"Android >= 4",
"ios >= 8"
],
"uni-app": {
"scripts": {}
}
}
一、选择uni-app的原因
九月初看了很多小程序框架测评文章,发现uniapp是对vue语法支持度最高的跨端框架,也是支持端最多的,所以对于那些从vue转手小程序的,使用uniapp会比较顺手,重构相对方便。
使用react的开发者可以尝试用taro,其他同事测试下来,react H5重构为taro 支付宝小程序基本没有问题。
二、创建项目
这个在uniapp官网可以得到详细的指导:快速上手 - uni-app官网
官方给出了两种创建项目的方式,一种是通过HBuilderX可视化界面,另一种是通过vue-cli命令行,也给出了两者的不同
总之,uni-app可以在其他编辑器里开发代码,然后通过命令行打包成相应平台的代码,打开官方平台运行,而HBuilder把库更新、打包、发布都集成了一遍,简化了操作。
对我来说,平时用惯了命令行运行打包,突然转换到使用编辑器的菜单栏来运行会相对感觉不舒畅 ( ̄▽ ̄)" ,所以我选择继续使用vscode来开发,命令行运行。
但是有点 坑 的地方出现了,
运行官方给出的命令 vue create -p dcloudio/uni-preset-vue my-project 出现了证书问题
尝试了几种网友给出的解法:
——都不行,还有的说是npm需要更新,或卸载重下,暂未尝试。
曲线救国法出现了!!!(上面的问题,在下才疏学浅,希望能人来解答)
打开支付宝小程序开发者工具
选取模板
新建项目
获得一个带完整编译器的模板
成功创建项目。
也许有人会思考为什么不直接在小程序开发者工具里开发,一个原因是惯用vscode,另一个主要原因是这个工具里没有命令行,编译靠右侧模拟器的手动操控刷新,因此编译时错误就不能及时看见解决。有时,只会在调试器里报这么一个错误:
还无法溯源,只能凭经验思考一下刚刚哪些地方是否有不符合逻辑的地方了。(不过后面也有很多时候终端编译不报错,这里也会报错,主要是因为平时用vue和用uniapp这版本写小程序有些规范上不支持,这些区别后面讲)
还一个原因是频繁打包运行易崩溃
三、开发代码
h5转小程序,有两种思路,一种是重构为小程序代码,一种是嵌入h5页面。此处,我暂时先讲我重构遇到的问题。
下一篇:为什么不使用webview来实现h5转换为小程序。(记一下提醒我自己)
uniapp官方提供了从其他项目转uni-app的指南:转换指南(<(o)↗[良心啊!],少走很多弯路)
大多数问题都可以从此篇指南里解决,比如插件的替换和留下:
开头我列出了我在h5项目中使用的几个插件:
"vue-router": "^3.2.0",
"vuex": "^3.4.0",
"axios": "^0.19.2",
"crypto-js": "^4.0.0", // 加密解密
"exif-js": "^2.3.0", // 获取图片元信息
"vant": "^2.10.0",
"vue-cli-plugin-svg-sprite": "^1.0.0",
其中vuex
和crypto-js
可以留下,不违反uni-app的规则,其余:
(1) vue-router
=> uni-simple-router
这个问题在指南中就有指出,uniapp不支持vue router
于是按照指南,我选择了插件市场里下载量最高的轮子试了一下
一开始还没用起来,之前写项目太急了文档看得不仔细,再加上文档里也没有写需要同时再page.json里配置一份相同的路由,就总是报错;总结的时候重新试了一遍,其实挺好用的。
尝试了一下在不同生命周期里打印console.log('beforeCreate: ', this.$Route, this.$Router)
ps:初始进入页面是不触发onLoad,onShow,onReady
总体来说,这个插件正如它自述所说的确是一个完全相似Vue-router的来操作路由插件(扩展了uniapp提供的路由操作,所以也不能缺少原有操作:在pages.json里写一份相同完整的路由,不过嵌套路由不支持),但是也局限于支付宝小程序的特性,如下:
- 支付宝小程序不支持组件作为页面使用(官方回复)
简单尝试一下:
// index.vue
import test from '../test/test.vue'
export default {
components: {
test
},
...
// page.json
{
"pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "uni-app"
}
}, {
"path": "pages/test/test",
"style": {
"navigationBarTitleText": "uni-app"
}
}
],
...
出现了报错:
尝试一:
即便按照模拟器中的提示,手动加上component: true
依旧报错(看提示一下子看不懂)
尝试二:
把组件置于components目录下(标准写法)
// index.vue
import test from '@/components/test/test.vue'
export default {
components: {
test
},
...
// page.json
{
"pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "uni-app"
}
}, {
"path": "components/test/test",
"style": {
"navigationBarTitleText": "uni-app"
}
}
],
...
再手动加上component: true,运行后依旧报错,多了一行提示,invalid Component usage,指的是把components下的组件放进pages不符合规范。
标准写法
自定义组件放components,不与pages扯上关系,不过正常h5开发中有时也有页面嵌套页面的效果,这种修改问题,可放下一篇讲。
下一篇:抽取相同组件和页面的共用方法mixins和公用样式css(记一下提醒我自己)
(为什么不用extends:哎,不一样啊つ﹏⊂,uniapp这版本的extends和mixins用法一样,template继承不了,会报错:继承的组件没有template标签)
示例:
// index.vue
import test from '@/components/test/test.vue'
export default {
components: {
test
},
...
// page.json
{
"pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "uni-app"
}
}, {
"path": "pages/test/test",
"style": {
"navigationBarTitleText": "uni-app"
}
}
],
...
(2) axios
=> luch-request
指南:
市场:
好用并且相较于axios改动量小。(Toast是仿照vant用uniapp的api封装的组件)
示例:
// http.js
import Vue from "vue";
import Request from 'luch-request';
import serviceCode from "./serviceCode.js";
import Toast from "@/components/Toast.js";
const env = process.env.NODE_ENV;
const isDev = env === "development";
const merge = require("deepmerge");
// 创建请求实例
const req = new Request({
timeout: 20000,
withCredentials: false,
baseURL: isDev ? `` : url // 接口ip
});
// 添加请求拦截器
req.interceptors.request.use(
config => {
if (
!(/login/.test(config.url) || /get/.test(config.url) || /find/.test(config.url) || /login/.test(config.url))
) {
Toast.loading({
message: "请等待...",
forbidClick: true,
duration: 0,
getContainer: "#app>.page-content>section"
});
}
const authorization = Vue.prototype.localStorage.getItem("authorization");
if (authorization) {
config.header["Authorization"] = authorization;
}
return config;
},
err => {
Toast.clear(true);
// 对请求错误做些什么
console.warn(err);
return Promise.reject(err);
}
);
// 添加响应拦截器
req.interceptors.response.use(
res => {
Toast.clear(true);
if (serviceCode.includes(res.data.code)) {
return res.data;
} else {
... // 出错处理
return Promise.reject(res.data);
}
},
err => {
Toast.clear(true);
// 对响应错误做点什么
...
return Promise.reject(err);
}
);
export default {
get (opts) {
return new Promise((resolve, reject) => {
const params = {
method: "get"
};
req.request(merge(params, opts))
.then(res => {
resolve(res);
})
.catch(err => {
reject(err);
});
});
},
post (opts) {
return new Promise((resolve, reject) => {
const params = {
method: "post",
headers: {
"Content-Type": "application/json"
}
};
req.request(merge(params, opts))
.then(res => {
resolve(res);
})
.catch(err => {
reject(err);
});
});
}
};
(3) exif-js
=> uni.getImageInfo(OBJECT)
虽说也都是普通的js代码,但是uni-app提供了获取图片信息的方法:uni.getImageInfo(OBJECT),虽然说最重要的orientation
有局限性,但只是在h5。如果不考虑h5就不用担心;如果要考虑,用exif-js
作为h5情况下的替代方案就行。
因为后续项目里关于这块的内容停止了,所以暂时没有完整的示例,就先借用了官方示例。
下一篇:支付宝小程序对比h5端的获取图片信息以及压缩图片(记一下提醒我自己)
简单示例:
my.chooseImage({
count: 1,
sizeType: ["compressed"],
success: res => {
uni.getImageInfo({
src: res.tempFilePaths[0],
success: function (image) {
console.log(image.width);
console.log(image.height);
}
});
}
});
(4) vant
=> 无(后续看时间出一个出一个改版)
本来看到有人通过 Antmove 小程序转换器基于 Vant-Weapp 转换出了vant-ailapp,甚是欣喜 (^U^)ノ ,但是没想到啊,运行起来很多常用的组件都是问题 _〆(´Д` ) ,用了一两天改了一下,实在不行,暴脾气的我直接放弃,投奔了官方组件的怀抱。。。
还有一些官方组件也没有解决的Form+Field问题,只能自己在官方组件的基础上改改了,后续放github上,再过来更新地址。
放点不复杂的替代vant的功能,仅部分功能上的替代(不全,只换了用到的),不替代样式。
放点简单的Toast:
// Toast.js
class Toast {
success (params) {
uni.hideLoading();
if (typeof params === 'object') {
uni.showToast({
title: params.message,
icon: 'success',
mask: params.forbidClick || false,
duration: params.duration || 1000,
position: 'center'
});
} else {
uni.showToast({
title: params,
icon: 'success',
mask: false,
duration: 1000,
position: 'center'
});
}
}
loading (params) {
uni.hideLoading();
if (typeof params === 'object') {
uni.showToast({
title: params.message,
icon: 'loading',
mask: params.forbidClick || false,
duration: params.duration || 1000,
position: 'center'
});
} else {
uni.showToast({
title: params,
icon: 'loading',
mask: false,
duration: 1000,
position: 'center'
});
}
}
fail (params) {
uni.hideLoading();
if (typeof params === 'object') {
uni.showToast({
title: params.message,
icon: 'fail',
mask: params.forbidClick || false,
duration: params.duration || 1000,
position: 'center'
});
} else {
uni.showToast({
title: params,
icon: 'fail',
mask: false,
duration: 1000,
position: 'center'
});
}
}
clear (params) {
uni.hideLoading();
}
}
export default new Toast();
简单的Dialog:
// Dialog.js
class Dialog {
confirm (params) {
if (typeof params === 'object') {
return new Promise(function (resolve, reject) {
uni.showModal({
title: params.title || '对话框',
content: params.message || '',
cancelText: params.cancelButtonText || '取消',
confirmText: params.confirmButtonText || '确定',
success: (res) => {
if (res.confirm) {
resolve();
} else if (res.cancel) {
reject();
}
}
});
});
} else {
return new Promise(function (resolve, reject) {
uni.showModal({
title: '对话框',
content: params,
cancelText: '取消',
confirmText: '确定',
success: (res) => {
if (res.confirm) {
resolve();
} else if (res.cancel) {
reject();
}
}
});
});
}
}
}
export default new Dialog();
(5)vue-cli-plugin-svg-sprite
=> background-image/
小程序里没有svg标签,都换成图片方式引入了。
四、坑点总结
不知不觉写的有点长。另起一篇吧:uniapp坑点(支付宝小程序)