uni-app 项目实战

举报
SHQ5785 发表于 2024/05/11 09:33:15 2024/05/11
【摘要】 一、前言在前期博文《跨平台应用开发进阶(一) 走近uni-app》中,讲解了利用uni-app框架跨端开发的基础知识,此篇博文主要讲解uni-app实战流程及涉及知识点。 二、项目实战 2.1 样式框架uni-ui扩展组件符合easycom组件规范,可直接应用。若组件不符合easycom规范,则需要在代码里手动import和注册组件,然后才能使用。标准js 和 浏览器js的区别uni-ap...

一、前言

在前期博文《跨平台应用开发进阶(一) 走近uni-app》中,讲解了利用uni-app框架跨端开发的基础知识,此篇博文主要讲解uni-app实战流程及涉及知识点。

二、项目实战

2.1 样式框架

uni-ui扩展组件符合easycom组件规范,可直接应用。

若组件不符合easycom规范,则需要在代码里手动import和注册组件,然后才能使用。

标准js 和 浏览器js的区别

  • uni-appjs代码,h5端运行于浏览器中。非h5端(包含小程序和App),Android平台运行在v8引擎中,iOS平台运行在iOS自带的jscore引擎中,都没有运行在浏览器或webview里。

  • H5端,虽不支持window、document、navigator等浏览器的js API,但也支持标准ECMAScript

  • 不要把浏览器的js扩展对象等价于标准js

  • uni-app的非H5端,一样支持标准js,支持if、for等语法,支持字符串、数字、时间、布尔值、数组、自定义对象等变量类型及各种处理方法。仅仅是不支持window、document、navigator等浏览器专用对象。

2.2 路由跳转

  • uni.navigateTo(OBJECT)
    保留当前页面,跳转到应用内的某个页面,使用uni.navigateBack可以返回到原页面。

注意⚠️:

  • 页面跳转路径有层级限制,不能无限制跳转新页面;
  • 跳转到 tabBar 页面只能使用 switchTab 跳转;
  • 路由API的目标页面必须是在pages.json里注册的vue页面。如果想打开web url,在App平台可以使用 plus.runtime.openURLweb-view组件;H5平台使用window.open;小程序平台使用web-view组件(url需在小程序的联网白名单中)。在hello uni-app中有个组件ulink.vue已对多端进行封装,可参考。
  • APP-NVUE平台暂不支持以this.getOpenerEventChannel()方式获取eventChannel,请换用this.$scope.eventChannel来获取,具体方式请参考上述示例。
  • uni.navigateBack(OBJECT)
    关闭当前页面,返回上一页面或多级页面。可通过getCurrentPages() 获取当前的页面栈,决定需要返回几层。

  • uni.redirectTo(OBJECT)
    关闭当前页面,跳转到应用内的某个页面。

  • uni.reLaunch(OBJECT)
    关闭所有页面,打开到应用内的某个页面。

注意⚠️:

  • 如果调用了 uni.preloadPage(OBJECT) 不会关闭,仅触发生命周期 onHide

  • H5端调用uni.reLaunch之后之前页面栈会销毁,但是无法清空浏览器之前的历史记录,此时navigateBack不能返回,如果存在历史记录的话点击浏览器的返回按钮或者调用history.back()仍然可以导航到浏览器的其他历史记录。

  • uni.switchTab(OBJECT)
    跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面。

2.3 路由传参

uni-app通过在跳转url中添加后缀形式进行路由传参。应用示例如下:

//在起始页面跳转到test.vue页面并传递参数
uni.navigateTo({
    url: 'test?id=1&name=uniapp'
});

目标页面onLoad()方法内接收上一页面传递的参数。应用示例如下:

// 在test.vue页面接受参数
export default {
    onLoad: function (option) { //option为object类型,会序列化上个页面传递的参数
        console.log(option.id); //打印出上个页面传递的参数。
        console.log(option.name); //打印出上个页面传递的参数。
    }
}

⚠️注意:url有长度限制,太长的字符串会传递失败⚠️,可改用窗体通信 postMessage全局变量 Vuex,另外参数中出现空格等特殊字符时需要对参数进行编码,如下为使用encodeURIComponent对参数进行编码的示例。

<navigator :url="'/pages/test/test?item='+ encodeURIComponent(JSON.stringify(item))"></navigator>

// 在test.vue页面接受参数
onLoad: function (option) {
    const item = JSON.parse(decodeURIComponent(option.item));
}

事件监听器应用示例如下:

// 在起始页面跳转到test.vue页面,并监听test.vue发送过来的事件数据
uni.navigateTo({
  url: 'pages/test?id=1',
  events: {
    // 为指定事件添加一个监听器,获取被打开页面传送到当前页面的数据
    acceptDataFromOpenedPage: function(data) {
      console.log(data)
    },
    someEvent: function(data) {
      console.log(data)
    }
    ...
  },
  success: function(res) {
    // 通过eventChannel向被打开页面传送数据
    res.eventChannel.emit('acceptDataFromOpenedPage', { data: 'data from starter page' })
  }
})

// 在test.vue页面,向起始页通过事件传递数据
onLoad: function(option) {
  // #ifdef APP-NVUE
  const eventChannel = this.$scope.eventChannel; // 兼容APP-NVUE
  // #endif
  // #ifndef APP-NVUE
  const eventChannel = this.getOpenerEventChannel();
  // #endif
  eventChannel.emit('acceptDataFromOpenedPage', {data: 'data from test page'});
  eventChannel.emit('someEvent', {data: 'data from test page for someEvent'});
  // 监听acceptDataFromOpenerPage事件,获取上一页面通过eventChannel传送到当前页面的数据
  eventChannel.on('acceptDataFromOpenerPage', function(data) {
    console.log(data)
  })
}

2.4 请求后台

  1. 在目录service下新建一个request.js文件,进行封装。
mport {
	baseURL
} from './config.js'
import {
	getToken,
	goToLogin
} from '@/common/util.js'

const setToken = (res) => {
	if (res.hasOwnProperty("access_token")) {
		getApp().globalData.token = getToken(res.token_type, res.access_token)
		console.log('token info is: ', getApp().globalData.token)
	}
	if (res.hasOwnProperty('msg')) {
		if (res.msg === '用户凭证已过期') {
			goToLogin()
		}
	}
};

const handleErr = (err) => {
	uni.hideLoading()
	uni.showModal({
		title: '提示',
		content: err.errMsg,
		showCancel: false,
		success: function(res) {
			if (res.confirm) {
				// console.log('用户点击确定');
			} else if (res.cancel) {
				// console.log('用户点击取消');
			}
		}
	});
};

const request = (url, params, method, tips) => {
	uni.showLoading({
		title: tips || "加载中..."
	})
	const curUrl = (url.indexOf('http') !== -1 || url.indexOf('https') !== -1) ? url : baseURL + url
	console.log('The request url info is: ', curUrl)
	console.log('The request params info is: ', params)
	return new Promise((resolve, reject) => {
		uni.request({
			url: curUrl,
			method: method || "POST",
			header: {
				'Content-Type': 'application/json',
				'Authorization': getApp().globalData.token || 'Basic YXBwOmFwcA=='
			},
			data: params || {},
			success(res) {
				setToken(res.data)
				resolve(res.data)
				console.log('The request success return info is: ', res.data)
			},
			fail(err) {
				reject(err)
				console.log('The request fail return info is: ', err)
			},
			complete() {
				uni.hideLoading()
			}
		})
	}).catch(err => {
		handleErr(err)
	})
};

export {
	requestForm,
	requestJSON
}

  1. common文件夹下一起建一个utils.js文件用来获取token
// 获取token
export function getToken(token_type, access_token) {
	let token = token_type.charAt(0).toUpperCase() + token_type.slice(1) + ' ' + access_token;
	return token;
}

// Model提示是否返回登录页
export function goToLogin() {
	uni.showModal({
		title: '温馨提示',
		content: "您还没有登录,请登录",
		success: function(res) {
			if (res.confirm) {
				// console.log('用户点击确定');
				uni.navigateTo({
					url: '/pages/ucenter/userinfo/userinfo'
				})
			} else if (res.cancel) {
				// console.log('用户点击取消');
			}
		}
	})
}

  1. 新建一个api目录,然后相应pages是什么页面就新建一个相对应名字的目录和文件,list.js文件就是请求接口。
// 引入工具类
import request from '@/service/request.js' 
 
export function get(data){ // 
    return request({
        url: '/forshop/getprefer',
        method: 'GET',
        contentType:'application/x-www-form-urlencoded',
        data: data
     })
}

注意⚠️:uni.request如果没有传入 success / fail / complete 参数,则会默认返回封装后的 Promise 对象。

  1. 然后在页面上list.vue上调用接口。
<script>
	import { get } from "@/api/list/list"
	export default {
		methods: {
			get().then(res => {
				console.log(res)
			}).catch (err => {
				console.log(err)
			})
		}
	}
</script>

以上为promise形式的接口请求方式。await形式的接口请求方式为:

import { get } from "@/api/list/list"
// Await
async function request () {
   var [err, res] = await get({
       url: 'https://www.example.com/request'
   });
   console.log(res.data);
}

2.5 uni-app https请求方式

传统的 HTTP 有以下安全性问题:

  1. 使用明文进行通信,内容可能会被窃听;

  2. 不验证通信方的身份,通信方的身份有可能遭遇伪装;

  3. 无法证明报文的完整性,报文有可能遭篡改。

为解决安全通信问题,HTTPS 应运而生。其实,HTTPS 并不是新协议,而是站在HTTP的肩膀上,让 HTTP 先和 SSLSecure Sockets Layer)通信,再由 SSLTCP 通信,也就是说 HTTPS 使用了隧道进行通信。 通过使用 SSLHTTPS 具有了加密(防窃听)、认证(防伪装)和完整性保护(防篡改)。

2.5.1 加密

(1)对称密钥加密(Symmetric-Key Encryption),加密和解密使用同一密钥。

(2)非对称密钥加密,又称公开密钥加密(Public-Key Encryption),加密和解密使用不同的密钥。

公开密钥所有人都可以获得,通信发送方获得接收方的公开密钥之后,就可以使用公开密钥进行加密,接收方收到通信内容后使用私有密钥解密。 非对称密钥除了用来加密,还可以用来进行签名。因为私有密钥无法被其他人获取,因此通信发送方使用其私有密钥 进行签名,通信接收方使用发送方的公开密钥对签名进行解密,就能判断这个签名是否正确。

HTTPS 采用混合加密机制,使用非对称密钥加密用于传输对称密钥来保证传输过程的安全性,之后使用对称密钥加密进行通信来保证通信过程的效率。

2.5.2 认证

通过使用证书来对通信方进行认证。

数字证书认证机构(CA,Certificate Authority)是客户端与服务器双方都可信赖的第三方机构。 服务器的运营人员向 CA 提出公开密钥的申请,CA 在判明提出申请者的身份之后,会对已申请的公开密钥做数字签名,然后分配这个已签名的公开密钥,并将该公开密钥放入公开密钥证书后绑定在一起。 进行 HTTPS 通信时,服务器会把证书发送给客户端。客户端取得其中的公开密钥之后,先使用数字签名进行验证, 如果验证通过,就可以开始通信了。

2.5.3 完整性保护

SSL 提供报文摘要功能来进行完整性保护。 HTTP 也提供了 MD5 报文摘要功能,但不是安全的。例如报文内容被篡改之后,同时重新计算 MD5 的值,通信接收方是无法意识到发生了篡改。

HTTPS 的报文摘要功能之所以安全,是因为它结合了加密认证这两个操作。试想一下,加密之后的报文,遭到篡改之后,也很难重新计算报文摘要,因为无法轻易获取明文。

2.5.4 HTTPS 的缺点

  • 因为需要进行加密解密等过程,因此速度会更慢;

  • 需要支付证书授权的高额费用。

uni-app应用https请求方式,需要配合SSL证书:

如果是HTTPS访问的证书,应该是服务端配置,前端发起请求时,地址写成https前缀即可,例如:https://www.example.com/login

2.6 应用规划、配置和调整

并不是说有了SSL证书就没事了,还要考虑应用中的使用问题,需要规划、服务器配置、应用调整等多个环节。

由于SSLhttp 要消耗更多cpu资源(主要是在建立握手连接阶段,之后还要对内容加密),所以对一般网站,只需要对部分地方采用https,大部分开放内容是没必要的,具体取决于你的业务要求。比如对于很多安全要求较低的网站,完全不用https也是可接受的。

某些页面是同时支持 httphttps ,还是只支持 https、强制 https

同时支持就是用户用什么协议访问都可以,那么用户的请求主要就是由页面本身的链接引导来的,因为一般用户不会自己特意去修改地址栏的。

一般我们的网站可以做成同时支持httphttps,都可以访问。但是这就容易有后面说的混合内容或混合脚本的问题。

还可以规划为部分页面支持 https,一般公开页面不用https,只是将部分地方的链接改为 https 就可以了。专门期望以 https 访问的页面中,引用的绝对URL可以明确的使用 https 链接。

是否强制 https ?对于安全性高的网站或网站中的部分页面,可以强制使用https访问, 即使用户在地址栏里手工把 https 改为 http, 也会被自动重定向回 https 上。比如可以通过配置web服务器 rewrite 规则将这些 http url 自动重定向到对应的 https url 上(这样维护比较简单),而不用改应用。

2.7 应用市场上架

应用HBuilder云打包时,提示如下校验信息:

其中,主要提示就是“当前应用缺少相关配置”,可通过如下措施解决:

打开HBuilder项目的manifest.json文件,切换到“源码视图”项

uni-app项目

在 “app-plus” -> “privacy” 节点下添加 prompt节点。

5+ App项目

在 “plus” -> “privacy” 节点下添加 prompt节点。

  • prompt 字符串类型,必填,隐私政策提示框配置策略,可取值template、custom、none,默认值为none

  • template 使用原生提示框模板,可自定义标题、内容已经按钮上的文本。

  • custom 自定义隐私政策提示框,uni-app项目中推荐使用nvue页面进行自定义,5+ APP使用html页面进行自定义。

  • none 不弹出隐私政策提示框

  • template json格式,可选,模板提示框上显示的内容

 "privacy": {
	"prompt": "template",
	"template": {
		"title": "服务协议和隐私政策",
		"message": "  请你务必审慎阅读、充分理解“服务协议”和“隐私政策”各条款,包括但不限于:为了更好的向你提供服务,我们需要收集你的设备标识、操作日志等信息用于分析、优化应用性能。<br/>  你可阅读<a href="">《服务协议》</a>和<a href="">《隐私政策》</a>了解详细信息。如果你同意,请点击下面按钮开始接受我们的服务。",
		"buttonAccept": "我知道了",
		"buttonRefuse": "暂不同意"
	}
}

三、问题分析及解决

在使用uni-app的时候, 忽然发现ios在使用onPullDownRefresh方法下拉刷新的时候 可以看到后台会一连访问几十次。

// 页面下拉
onPullDownRefresh() {
    uni.startPullDownRefresh({}) // 开启刷新, 但是在这里不能再用了,因为onPullDownRefresh已经自动开启刷新了
	this.getCommend(this.mobile)
	let url = '/product/mobile/list'
	if (this.mobile != '') {
		url += '?mobile=' + this.mobile
	}
	this.apireq.req({
		url: url,
		mothod: 'GET',
		success: (res) => {
			// console.log(res)
			this.current = 0
			this.classInfoList = []
			this.setData = []
			this.classInfoList = res.data
			this.setData = res.data.slice(this.current, this.current + 2)
			this.current = this.current + 2
			// console.log(this.current, this.setData)
			uni.stopPullDownRefresh()
		}
	})
},

经过查看文档发现, 原来onPullDownRefresh方法使用后, 只要下拉就已经触发事件, 不能再使用uni.startPullDownRefresh()。只需要在数据请求完成后使用 uni.stopPullDownRefresh 停止刷新就可以。

四、拓展阅读

【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。