uniapp — 多端开发

 

一、基础

 01.目录结构

components        组件
pages             页面
static            静态资源
   - images

App.vue           APP运行的根组件
main.js           APP运行的JS文件
manifest.json     全局文件:应用的配置
pages.json        全局文件:页面的配置
uni.scss          全局样式文件
index.html


02.生命周期

1. 页面生命周期

import { onLoad, onShow, onHide, onReady, onUnload } from '@dcloudio/uni-app'

// 页面第一次加载时触发。(该生命周期内,可以接收传参)
onLoad((options) => {
    console.log('onLoad');
})

// 每次页面显示时触发
onShow(() => {
    console.log('onShow');
})

// 每次页面隐藏时触发
onHide(() => {
    console.log('onHide');
})

// 页面初次渲染完成时触发(这里可以获取DOM)
onReady(() => {
    console.log('onReady');
})

// 页面卸载时触发
onUnload(() => {
    console.log('onUnload');
})

2. 组件生命周期(和微信小程序一样)

        组件中,只能使用组件的生命周期。

3. 应用生命周期

// 注意: 在APP.vue文件是编写

import { onLaunch, onShow, onHide } from '@dcloudio/uni-app'

// APP第一次加载时触发。
onLaunch(() => {
    console.log('onLoad');
})

// APP显示时触发
onShow(() => {
    console.log('onShow');
})

// APP隐藏时触发
onHide(() => {
    console.log('onHide');
})


03.路由跳转

方法1:通过JS调用api跳转。

// 1.非tabBar,有历史记录。
uni.navigateTo({
    url: 'xxx'
})

// 2.非tabBar,没有历史记录。
uni.redirectTo({
    url: 'xxx'
})

// 3.非tabBar,并关闭全部页面。
uni.relaunch({
    url: 'xxx'
})

// 4.tabBar页面
uni.switchTab({
    url: 'xxx'
})

// 5.返回上一页
uni.navigateBack()


// 获取当前页面是处于历史记录中的第几个记录。
getCurrentPages()

方法2:通过标签按钮跳转


04.跳转传参

路径传参

// 1.传参
uni.navigateTo({
    path: '/user.vue?username=思密达'
})


// 2.接收参数(只能在OnLoad生命周期中获取)
<script setup>
    onLoad(e) {
        const {username} = e
    }
<script>


05.组件通信

方法1:父子通信。

(1)父传子:uni.$emit('count',参数)

(2)子传父:uni.$on('count', (resp) => { })


06.条件编译

情况1:在某端生效。

// H5
<!-- #ifdef H5 -->
1234
<!-- #endif -->

// 微信小程序
<!-- #ifdef MP-WEIXIN -->
1234
<!-- #endif -->
   

情况2:在某端不要生效

// H5
<!-- #ifndef H5 -->
1234
<!-- #endif -->

情况3:APP端又分为ios和andriod(因为条件编译无法识别ios还是Android)

// 区分ios和android

const system = uni.getSystemInfoSync();
<view v-if='system.platform === android'></view>

-----------------------------

二、项目配置

 1.配置页面、导航栏、tabBar

记住:tabBar页面需要在pages中声明,tabBar的list的第一个路径要和pages的第一个路径相同。

// 1.在哪里配置
    答:新建页面、导航栏、tabBar都是在pages.json文件里面配置。

// 2.可配置的属性:
(1)pages(配置页面信息)
     - path:       页面路径
     - style:      配置页面的导航栏(属性和globalStyle是一样的。)
     - needLogin:  是否登录后才能访问

(2)globalStyle(配置全局导航栏) 
     - navigationBarBackgroundColor:导航栏背景颜色
     - navigationBarTextStyle:导航栏标题颜色(仅支持 black/white)
     - navigationBarTitleText:导航栏文字内容
     - navigationBarShadow:   导航栏下方阴影
         - colorType:         阴影颜色,支持blue、grey、green、orange、red、yellow
     - navigationStyle:       自定义导航栏(default表示默认,custom表示自定义)
     - backgroundColor:       窗口的背景颜色
     - backgroundTextStyle:   下拉loading的背景颜色
     - backgroundColorTop:    顶部窗口的背景色(bounce回弹区域)
     - backgroundColorBottom: 底部窗口的背景色(bounce回弹区域)
     - enablePullDownRefresh: 是否开启下拉刷新
     - onReachBottomDistance: 触底刷新的距离(默认50)   

// 注意配置tabBar有坑:首页必须在tabBar中,否则tabBar不会显示出来。

(3)tabBar(配置全局tabBar)   
     - color:tabBar的文字颜色
     - selectedColor:  被选中的tabBar的文字颜色
     - backgroundColor:tabBar的背景颜色
     - borderStyle:    tabBar的上边框颜色(可选的有black/white,还可以自定义颜色)
     - position: tabBar的位置(默认是bottom,可选的有bottom/top)
     - fontSize: tabBar的文字大小
     - iconWidth:icon图标的宽度(默认24px)
     - spacing:  icon和文字的间距(默认3px)
     - height:   tabBar的高度(默认50px)
     - midButton:中间按钮 仅在 list 项为偶数时有效(对象)
         - text:
         - height:
         - iconPath:
     - iconfontSrc:list设置 iconfont 属性时,要指定字体文件路径(一般list都是数组)
     - backgroundImage:  背景图片(优先级高于backgroundColor)
     - backgroundRepeat: 背景平铺
     - redDotColor:tabBar上面红点的颜色
     - list:配置每一个tabBar的icon(数组包对象)
         - pagePath:页面路径
         - text:    tab上按钮文字,在 App 和 H5 平台为非必填
         - iconPath:icon路径
         - selectedIconPath:被选中的icon的路径


2.APP配置

manifest.json配置文件中,勾选需要的功能即可。


3.配置全局样式

好处:在该uni.scss中声明的scss变量,在使用时无需导入。


4.配置分包(早期)

1. 分包的意义

// 1.微信小程序分包的限制如下:
(1)单个分包、主包大小不超过2M。(tabBar必须在主包内。)
(2)总体分包大小不超过16M。 
(3)总包大小不超过20M。

// 2.单个分包、主包
pages下的每一个目录都是一个分包,一般把index或者tabBar作为主包。
pages
    index
    list
    user
    tabBar

// 3.总体分包
正常情况下,在pages目录下,一个目录就放一个.vue文件,表示一个页面;
但是有时候,一个目录里面放了好几个.vue文件,他们的总和就是总体分包。

2. 如何配置分包?

步骤1:开启分包(在manifes.json中)。

/* 小程序特有相关 */
	"mp-weixin": {
		"appid": "",
		"setting": {
			"urlCheck": false
		},
		// 配置分包(这里开启分包)
		"optimization": {
			"subPackages": true
		}

 步骤2:配置分包目录(在pages.json中)。

// 假设项目目录结构如下:

main.js
APP.vue

// 主包目录:一般是tabBar。
pages        
  └── tabBar
        ├── index.vue
        ├── middle.vue
        └── my.vue  

// 分包目录:配置多个子包,子包内又有页面。
package      
    ├── commonPackage  // 公共分包
    │       └── pages
    │             ├── cat.vue
    │             └── dog.vue
    ├── login // 登录页
    │      └── pages
    │             └── login.vue
    │             
    └── userConfig  // 设置页
           └── pages
                 └── userConfig.vue
 

***********************************************************

// 在pages.json中配置主包、分包。
{

  // 配置主包
  "pages":[
    "pages/index",
    "pages/logs"
  ],

  // 配置分包
  "subPackages": [
    {
      "root": "packageA",
      "pages": [
        {
            "path": "pages/cat",
            "navigationBarTitleText": "设置"
        },
        {
            "path": "pages/dog",
            "navigationBarTitleText": "设置"
        },
      ]
   }, 
   {
      "root": "packageB",
      "name": "pack2",
      "pages": [
         {
            "path": "pages/apple",
            "navigationBarTitleText": "设置"
         },
         {
            "path": "pages/banana",
            "navigationBarTitleText": "设置"
         },
      ]
    }
  ]
}


5.配置请求模块(必须)

方法1:使用uView提供的api。

步骤1:新建utils文件夹,创建request.js文件。

// 此vm参数为页面的实例,可以通过它引用vuex中的变量
module.exports = (vm) => {
    // 初始化请求配置
    uni.$u.http.setConfig((config) => {
        /* config 为默认全局配置*/
        config.baseURL = 'http://uat.banlu.xuexiluxian.cn'; /* 根域名 */
        return config
    })
	
	// 请求拦截
	uni.$u.http.interceptors.request.use((config) => { // 可使用async await 做异步操作
	    return config 
	}, config => { // 可使用async await 做异步操作
	    return Promise.reject(config)
	})
	
	// 响应拦截
	uni.$u.http.interceptors.response.use((response) => { /* 对响应成功做点什么 可使用async await 做异步操作*/
		return response.data
		
	}, (response) => { 
		// 对响应错误做点什么 (statusCode !== 200)
		return Promise.reject(response)
	})
}

步骤2:在main.js中导入request.js模块。(切记放要在new Vue之后)

const app = new Vue({
  ...App
})

// 引入请求封装,将app参数传递到配置中
require('./utils/request.js')(app)

app.$mount()

步骤3:在api目录下,配置请求。(请求参数的写法和axios一致)

// get请求
export const loginByWechatApi = (data) => {
	return http.get('/u/loginByWechat', {
		params: {
			code: data
		}
	})
}

// post请求
export const loginByWechatApi = (data) => {
	return http.get('/u/loginByWechat', data)
}

方法2:手动封装。

了解uniapp原生请求方法:uni.request(参数)。

uni.request({
    url: 'http:www.baidu.com',
    method: 'GET',
    // GET请求参数一般放url后面,如果放data中,将来会被自动放到url后面,也是可以的。
    data: {},
    header: {},
    // 成功的回调
    success: () => {

    },
    // 失败的回调
    fail: () => {
    
    },
    // 无论成功或者失败,都会触发的回调
    complete: () => {

    }

})

封装的整个过程:


const TIMEOUT = 6000 // 超时时间


/****************************************************************/


export default {
	// 定义请求拦截器。
	 requestIntercepted(config) {
		return config
	},
	
	// 响应拦截器。
	 responseInterceoted(resp) {
		return resp
	},
	
	// 取消重复的请求。
	 removePendingRequest(config) {},
	
	
	// 发请求:该方法返回一个Promise对象。
	request(options) {
		return new Promise((resolve, reject) => {
			// 处理请求参数
			const finalOptions = {
				...options,
				timeout: TIMEOUT,
				header: {
					'Content-type': 'application/json',
					 ...options.header
				}
			}
			
			// 调用请求拦截器
			const handlerOptions = this.requestIntercepted(finalOptions)
			
			// 调用取消重复的请求
			this.removePendingRequest(handlerOptions)
			
			// 调用响应拦截器
			const requestTask = uni.request({
				...handlerOptions,
				success: (res) => {
					const response = this.responseInterceoted(res)
					if (response.statusCode >= 200 && response.statusCode < 300) {
						resolve(response.data)
					} else {
						reject(response)
					}
				},
				fail: (error) => {
					reject(error, '网络出现问题!')
				}
			})
		})
	},
	
	// 便捷POST请求。
	post(url, data={}, config={}) {
		return this.request({
			method: 'POST',
			url,
			data,
			...config
		})
	},
	
	// 便捷GET请求。
	get(url, data={}, config={}) {
		return this.request({
			method: 'GET',
			url,
			data,
			...config
		})
	}
}


6.配置webView页面(做活动)

1. web-view的使用姿势

​// 1.使用方式:必须v-if,不能v-show。
<web-view v-if="isShow" src="https://www.baidu.com"></web-view>

// 2.使用场景
做一个短期活动,比如7月-9月之间搞活动,不需要把每一个端的代码都重新写一遍再发布,
直接做H5网页即可,使用web-view标签嵌入即可。

// 3.缺点
(1)打开是全屏的,无法设置大小。
(2)通过小程序打开的,是没有返回按钮的,所以要做一个返回操作。
     利用uni.navigateTo()来跳转到webView页面,这样就可以有返回了,
     并且给webView单独配置成一个分包页面。
     

2. 微信小程序使用web-view的注意事项

在上线之前,需要去微信小程序后台,把webView的url地址,在业务域名中配置,如下图:

3. webview传值问题

传参:小程序跳转到webview,可以在webview的src中以?拼接参数形式传参。
     <web-view v-if="showActivity" src="https://www.baidu.com?username=思密达"></web-view>

注意事项:小程序和APP的区别。

    (1)小程序可以传参给webview没问题,但是webview无法传参回去给小程序。
    (2) APP可以可以传参给webview,也可以通过webview的@postMessage方法
         把参数从webview回传给APP。


 7.解决跨域问题

// 情况1:微信小程序
开发阶段:不校验合法域名。
生产阶段:去微信小程序后台页面,配置合法域名。

// 情况2:H5
开发阶段:和vue3一样,去vite.config.js中配置代理。
生产阶段:需要后端解决好跨域。

// 情况3:APP
开发阶段:和vue3一样,去vite.config.js中配置代理。
生产阶段:需要后端解决好跨域。


// 注意:小程序不支持vite.config.js文件的配置,所以代理配置不生效。


8.打包上线

1. 微信小程序打包上线

 前提:

(1) 保证包,不能超过最大限制。
(2) 不校验url取消勾选。
(3) request合法域名加上,必须是https。
(4) 如果用webview,也必须加上到业务域名,同样是https。
(5) 拿到本小程序的AppId。

 上架流程:

(1) 在微信开发者工具,点击上传。
(2) 进入小程序后台
(3) 管理 => 版本控制 => 开发版本(本次上传的小程序包)

2. H5打包上线

1. 在HBuilder的最上方找到并点《发行》 => 选中《网站-PC,web、H5》
2. 提前在manifest.json中找到《web配置》,把运行基础路径改为:./  其他选项自定义。
3. 打包后的文件,给后端或者运维部署即可。

3. Android打包上线

 前提:

(1) 准备好:包名、别名、证书密码、证书文件。
包名:公司给。
别名:公司给。
证书密码:公司给。
证书文件:公司给。(一般是用工具生成的,如:香蕉云编)

例如:
包名:cn.xuexiluxian.ai
别名:xiaoluxianai
密码:xiaoluxianai96
证书文件:已发放

(2) 网站备案,pc端网站还有,要准备一个用户隐私协议的url。
(3) 要做好公司app产品类目的公司资质。
(4) 软件著作权。
(5) 上传到应用商店需要提供以下内容:
    1. 软件著作权
    2. 隐私政策
    3. 应用图标
    4. app内截图
    5. 应用简介
    6. 应用分类

提交到应用商店的过程:

(1) 发行云打包 => 填写对应内容(证书文件与开发阶段一模一样) => 生成apk包
(2) 申请对应的app应用商店的开放平台,比如:oppo、小米、华为、vivo等...
(3) 填写app的信息,然后上传即可。

4. ios打包上线

前提:

(1) 准备好:包名、别名、证书密码、证书文件、证书profile文件。
包名:公司给。
别名:公司给。
证书密码:公司给。
证书文件:公司给。(证书文件与开发阶段不一样,需要重新生成)
证书profile文件:公司给。

例如:
包名:cn.xuexiluxian.ai
别名:xiaoluxianai
证书密码:luxianai
证书文件:已发放

打包完后生成ipa包下载地址

去ios开放平台

填写app信息、上传ipa包即可。

注意:所有app第一次上架时,必须提供账号密码登录的方式,并提供账号密码给开放平台;

否则微信登录、手机验证码登录无效。

-----------------------------

三、功能模块

01.登录功能

参考链接:登录功能实现-优快云博客(微信登录才需要配置Link,支付宝不需要)(10)当以上步骤完全没问题,点击:完成, 此时会自动生成links链接。3. 唤醒微信登录(情况1:浏览器打开。(1)创建identifiers,添加上其他登录和苹果登录的权限。说明:以上3项,在uniapp中提供,直接复制粘贴就可以了。第一种实现方式:点击登录,然后绑定手机号,最后提示登录成功。(9)添加需要需要找到,域名解析,添加一个记录。第二种实现方式:点击登录 => 登录成功。(7)配置步骤 >> 点击参数配置。步骤3:自定义基座,然后运行即可。icon-default.png?t=O83Ahttps://blog.youkuaiyun.com/weixin_57136547/article/details/142032493?csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22142032493%22%2C%22source%22%3A%22weixin_57136547%22%7D


02.支付功能

参考链接:支付功能实现-优快云博客文章浏览阅读83次。(2)要有证书(因为要做自定义基座调试)【appid、证书私钥密码、profile文件、私钥证书】, 并且在生成证书的时候,要勾选应用内付费的服务。(2)将来获取的h5_url回调地址的域名不能是localhost,要是上面配置的支付域名。2. 支付宝支付(特殊:不走uniapp,而是生成一个h5支付页面。1. 微信支付(特殊:不走uniapp,而是生成一个h5支付页面。(1)需要在微信开发者后台,配置H5支付域名。(1)要有开发者账号,并且付费(一年是:688的订阅费用)步骤2:打自定义基座,运行即可。icon-default.png?t=O83Ahttps://blog.youkuaiyun.com/weixin_57136547/article/details/142033327?csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22142033327%22%2C%22source%22%3A%22weixin_57136547%22%7D


03.分享朋友圈、好友

步骤1:创建mixins目录,新建share.js文件,将来混入到main.js中。

export default {
	// 分享给好友
	onShareAppMessage() {
		return {
			title: '分享给好友的标题',
			imageUrl: '../../static/logo.png',
			path: '/pages/my/my'
		}
	},
	// 分享到朋友圈
	onShareTimeLine() {
		return {
			title: '分享给朋友圈的标题',
			imageUrl: '../../static/logo.png',
			query: ' '
		}
	}
}

步骤2:去main.js中导入混入。

import share from '@/mixins/share.js'
Vue.mixin(share)

04.虚拟滚动

xxx

05.文件上传

原始http消息格式 

1. 单文件

 

async function fileUpload (e) {

    // 在这里先对要上传的文件进行验证(文件大小、文件格式是否正确)

    // FormData对象的好处:自动帮我们设置了content-Type: 'mutipart/form-data'
    const formData = new FormData();

    // 键名:是看接口文档规定的。 值:文件数据。
    formData.append('file', e.target.files[0]) 
    
    // 请求后端接口
    const resp = await fetch('https:www.baidu.com/api/upload', {
        method: 'POST',
        body: formData
    })
}


06.数据加密


-----------------------------

四、兼容性问题

1.动态组件

(1)h5端:支持。
(2)app端:组件注册后才能支持。
(3)小程序:不支持。


2.fetch(SSE)

1. 为什么用SSE?

 (1)如果使用webSocket,性能不如SSE。

(1)h5端:必须使用wss协议
new webSocket('wss://')  
(2)小程序、app端:必须使用wss协议  
uni.connetSocket({
   url: 'wss://'            
})

(2)如果使用fetch,只有h5端支持,其他端都不支持。

(3)使用SSE:默认不支持post,只支持get。

如果要支持post,需要一个库,示例如下:
import { fetchEventSource } from @microsoft/fetch-event-source。

(1)h5端:支持@microsoft/fetch-event-source。
(2)小程序端:不支持@microsoft/fetch-event-source,需要
    uni.request({
        enableChunked: true // 让微信小程序支持SSE
        success(res) {
          console.log('连接成功')
        }
    })
(3)app端:本身不支持@microsoft/fetch-event-source,可以通过xxx方式支持。

2. 如何使用SSE?

微信小程序端:

(1)安装一个库:npm i @microsoft/fetch-event-source
          import { fetchEventSource } from @microsoft/fetch-event-source。
(2)小程序是通过uni.request中,配置enableChunked: true来支持SSE的。

(3)要使用onHeadersReceivedonChunkReceived,那么uni.request中必须有success

         或者complete的其中一个。

【后端返回数据格式】:

 【代码实现】:

wx_send() {
	const that = this;
	let content = ''   //收集ai的回答
	let len = this.msgList.length
			
	let requestTask = uni.request({
		url: 'https://ai.xuexiluxian.cn/api/xlx-chat/chatMessage/sendMessageSSE',
		enableChunked: true, //微信小程序支持SSE
		method: 'post',
		header: {
			'Content-Type': 'application/json',
			'Authorization': this.token
		},
		data: JSON.stringify({
			id:this.chatId, 
			userId:this.userId,
			question:this.msgList.at(-1).content
		}),
		success: (resp) => { //相当于onclose
			console.log('连接关闭')
		}
		})
		requestTask.onHeadersReceived(res => { //相对于onopen
			console.log('onHeadersReceived');
		})
		requestTask.onChunkReceived(res => { //相当于onmessage(这里解析后端返回的内容)
			const arrayBuffer = res.data;
			const uint8Array = new Uint8Array(arrayBuffer);
			let data = uni.arrayBufferToBase64(uint8Array);
			data = new Buffer(data, 'base64');
			data = data.toString('utf8');
			const lines = data.split("\n\n");
			lines.forEach(line => {
				if( line.indexOf('{') != -1 ){
					//使用正则表达式提取 data 部分
					const dataMatch = line.match(/data:(\{.*\})/);
					//检查是否成功匹配
					if (dataMatch && dataMatch[1]) {
						const dataString = dataMatch[1];
							
						//将 JSON 字符串转换为对象
						let data;
						try {
							data = JSON.parse(dataString);
							content += data.content
							that.$set(that.msgList,len,{
								role:'system',
								content:content
							});
								
						} catch (error) {
							console.error('解析 JSON 失败:', error);
						}
					} else {
						console.error('未找到 data 部分');
					}
				}
			})
		})
	}

H5、APP端:其中APP不分ios和android。

(1)安装一个库:npm i @microsoft/fetch-event-source
          import { fetchEventSource } from @microsoft/fetch-event-source。

h5_send() {
	const that = this
	let content = '' //收集ai的回答
	const ctrl = new AbortController() //用于中断请求
	const len = this.msgList.length
			
	fetchEventSource('/api/xlx-chat/chatMessage/sendMessageSSE', {
		method: 'POST',
		headers: {
			'Content-Type': 'application/json',
			'Authorization': this.token
		},
		signal: ctrl.signal,
		body: JSON.stringify({
			id:this.chatId,
			userId:this.userId,
			question: this.msgList.at(-1).content 
		}),
		onopen: (resp) => { 
					
		},
		onmessage: (msg) => {
			const { data } = msg
		    //校验返回的内容
		 if( data &&  !data.includes("<br/><br/>tokens") && !data.includes("[DONE]")) {
				const res = JSON.parse(data)
				content += res.content
				that.$set(that.msgList,len,{
					role:'system',
					content:content
				})
		 }  
		},
		onclose: () => {
			console.log('连接关闭!');
		}
	})
}

3. 如果使用SSE,发现数据一次性返回,需要去nginx中取消缓存!

SSE长连接,服务端推送,web端GET和POST用法及问题处理 - 简书1. GET 形式 使用的库:event-source-polyfill 写法: let evtSource; function startSSE() { e...icon-default.png?t=O83Ahttps://www.jianshu.com/p/843f50727aa6

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值