Cordova配置(WebApp混合开发环境)&&Hbuilder打包配置
Wscats opened this issue · comments
必备环境安装
1.配置JDK和Gradle环境
用的是1.8的版本,网上很多地方可以下载,这里不上链接了
在本地配置sdk变量,如图,点击桌面(计算机)->右键属性->高级系统设置->系统属性面板高级->点击环境变量->在下面框中的系统变量中新建
JAVA_HOME D:\Program Files\Java\jdk1.8.0_112//这个路径根据你安装JDK的环境对应其地址
PATH %JAVA_HOME%\bin;%JAVA_HOME%\jre\bin;//PATH本来是有内容的,不要全部替换,用分号隔开,再往后继续添加
CLASSPATH .;%JAVA_HOME%\lib;%JAVA_HOME%\lib\tools.jar//注意不要漏了前面的点
//新版本需要配置如下Gradle环境
GRADLE_HOME D:\Android_Studio\gradle\gradle-2.14.1-all //指向gradle的已解压文件的根目录配置path:
PATH %GRADLE_HOME%\bin //继续往PATH后面添加环境变量
2.下载Android SDK
推荐国内的android-studio下载
然后配置Android SDK环境,ANDROID_HOME定位到你的SDK文件夹的根目录
ANDROID_HOME D://android-sdk-windows //对应你的SDK文件夹的目录
重启电脑
Cordova安装
1.下载Node.js
2.在mac终端运行下面命令,输入密码安装cordova
sudo npm install -g cordova
如果是Window系统,去掉sudo
npm install -g cordova
sudo是因为需要管理员权限,-g是全局安装
cordvoa官网命令行帮助
3.执行以下代码创建一个cordvoa应用
cordova create hello com.example.hello HelloWorld
第一个参数是文件目录,第二个参数是app id, 第三个参数是显示的title
IOS打包
4.定位到hello目录文件夹,为项目安装平台模块
cd hello
cordova platform add ios
5.打开xcodeproj项目的文件位置双击打开,如果已安装Xcode就能顺利的打开项目了
6.可以用编写html项目的IDE打开www下的index.html查看效果,在浏览器打开即可
7.在Xcode打开iOS 模拟器如果看到以下效果说明环境已经搭建成功
ANDROID打包
续前面三步
4.定位到hello目录文件夹,为项目安装平台模块
cd hello
cordova platform add android
5.生成APK文件
cordova build android
遇到这个问题就是gradle下载失败了,可以尝试拿图中的链接手动下载然后把它放到对应的系统文件下,如下,注意版本一定要对应上
C:\Users\Administrator\.gradle\wrapper\dists\gradle-2.14.1-all\4cj8p00t3e5ni9e8iofg8ghvk7
gradle-2.14.1-all.zip下载地址
或者android-studio下载
成功就会显示如下
apk的目录如下:
项目路径\test\platforms\android\build\outputs\apk
6.更改应用桌面图标
在cordova生成项目的跟目录创建res文件夹
然后更改config.xml文件
ldpi : 36x36 px
mdpi : 48x48 px
hdpi : 72x72 px
xhdpi : 96x96 px
xxhdpi : 144x144 px
xxxhdpi : 192x192 px
<platform name="android">
<allow-intent href="market:*" />
<icon src="res/android/36.png" density="ldpi" />
<icon src="res/android/48.png" density="mdpi" />
<icon src="res/android/72.png" density="hdpi" />
<icon src="res/android/96.png" density="xhdpi" />
<icon src="res/android/144.png" density="xxhdpi" />
<icon src="res/android/192.png" density="xxxhdpi" />
</platform>
7.调用相机
cordova plugin add cordova-plugin-camera
安装成功就会在plugin出现两个文件夹cordova-plugin-camera
和cordova-plugin-compat
HBuilder 打包流程
1.运行HBuilder
百度搜索HBuilder,官网下载安装包,解压,运行HBuilder.exe。注册账号,并登陆
2.新建App
3.填入信息
在弹出的窗口,填应用名称,根据需求选择项目位置,以及模板内容,注意名字不要带有@等特殊字符,最后点击完成
4.真机调试
创建好之后,选择刚刚创建好的项目,在顶部选择运行,根据你的情况现在运行方式
5.真机演示
这是我刚刚选择的模板app展示,功能齐全,基本涵盖安卓常用的底层接口,如摄像头,地图,震动,下载等功能
6.打包成app
7.云端打包
在弹出的窗口,选择相应证书,如果参数配置未完成,点击顶部参数配置
8.图标配置
上传图标,如果不想逐个逐个图标替换,我们可以点击下方生成并替换
9.参数配置
这里的SDK配置选项要填上去才能打包,你可以暂时随机填一些乱串上去,最后就是保证所有提醒红叉的地方都要填满,微信登录,微信支付和微信消息及朋友圈的appid三者要统一,其他在测试时可随意
- 1,配置相应的app入口位置,app名称,版本
- 2,配置图标
- 3,配置启动图片
- 4,sdk配置(可选,如果需要同步登录,需配置相应的参数,获得相应的参数需到相应的开放平台申请)
10.打包成app---选择要打包的项目,在顶部选择运行,发型原生安装包
11.云端打包
在弹出的窗口,选择相应证书,如果参数配置未完成,点击顶部参数配置,如果配置完成,点击底部打包
12.稍等一会儿
13.如果刚刚不小心关闭了,或者后面某天想找到打包的App,在顶部选择运行,根据需要选择查看。发送到手机上,安装试试
其他注意事项
真机调试过程中,如果出现如下错误提醒
安装失败,失败原因:Failure [INSTALL_CANCELED_BY_USER]。
请手动点亮手机屏幕,并重新运行真机调试,注:可能需要在手机上确认安装。
调用plus
之前试过用Vue写的项目,用hbuilder打包APP,在页面第一次加载的时候无法获取cookie,storage等
后来发现原来是一定要plus加载完毕之后才能获取对应的信息,所以如果用了其他框架写的项目不要直接加载对应的JS,可以改成这样
function plusReady() {
var script = document.createElement("script");
script.src = "main-fa9faab1705d2e80a7c5.js";//项目的JS
document.body.appendChild(script);
console.log(plus.storage.getItem("token"))
}
document.addEventListener("plusready", plusReady, false);//等待plus准备就绪在加载
H5+支付接口
这里的官方文档的支付插件配置的请求DEMO是错误的,注意是用 POST 请求,而非 GET 请求
var channel = null;
// 1. 获取支付通道
function plusReady() {
// 获取支付通道
plus.payment.getChannels(function (channels) {
channel = channels[0];
}, function (e) {
alert("获取支付通道失败:" + e.message);
});
}
document.addEventListener('plusready', plusReady, false);
var ALIPAYSERVER = 'http://demo.dcloud.net.cn/helloh5/payment/alipay.php';
var WXPAYSERVER = 'http://demo.dcloud.net.cn/helloh5/payment/wxpay.php';
// 2. 发起支付请求
function pay(id) {
// 从服务器请求支付订单
var PAYSERVER = '';
if (id == 'alipay') {
PAYSERVER = ALIPAYSERVER;
} else if (id == 'wxpay') {
PAYSERVER = WXPAYSERVER;
} else {
plus.nativeUI.alert("不支持此支付通道!", null, "捐赠");
return;
}
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
switch (xhr.readyState) {
case 4:
if (xhr.status == 200) {
plus.payment.request(channel, xhr.responseText, function (result) {
plus.nativeUI.alert("支付成功!", function () {
back();
});
}, function (error) {
plus.nativeUI.alert("支付失败:" + error.code);
});
} else {
alert("获取订单信息失败!");
}
break;
default:
break;
}
}
xhr.open('POST', PAYSERVER);
//如果是POST请求方式,设置请求首部信息
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhr.send("total=0.01");
}
调用定位
调用定位可选用这个方法plus.geolocation.getCurrentPosition
location() {
var that = this;
if (window.plus) {
plus.geolocation.getCurrentPosition(
function (p) {
that.latitude = p.coords.latitude;
that.longitude = p.coords.longitude;
},
function (e) {
alert("Geolocation error: " + e.message);
}
);
}
}
当然我们可以把经纬度直接发到后台记录,实现实时跟踪定位
sendLocation() {
var that = this;
axios
.get("http://10.3.136.180:9999/sendPostion", {
params: {
latitude: that.latitude,
longitude: that.longitude
}
})
.then(data => {
console.log(data);
})
.catch(() => {});
}
录音
调用 plus.audio.getRecorder()
获取音频对象,可以在成功的回调里面获取录音的地址
mounted() {
this.r = plus.audio.getRecorder();
},
startRecord() {
if (this.r == null) {
alert("Device not ready!");
return;
}
this.r.record({
filename: "_doc/audio/"
},
e => {
console.log(e);
this.path = e;
alert("Audio record success!");
},
e => {
alert("Audio record failed: " + e.message);
}
);
}
播放
可以在上面把获取到的录音地址放进来用plus.audio.createPlayer(this.path)
打开,然后播放音频信息,当然也可以播放其他格式的音频信息
startPlay() {
console.log(this.path);
if (plus.audio == undefined) {
alert("Device not ready!");
}
this.p = plus.audio.createPlayer(this.path);
this.p.play(
function () {
alert("Audio play success!");
},
function (e) {
alert("Audio play error: " + e.message);
}
);
}
暂停播放
stopRecord() {
this.r.stop();
}
上传文件
前端上传逻辑可以直接使用H5的input
配合FormData
格式
uploadImg() {
var fileNode = document.getElementById("file");
var xmlhttp = new XMLHttpRequest();
//设置回调,当请求的状态发生变化时,就会被调用
xmlhttp.onreadystatechange = function () {
//上传成功,返回的文件名,设置到父节点的背景中
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
console.log(xmlhttp.responseText);
}
};
//构造form数据
var data = new FormData();
console.log(fileNode.files);
data.append("avatar", fileNode.files[0]);
console.log(data);
//设置请求,true:表示异步
xmlhttp.open("post", "http://10.3.136.180:9999/requireImg", true);
//不要缓存
//xmlhttp.setRequestHeader("If-Modified-Since", "0");
//提交请求
xmlhttp.send(data);
//清除掉,否则下一次选择同样的文件就进入不到onchange函数中了
fileNode.value = null;
}
<input @change="uploadImg" type="file" id="file" name="avatar">
后端逻辑,配合 express 和 multer 模块可以获取到传过来的文件
var express = require("express");
var multer = require('multer')
var upload = multer({
dest: 'uploads/'
})
var app = express();
app.post("/requireImg", upload.single('avatar'), (req, res) => {
res.append("Access-Control-Allow-Origin", "*")
res.send({
state: "success"
})
})
app.listen(9999)
uni
uni-app
是一个使用Vue.js
开发跨平台应用的前端框架,开发者编写一套代码,可编译到iOS、Android、H5、小程序
等多个平台。
目录结构
一个uni-app工程,默认包含如下目录及文件:
┌─components uni-app组件目录
│ └─comp-a.vue 可复用的a组件
├─hybrid 存放本地网页的目录,详见
├─platforms 存放各平台专用页面的目录,详见
├─pages 业务页面文件存放的目录
│ ├─index
│ │ └─index.vue index页面
│ └─list
│ └─list.vue list页面
├─static 存放应用引用静态资源(如图片、视频等)的地方,注意:静态资源只能存放于此
├─main.js Vue初始化入口文件
├─App.vue 应用配置,用来配置App全局样式以及监听 应用生命周期
├─manifest.json 配置应用名称、appid、logo、版本等打包信息
└─pages.json 配置页面路由、导航条、选项卡等页面类信息
全局样式库
可以在App.vue
组件里面引入uni.css
定义好的样式,配合已经写好的组件和模板快速开发
<style>
/* uni.css - 通用组件、模板样式库,可以当作一套ui库应用 */
@import "./common/uni.css";
</style>
应用生命周期
函数名 | 说明 |
---|---|
onLaunch | 当uni-app 初始化完成时触发(全局只触发一次) |
onShow | 当 uni-app 启动,或从后台进入前台显示 |
onHide | 当 uni-app 从前台进入后台 浏览器页面切换时候会触发,Mac显示屏切换会触发 |
onUniNViewMessage | 对 nvue 页面发送的数据进行监听,可参考 nvue 向 vue 通讯 |
注意
- 应用生命周期仅可在App.vue中监听,在其它页面监听无效。
- 在应用生命周期函数内进行页面跳转时需要注意,不要和pages.json内配置的首页打开时机冲突
页面生命周期
onPullDownRefresh
生命周期配合enablePullDownRefresh
使用
在 js 中定义onPullDownRefresh
处理函数(和onLoad等生命周期函数同级),监听该页面用户下拉刷新事件。需要在pages.json
里,找到的当前页面的 pages 节点,并在 style 选项中开启enablePullDownRefresh
。当处理完数据刷新后,uni.stopPullDownRefresh
可以停止当前页面的下拉刷新。
onPullDownRefresh() {
setTimeout(() => {
uni.stopPullDownRefresh()
}, 1000)
}
触底触发的生命周期
onReachBottom() {
console.log('到底了')
}
pages.json
配置路由和底部的tabbar
,和小程序是差不多的,注意当我们在page
文件夹新建页面的时候,pages.json
文件的pages
内容会自动添加
{
"pages" : [
//pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
{
"path" : "pages/index/index",
"style" : {
"navigationBarTitleText" : "uni-app"
}
},
{
"path" : "pages/home/home",
"style" : {
"navigationBarTitleText" : "设置页"
}
},
{
"path" : "pages/detail/detail",
"style" : {}
}
],
"globalStyle" : {
"navigationBarTextStyle" : "black",
"navigationBarTitleText" : "uni-app",
"navigationBarBackgroundColor" : "#F8F8F8",
"backgroundColor" : "#F8F8F8"
},
"tabBar" : {
"color" : "#7A7E83",
"selectedColor" : "#007AFF",
"borderStyle" : "black",
"backgroundColor" : "#ffffff",
"list" : [
{
"pagePath" : "pages/index/index",
"iconPath" : "static/component.png",
"selectedIconPath" : "static/componentHL.png",
"text" : "组件"
},
{
"pagePath" : "pages/home/home",
"iconPath" : "static/component.png",
"selectedIconPath" : "static/componentHL.png",
"text" : "组件"
}
]
}
}
路由跳转
声明式导航:用绝对路径或者相对当前组件的相对路径,这里的路由跳转用了绝对路径
<navigator url="/pages/detail/detail" hover-class="navigator-hover"></navigator>
<navigator url="navigate/navigate?title=navigate" hover-class="navigator-hover"></navigator>
编程式导航
// 保留当前页面,跳转到应用内的某个页面,使用uni.navigateBack可以返回到原页面。
uni.navigateTo({
url: 'test?id=1&name=uniapp'
});
// 关闭当前页面,跳转到应用内的某个页面。
uni.redirectTo({
url: 'test?id=1'
});
// 关闭所有页面,打开到应用内的某个页面。
uni.reLaunch({
url: 'test?id=1'
});
// 跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面。
uni.switchTab({
url: '/pages/index/index'
});
// 关闭当前页面,返回上一页面或多级页面。可通过 getCurrentPages() 获取当前的页面栈,决定需要返回几层
// 此处是A页面
uni.navigateTo({
url: 'B?id=1'
});
// 此处是B页面
uni.navigateTo({
url: 'C?id=1'
});
// 在C页面内 navigateBack,将返回A页面
uni.navigateBack({
delta: 2
});
组件
可以使用uni-app
帮我封装好的组件,但有几个比较注意的是,组件的名字不要用一些微信小程序或者一些常用的名字,我们可以在前面加个后缀,防止无效
<template>
<view>
<xswiper />
<xaudio />
<xmediaList />
<xmap />
</view>
</template>
import xaudio from '../../components/audio/audio.vue'
import xmap from '../../components/map/map.vue'
import xswiper from '../../components/swiper/swiper.vue'
import xmediaList from '../../components/media-list/media-list.vue'
发起请求
可以使用uni.request
发起请求
const requestUrl = "https://cnodejs.org/api/v1/topics"
const duration = 2000
export default {
data() {
return {
loading: false,
res: ""
}
},
methods: {
makeRequest: function() {
this.loading = true
uni.request({
url: requestUrl,
data: {
noncestr: Date.now()
},
success: (res) => {
uni.showToast({
title: '请求成功',
icon: 'success',
mask: true,
duration: duration
})
this.res = '请求结果 : ' + JSON.stringify(res);
console.log('request success', res)
},
fail: (err) => {
console.log('request fail', err);
uni.showModal({
content: err.errMsg,
showCancel: false
})
},
complete: () => {
this.loading = false
}
})
}
}
}
调用微信小程序接口
这里注意要把小程序的单向数据绑定和指令改为vue
的写法,也可以使用uni.createCameraContext()
兼容性会更好
<template>
<view>
<text>text</text>
<camera device-position="back" flash="off" binderror="error" style="width: 100%; height: 300px;"></camera>
<button type="primary" @click="takePhoto">拍照</button>
<view>预览</view>
<image mode="widthFix" :src="src"></image>
</view>
</template>
<script>
export default {
data() {
return {
src:""
};
},
methods: {
takePhoto() {
const ctx = wx.createCameraContext()
ctx.takePhoto({
quality: 'high',
success: (res) => {
this.src = res.tempImagePath
}
})
},
}
}
</script>