uni-app组件以及页面通讯方法(五)


前言

uniapp页面间数据通信方式有很多:通过url传参,状态管理库vuex/pinia,本地存储,事件通道eventChannel,EventBus等。

1、通过url传参?

A页面向B页面传递参数

uni.navigateTo({  
    url: 'test/test?id=1&url=' + encodeURIComponent('https://dcloud.io')  
});

B页面接收A页面传递的参数

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

适合数据量小的情况,如数据量大的情况不建议此方式。注意:如传递了number类型数据,接收回来的数据会转换成string类型

2、状态管理库vuex/pinia

2.1 vuex

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化
初始化Vuex store:

import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    someData: ''
  },
  mutations: {
    setSomeData(state, data) {
      state.someData = data;
    }
  },
  actions: {
    updateSomeData({ commit }, data) {
      commit('setSomeData', data);
    }
  }
});

更新状态:

this.$store.dispatch('updateSomeData', 'new value');

访问状态:

computed: {
  someData() {
    return this.$store.state.someData;
  }
}

2.2 pinia

Pinia 是 Vue 的存储,它允许您跨组件、页面共享状态。

  • pinia 安装
    1. uni-app 内置了 Pinia 。Vue 2 项目暂不支持
    2. 使用 HBuilder X
      HBuilder X 已内置了 Pinia,无需手动安装。
      App 升级时,如果之前使用 HBuilder X 版本 < 4.14 打包,现在使用 HBuilder X 版本 >= 4.14,更新时需要整包更新不可使用wgt更新(在 4.14 时升级了 vue 版本,低版本的基座和高版本 wgt 资源包会导致使用 Pinia 时报错)
    3. 使用 CLI
      4.14版本 之前:执行 yarn add pinia@2.0.36 或 npm install pinia@2.0.36 安装,要固定版本
      4.14版本 之后:执行 yarn add pinia 或 npm install pinia 安装,可不指定版本
  • pinia 优点
    1. 追踪 actions、mutations 的时间线
    2. 在组件中展示它们所用到的 Store
    3. 让调试更容易的 Time travel
    4. 不必重载页面即可修改 Store
    5. 开发时可保持当前的 State
    6. 为 JS 开发者提供适当的 TypeScript 支持以及 自动补全 功能。
  • pinia 代码示例
    main.js
    import App from './App'
    // #ifndef VUE3
    import Vue from 'vue'
    import './uni.promisify.adaptor'
    Vue.config.productionTip = false
    App.mpType = 'app'
    
    const app = new Vue({
      ...App
    })
    app.$mount()
    // #endif
    
    // #ifdef VUE3
    import * as Pinia from 'pinia';
    import { createSSRApp } from 'vue'
    export function createApp() {
      const app = createSSRApp(App)
      app.use(Pinia.createPinia());
      return {
        app,
    	Pinia
      }
    }
    // #endif
    
    首先创建一个 Store:
    // stores/counter.js
    import { defineStore } from 'pinia';
    export const useCounterStore = defineStore('counter', {
    	state: () => {
    		return { count: 0 };
    	},
    	getters: {
    		double: (state) => state.count * 2,
    	},
    	// 也可以这样定义
    	// state: () => ({ count: 0 })
    	actions: {
    		increment() {
    			this.count++;
    		},
    	},
    });
    
    在组件中使用它:
    	<template>
     <view class="tool-version">
    	<button @click="handleCounter">counter++</button>
    	<button>{{	doubleCounter }}</button>
    	<button @click="handleTest">跳转test</button>
     </view>
    </template>
     
    <script>
    	import { useCounterStore } from '@/stores/counter'
    	import { mapState, mapStores, mapActions } from 'pinia'
    	 const counter = useCounterStore()
      export default {
        data() {
          return {
    		  
          };
        },
    	  computed: {
    		// 允许访问 this.counterStore
    		...mapStores(useCounterStore),
    		// 允许读取 this.count 和 this.double
    		...mapState(useCounterStore, ['count', 'double']),
    		doubleCounter() {
    			return counter.double
    		}
    	  },
        methods: {
    		...mapActions(useCounterStore, ['increment']),
    	  handleTest() {
    	   uni.navigateTo({
    		  url: '/pages/index/test?id=1&name=uniapp',
    		  events: {
    		  	// 为指定事件添加一个监听器,获取被打开页面传送到当前页面的数据
    		  	acceptDataFromOpenedPage: function(data) {
    		  	  console.log(data)
    		  	},
    		  	someEvent: function(data) {
    		  	  console.log(data)
    		  	}
    		  },
    		  success: function(res) {
    		  	// 通过eventChannel向被打开页面传送数据
    		  	res.eventChannel.emit('acceptDataFromOpenerPage', { data: 'data from starter page' })
    		  }
    		    });
    	  },
    	  handleCounter() {
    		  // console.log(this.counterStore.double)
    		  console.log(counter)
    		counter.$patch({ count: counter.count + 1 })
    		  // 自动补全!
    		  // 或使用 action 代替
    		  // counter.increment()
    	  }
        }
      };
    </script>
    

在这里插入图片描述

3、本地存储

此类方法虽简单适用所有页面,但一般不推荐使用该方法。

/** A.vue **/
<script>
methods: {
	goRouter(){
		uni.setStorageSync('pageData', {data: 123, title: "A页面",source: 1, content: "点个赞点个赞,求求了"})
		uni.navigateTo({
			url: `/pages/index/test`
		})
	}
}
</script>
/** B.vue **/
<script>
onLoad() {
  const data = uni.getStorageSync('pageData');
  console.log(data) // {data: 123, title: "A页面",source: 1, content: "点个赞点个赞,求求了"}
}
</script>

4、事件通道eventChannel

4.1 EventChannel.emit

EventChannel.emit(string eventName, any args)触发一个事件
string eventName 事件名称
any args 事件参数

4.2 EventChannel.off

EventChannel.off(string eventName, function fn)
取消监听一个事件。给出第二个参数时,只取消给出的监听函数,否则取消所有监听函数
string eventName 事件名称
function fn 事件监听函数
参数 any args 触发事件参数

4.3 EventChannel.on

EventChannel.on(string eventName, function fn)
持续监听一个事件
string eventName 事件名称
function fn 事件监听函数
参数 any args 触发事件参数

4.4 EventChannel.once

EventChannel.once(string eventName, function fn)
监听一个事件一次,触发后失效
string eventName 事件名称
function fn 事件监听函数
参数 any args 触发事件参数

4.5 注意事项

  • navigateTo, redirectTo 只能打开非 tabBar 页面。
  • switchTab 只能打开 tabBar 页面。
  • reLaunch 可以打开任意页面。
  • 页面底部的 tabBar 由页面决定,即只要是定义为 tabBar 的页面,底部都有 tabBar。
  • 不能在首页 onReady 之前进行页面跳转。
  • H5端页面刷新之后页面栈会消失,此时navigateBack不能返回,如果一定要返回可以使用history.back()导航到浏览器的其他历史记录。

4.6 应用场景

适用于页面栈内的父子页面通信

/** A.vue **/
<script>
methods: {
	goRouter(){
		uni.navigateTo({
			url: `/pages/index/test`,
			success: function(res) {
   			 	res.eventChannel.emit('pageData', {data: 123, title: "A页面",source: 1, content: "点个赞点个赞,求求了"})
  			}
		})
	}
}
</script>
/** B.vue **/
<script>
onLoad(option) {
  const eventChannel = this.getOpenerEventChannel();
  // eventChannel.on监听,获取上一页面通过eventChannel事件传送的数据
  eventChannel.on('pageData', data=> {
    console.log(data) // {data: 123, title: "A页面",source: 1, content: "点个赞点个赞,求求了"}
  })
}
</script>

适合数据量大的情况。注意:此类方法如果不通过A页面直接跳转B页面会报错

5、 uni.$emit、uni.$on 、 uni.$once 、uni.$off 页面通讯

5.1 监听事件 uni.$on

监听全局的自定义事件,事件由 uni.$emit 触发,回调函数会接收事件触发函数的传入参数。

属性类型描述
eventNameString事件名
callbackFunction事件的回调函数
 // 监听事件  
uni.$on('update',function(data){
	console.log('监听到事件来自 update ,携带参数 msg 为:' + data.msg);
})

5.2 触发事件 uni.$emit

触发全局的自定义事件,附加参数都会传给监听器回调函数。

属性类型描述
eventNameString事件名
objectObject触发事件携带的附加参数
uni.$emit('update',{msg:'页面更新'})

5.3 uni.$once 、uni.$off

  1. uni.$once
    监听全局的自定义事件,事件由 uni.$emit 触发,但仅触发一次,在第一次触发之后移除该监听器。
属性类型描述
eventNameString事件名
callbackFunction事件的回调函数
uni.$once('update',function(data){
	console.log('监听到事件来自 update ,携带参数 msg 为:' + data.msg);
})
  1. uni.$off
    移除全局自定义事件监听器。
属性类型描述
eventNameString事件名
callbackFunction事件的回调函数
uni.$off('update', function(data){
	console.log(data);
})

注:

  • 如果uni.$off没有传入参数,则移除App级别的所有事件监听器;
  • 如果只提供了事件名(eventName),则移除该事件名对应的所有监听器;
  • 如果同时提供了事件与回调,则只移除这个事件回调的监听器;
  • 提供的回调必须跟$on的回调为同一个才能移除这个回调的监听器;

5.4 更多使用场景

  1. 非 TabBar 的页面
    在非 TabBar 的页面,可以在页面 onLoad 里边 uni.$on 注册监听,onUnload 里边 uni.$off 移除,官方也是这么推荐的

    注意事项
    uni.$emit、uni.$on 、 uni.$once 、uni.$off 触发的事件都是 App 全局级别的,跨任意组件,页面,nvue,vue 等
    使用时,注意及时销毁事件监听,比如,页面 onLoad 里边uni.$on注册监听,onUnload 里边 uni.$off 移除,或者一次性的事件,直接使用 uni.$once 监听
    注意 uni.$on 定义完成后才能接收到 uni.$emit 传递的数据

  2. Tabbar 页面
    在 Tabbar 页面,onLoad 只会执行一次,onUnload 不会执行。会频繁的执行 onShow 和 onHide,所以监听的处理思路如下

    • onShow 进行 注册监听。注意需要先移除监听,在注册监听。因为重复的注册会导致监听事件被执行多次
    • 在此页面要跳转的地方 移除监听
    • 在其他 tabBar 页面的 onTabItemTap 方法中移除监听
    • 如果页面没有打开,将不能 注册监听事件 uni.$onuni.$once
    • 一次性的事件,直接使用 uni.$once 监听,不需要移除。
  3. 习惯了 vue 中的 emit 写法,
    要注意:

  • vue 是 this.$emit() ,而 uniapp 使用的是 uni.
  • 接收事件不再使用 @xxx=“” 的形式

5.5 注意事项

  • uni.$emit、 uni.$on 、 uni.$once 、uni.$off 触发的事件都是 App 全局级别的,跨任意组件,页面,nvue,vue 等
  • 使用时,注意及时销毁事件监听,比如,页面 onLoad 里边 uni.$on 注册监听,onUnload 里边 uni.$off 移除,或者一次性的事件,直接使用 uni.$once 监听
  • 注意 uni.$on 定义完成后才能接收到 uni.$emit 传递的数据

6、EventBus进行通讯

将eventBus对象注册到Vue的原型上

Vue.prototype.$eventBus = new Vue()

在 A 页面添加点击事件向 B 页面发送消息

<button @click=“sendMsg”>Send</button>
sendMsg() {
  this.$eventBus.$emit(“getId”, 12)
}

// 在 B 页面注册监听事件

created() {
  this.$eventBus.$on(“getId”, function(id) {
     this.id = id 
  }
}

7、利用全局变量进行通讯

7.1 公用模块

定义一个专用的模块,用来组织和管理这些全局的变量,在需要的页面引入。

注意,这种方式只支持多个vue页面或多个nvue页面之间公用,vue和nvue之间不公用。
示例如下:
在 uni-app 项目根目录下创建 common 目录,然后在 common 目录下新建 helper.js 用于定义公用的方法。

const websiteUrl = 'http://uniapp.dcloud.io';  
const now = Date.now || function () {  
    return new Date().getTime();  
};  
const isArray = Array.isArray || function (obj) {  
    return obj instanceof Array;  
};  

export default {  
    websiteUrl,  
    now,  
    isArray  
}

接下来在 pages/index/index.vue 中引用该模块。


<script>  
    import helper from '../../common/helper.js';  

    export default {  
        data() {  
            return {};  
        },  
        onLoad(){  
            console.log('now:' + helper.now());  
        },  
        methods: {  
        }  
    }  
</script>

这种方式维护起来比较方便,但是缺点就是每次都需要引入。

7.2 挂载 Vue.prototype

将一些使用频率较高的常量或者方法,直接扩展到 Vue.prototype 上,每个 Vue 对象都会“继承”下来。
注意这种方式只支持vue页面
示例如下:
在 main.js 中挂载属性/方法

Vue.prototype.websiteUrl = 'http://uniapp.dcloud.io';  
Vue.prototype.now = Date.now || function () {  
    return new Date().getTime();  
};  
Vue.prototype.isArray = Array.isArray || function (obj) {  
    return obj instanceof Array;  
};

然后在 pages/index/index.vue 中调用

<script>  
    export default {  
        data() {  
            return {};  
        },  
        onLoad(){  
            console.log('now:' + this.now());  
        },  
        methods: {  
        }  
    }  
</script>

这种方式,只需要在 main.js 中定义好即可在每个页面中直接调用。
Tips
每个页面中不要在出现重复的属性或方法名。

建议在 Vue.prototype 上挂载的属性或方法,可以加一个统一的前缀。比如 $url、global_url 这样,在阅读代码时也容易与当前页面的内容区分开。

7.3 globalData

小程序中有个globalData概念,可以在 App 上声明全局变量。Vue 之前是没有这类概念的,但 uni-app 引入了globalData概念,并且在包括H5、App等平台都实现了。
在 App.vue 可以定义 globalData ,也可以使用 API 读写这个值。
globalData支持vue和nvue共享数据。
globalData是一种比较简单的全局变量使用方式。
定义:App.vue

<script>  
    export default {  
        globalData: {  
            text: 'text'  
        },  
        onLaunch: function() {  
            console.log('App Launch')  
        },  
        onShow: function() {  
            console.log('App Show')  
        },  
        onHide: function() {  
            console.log('App Hide')  
        }  
    }  
</script>  

<style>  
    /*每个页面公共css */  
</style>

js中操作globalData的方式如下:
赋值:getApp().globalData.text = ‘test’
取值:console.log(getApp().globalData.text) // ‘test’

this.appGlobal.globalData.userInfo

如果需要把globalData的数据绑定到页面上,可在页面的onshow声明周期里进行变量重赋值。HBuilderX 2.0.3起,nvue页面在uni-app编译模式下,也支持onshow。

8、nvue 和 vue 相互通讯

.vue 和 .nvue 并不是一个规范,因此一些在 .vue 中适用的方案并不适用于 .nvue。Vue 上挂载属性,不能在 .nvue 中使用。

  • 步骤
    1、在nvue页面使用uni.postMessage(data),发送数据,data只能为json数据,
    2、在app.vue页面里使用 onUniNViewMessage 进行监听,接受数据
    代码示例
    nvue页面
<template>
    <div @click="test">
        <text>点击页面发送数据</text>
    </div>
</template>
<script>
    export default {
        methods: {
            test(e) {
                uni.postMessage({test: "数据",value:"数据"});
            }
        }
    }
</script>

App.vue

<script>
    export default {
        onUniNViewMessage:function(e){
          console.log("App.vue收到数据")
          console.log(JSON.stringify(e.data))  
        },
        onLaunch: function() {
            console.log('App Launch');
        }
    }
</script>

总结

选择哪种方式取决于你的具体需求和应用的复杂度。对于简单的需求,可以直接使用URL参数或者eventChannel;对于更复杂的应用,则推荐使用Vuex或者globalData来管理状态。

### 如何在 UniApp 中实现小程序拼团功能 #### 设计思路 为了实现在 UniApp 小程序中的拼团功能,设计上需考虑几个核心模块:商品展示、发起拼团、加入已有拼团以及订单管理。通过合理利用 Vue 组件化特性来构建这些页面逻辑[^1]。 #### 技术选型与准备 - **前端框架**: 使用 Vue.js 构建视图层,得益于其简洁的数据绑定机制和组件生态。 - **跨平台能力**: 利用 UniApp 提供的一套代码多端运行的能力,确保一次开发即可适配微信等多个主流移动应用环境。 #### 关键技术点解析 ##### 商品详情页集成拼团按钮 当用户浏览某个特定的商品时,在商品详情界面应显示“立即参团”或“单独购买”的选项。点击相应按钮后触发对应的业务处理函数: ```javascript methods: { joinGroup(id){ uni.navigateTo({ url:`/pages/groupDetail?id=${id}` }); }, buyNow(){ // 处理直接下单逻辑... } } ``` ##### 创建新的团购活动 对于希望成为团长并创建新团体的顾客来说,需要填写必要的表单信息(如收货地址),之后提交请求至服务器端验证数据合法性,并记录此次交易行为;成功后跳转到支付环节或者等待其他成员参与进来形成完整的团队。 ##### 加入现有团购队伍 如果访客选择了参加正在进行中的某次促销,则只需传递当前用户的个人信息给后台服务进行匹配操作——查找是否有符合条件的小队可供加入。一旦确认可接纳新人加入,则更新数据库状态并将参与者重定向回个人中心查看进度。 ##### 生命周期内的接口调用时机 考虑到用户体验流畅度的要求,在进入首页或其他涉及大量异步加载内容的地方之前应当提前做好预取工作。具体而言就是在 `onLoad` 或者更早阶段发出获取最新资讯列表之类的网络请求[^2]。 ```javascript export default { onLoad() { this.fetchData(); }, methods:{ fetchData(){ uni.request({ url:'https://example.com/api/groups', success:(res)=>{ console.log('data:', res.data); } }) } } }; ``` ##### 解决第三方依赖带来的体积膨胀问题 针对因引入外部库而导致打包后的文件尺寸过大的情况,可以通过 Webpack 插件等方式精简不必要的部分,亦或是采用按需加载策略减少初始下载量。另外还可以借助云服务平台提供的字体压缩工具进一步优化资源占用效率[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程楠楠&M

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值