Vue深入了解

news2024/10/23 21:25:05

Vue深入了解

  • MVVM
  • v-model (双向数据绑定原理)
  • 异步更新
  • keep-alive原理
  • $nextTick原理
  • computed 和 watch 的区别
  • css-scoped
  • 虚拟DOM
  • Vuex && Pinia
  • Vue-router原理
  • proxy 与 Object.defineProperty
  • 组件通信方式

MVVM

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<title>mini MVVM</title>
	</head>
	<body>
		<div id="app">
			<p>姓名: <span>{{ name }}</span></p>
			<p>年龄: <span>{{ age }}</span></p>
		</div>
		<script>
			window.onload = function() {
				const vue = new Vue({
					el: '#app',
					data: {
						name: '加载中...',
						age: '加载中...'
					}
				})
				setTimeout(() => {
					vue.$data.name = '小明'
					vue.$data.age = 20
				}, 2000)
			}
			
			class Dep {
				constructor() {
					this.watchList = []
				}
				add(node) {
					this.watchList.push(node)
				}
				update(newValue) {
					this.watch.forEach((node) => {
						node.textContent = value
					})
				}
			}
			class Vue {
				constructor(options) {
					this.options = options
					this.$data = options.data
					this.$el = document.querySelector(options.el)
					this.obsever(this.$data)
					this.compile(this.$el) 
				}
				/*
					[observe 函数]:
					  利用Object.defineProperty把data中的属性变成响应式的,同时给每一个属性添加一个dep对象(用来存储对应的watcher观察者)
					  首先我们会对需要响应式的 data 对象进行 for 循环遍历,为 data 的每一个 key 映射一个观察者对象
					  在 ES6 中,for 循环每次执行,都可以形成闭包,因此这个观察者对象就存放在闭包中
				*/
				observer(data) {
					Object.keys(data).forEach((key) => {
						// 给data中的每一个属性添加一个dep对象(该对象用来存储对应的watcher观察者)
						const dep = new Dep()
						// 利用闭包 获取和设置属性的时候,操作的都是value
						let value = data[key]
						Object.defineProperty(data, key, {
							get() {
								// 观察者对象添加对应的dom节点
								Dep.target && dep.add(Dep.target)
								return value
							},
							set(newValue) {
								// 属性值变化时,更新观察者中所有节点
								value = newValue
								dep.update(value)
							}
						})
					})
				}
				/*
					[compile 函数]:
						  我们从根节点向下遍历 DOM,遇到 mustache 形式的文本,则映射成 data.key 对应的值,同时记录到观察者中
						  当遍历到 {{xxx}} 形式的文本,我们正则匹配出其中的变量,将它替换成 data 中的值
						  当data的数据变化时,调用dep对象的update方法,更新所有观察者中的dom节点
				*/
				compile(dom) {
					const mustache = /\{\{(.*)\}\}/
					Array.from(dom.childNodes).forEach((child) => {
						// nodeType 为3时为文本节点,并且该节点的内容包含`mustache`(双大括号{{}})
						if(child.nodeType === 3 && mustache.test(child.textContent)) {
							const key = mustache.exec(child.textContent)[1].trim()
							const keyNoTrim = mustache.exec(child.textContent)[1]
							// 将该节点添加到对应的观察者对象中,在下面的的this.$data[key]中触发对应的get方法
							Dep.target = child
							let value = this.$data[key]
							child.textContent = child.textContent.replace(`{{${keyNoTrim}}}`, value)
							Dep.target = null
						}
						// 递归遍历子节点
						if(child.childNodes.length) {
							this.compile(child)
						}
					})
				}
			}
		</script>
	</body>
</html>

v-model (双向数据绑定原理)

采取数据劫持,通过Object.defineProperty()劫持各个属性,给各个属性添加getter和setter,数据变动时触发相应的回调
Observer:给数据加上getter和setter,改变数据时触发setter
Complie:模板解析,将模板中的变量替换成数据,绑定更新函数
Watcher:订阅者,是Observer和Complie之间通信的桥梁,往订阅器中添加自己,有一个update方法,当属性变动通知时,调用update方法,触发complie中绑定的更新函数

在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<title>v-model</title>
	</head>
	<body>
		<div id="app">
			<div>年龄: <span>{{ info.person.name }}</span></div>
			<p>{{ job }}</p>
			<input v-model="job" placeholder="请输入工作" type="text" />
		</div>
	</body>
	<script>
		window.onload = function () {
			const vue = new Vue({
				el: '#app',
				data: {
					info: {
						person: {
							name: '加载中',
						},
					},
					job: '程序猿',
				},
			})
			setTimeout(() => {
				vue.info.person.name = '小明'
			}, 2000)
		}
		class Dep {
			constructor() {
				this.watchList = []
			}
			add(node){
				this.watchList.push(node)
			}
			update(value) {
				this.watchList.forEach((node) => {
					if(node.tagName === 'INPUT' || node.tagName === 'TEXTAREA') {
						node.value = value 
					} else {
						node.textContent = value
					}
				})
			}
		}
		class Vue {
			constructor(options){
				this.options = options
				this.$data = options.data
				this.$el = document.querySelector(options.el)
				this.observer(this.$data)
				this.compile(this.$el, this)
				this.proxy(this.$data, this)
			}
			observer(data) {
				if(data && typeof data === 'object') {
					const _this = this
					Object.keys(data).forEach((key) => {
						const dep = new Dep()
						let value = data[key]
						// 数据劫持,对data增加了递归和设置新值的劫持,让data中每一层数据都是响应式的
						_this.observer(data[key])
						Object.defineProperty(data, key, {
							get(){ 
								Dep.target && dep.add(Dep.target)
								return value
							}
							set(newValue) {
								value = newValue
								// 数据劫持,对data增加了递归和设置新值的劫持,让data中每一层数据都是响应式的
								_this.observer(newValue)
								dep.upadte(value)
							}
						})
					})
				}
			}
			compile(dom, vm) {
				const mustache = /\{\{(.*)\}\}/
				Array.from(dom.childNodes).forEach((child) => {
					if(child.nodeType === 1) {
						Array.from(child.attributes).forEach((attr) => {
							if(attr.name.includes('v-model')) {
								Dep.target = child
								child.value = vm.$data[attr.value]
								Dep.target = null
								//给input元素绑定input事件,当输入值变化会触发对应属性的dep.update方法,通知对应的观察者发生变化
								child.addEventLister('input', (e) => {
									vm.$data[attr.value] = e.target.value
								})
							}
						})
					}
					if(child.nodeType === 3 && mustache.test(child.textContent)) {
						const key = mustache.exec(child.textContent)[1].trim()
						const keyNoTrim = mustache.exec(child.textContent)[1]
						const keyList = key.split('.')
						Dep.target = child
						let value = vm.$data
						ketList.forEach((item) => value = value[item])
						child.textContent = child.textContent.replace(`{{${keyNoTrim}}}`, value)
						Dep.target = null
					}
					if(child.childNodes.length) {
						this.compile(child, vm)
					}
				})
			}
			// 增加了数据代理,通过this.info.person.name就可以直接修 $data对应的值,实现了this对this.$data的代理
			proxy(data, vm) {
				Object.keys(data).forEach((key) => {
					Object.defineProperty(vm, key, {
						get() {
							return data[key]
						},
						set(newValue) {
							data[key] = newValue
						}
					})
				})
			}
		}
	</script>
</html>

异步更新

 Vue数据更新频繁,但dom只会更新一次,为什么?
  1、Vue更新dom是异步更新,当Vue的数据更新后,不会立即更新dom
  2、侦听到数据变化,Vue会开启一个队列, 并缓存在同一事件循环中发生的所有数据变更
  3、同一个watcher被多次出发,只会被推入队列中一次,避免重复修改相同的dom
  4、同步任务执行完,执行异步watcher队列任务,一次性更新dom

keep-alive原理

缓存策略时LRU,组件切换时,保存一些组件的状态,防止多次渲染

三大属性:include、exclude、max

- 根据include/exclude配置的组件名,与对应组件的name进行条件匹配
- 根据组件ID和tag生成缓存的key,在缓存对象中查找是否已经缓存,存在取出并更新
- 检查是都超过了max设置的值,超过的话,根据LRU缓存策略,删除最近最久没有使用的组件
- 将KeepAlive属性更改为true,actived和deactivated两个钩子函数会用到

$nextTick原理

本质是对JavaScript执行原理EventLoop的一种应用
核心是模拟对应的微/宏任务的实现,利用JavaScript的异步回调任务队列来实现Vue框架自己的异步回调队列

Vue.$nextTick 为什么优先使用微任务实现:
  根据 event loop 与浏览器更新渲染时机,宏任务 → 微任务 → 渲染更新,使用微任务,本次event loop轮询就可以获取到更新的dom
  如果使用宏任务,要到下一次event loop中,才能获取到更新的dom

computed 和 watch 的区别

  computed关键点:
    computed属性用于创建派生数据,这些数据是基于响应式依赖自动计算的。
    它们提供了缓存机制,只有当依赖项变化时,计算属性才会重新计算。
    computed适合于声明性地描述数据如何从其他数据派生,常用于视图渲染优化
  watch关键点:
    watch用于侦听响应式数据的变化,并在变化发生时执行定义的逻辑。
    它不具备缓存机制,每次数据变化都会触发回调函数。
    watch适合于执行复杂的业务逻辑,如异步请求、DOM操作,或者在数据变化时执行条件性响应。
  
  computed是声明式的,用于计算并缓存视图所需的数据,它根据响应式数据的变化自动重新计算并提供缓存。只有当其依赖的响应式数据变化时,才会重新执行计算。
  computed在开始时自动建立依赖关系,默认第一次加载的时候就开始监听

  watch是命令式的,用于监听响应式数据的变化,每次变化都会触发执行预定义的回调函数。
  watch默认在开始时不执行监听,除非设置immediate: true,这允许在数据变化时立即执行回调

  computed原理:
	1、初始化计算属性时,遍历计算computed对象,给每一个计算属性分别生成一个computed watcher, 并将watcher的dirty设置为true,初始化时不会立即计算,只有在获取计算的值时才会进行计算
	2、初始化时将Dep.target设置成当前的computer watcher,将computed watcher 添加到所依赖的data值对应的dep中,然后计算computed对应的值,然后将dirty改为false
	3、当所依赖的data中的值发生变化时,调用set方法触发dep 的notify方法,将watcher中dirty设置为true
	4、下次获取计算属性的值时,如果dirty为true,重新计算值
    5、dirty是控制缓存的关键,当依赖的data发生变化时,dirty设置为true,再次获取值时,就会重新计算值
 
  watch原理:
    1、遍历watch对象, 给其中每一个watch属性,生成对应的user watcher
    2、调用watcher中的get方法,将Dep.target设置成当前的user watcher,并将user watcher添加到监听data值对应的dep中(依赖收集的过程)
    3、当所监听data中的值发生变化时,会调用set方法触发dep的notify方法,执行watcher中定义的方法
    4、设置成deep:true的情况,递归遍历所监听的对象,将user watcher添加到对象中每一层key值的dep对象中,
     这样无论当对象的中哪一层发生变化,wacher都能监听到。通过对象的递归遍历,实现了深度监听功能

css-scoped

  原理:
    编译时,给每一个Vue文件生成一个唯一的id,将此id添加到当前文件的所有html标签上
      如:<div class="demo"></div>会被编译成<div class="demo" data-v-27e4e96e></div>
    编译style标签时,将css选择器改造成为属性选择器
      如:.demo{color: red;}会被编译成.demo[data-v-27e4e96e]{color: red;}

虚拟DOM

 什么是虚拟Dom
  使用JS对象模拟真实DOM节点,但是对比真实DOM更加轻量级
  1、前端性能的优化,尽量减少真实DOM的操作,频繁的操作DOM会导致浏览器的回流会重绘
  2、使用虚拟DOM,当数据变化,页面需要更新的时候,通过diff算法,对新旧的虚拟dom节点进行对比,比较两棵树的差异生成差异对象,一次性对DOM进行批量操作
  3、虚拟DOM本质上是JS对象,使用虚拟DOM可以进行更方便的跨平台操作
	// 真实 转 虚拟
	function dom2Json(dom) {
		if (!dom.tagName) return
		let obj = {}
		obj.tag = dom.tagName
		obj.props = {}
		Array.from(dom.attributes).forEach((attr) => {
			obj.props[attr.name] = attr.value
		})
		obj.children = []
		dom.childNodes.forEach((item) => {
			// 去除空的节点
			dom2Json(item) && obj.children.push(dom2Json(item))
		})
		return obj
	}

	class Element {
		constructor(type, props, children) {
			this.type = type
			this.props = props
			this.children = children
		}
	}

	// 虚拟 转 真实
	function render(domObj) {
		let el = document.querySelector(domObj.type)
		Object.keys(domObj.props).forEach((key) => {
			let value = domObj.props[key]
			switch (key) {
				case 'value':
					if (el.tagName === 'INPUT' || el.tagName === 'TEXTAREA') {
						el.value = value
					} else {
						el.setAttribute(key, value)
					}
					break
				case 'style':
					el.style.cssText = value
					break
				default:
					el.setAttribute(key, value)
			}
		})
		domObj.childeren.forEach((child) => {
			child =
				child instanceof Element
					? render(child)
					: document.createTextNode(child)
		})
		return el
	}

Vuex && Pinia

Vuex 与 Pinia 的区别
  语法和结构:Vuex 的语法相对较为复杂,而 Pinia 的语法更加简洁和直观。
  模块系统:Vuex 支持模块系统,可以将状态拆分成多个模块进行管理,而 Pinia 也提供了类似的功能,但更加灵活和易于使用。
  类型支持:Pinia 提供了更好的类型支持,可以在代码中获得更好的类型推断和提示。
  开发体验:Pinia 在开发体验上更加友好,提供了更多的辅助函数和工具,使开发更加高效。
VueX的原理
	1、store本质就是一个没有template的组件
    2、利用mixin机制在beforeCreate钩子前混入VuexInit方法
    3、VuexInit方法实现将store 注册到当前组件的$store中
    4、state 相当于组件内的data,定义在state上的变量相当于定义在组件的data中的变量,都是响应式的
    5、当页面中使用了state中的数据,就是依赖收集的过程,
    6、当state中的数据发生变化,就通过调用对应属性的dep对象的notify方法,去修改视图变化

在这里插入图片描述

Vue-router原理

   1、创建的页面路由会与该页面形成一个路由表(key-value模式,key为路由,value为页面)
   2、通过监听浏览器地址栏URL的变化,匹配路由表,将对应路由的页面替换旧页面,达到无需刷新的效果
   3、目前单页面使用的路由有两种实现方式: hash 模式、history 模式
   4、hash模式(路由中带#号),通过hashchange事件来监听路由的变化
        window.addEventListener('hashchange', ())=>{})
   5、history 模式,利用了pushState() 和replaceState() 方法,实现往history中添加新的浏览记录、或替换对应的浏览记录
        通过popstate事件来监听路由的变化,window.addEventListener('popstate', ())=>{})

proxy 与 Object.defineProperty

1)初始化性能优化:
	Vue 2 在初始化响应式数据时,会递归遍历对象的所有属性并使用 Object.defineProperty为每个属性添加 getter 和 setter。这样的初始化过程会产生大量的 getter 和 setter,对于大规模的对象或数据,初始化时间会较长。
    Vue 3 中,使用 Proxy 对象进行拦截,初始化性能得到了显著提升,因为 Proxy 是在整个对象级别上进行拦截,无需遍历每个属性。
2)深层属性监听优化:
	Vue 2 中,对于深层嵌套的属性,需要通过递归方式为每个属性添加响应式处理,这在大型对象上可能会导致性能下降。
    Vue 3 中,Proxy 可以递归地拦截整个对象的操作,无需为每个属性单独处理,从而提高了深层属性监听的性能。
3)删除属性性能优化:
	Vue 2 中,当删除一个属性时,需要通过 Vue.$delete 或者 Vue.delete 方法来触发更新。这是因为 Vue 2 使用的 Object.defineProperty 无法拦截属性的删除操作。
    Vue 3 中,使用 Proxy 可以直接拦截属性的删除操作,从而简化了删除属性的处理逻辑,并提高了性能。
4)动态添加属性性能优化:
	Vue 2 中,动态添加新属性需要通过 Vue.set 方法来触发更新,否则新添加的属性将不会是响应式的。
    Vue 3 中,Proxy 可以直接拦截动态添加属性的操作,并将其设置为响应式属性,无需额外的处理方法,提高了性能和代码的简洁性。

组件通信方式

  • 通信的种类

    • 父组件向子组件通信
    • 子组件向父组件通信
    • 隔代组件间通信
    • 兄弟组件间通信
  • 实现通信的方式

    • props
    • vue自定义事件
    • 消息订阅与发布
    • vuex
    • slot
    • 依赖注入
  • 方式一:props

    • 通过一般属性实现父向子通信
    • 通过函数属性实现子向父通信
    • 缺点:隔代组件和兄弟组件间通信比较麻烦
  • 方式二:vue自定义组件

    • vue内置实现,可以代替函数类型的props
      a.绑定监听:<MyComp @eventName=“callback”>

      b.触发(分发)事件: this.$emit(“eventName” , data)

    • 适用于子组件与父组件通信居多,可以利用事件总线,进行兄弟组件间的通信,类似于vuex

  • 方式三:消息订阅发布

    • 需要引入消息订阅与发布的实现库,如: pubsub-js
      a.订阅消息:PubSub.subscribe(‘msg’, (msg,data)=>{})

      b.发布消息: PubSub.publish(‘msg’, data)

    • 优点:此方式可实现任意关系组件间通信

  • 方式四:vuex

    • 是什么:vuex是vue官方提供的集中式管理vue多组件共享状态数据的vue插件
    • 优点:对组件间关系没有限制,且相比于pubsub库管理更集中,更方便
  • 方式五:slot

    • 是什么:专门用来实现父向子传递带数据的标签

      a.子组件

      b.父组件

    • 注意:通信的标签模板是在父组件中解析好后再传递给子组件的

  • 方式六:依赖注入

    • provide、inject
    provide() { 
        return {     
            num: this.num  
        };
    }
    inject: ['num']
    

    注意: 依赖注入所提供的属性是非响应式的。

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

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

相关文章

声波驱鸟 全向强声广播的应用

HT-360A多层叠形360向广播是恒星科通自主研发的一款应急广播专用设备&#xff0c;该设备内部采用1-4组换能器垂直阵列设置&#xff0c;水平采用指数函数碟形堆叠技术&#xff0c;在垂直方向上多层碟扬声器可实现360度环形垂直阵列&#xff0c;实现多层声场叠加。 系统可采用4G…

Linux驱动中的并发与竞争处理

Linux是一个多任务操作系统&#xff0c;肯定会存在多个任务共同操作同一段内存或者设备的情况&#xff0c;多个任务甚至中断都能访问的资源叫做共享资源&#xff0c;就和共享单车一样。在驱动开发中要注意对共享资源的保护&#xff0c;也就是要处理对共享资源的并发访问。比如共…

智慧校园打架斗殴检测预警系统 异常奔跑检测系统 Python 和 OpenCV 实现简单

在当今数字化时代&#xff0c;智慧校园建设已成为教育领域的重要发展方向。校园安全作为学校管理的重中之重&#xff0c;如何借助先进的技术手段实现高效、精准的安全监控&#xff0c;成为了教育工作者和技术专家共同关注的焦点。其中&#xff0c;智慧校园打架斗殴检测预警系统…

案例|当大型车企水灵灵地用上美创数据透明加解密“四折叠“...

你知道么&#xff1f; 2024年1-8月全球累计乘用车销量4494.5万辆 中国销量约为1361.37万辆 你知道么&#xff1f; 一辆智能网联汽车每天至少收集10TB的数据 数量庞大 涉及车身状态、出行轨迹、语音、视频... 这还不包括汽车制造产业链、内部管理... 你知道么&#xff1f;…

JDK安装环境配置保姆间教程

文章介绍了Java编程语言的基本知识&#xff0c;包括其创始人和发布年份&#xff0c;然后详细阐述了如何下载和安装JDK&#xff0c;以及如何配置JAVA_HOME和Path环境变量&#xff0c;以确保Java开发环境的正确设置。最后&#xff0c;作者提到在JDK1.5以后的版本中&#xff0c;无…

linux jdk环境变量变量新配置方式

1.jdk17--> jdk8环境变量配置,source /etc/profile了也不生效 which java #假设上命令运行结果为/usr/bin/java rm -rf /usr/bin/javaln -s $JAVA_HOME/bin/java /usr/bin/java source /etc/profile# 断开本次远程连接&#xff0c;重连检查java -version 2.jdk环境变量变…

手动、半自动、全自动探针台的区别有哪些

手动探针台、半自动探针台和全自动探针台是三种不同类型的探针台&#xff0c;它们在使用类型、功能、操作方式和价格等方面都有所不同。 手动探针台是一种手动控制的探针台&#xff0c;通常用于没有很多待测器件需要测量或数据需要收集的情况下。该类探针台的优点是灵活、可变…

EXCEL 随机函数 RAND 生成数字填充

大家好&#xff0c;这里是效率办公指南&#xff01; &#x1f522; 在数据分析和模拟中&#xff0c;经常需要生成随机数字。Excel 提供了多个函数来满足这一需求&#xff0c;包括 RAND、RANDARRAY 和 RANDBETWEEN。今天&#xff0c;我们将介绍这些函数的使用方法&#xff0c;并…

FFMPEG音视频API-----------------复用(编码)篇

1.获取AVFormatContext音视频格式上下文 根据后面的几个参数分配AVFormatContext&#xff08;格式上下文&#xff09; 参数1&#xff1a;AVFormatContext **ctx分配返回的地址 参数2&#xff1a;AVOutputFormat *oformat如果提供该参数则通过该参数分配结构体&#xff0c;如…

利用Microsoft Entra Application Proxy在无公网IP条件下安全访问内网计算机

在现代混合办公环境中&#xff0c;如何让员工能够从任何地方安全访问公司内部资源成为了企业的重要挑战。传统的VPN解决方案虽然可以满足需求&#xff0c;但有时配置复杂&#xff0c;并可能涉及公网IP的问题。为了解决这个问题&#xff0c;Microsoft Entra&#xff08;原Azure …

WordPress 禁用上传媒体图片自动生成缩略图及多尺寸图片教程

一、在 设置-媒体-媒体设置 中几个尺寸大小的设置不勾选或设置为 0&#xff0c;如下图&#xff1a; 二、找到主题文件 function.php 文件&#xff0c;打开后&#xff0c;在 <?php 后面添加如下代码&#xff1a; function.php 文件路径一般为&#xff1a;WordPress网站根目录…

使用Matlab实现光线追迹详解(含代码)

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

ExtraTree|GBDT|XGBoost模型原理

目录 1. 总述2. ExtraTree3. GBDT4. XGBoost 1. 总述 ExtraTree、GBDT 和 XGBoost 都是基于决策树的算法。ExtraTree属于Bagging&#xff08;装袋法&#xff09;方法&#xff0c;GBDT和XGBoost则属于Boosting&#xff08;提升树&#xff09;方法&#xff0c;通过逐步优化残差&…

U盘剪切文件丢失:原因、恢复方案与预防措施

一、U盘剪切文件的定义与特性 U盘剪切文件&#xff0c;是指用户在将文件从U盘移动到其他存储设备&#xff08;如电脑硬盘、其他U盘等&#xff09;时&#xff0c;通过操作系统的剪切功能&#xff0c;将文件从U盘中原位置删除&#xff0c;并暂存于系统剪贴板中&#xff0c;等待用…

双回路防静电监控仪安全保护生产全流程

在现代工业生产中&#xff0c;静电防护成为了确保安全生产的重要环节&#xff0c;尤其是在电子、化学等易燃易爆气体环境中。静电的存在不仅可能导致设备故障&#xff0c;还可能引发火灾或爆炸等严重事故。为了解决这一隐患&#xff0c;双回路防静电监控仪应运而生&#xff0c;…

java面试-每日随机(1014)

1、zk如何进行故障转移&#xff1f; 在 Zookeeper 集群中&#xff0c;当节点故障时&#xff0c;集群需要自动剔除故障节点并进行故障恢复&#xff0c;确保集群的高可用性和一致性。具体来说&#xff0c;当跟随者节点故障时&#xff0c;集群可以继续运行&#xff0c;但当领导节…

关于Keil Compiler Version 6 移植FreeRTOS-Kernel 出现 portmacro.h 错误 - __forceinline

问题现象 在移植FreeRTOS过程中&#xff0c;使用Keil ARM 编译器版本 6 编译代码时出现protmacro.h错误- __forceinline 相关信息&#xff1a; FreeRTOS版本&#xff1a;V11.1.0KEIL 编译器版本&#xff1a;Compiler Version 6Portable路径&#xff1a;RVDS/ARM_CM4F/ 问题解…

P327. 渔夫捕鱼算法问题

问题描述&#xff1a; A、B、C、D、E 这5个人合伙夜间捕鱼&#xff0c;凌晨时都已经疲惫不堪&#xff0c;于是各自在河边的树丛中找地方睡着了。第二天日上三竿时&#xff0c;A第一个醒来&#xff0c;他将鱼平分为5份&#xff0c;把多余的一条扔回河中&#xff0c;然后拿着自己…

雷池社区版如何使用静态资源的方式建立站点

介绍&#xff1a; SafeLine&#xff0c;中文名 “雷池”&#xff0c;是一款简单好用, 效果突出的 Web 应用防火墙(WAF)&#xff0c;可以保护 Web 服务不受黑客攻击。 雷池通过过滤和监控 Web 应用与互联网之间的 HTTP 流量来保护 Web 服务。可以保护 Web 服务免受 SQL 注入、X…