从入门到项目实战 - vue2 与 vue3 中实现全局事件总线

news2025/7/8 14:26:06
VUE组件跨通信
vue2 与 vue3 中实现全局事件总线

上一节:《Vue中的 虚拟 Dom | 下一节:《Vue 组件常见的数据访问方式总结

jcLee95
邮箱 :291148484@163.com
CSDN 主页https://blog.csdn.net/qq_28550263?spm=1001.2101.3001.5343
本文地址https://blog.csdn.net/qq_28550263/article/details/127336405

目 录

1. 引言

  • 1.1 总线
  • 1.2 实现任意组件通信
  • 1.2 全局可访问的事件

2. 在 Vue2 中实现全局事件总线

  • 2.1 vue2 中的 Vue 对象
    • 关于 VueComponent 是什么
  • 2.2 Vue 中的组件自定义事件
    • 2.2.1 事件的触发与监听
      • 通过 v-on 绑定事件
      • 通过 ref 绑定事件
      • 自定义事件的监听的方法
    • 2.2.2 事件的解绑

3. 在 Vue3 中实现全局事件总线

  • 3.1 Vue3 API 的相关变化
  • 3.2 Vue3 实现全局事件总线

1. 引言

1.1 总线

总线(Bus)一词源于工业通信网络,原表示计算机各种功能部件之间传送信息的公共通信干线。我们借用总线的概念,希望在 Vue 开发中寻找到一种能够在 Vue 的各个组件之间传送信息的公共通信干线,这就是我们所说的 事件总线

1.2 全局可访问的事件

简而言之,实现 事件总线 的目标是便于 Vue 不同组件之间的通信。为了实现这一目标我们需要找到一个虽然不属于任何组件,但是所有的组件都必须要能够访问。

需要找到这么一个布置总线的地方,在 Web 开发环境中当然最先想到的就是 全局 Window 对象。你可以在该对象上挂载一个对象(这里我们取名为 $bus)到该对象上:

window.$bus = {}

虽然功能上看,Window 对象上确实都可以访问到,但是我们几乎没有人会去修改环境自带的 Window 对象,而通过其它的方式来实现。具体的实现方式在 Vue2 和 Vue3 中有所不同,比如在 Vue2 中我们会通过将这个对象挂载到 Vue 的原型上来实现,而在 Vue3 中却没有暴露一个 Vue 对象来给我们用于创建 Vue 的根组件实例。本文在后面的内容中,将详细讲解 Vue2 和 Vue3 中具体的实现方式。

首先,我们需要在组件A上实现一个回调,并将其绑定一个 自定义事件(event function),挂到 全局事件总线 bus 上。在 B 组件中需要向 A 组件传递数据时,通过访问 全局事件总线 上的该事件函数,将参数传入 组件 A。通过这样的方式,就实现了 跨组件的事件触发和参数传递。

2. 在 Vue2 中实现全局事件总线

2.1 vue2 中的 Vue 和 VueComponent

在 Vue2 中,我们是通过 Vue 对象来创建 Vue 的实例对象的。在脚手架初始化的 Vue 项目中,项目 main.js 的内容大致如下:

// vue2 pack entrance mian.js
import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
}).$mount('#app')

除了根组件是传入 Vue 构造函数时有构造函数建立的,其它的组件是在根组件的基础上,在构建时通过组件的父子使用方式使用其它引入的 vue 文件,由 vue.extend 进行生成的。

在 Vue 中,为了让组件实例对象可以访问到 Vue 原型上的属性和方法,在Vue内部有:

// 见 《关于 VueComponent 是什么》 部分
VueComponent.prototype.__proto__= Vue.prototype

因此我们需要在使用 Vue 构造函数创建实例对象之前在 Vue 构造函数的原型上绑定一个变量,那么之后不论在任意 Vue 组件 实例对象,还是 Vue的实例对象上,都可以对这个变量进行访问。

关于 VueComponent 是什么

Vue2 中,在 Vue 构建时的最后阶段将依次执行initUseinitMixininitExtendinitAssetRegisters这几个过程。
其中 initExtend 相当于递归构建组件树,其代码如下:

// 摘选自 vue 源码
function initExtend(Vue) {
    /**
     * 每个实例构造函数,包括Vue,都有一个唯一的cid。
     * 这使我们能够为原型继承创建包装的“子构造函数”并缓存它们。
     */
    Vue.cid = 0;
    var cid = 1;
    /**
     * Class 继承
     */
    Vue.extend = function (extendOptions) {
        extendOptions = extendOptions || {};
        var Super = this;
        var SuperId = Super.cid;
        var cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {});
        if (cachedCtors[SuperId]) {
            return cachedCtors[SuperId];
        }
        var name = getComponentName(extendOptions) || getComponentName(Super.options);
        if (name) {
            validateComponentName(name);
        }
        
        var Sub = function VueComponent(options) {
            this._init(options);
        };
        // 注意这行代码
        Sub.prototype = Object.create(Super.prototype);
        Sub.prototype.constructor = Sub;
        Sub.cid = cid++;
        Sub.options = mergeOptions(Super.options, extendOptions);
        Sub['super'] = Super;
        // 对于props和computed属性,我们在扩展时,在扩展的原型上,在Vue实例上定义代理getters。
        // 这避免了为每个创建的实例调用Object.defineProperty。
        if (Sub.options.props) {
            initProps(Sub);
        }
        if (Sub.options.computed) {
            initComputed(Sub);
        }
        // 允许进一步使用 extension/mixin/plugin
        Sub.extend = Super.extend;
        Sub.mixin = Super.mixin;
        Sub.use = Super.use;
        // 创建asset注册,这样扩展类也可以拥有它们的私有asset。
        ASSET_TYPES.forEach(function (type) {
            Sub[type] = Super[type];
        });
        // 启用 递归 自查找
        if (name) {
            Sub.options.components[name] = Sub;
        }
        // 在扩展时保留对超类选项的引用。
        // 稍后在实例化时,我们可以检查Super的选项是否已经更新。
        Sub.superOptions = Super.options;
        Sub.extendOptions = extendOptions;
        Sub.sealedOptions = extend({}, Sub.options);
        // cache constructor
        cachedCtors[SuperId] = Sub;
        return Sub;
    };
}

模拟类的继承的实现过程中,其中有一行代码是:

Sub.prototype = Object.create(Super.prototype);

其中这里的 Sub 就是函数 VueComponent

下面的 objCreate 函数模拟 Object.create 的方法具体步骤:

function objCreate(proto, propertiesObject){
  const _obj = {} 
  _obj.__proto__ = proto;
  if(propertiesObject){
    Object.defineProperties(_obj, propertiesObject)
  }
  return _obj 
}

因此上面的过程相当于:

const _obj = {};
_obj.__proto__ = Vue.prototype;
VueComponent.prototype = _obj;  // 相当于VueComponent.prototype.__proto__ = Vue.prototype

因而相当于:

VueComponent.prototype.__proto__ = Vue.prototype

先记住这个结论,后面我们还会用到。

这里多插一句,有人自己无法分析是因为他实现 Object.create 的方法是思路是完全错误的,比如一个典型的错误但似乎可以用的实现:

// Object.create 的错误实现
// 以下未曾实现Object.create ,只是通过 new  关键字间接调用了现有的 Object.create 方法
function objectCreate(obj){
  function F(){}; 
  F.prototype = obj; 
  return new F(); 
}

这个实现中使用了 new 关键字,而实际上,实现 new 需要使用到 Object.create()函数,因此这是一个典型的套娃形式。试想,如果JavaScript 内部 new 关键字调用 Object.create() 函数实现,而实现Object.create() 函数又使用 new,那么Object.create() 的真正实现到底在哪里呢?因此这种实现相当于完全未曾实现,只不过是借用关键字new中调用的Object.create(),仅此而已。

ES6 后很多初学者对于原型继承并不是那么熟悉,因此在讲解上面源码,我们尽量讲解基础一些。

在 JavaScript 面向对象编程中,构造函数的prototype属性 和 实例的 __proto__ 属性都指向了 构造函数的 prototype 对象,即这个 构造函数的原型对象。因此,对于 Vue 和 VueComponent 分别有:
从 Vue 对象看:
在这里插入图片描述
从 VueComponent 对象看:
在这里插入图片描述
其中,一个对象的原型对象(prototype)上的 __proto__ [[Prototype]],它来源于 ECMA-262规范中的定义,但实际在很多浏览器的实现中,为每个对象都提供了一个 __proto__ 的指针,它就相当于 [[Prototype]]
在实例对象上 __proto__ 指针用于 指向发生构造调用的函数(构造函数)的原型对象(即构造函数的prototype 属性)的指针。

而在原型对象(prototype)上的 __proto__ 指针(即构造函数.prototype.__proto__)可以用来模拟类的继承

在 JavaScript 中,查找一个被访问的属性时,先查找对象自身是否存在这个属性,如果没有找到,则继续查找这个构造器的原型对象(prototype)。这个原型对象(prototype)自身又可以访问它构造器上的原型对象,也就是说假设这个 prototype 对象是另外一个构造器构造出来的实例对象,则 prototype 对象视作实例对象时的 __proto__ 属性(prototype.__proto__)指向即它自己构造器的原型。依此类推,就构成了一个链式结构,即所谓原型链。这样,访问一个对象的属性,就可以顺着原型链去查找。我们之前分析的,VueComponent.prototype.__proto__ = Vue.prototype,画在图上就是:

在这里插入图片描述
因此可以得出结论:

组件实例对象(VueComponent instance)可以访问到 Vue 原型(Vue.prototype)上的属性和方法。

往往 VueComponent 对象有很多个,但是 Vue 却始终只有一个,它就是我们在main.js中通过导入的 Vue 构造器函数。因此,我们只要构造Vue实例前,在 这个Vue构造器函数的原型对象即 Vue.prototype 上添加新的属性,可以实现全局访问。

注:
vcvm 简写出均自于 vue2 官方文档。其中 vc 表示某个组件实例对象,即 VueComponent 的实例对象。而 vm 表示某个 Vue 对象实例。之所以叫 vm 是因为文档给出过这样的示例:

var vm = new Vue({
  data: data
})

2.2 Vue 中的组件自定义事件(简要)

2.2.1 事件的触发与监听

通过 v-on 绑定事件

原生事件 的处理类似,我们可以使用 v-on 指令 (简写为 @) 来监听 DOM 事件,并在事件触发时执行对应的 JavaScript。

例如在父子组件中:

<template>
  <MyComponent @myevent="myCallback" />
</template>

<script>
import MyComponent from "./MyComponent.vue"
export default {
  components: [MyComponent],
  methods:{
    myCallback(){
      console.log("myevent 事件的回调 myCallback 被调用了。")
    }
  }
}
</script>

这里的 myevent 是一个定义在子组件 MyComponent 上的自定义事件。这样就要求子组件 MyComponent 上有什么方式来触发属于它的 myevent 事件。

在 Vue 中,为我们提供了 $emit 来触发某个事件,其语法格式如下:

vm.$emit( eventName, […args] )

我们可以依据一定的条件,比如监视某个数据的值到达多少,又比如通过其它事件的回调,对 $emit 方法进行调用。

例如在 MyComponent 组件中定义一个按钮,它点击事件的回调中调用 $emit 来触发组件 MyComponent 的 自定义 myevent 事件:

<template>
  <button @click="trig_myevent" ></button>
</template>

<script>
export default {
  methods:{
    // 触发 myevent 事件
    trig_myevent:(){
      this.$emit("myevent")
    }
  }
}
</script>

注: emit 方法也可以用于触发原生事件,还可以用在模板中。比如:

<button @click="$emit('increaseBy', 1)"></button>

通过 ref 绑定事件

<template>
  <MyComponent ref="xxx" />
</template>

<script>
import MyComponent from "./MyComponent.vue"
export default {
  components: [MyComponent],
  methods:{
    myCallback(){
      console.log("myevent 事件的回调 myCallback 被调用了。")
    }
  },
  mounted() {
      // this.$ref.xxx 是引用当前组件中的 xxx 元素的引用
      // 这里 xxx 即 MyComponent 子组件的 VueComponent 对象。
      // 其 $on 方法用于自定义事件的监听,也就是事件的绑定,实现了和 v-on 一样的效果
      this.$ref.xxx.$on("myevent", this.myCallback)
  }
}
</script>

自定义事件的监听的方法

vm.$on用于监听当前实例上的自定义事件。事件可以由 vm.$emit 方法触发。回调函数会接收所有传入事件触发函数的额外参数。

vm.$on( event, callback )

使用 vm.$once 可以只监听一次,者也同样可以使用 v-on 和 .once 修饰符实现:

vm.$once( event, callback )

2.2.2 事件的解绑

在上一小节,我们介绍过使用 v-on(或其@语法糖)、vm.$onvm.$once绑定(或说监听)一个事件,以及使用vm.$emit来触发一个事件。对于一个绑定的事件也可以对其进行解绑,解绑后的事件无法再对其进行触发。
使用 $off 方法可以对一个自定义事件进行解绑。其语法格式为:

vm.$off( [event, callback] )

比如,我们在上面的子组件中再添加一个用于解绑事件的按钮:

<template>
  <button @click="trig_myevent" ></button>
  <button @click="unbundling_event" ></button>
</template>

<script>
export default {
  methods:{
    trig_myevent:(){
      this.$emit("myevent")
    },
    // 解绑 myevent 事件
    unbundling_event:(){
      this.$off("myevent")
    }
  }
}
</script>

this.$off可以传入一个字符串数组,以实现同时解绑该组件的多个事件。如果不传入参数,默认解绑所有事件。

组件上的自定义事件如果一旦组件(VueComponent)的实例对象销毁,比如手动调用this.$destroy(),则自动失效。

2.2 在 Vue 对象的原型上实现事件总线

现在我们已经做完了两方面的知识铺垫,第一个方面是如何让 vc 和 vm 都可以访问到,而另一个方面是Vue中的事件相关API。

要实现全局事件总线,我们希望在一个全局变量上能够访问$emit$on$off等方法,而这些方法都是存在于 Vue 的原型对象(prototype)上,可知 vc 和 vm上都可以访问这些方法。

我们如果要创建一个组件的实例,需要如下两个步骤:

const MyComponent = Vue.extend({...}) // ...表示配置信息
const vc = new MyComponent()

这样 vc 上就可以访问 $emit$on$off 等方法。

但是如果需要全局访问,就需要绑定在全局变量上。因此在 main.js 中我们这么做:

import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

const MyComponent = Vue.extend({});
const vc = new MyComponent();
Vue.prototype.$bus = vc;

new Vue({
  render: h => h(App),
}).$mount('#app')

这样,从此我们可以通过 this.$bus 在任意组件内访问该 vc 上的 $emit$on$off 方法,如 this.$bus.$emit。但是显然实际上我们不需要实例化一个新的组件来实现,也就是说不仅 可以 利用 vc 上的这些方法,还可以利用 vm 上的这些方法。

只不过以下做法是错误的:

// 错误方式
import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

let vm = new Vue({
  render: h => h(App),
}).$mount('#app')

Vue.prototype.$bus = vm;

因为,一旦 Vue 实例创建,则 Vue.prototype.$bus 不再能够访问到。

为了让其能够在执行 new 时能够同时绑定在Vue.prototype上,需要使用 beforeCreate 声明周期选项:

// 正确方式
import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

let vm = new Vue({
  render: h => h(App),
  beforeCreate() {
    // 安装全局事件总线
    Vue.prototype.$bus = this;
  }
}).$mount('#app')

这里 this 指向的即是当前 vm (Vue实例),这样我们就巧妙地将当前 Vue 直接绑定到实例化它地原型上的 bus 属性上了。

3. 在 Vue3 中实现全局事件总线

## [3.1 Vue3 API 的相关变化](#3-1) vue3 中,不再暴露 Vue 用于创建 vm,而是提供了一个 `createApp`的 API:
import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";

const app = createApp(App);

app.use(store).use(router).mount("#app");

在控制台打印它可发现显然不是之前的 Vue 对象实例:
在这里插入图片描述

createApp(rootComponent: Component<any, any, any, ComputedOptions, MethodOptions>, rootProps?: Data | null | undefined): App<Element>

可知,使用 createApp API 创建的应用实例中有一个 config 属性。应用实例会暴露一个 .config 对象允许我们配置一些应用级的选项,例如定义一个应用级的错误处理器,它将捕获所有由子组件上抛而未被处理的错误:

app.config.errorHandler = (err) => {
  /* 处理错误 */
}

我们可以在控制台上打印一下app.config对象:
在这里插入图片描述

其中我们考到了一个叫 globalProperties 的属性,在Vue3中,他是一个用于注册能够被应用内所有组件实例访问到的全局属性的对象,这是对 Vue 2 中 Vue.prototype 使用方式的一种替代

比如我们若在 mian.js 绑定一个全局变量,只需要:

app.config.globalProperties.msg = 'hello'

这使得属性 msg 在应用的任意组件模板上都可用,并且也可以通过任意组件实例的 this 访问到:

export default {
  mounted() {
    console.log(this.msg) // 'hello'
  }
}

事件这一块,在 Vue3 中,被代理的当前组件实例上事件相关的 API,相对于 Vue2 中的 VueComponent 实例,$on$off$once 实例方法已被移除,只保留了 $emit

3.2 Vue3 实现全局事件总线

这个主要是为了升级旧的项目。
在 Vue 的 API 做出更改后,我们需要 使用第三方库 mitt 为我们提供类似于 Vue2 中的事件API。

npm i mitt

然后配置到全局:

import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";
import mitt from 'mitt';

const app = createApp(App);

app.config.globalProperties.$bus = mitt()

app.use(store).use(router).mount("#app");

这样,在组件中就可以直接使用 this 进行访问,比如:

import { defineComponent } from 'vue';
import HelloWorld from '@/components/HelloWorld.vue'; // @ is an alias to /src

export default defineComponent({
  name: 'HomeView',
  components: {
    HelloWorld,
  }, 
  mounted() {
    console.log("$bus =",(this as any).$bus);
  }
  
});

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/18436.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【物理应用】基于Matlab模拟极化雷达回波

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;修心和技术同步精进&#xff0c;matlab项目合作可私信。 &#x1f34e;个人主页&#xff1a;Matlab科研工作室 &#x1f34a;个人信条&#xff1a;格物致知。 更多Matlab仿真内容点击&#x1f447; 智能优化算法 …

服务器安装Ubuntu20及系统扩容

文章首发及后续更新&#xff1a;https://mwhls.top/4060.html&#xff0c;无图/无目录/格式错误/更多相关请至首发页查看。 新的更新内容请到mwhls.top查看。 欢迎提出任何疑问及批评&#xff0c;非常感谢&#xff01; 参考&#xff1a;Ubuntu20.04安装详细图文教程&#xff08…

C++初阶作业 String类作业详解

作者&#xff1a;小萌新 专栏&#xff1a;C初阶作业 作者简介&#xff1a;大二学生 希望能和大家一起进步&#xff01; 本篇博客简介&#xff1a;会梳理一遍博主做过了String类题目 并且较为详细的讲解出来 _ String类的特性和使用 题目一 考察c_str&#xff08;&#xff09;…

黑塞矩阵理解

文章目录1&#xff1a;一元泰勒展开公式2&#xff1a;二元泰勒展开公式3&#xff1a;二元函数的黑塞矩阵4&#xff1a;多元函数的黑塞矩阵其他链接1&#xff1a;一元泰勒展开公式 举例&#xff1a;f(x) 3x 2x 5 在x0或x1处的泰勒展开 当x0时&#xff1a; 当x1时&#xff…

m基于MATLAB的上行链路MIMO关键技术的研究与性能分析

目录 1.算法概述 2.仿真效果预览 3.MATLAB部分代码预览 4.完整MATLAB程序 1.算法概述 多输入多输出(MIMO)天线技术的巨大潜力为新一代无线通信技术的研究提供了广阔的舞台。近年来&#xff0c;多用户的MIMO研究逐渐成为热点问题。针对多用户的MIMO&#xff0c;天线选择和用…

Allegro如何输出EMN文件操作指导

Allegro如何输出EMN文件操作指导 Allegro支持输出emn文件以供其他结构软件导入以查看实物效果 比如Proe,Soild work等等 下面介绍如何输出EMN文件 选择 File-export-IDF 出现一个对话框 File Name Type选择PTC,并且勾选Use Fillter 点击Fillter, 过滤掉不需要的东西,比…

快速排序、求和、模拟阶乘并利用vscode c++和matlab对程序进行计时

一、软件性能获取 分别使用 Matlab 工具和C语言获得计算下列算法的最大时间&#xff0c;最小时间及平均时间,以及相应的软件功耗&#xff1a; 1.1 一维数组排序 数组选择一万个&#xff0c;使用快速排序。处理器为英特尔i5-7200U&#xff0c;2.5GHz&#xff0c;热设计功耗为…

智慧水务解决方案-最新全套文件

智慧水务解决方案-最新全套文件一、建设背景1、安全水务&#xff0c;监管提效2、清洁水务&#xff0c;防污治污3、节能水务&#xff0c;降本增效4、民生水务&#xff0c;体验先行二、建设思路三、建设方案四、获取 - 智慧水务全套最新解决方案合集一、建设背景 建设智慧水务的…

Dubbo入门使用

Dubbo入门案例Dubbo的特性dubbo支持的注册中心Dubbo入门案例Dubbo的特性 服务治理框架服务的监控服务的注册发现服务的通信服务的容错服务的负载均衡 dubbo支持的注册中心 nacosconsolezookeeperrediseurekaetcd …… Dubbo入门案例 通过两个demo来演示如何使用dubbo进行R…

【Java基础】Java基础题

1. 重载和重写的区别 1&#xff09;重载&#xff1a; 发生在同一个类中&#xff0c;要求方法名相同、参数类型不同&#xff08;个数不同、顺序不同、类型不同&#xff09; 和方法的返回值和访问修饰符没关系&#xff08;在只有返回值和访问修饰符不同的这种情况下&#xff0c…

Linux--进程控制

目录 进程创建 fork函数初识 fork函数返回值 写时拷贝 fork的常规用法 fork调用失败的原因 进程终止 进程退出场景 退出方法 ①调用_exit函数 ②调用exit函数 ③main函数return ④异常退出 进程等待 进程等待的必要性 进程等待的方法 ①wait方法 ②waitpid方…

常用的路径规划算法浅析

路径规划 所谓路径规划&#xff0c;也就是在起点和终点之间找到一条连续的运动轨迹&#xff0c;在尽可能优化路径的同时避开环境中的障碍物。 常用的路径规划算法有传统的基于图搜索算法、基于采样的路径规划算法&#xff0c;以及考虑动力学的路径规划算法等。那么&#xff0…

位于kernel的文件系统大管家--Virtual File System

一、全局视角&#xff1a;VFS 的任务 VFS 作为中间适配层&#xff0c;他需要招呼 3 个大佬&#xff1a;1. 应用进程 &#xff0c;2.系统内核 3.底层磁盘。 二、VFS负责处理 3 个任务的核心接口 1. VFS 提供给 【应用进程 】的接口: 进程的 “办公地点” – struct fs_struc…

【C】—文件版本通讯录的实现

关于C语言的知识放在专栏&#xff1a;C 小菜坤日常上传gitee代码&#xff1a;https://gitee.com/qi-dunyan ❤❤❤ 个人简介&#xff1a;双一流非科班的一名小白&#xff0c;期待与各位大佬一起努力&#xff01; 目录思路代码实现完整代码&#xff08;可自取&#xff09;思路 …

【SQL】索引的创建与设计原则

【SQL】索引的创建与设计原则索引的声明与使用索引的创建删除索引索引设计原则数据准备&#xff1a;哪些情况适合创建索引不适合创建索引的情况索引的声明与使用 索引的创建 # 索引的创建 #法1:create table # 隐式方式创建索引.在声明有主键约束、唯一性约束、外键约束的字段…

java项目-第146期ssm人事工资管理系统(spring+springmvc+mybatis+jsp)_java毕业设计_计算机毕业设计

java项目-第146期ssm人事工资管理系统(springspringmvcmybatisjsp)_java毕业设计_计算机毕业设计 【源码请到资源专栏下载】 今天分享的项目是《ssm人事工资管理系统》 该项目分为2个角色&#xff0c;管理员和用户。 用户可以浏览前台,包含功能有&#xff1a; 个人中心、公告信…

gin binding方法汇总

为了能够更方便的获取请求相关参数&#xff0c;提高开发效率&#xff0c;gin框架提供了Bind系列方法。 分类 Bind系列方法大致分两类&#xff1a; Should类型Must类型 二者的区别就是&#xff1a;should和must的区别&#xff0c;即must绑定失败会报错 关系图 Should类型 …

【licheePi-dock】驱动一个OLED-SSD1306

【licheePi-dock】驱动一个OLED-SSD1306OLED驱动修改设备树向内核添加驱动1.找到一个合适的位置&#xff0c;创建oled驱动文件&#xff1a;修改当前目录下的Kconfig和Makefile把驱动添加进内核编译编写应用程序编译我使用的是cmakeOLED驱动 本文使用的是i2c驱动的OLED【Lichee…

【PowerQuery】在Excel中手动刷新数据连接

将数据通过PowerQuery 导入进来后,这里将进行数据分组运算,最终的数据计算结果将保存在Excel 表格中,图1为数据导入结果。 图1 使用PowerQuery 加载导入的数据 在Excel中,如果我们希望进行销售统计的手动更新可以使用几种不同的方法来进行刷新这些数据。 1.刷新单一数据连…

UJNOJ_1000-1007_python

1000: AB Problem 内存限制&#xff1a;128 MB时间限制&#xff1a;10 S 评测方式&#xff1a;文本比较命题人&#xff1a;jiangyan 提交&#xff1a;109解决&#xff1a;87 提交提交记录统计 题目描述 Calculate ab result 输入 Two integer a,b (0<a,b<10) …