一文读懂Vue生命周期(Vue2)

一文读懂Vue生命周期(Vue2)

目录

    • 一文读懂Vue生命周期(Vue2)
      • 1 前言
      • 2 Vue生命周期
        • 2.1 基本生命周期
          • 2.1.1 8个生命周期
          • 2.1.2 案例
        • 2.2 组件生命周期
          • 2.2.1 父子生命周期
          • 2.2.2 案例
        • 2.3 keep-alive生命周期
          • 2.3.1 案例
        • 2.4 其他
      • 3 总结

vue生命周期详解,vue更新生命周期,keepalive生命周期,

1 前言

对于一个Vue开发者而言,学习Vue生命周期是必不可少的内容,事实上学习Vue生命周期对于开发高质量的Vue应用至关重要。在初级阶段,需要学习基本组件的生命周期,以了解页面的加载逻辑。而在Vue工程化阶段,就必须要对Vue生命周期有更深的了解。这对以下几个方面来说是基础:

  1. 理解组件行为:通过了解Vue的生命周期,你可以更深入地理解Vue组件是如何创建、更新和销毁的。这有助于你预测组件在不同阶段的行为,并避免潜在的问题。
  2. 优化性能:在生命周期的不同阶段,你可以执行特定的操作来优化应用的性能。例如,在created阶段,你可以进行数据的初始化或发送网络请求,以避免在模板渲染时产生不必要的延迟。在beforeDestroy阶段,你可以清除定时器、解绑全局事件或销毁子组件,以防止内存泄漏。
  3. 更好的状态管理:Vue的生命周期允许你在组件的不同状态下执行特定的逻辑。例如,在updated阶段,你可以检查数据的变化并据此更新DOM或触发其他操作。这有助于你更好地管理组件的状态和响应数据的变化。

2 Vue生命周期

学习Vue生命周期是从简单到复杂的循序渐进过程,以下我们把其拆解成三个部分:

  1. 基本生命周期:单个Vue实例的生命周期;
  2. 组件生命周期:添加子组件时加载的生命周期;
  3. keep-alive生命周期:当使用keep-alive包裹组件时,该组件的生命周期;
2.1 基本生命周期
2.1.1 8个生命周期

Vue2基本生命周期钩子函数有11个,主要的生命周期有8个;主要的8个生命周期如下:

  1. beforeCreate(创建前):在实例初始化之后,数据观测(data observer) 和 event/watcher 事件配置之前被调用。
  2. created(创建后):在这一步,实例已完成以下的配置:数据观测(data observer),属性和方法的运算,以及 watch/event 事件回调。然而,挂载阶段还没开始,$el 属性目前不可见。
  3. beforeMount(挂载前):在挂载开始之前被调用:相关的 render 函数首次被调用。此时模板编译已经完成但是还未挂载到页面上。
  4. mounted(挂载后):el 被新创建的 vm.el替换,并挂载到实例上去之后调用该钩子。如果root实例挂载了一个in−document元素,当mounted被调用时vm.el 也在文档内。此时可以访问到 $el 属性,也可以操作 DOM 和通过 AJAX 获取数据。
  5. beforeUpdate(更新前):数据更新时调用,发生在虚拟DOM打补丁之前。可以在这个钩子中访问现有的DOM,比如手动移除已添加的事件监听器。
  6. updated(更新后):由于数据更改导致的虚拟DOM重新渲染和打补丁,在这之后会调用该钩子。当这个钩子被调用时,组件DOM已经更新,所以你现在可以执行依赖于DOM的操作。然而在大多数情况下,你应该避免在此期间更改状态,因为这可能会导致更新无限循环。
  7. beforeDestroy(销毁前):实例销毁之前调用。在这一步中,实例仍然完全可用。
  8. destroyed(销毁后):Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑,所有的事件监听器都会被移除,所有的子实例也会被销毁。

官网对8个生命周期图解如下:

生命周期

2.1.2 案例

仅仅学习理论是不够的,我们来通过案例来看各个生命周期实际执行顺序和各个生命周期能访问得到的内容:

执行顺序:

直接上最简单的能触发所有生命周期代码:

<template><div><title ref="myPage">生命周期</title><div>参数:{{ parama }}</div><el-button @click="updateParama">更新数据</el-button><el-button @click="leavePage">离开页面</el-button></div>
</template>
<script>
import Vue from "vue";
import { Button } from "element-ui";
Vue.use(Button);export default {beforeCreate() {console.log("页面加载:beforeCreate");},created() {console.log("页面加载:created");},beforeMount() {console.log("页面加载:beforeMount");},mounted() {console.log("页面加载:mounted");},beforeUpdate() {console.log("页面更新:beforeUpdate");},updated() {console.log("页面更新:updated");},beforeDestroy() {console.log("页面销毁:beforeDestroy");},destroyed() {console.log("页面销毁:destroyed");},data() {return {parama: 0};},methods: {leavePage() {this.$router.push({ path: "/home" });},updateParama() {this.parama++;}}
};
</script>

页面效果如下:

在这里插入图片描述

当进入该页面时,触发前四个生命周期,执行顺序如控制台打印:

页面加载:beforeCreate
页面加载:created
页面加载:beforeMount
页面加载:mounted

当点击"更新数据"按钮时,依次触发beforeUpdate,updated;控制台打印:

页面更新:beforeUpdate
页面更新:updated

当点击“离开页面”按钮时,依次触发beforeDestroy,destroyed;控制台打印:

页面销毁:beforeDestroy
页面销毁:destroyed

大家可以复制代码自己试一下;

访问数据

再来看各个生命周期页面数据的访问情况,我们在每个生命周期都打印一下数据,代码如下:

beforeCreate() {console.log("页面加载:beforeCreate");console.log(this.parama);},created() {console.log("页面加载:created");console.log(this.parama);},beforeMount() {console.log("页面加载:beforeMount");console.log(this.parama);},mounted() {console.log("页面加载:mounted");console.log(this.parama);},beforeUpdate() {console.log("页面更新:beforeUpdate");console.log(this.parama);},updated() {console.log("页面更新:updated");console.log(this.parama);},beforeDestroy() {console.log("页面销毁:beforeDestroy");console.log(this.parama);},destroyed() {console.log("页面销毁:destroyed");console.log(this.parama);},

首先是页面加载时,控制台打印如下:

页面加载:beforeCreate
parama undefined页面加载:created
parama 0页面加载:beforeMount
parama 0页面加载:mounted
parama 0

我们可以看到beforeCreate时是访问不到数据的,因为属性还未初始化;其他生命周期都能正常访问到数据。

我们再来看一下数据更新,点击“更新数据按钮”,控制台打印如下:

页面更新:beforeUpdate
parama 1页面更新:updated
parama 1

我们可以看到两个更新的生命周期打印的数据相同,这里要注意的是,数据更新与生命周期触发的顺序是:

parama变成1 -> beforeUpdate -> updated

到这里大家都能明白生命周期顺序与数据访问之间的关系了。

2.2 组件生命周期
2.2.1 父子生命周期

当页面没有引入子组件时,如果没有做过异步处理,页面生命周期将会顺序执行,当引入子组件之后,父组件生命周期+子组件生命周期会交叉执行,具体执行顺序如下:

  • 页面加载:父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted
  • 数据更新:当父子不涉及传参时,父子数据更新单独触发,不会相互影响;当父子涉及传参时:父beforeUpdate ->子beforeUpdate ->子updated->父updated
  • 组件销毁:父beforeDestroy->子beforeDestroy->子destroyed->父destroyed
2.2.2 案例

我们看一下案例的完整代码,将上面的案例添加子组件代码,并将父组件的参数传给子组件,子组件参数传给父组件,页面如下:

在这里插入图片描述

父页面:

<template><div><title ref="myPage">生命周期</title><div>父页面:</div><div>参数:{{ parama }}</div><div>子参数:{{ childData }}</div><el-button @click="updateParama">更新父数据</el-button><el-button @click="leavePage">离开页面</el-button><child :param="parama" @childData="getChildData"></child></div>
</template>
<script>
import Vue from "vue";
import { Button } from "element-ui";
import child from "./conponents/child";
Vue.use(Button);export default {beforeCreate() {console.log("页面加载:beforeCreate");console.log("parama", this.parama);},created() {// await setTimeout({}, 1000);console.log("页面加载:created");console.log("parama", this.parama);},beforeMount() {console.log("页面加载:beforeMount");console.log("parama", this.parama);},mounted() {console.log("页面加载:mounted");console.log("parama", this.parama);},beforeUpdate() {console.log("页面更新:beforeUpdate");console.log("parama", this.parama);},updated() {console.log("页面更新:updated");console.log("parama", this.parama);},beforeDestroy() {console.log("页面销毁:beforeDestroy");console.log("parama", this.parama);},destroyed() {console.log("页面销毁:destroyed");console.log("parama", this.parama);},components: {child},data() {return {parama: 0,childData: 0};},methods: {leavePage() {this.$router.push({ path: "/home" });},updateParama() {this.parama++;},getChildData(a) {this.childData = a;}}
};
</script>

子组件:

<template><div class="child"><title>子组件</title><div>子组件:</div><div>参数:{{ paramaChild }}</div><div>父传参:{{ param }}</div><el-button @click="updateParama">更新子页面数据</el-button></div>
</template>
<script>
import Vue from "vue";
import { Button } from "element-ui";
Vue.use(Button);export default {beforeCreate() {console.log("子页面加载:beforeCreate");console.log("paramaChild", this.paramaChild);},created() {// await setTimeout({}, 1000);console.log("子页面加载:created");console.log("paramaChild", this.paramaChild);},beforeMount() {console.log("子页面加载:beforeMount");console.log("paramaChild", this.paramaChild);},mounted() {console.log("子页面加载:mounted");console.log("paramaChild", this.paramaChild);},beforeUpdate() {console.log("子页面更新:beforeUpdate");console.log("paramaChild", this.paramaChild);},updated() {console.log("子页面更新:updated");console.log("paramaChild", this.paramaChild);},beforeDestroy() {console.log("子页面销毁:beforeDestroy");console.log("paramaChild", this.paramaChild);},destroyed() {console.log("子页面销毁:destroyed");console.log("paramaChild", this.paramaChild);},props: {param: Number},data() {return {paramaChild: 0};},methods: {updateParama() {this.paramaChild++;this.$emit("childData", this.paramaChild);}}
};
</script>
<style>
.child {margin: 20px;border: 1px solid gray;
}
</style>
  • 页面加载

页面加载时,父子生命周期:父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted

控制台日志如下:

页面加载:beforeCreate
parama undefined
页面加载:created
parama 0
页面加载:beforeMount
parama 0
子页面加载:beforeCreate
undefined
子页面加载:created
paramaChild 0
子页面加载:beforeMount
paramaChild 0
子页面加载:mounted
paramaChild 0
页面加载:mounted
parama 0
  • 页面更新

页面更新的情况分两类;

一类是父子组件不涉及传参,这种情况下,父页面参数更新不会触发子组件更新的生命周期,子组件的数据更新也不会触发父页面的生命周期。

第二类是父子组件涉及传参,不管是父传子还是子传父,父子生命周期相同,都为:父beforeUpdate ->子beforeUpdate ->子updated->父updated。

在以上父子相互传参案例下,我们点击父页面按钮:更新父数据;以及点击子组件按钮:更新子页面数据;控制台打印顺序都如下:

页面更新:beforeUpdate
子页面更新:beforeUpdate
子页面更新:updated
页面更新:updated
  • 页面销毁

页面销毁

点击”离开页面“按钮:控制台打印顺序如下:

页面销毁:beforeDestroy
子页面销毁:beforeDestroy
子页面销毁:destroyed
页面销毁:destroyed
2.3 keep-alive生命周期

<KeepAlive> 是一个内置组件,它的功能是在多个组件间动态切换时缓存被移除的组件实例。被缓存的实例普通Vue实例相比,多了activateddeactivated两个生命周期。

  • activated:被 keep-alive 缓存的组件激活时调用。
  • deactivated:被 keep-alive 缓存的组件失活时调用。

光这两个生命周期来讲,就是组件激活和失活的时候调用,但直到这个是不够的,这两个生命周期与其他八个生命周期顺序是什么样子的呢,我们先说结论:(子组件被keep-alive包裹)

  • 页面加载:父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->子activated(当子组件初次加载即可见时)->父mounted
  • 数据更新导致组件失活:父beforeUpdate ->子deactivated->父updated
  • 父页面销毁:父beforeDestroy->子deactivated(如果销毁前子组件是激活状态)->子beforeDestroy->子destroyed->父destroyed

我们结合案例来看实际情况;

2.3.1 案例

我们在案列中添加了一个组件来用于子组件的切换,并在子组件中添加activateddeactivated两个生命周期的监听函数,添加的代码如下:

父页面:

<template><div><title ref="myPage">生命周期</title><div>父页面:</div><div>参数:{{ parama }}</div><div>子参数:{{ childData }}</div><el-button @click="changeActiveCom">更换组件</el-button><el-button @click="leavePage">离开页面</el-button><keep-alive><component :is="activeComponent"></component></keep-alive></div>
</template>
<script>
import Vue from "vue";
import { Button } from "element-ui";
import child from "./conponents/child";
import comA from "./conponents/comA";
Vue.use(Button);export default {beforeCreate() {console.log("页面加载:beforeCreate");console.log("parama", this.parama);},created() {// await setTimeout({}, 1000);console.log("页面加载:created");console.log("parama", this.parama);},beforeMount() {console.log("页面加载:beforeMount");console.log("parama", this.parama);},mounted() {console.log("页面加载:mounted");console.log("parama", this.parama);},beforeUpdate() {console.log("页面更新:beforeUpdate");console.log("parama", this.parama);},updated() {console.log("页面更新:updated");console.log("parama", this.parama);},beforeDestroy() {console.log("页面销毁:beforeDestroy");console.log("parama", this.parama);},destroyed() {console.log("页面销毁:destroyed");console.log("parama", this.parama);},components: {child,comA},data() {return {parama: 0,childData: 0,activeComponent: "child"};},methods: {leavePage() {this.$router.push({ path: "/home" });},updateParama() {this.parama++;},getChildData(a) {this.childData = a;},changeActiveCom() {this.activeComponent = this.activeComponent == "comA" ? "child" : "comA";}}
};
</script>

子组件child:

  activated() {console.log("child组件激活:activated");},deactivated() {console.log("child组件失活:deactivated");},

页面效果:

在这里插入图片描述

点击**更换组件**按钮实现组件切换功能,用于触发activateddeactivated两个生命周期,

  • 页面加载

页面加载时打印结果如下:

页面加载:beforeCreate
页面加载:created
页面加载:beforeMount
子页面加载:beforeCreate
子页面加载:created
子页面加载:beforeMount
子页面加载:mounted
child组件激活:activated
页面加载:mounted
  • 页面更新

点击更换组件按钮其实就进行了data数据更新操作,如果组件进入不可见状态,此时控制台打印日志如下:

页面更新:beforeUpdate
child组件失活:deactivated
页面更新:updated

如果组件进入可见状态,控制台打印如下:

页面更新:beforeUpdate
child组件失活:activated
页面更新:updated
  • 页面销毁

点击离开页面按钮,父页面销毁,此时子组件页销毁,若是此时子组件处于激活状态,那么控制台打印如下:

页面销毁:beforeDestroy
child组件失活:deactivated
子页面销毁:beforeDestroy
子页面销毁:destroyed
页面销毁:destroyed

大家可以进行案例尝试;

2.4 其他

生命周期不是一成不变的,当生命周期执行异步操作时,生命周期的结束时间顺序就可能发生变化;如下我们在父组件created执行异步操作:

  async created() {await setTimeout({}, 1000);console.log("页面加载:created");},

此时控制台打印日志如下

页面加载:beforeCreate
页面加载:beforeMount
页面加载:mounted
页面加载:created

我们可以看到,created是最后执行完的;由此,要注意异步操作的生命周期变化。

若是我们在相应生命周期做了异步操作,并返回相应的值,在后续生命周期可能会出现无法渠道返回值的情况,这也是实际生产开发容易出问题的点。特别是生产情况客户手机网络较慢的情况下。

3 总结

掌握 Vue.js 的生命周期非常重要,因为它允许你在组件的不同阶段执行自定义的代码,从而实现对组件行为的精细控制和管理。但同时也有一些注意事项,以下也是一些注意事项:

  1. 避免在 render 函数中直接修改数据:render 函数中修改数据可能会导致不可预料的结果,因为 render 函数是用来生成虚拟 DOM 的,应该是一个纯函数。如果需要在渲染过程中修改数据,应该使用 computed 属性或者 watch 监听器。
  2. 谨慎使用异步操作: 在生命周期钩子函数中进行异步操作时,一定要注意异步操作完成的时机和影响。尤其是在 created 钩子中,当组件实例已经创建但是 DOM 还未挂载时,执行异步操作可能会导致一些问题。最好的做法是在 mounted 钩子中执行异步操作。
  3. 避免频繁使用 beforeUpdateupdated 虽然 beforeUpdateupdated 钩子提供了在组件更新时执行逻辑的机会,但频繁使用这些钩子可能会导致性能问题。应该仔细考虑哪些逻辑需要在组件更新时执行,以避免不必要的性能开销。
  4. 合理利用 activateddeactivated 钩子: 当使用 <keep-alive> 组件缓存组件时,activateddeactivated 钩子会被调用。在这些钩子中,你可以执行一些与组件缓存和恢复相关的逻辑,比如重置组件状态或重新加载数据。
  5. beforeDestroy 进行清理:beforeDestroy 钩子中,你可以执行一些清理工作,比如清除定时器、取消订阅、解绑事件等。这样可以确保在组件销毁前进行必要的资源释放,避免内存泄漏和其他问题。

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

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

相关文章

安装ESXI 7.0的系统盘小于120G,安装后无本地datastore存储的处理办法

1、应用场景 在全新安装ESXI 7.0后&#xff0c;系统将会划分120G空间作为虚拟闪存&#xff0c;在大容量硬盘的设备中&#xff0c;120G无足轻重&#xff0c;但是当ESXI系统盘容量非常小的时候会导致无可用本地存储空间。 我这里的情况就是服务器里内置了2个120G的硬盘&#xff…

Java找不到包解决方案

在跟着教程写Spingboot后端项目时&#xff0c;为了加快效率&#xff0c;有时候有的实体文件可以直接粘贴到目录中&#xff0c;此时运行项目会出现Java找不到包的情况&#xff0c;即无法找到导入的实体文件&#xff0c;这是项目没有更新的原因。解决方法&#xff1a; 刷新Maven:…

《Python编程从入门到实践》day24

# 昨日知识点学习 创建外星人从一个到一行 # 主程序snipdef _create_fleet(self):"""创建外星人群"""# 创建一个外星人并计算一行可容纳多少个外星人# 外星人的间距为外星人的宽度alien Alien(self)alien_width alien.rect.widthavailable_sp…

大模型常用微调数据集

文章目录 指令微调数据集人类对齐数据集 为了增强模型的任务解决能力&#xff0c;大语言模型在预训练之后需要进行适应性微调&#xff0c;通常涉及两个主要步骤&#xff0c;即指令微调&#xff08;有监督微调&#xff09;和对齐微调。 指令微调数据集 在预训练之后&#xff0c…

动手学深度学习——多层感知机

1. 感知机 感知机本质上是一个二分类问题。给定输入x、权重w、偏置b&#xff0c;感知机输出&#xff1a; 以猫和狗的分类问题为例&#xff0c;它本质上就是找到下面这条黑色的分割线&#xff0c;使得所有的猫和狗都能被正确的分类。 与线性回归和softmax的不同点&#xff1…

Ubuntu/Linux 安装Docker + PyTorch

文章目录 1. 提前准备2. 安装Docker2.1. 卸载冲突软件&#xff08;非必要&#xff09;2.2. 在Ubuntu系统上添加Docker的官方GPG密钥2.3. 将Docker的仓库添加到Ubuntu系统的APT源列表中2.4. 安装最新Docker2.5. 检查 3. 安装Nvidia Container Toolkit3.1. 在Ubuntu系统上添加官方…

求一个B站屏蔽竖屏视频的脚本

求一个B站屏蔽竖屏视频的脚本 现在B站竖屏竖屏越来越多了&#xff0c;手机还好点给我一个按钮&#xff0c;选择不喜欢&#xff0c;但是我一般都用网页版看视屏&#xff0c;网页版不给我选择不喜欢的按钮&#xff0c;目测大概1/4到1/3的视频都是竖屏视频。 目前网页版唯一的进…

MarkText 下载安装和运行

1 官网页面 2 Github 页面 3 选择合适的版本&#xff0c;下载后运行。 附录&#xff1a; 官网&#xff1a; https://www.marktext.cc/ Github 地址&#xff1a; https://github.com/marktext/marktext/releases 目前最新版 v0.17.1&#xff0c;Mar 8, 2022。

二叉树的遍历(前序 中序 后序)

一、前序遍历 顺序为&#xff1a; 根-->左子树---->右子树 先访问根节点&#xff0c;再递归进入根节点的左子树&#xff08;通过递归不断往下遍历&#xff09;&#xff0c;直到访问的节点没有左子树&#xff0c;此时递归进入其右子树&#xff08;通过递归进行相同操作&a…

有限单元法-编程与软件应用(崔济东、沈雪龙)【PDF下载】

专栏导读 作者简介&#xff1a;工学博士&#xff0c;高级工程师&#xff0c;专注于工业软件算法研究本文已收录于专栏&#xff1a;《有限元编程从入门到精通》本专栏旨在提供 1.以案例的形式讲解各类有限元问题的程序实现&#xff0c;并提供所有案例完整源码&#xff1b;2.单元…

在centos7中运行向量数据库PostgreSQL连接不上如何排查?

1. 检查 PostgreSQL 服务状态 首先&#xff0c;您需要确认 PostgreSQL 服务是否正在运行。您可以使用以下命令来检查服务状态&#xff1a; sudo systemctl status postgresql如果服务没有运行&#xff0c;您需要启动它&#xff1a; sudo systemctl start postgresql2. 确认 …

OSPF链路状态数据库

原理概述 OSPF是一种基于链路状态的动态路由协议&#xff0c;每台OSPF路由器都会生成相关的LSA&#xff0c;并将这些LSA通告出去。路由器收到LSA后&#xff0c;会将它们存放在链路状态数据库LSDB中。 LSA有多种不同的类型&#xff0c;不同类型的LSA的功能和作用是不同的&…

【智能优化算法】金豺狼优化算法(Golden jackal optimization,GJO)

金豺狼优化(Golden jackal optimization,GJO)是期刊“Expert Systems with Applications”&#xff08;中科院一区IF 8.3&#xff09;的2022年智能优化算法 01.引言 金豺狼优化(Golden jackal optimization,GJO)旨在为解决实际工程问题提供一种替代的优化方法。GJO的灵感来自金…

【智能优化算法】卷尾猴搜索算法(Capuchin search algorithm,CapSA)

【智能优化算法】卷尾猴搜索算法(Capuchin search algorithm,CapSA)是期刊“NEURAL COMPUTING & APPLICATIONS”&#xff08;IF 6.0&#xff09;的2021年智能优化算法 01.引言 【智能优化算法】卷尾猴搜索算法(Capuchin search algorithm,CapSA)用于解决约束和全局优化问…

VMware Workstation 17 Player 创建虚拟机教程

本教程是以windows server 2012物理机服务器安装好的VMware Workstation 17 Player为例进行演示&#xff0c;安装VMware Workstation 17 Player大家可以自行网上搜索安装。 1、新建虚拟机 双击安装好的VMvare图标&#xff0c;点击创建虚拟机。 2、选择是否安装系统 本步骤选…

【静态分析】软件分析课程实验A4-类层次结构分析与过程间常量传播

官网&#xff1a;作业 4&#xff1a;类层次结构分析与过程间常量传播 | Tai-e 参考&#xff1a;https://www.cnblogs.com/gonghr/p/17984124 ----------------------------------------------------------------------- 1 作业导览 为 Java 实现一个类层次结构分析&#xf…

shiro-quickstart启动报错

说明&#xff1a;最近在学登录框架&#xff0c;记录一下学习刚shiro框架&#xff0c;启动快速入门样例的错误&#xff1b; 场景 把shiro代码download下来&#xff0c;打开samples&#xff08;样例&#xff09;包&#xff0c;打开快速入门&#xff0c;启动&#xff0c;报错&am…

聊天室项目思路

发起群聊&#xff1a; 从好友表选取人发送到服务器&#xff0c;服务器随机生成不重复的群号&#xff0c;存储在数据库&#xff0c;同时建立中间表&#xff0c;处理用户与群聊的关系 申请入群&#xff1a; 输入群号&#xff0c;发消息给服务器&#xff0c;服务器查询是否存在…

06-xss攻防于绕过

xss的攻击于防御 攻击的利用方式 1&#xff09;获取cookie&#xff0c;实现越权&#xff0c;如果是获取到网站管理员的cookie&#xff0c;也可以叫提权。注意尽量尽快退出账号&#xff0c;删除session&#xff0c;让session失效 2&#xff09;钓鱼网站&#xff0c;模拟真实的…

Android 按钮Button点击音效

一、新建工程 编译运行&#xff0c;确保工程无误&#xff0c;这里不过多赘述。 二、UI布局 添加两个播放音效Button <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android"…