当前位置: 首页 > news >正文

vue2面试题|[2024-12-5]

开题答辩终于结束了,又要开始我的前端面试学习啦!!!

1.v-model双向绑定原理

class Vue{constructor(options){this.$options = optionsthis.$watchEvent = {}if(typeof options.beforeCreate == 'function'){options.beforeCreate.bind(this)()}// 这是datathis.$data = options.datathis.proxyData()this.observe()if(typeof options.created == 'function'){options.beforeCreate.bind(this)()}if(typeof options.beforeMount == 'function'){options.beforeCreate.bind(this)()}// 这是节点this.$el = document.querySelector(options.el)// 模板渲染this.compile(this.$el)if(typeof options.mounted == 'function'){options.beforeCreate.bind(this)()}}// 1.给vue大对象赋属性,来自于data中// 2.data中的属性值和vue大对象的属性双向(劫持)proxyData(){for(let key in this.$data){Object.defineProperty(this,key,{get(){return this.$data[key]},set(val){this.$data[key] = val}})}}// 触发data中的数据发生变化来执行watch中的updateobserve(){for(let key in this.$data){let value = this.$data[key]let that = thisObject.defineProperty(this.$data,key,{get(){return value},set(val){value = valif(that.$watchEvent[key]){that.$watchEvent[key].forEach((item,index) => {item.update()})}}})}}compile(node){node.childNodes.forEach((item.index) => {// 元素节点if(item.nodeType == 1){// 判断元素节点是否绑定了@clickif(item.hasAttribute('@click')){// @click后绑定的属性值let vmKey = item.getAttribute('@click').trim()item.addEventListener('click',(event) => {this.eventFn = this.$options.methods[vmKey].bind(this)this.eventFn(event)})}// 判断元素节点是否添加了v-modelif(item.hasAttribute('v-model')){let vmKey = item.getAttribute('v-model').trim();if(this.hasOwnProperty(vmKey)){item.value = this[vmKey];}item.addEventListener('input',(event) => {this[vmKey] = item.value;})}if(item.childNodes.length>0){this.compile(item)}}// 这里是文本节点,如果有{{}}就替换成数据if(item.nodeType == 3){// 正则匹配let reg = /\{\{(.*?)\}\}/glet text = item.textContent// 给节点赋值item.textContent = text.replacce(reg,(match,vmKey) => {vmKey = vmKey.trim()if(this.hasOwnProperty(vmKey)){let watch = new Watch(this,vmKey,item,'textContent')if(this.$watchEvent[vmKey]){this.$watchEvent[vmKey].push(watch)}else{this.$watchEvent[vmKey] = []this.$watchEvent[vmKey].push(watch)}}return this.$data[vmKey]})}})}
}class Watch{constructor(vm,key,node,attr){// 对象this.vm = vm// 属性名称this.key = key// 节点this.node = node// 改变文本节点内容的字符串this.attr = attr}//执行改变(update)操作update(){this.node[this.attr] = this.vm[this.key]}}

通过Object.defineProperty劫持数据发生的改变,如果数据发生改变了(在set中进行赋值的),触发update方法进行更新节点内容({{ str }}),从而实现了数据的双向绑定原理。

2.diff算法

功能:提升性能

虚拟dom ====>  其实就是数据(把dom数据化)

<script type="text/javascript">let box = document.getElementById("box");// 第一种:操作domconsole.time('a');for(let i = 0; i <= 10000; i++){box.innerHTML = i;}console.timeEnd('a');// 第二种:数据化console.time('b');let num = 0;for(let i = 0; i <= 10000; i++){num = i;}bpx.innerHTMML = num;console.timeEnd('b');
</script>

 对于这两种方法,直接操作dom比较费时,将其数据化可以节省很多时间。

操作dom:73ms,但是数据化:0.28ms

主流:snabbdom、virtual-dom

2.1搭建环境:

npm init -y
cnpm install webpack@5 webpack-cli@3 webpack-dev-server@3 -S
cnpm install snabbdom -S
新建webpack.config.js
配置webpack.config.js

 2.2 虚拟节点和真实节点

虚拟节点:

{children: undefineddata:{}elm:h1key:undefinedsel:"h1"text:"你好h1"
}

        这是对于h1的虚拟节点,其中虚拟节点在表示时用h('h1',{},"你好h1")

        下面就是对于ul的虚拟节点:

{children:[0:{children:undefineddata:{}elm:likey:undefinedsel:"li"text:"a"}1:{...}2:{...}]data:{}elm:ulkey:undefinedsel:"ul"text:undefined}

真实节点:

<h1>你好h1</h1>

2.3 新老节点替换的规则

        1、如果新老节点不是同一个节点名称,那么就暴力删除旧的节点,创建插入新的节点

        2、只能同级比较,不能跨层比较。如果跨层那么就暴力删除旧的节点,创建插入新的节点。

        3、如果是相同节点,又分很多情况

                3.1 新节点没有children

                        如果新的节点没有children,那就证明新的节点是文本,那么直接把旧的替换成新的文本

                 3.2 新节点有children

                        新的有children,旧的也有children ===》就是diff算法的核心了

                        新的有children,旧的没有 ===》创建元素添加(把旧的内容删除清空,增加新的)

***注意:如果要提升性能,一定要加入key,key是唯一标识,在更改前后,确认是不是同一个节点。

const container = document.getElementById("container");
const btn = document.getElementById("btn");const vnode1 = h('h1',{},'你好');
patch(container,vnode1);const vnode2 = h('div',{},'hi');btn.onclick = function(){patch(vnode1,vnode2);
}

添加key,这样就可以提升性能:

const container = document.getElementById("container");
const btn = document.getElementById("btn");const vnode1 = h('ul',{},[h('li',{key:'a'},'a'),h('li',{key:'b'},'b'),h('li',{key:'c'},'c')
]);
patch(container,vnode1);const vnode2 = h('ul',{},[h('li',{key:'c'},'c'),h('li',{key:'b'},'b'),h('li',{key:'a'},'a')
]);btn.onclick = function(){patch(vnode1,vnode2);
}

3.手写diff算法-生成虚拟dom

index.js

import h from './dom/h'
let vnode1 = h('div',{},'你好吖');
console.log(vnode1)-----运行后,应该得到-----
{children:undefineddata:{}elm:undefinedkey:undefinedsel:"div"text:"你好吖"
}-------------------------------
let vnode2 = h('ul',{},[h('li',{},'a'),h('li',{},'b'),h('li',{},'c')
])
console.log(vnode2)
-----运行后,应该得到-----
{children:[0:{children:undefineddata:{}elm:likey:undefinedsel:"li"text:"a"}1:{...}2:{...}]data:{}elm:ulkey:undefinedsel:"ul"text:undefined}-------------------------------

创建h.js

import vnode from './vnode'
export default function(sel, data, params){// h函数的 第三个参数是字符串类型【意味着:他没有子元素】if( typeof params == 'string'){return vnode(undefined, data,undefined, sel, params);}else if(Array.isArray(params)){// h函数的 第三个参数是数组类型【意味着:他有子元素】let children = [];for(let item of params){children.push(item);}return vnode(children, data,undefined, sel, undefined)}
}

创建vnode.js

export default function vnode(children, data, elm, sel, text){return {children, data, elm, sel, text}
}

 4.手写diff算法-patch不是同一个节点

旧的节点为真实的节点,要将其转换为真实的虚拟节点

//index.html<div id="container">这里是container
</div>//index.jsimport h from './dom/h'
import patch from './dom/patch'
// 获取真实的dom节点
let container = document.getElementById('container');// 虚拟节点
let vnode1 = h('h1',{},''你好吖);patch(container,vnode1);
//patch.js
import vnode from './vnode'
export default function(oldVnode, newVnode){// 如果oldVnode 没有sel,就证明是非虚拟节点(就让他变成虚拟节点)if(oldVnode.sel == undefined){oldVnode = vnode([], //children{}, //dataoldVnode, //elmoldVnode.tagName.toLowerCase(), //selundefined // text)}// 判断 旧的虚拟节点 和 新的虚拟节点 是不是同一个节点if(oldVnode.sel === newVnode.sel){// 判断的条件就复杂了(很多了)}else{ // 不是同一个节点,那么就暴力删除旧的节点,创建插入新的节点// 把新的虚拟节点 创建为 dom节点let newVnodeElm = createElement(newVnode);// 获取旧的虚拟节点 .elm就是真正节点let oldVnodeElm = oldVnode.elm;// 创建新的节点if(newVnodeElm){oldVnodeElm.parentNode.insertBefore(newVnodeElm,oldVnodeElm);}// 删除旧节点oldVnodeElm.parentNode.removeChild(oldVnode);}}
// createElement.js
// vnode 为新节点,就是要创建的节点
export default function createElement(vnode){// 创建dom节点let domNode = document.createElement(vnode.sel);// 判断有没有子节点 children 是不是为undefinedif(vnode.children == undefined){domNode.innerText = vnode.text;} else if(Array.isArray(vnode.children)){// 说明内部有子节点,需要递归创建节点for(let child of vnode.children){let childDom = createElement(child);domNode.appendChild(childDom);}}// 补充elm属性vnode.elm = domNode;return domNode;
}

 5.手写diff算法-相同节点有没有children

//patch.js
import vnode from './vnode'
export default function(oldVnode, newVnode){// 如果oldVnode 没有sel,就证明是非虚拟节点(就让他变成虚拟节点)if(oldVnode.sel == undefined){oldVnode = vnode([], //children{}, //dataoldVnode, //elmoldVnode.tagName.toLowerCase(), //selundefined // text)}// 判断 旧的虚拟节点 和 新的虚拟节点 是不是同一个节点if(oldVnode.sel === newVnode.sel){// 判断的条件就复杂了(很多了)patchVnode(oldVnode,newVnode);}else{ // 不是同一个节点,那么就暴力删除旧的节点,创建插入新的节点// 把新的虚拟节点 创建为 dom节点let newVnodeElm = createElement(newVnode);// 获取旧的虚拟节点 .elm就是真正节点let oldVnodeElm = oldVnode.elm;// 创建新的节点if(newVnodeElm){oldVnodeElm.parentNode.insertBefore(newVnodeElm,oldVnodeElm);}// 删除旧节点oldVnodeElm.parentNode.removeChild(oldVnode);}}
//patchVnode.js
import createElement from './createElement'
export default function patchVnode(oldVnode,newVnode){// 判断新节点有没有childrenif(newVnode.children === undefined){// 没有子节点// 新的节点的文本 和 旧节点的文本内容是不是一样的if(newVnode.text !== oldVnode.text){oldVnode.elm.innerText = newVnode.text}}else{// 新的有子节点// 新的虚拟节点有, 旧的虚拟节点有if(oldVnode.children !== undefined && oldVnode.children.length > 0){// 最复杂的情况了,diff核心了 console.log('新旧节点都有children');}else{ // 新的虚拟节点有,旧的虚拟节点“没有”// 把旧节点的内容 清空oldVnode.elm.innerHTML = '';// 遍历新的 子节点,创建dom元素,添加到页面for( let child of newVnode.children){let childDom = createElement(child);oldVnode.elm.appendChild(childDom);}}}
}

6.diff算法核心-理论部分

每次都从1开始,不满足就依次往下执行

1.旧前 和新前

        匹配:旧前的指针++、新前的指针++

2.旧后 和新后

        匹配:旧后的指针--、新后的指针--

3.旧前 和 新后

        匹配:旧前的指针++、新后的指针--

4.旧后 和 新前

        匹配:旧后的指针--、新前的指针++

5.以上都不满足条件 ===》 查找

        新的指针++,新的添加到页面上并且新在旧的节点中有,要给旧的复制成undefined

6.创建或删除

        旧的指针指向空,新还有,新就要创建

        旧的指针不指向空,新的指针指向空,旧就要删除

注意:若对于旧的指针加或者减指向的是undefined,直接继续加或者减

 7.手写diff算法-判断前四种情况

// index.js
// 获取到了真实的dom节点
const container = document.getElementById("container");
// 获取到了按钮
const btn = document.getElementById("btn");// 虚拟节点
const vnode1 = h('ul',{},[h('li',{key:'a'},'a'),h('li',{key:'b'},'b'),h('li',{key:'c'},'c')
]);
patch(container,vnode1);const vnode2 = h('ul',{},[h('li',{key:'c'},'c'),h('li',{key:'b'},'b'),h('li',{key:'a'},'a')
]);btn.onclick = function(){patch(vnode1,vnode2);
}
// vnode.js
export default function vnode(children, data, elm, sel, text){let key = data.key;return {children, data, elm, key,sel, text}
}
//patchVnode.js
import createElement from './createElement'
import updateChildren from './updateChildren'
export default function patchVnode(oldVnode,newVnode){// 判断新节点有没有childrenif(newVnode.children === undefined){// 没有子节点// 新的节点的文本 和 旧节点的文本内容是不是一样的if(newVnode.text !== oldVnode.text){oldVnode.elm.innerText = newVnode.text}}else{// 新的有子节点// 新的虚拟节点有, 旧的虚拟节点有if(oldVnode.children !== undefined && oldVnode.children.length > 0){// 最复杂的情况了,diff核心了 console.log('新旧节点都有children');updateChildren()}else{ // 新的虚拟节点有,旧的虚拟节点“没有”// 把旧节点的内容 清空oldVnode.elm.innerHTML = '';// 遍历新的 子节点,创建dom元素,添加到页面for( let child of newVnode.children){let childDom = createElement(child);oldVnode.elm.appendChild(childDom);}}}
}
// updateChildren.js
import patchVnode from './patchVnode'
// 判断两个虚拟节点是否为同一个节点
function sameVnode(vnode1,vnode2){return vnode1.key == vnode2.key;
}
// 参数一:真实的dom节点
// 参数二:旧的虚拟节点
// 参数三:新的虚拟节点
export default (parentElm, oldCh, newCh) => {let oldStartIdx = 0;            //旧前的指针let oldEndIdx = oldCh.length-1; //旧后的指针let newStartIdx = 0;            //新前的指针let newEndIdx = newCh.length-1; //新后的指针let oldStartVnode = oldCh[oldStartIdx]; //旧前虚拟节点let oldEndVnode = oldCh[oldEndIdx];     //旧后虚拟节点let newStartVnode = newCh[newStartIdx]; //新前虚拟节点let newEndVnode = newCh[newEndIdx];     //新后虚拟节点while( oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx){if( sameVnode(oldStartVnode,newStartVnode)){// 第一种情况:旧前 和 新前console.log("1")patchVnode(oldStartVnode,newStartVnode);if(newStartVnode) newStartVnode.elm = oldStartVnode?.elm;oldStartVnode = oldCh[++oldStartIdx];newStartVnode = newCh[++newStartIdx];}else if(sameVnode(oldEndVnode,newEndVnode)){// 第二种情况:旧后 和 新后console.log("2")patchVnode(oldEndVnode,newEndVnode);if(newEndVnode) newEndVnode.elm = oldEndVnode?.elm;oldEndVnode= oldCh[--oldEndIdx];newEndVnode= newCh[--newEndIdx];}else if(sameVnode(oldStartVnode,newEndVnode)){// 第三种情况:旧前 和 新后console.log("3")patchVnode(oldStartVnode,newEndVnode);if(newEndVnode) newEndVnode.elm = oldStartVnode?.elm;// 把旧前指定的节点移动到旧后指向的节点的后面parentElm.insertBefore(oldStartVnode.elm,oldEndVnode.elm.nextSibling);oldStartVnode= oldCh[++oldStartIdx];newEndVnode= newCh[--newEndIdx];}else if(sameVnode(oldEndVnode,newStartVnode)){// 第四中情况:旧后 和 新前console.log("4")patchVnode(oldEndVnode,newStartVnode);if(newStartVnode) newStartVnode.elm = newEndVnode?.elm;// 把旧后指定的节点移动到旧前指向的节点的前面parentElm.insertBefore(oldEndVnode.elm,oldStartVnode.elm);oldEndVnode= oldCh[--oldEndIdx];newStartVnode= newCh[++newStartIdx];}else{// 第五种情况:以上都不满足条件 ===》 查找}}
}

8.手写diff算法-判断第五种情况

 

// updateChildren.js
import patchVnode from './patchVnode'
import createElement from './createElement'
// 判断两个虚拟节点是否为同一个节点
function sameVnode(vnode1,vnode2){return vnode1.key == vnode2.key;
}
// 参数一:真实的dom节点
// 参数二:旧的虚拟节点
// 参数三:新的虚拟节点
export default (parentElm, oldCh, newCh) => {let oldStartIdx = 0;            //旧前的指针let oldEndIdx = oldCh.length-1; //旧后的指针let newStartIdx = 0;            //新前的指针let newEndIdx = newCh.length-1; //新后的指针let oldStartVnode = oldCh[oldStartIdx]; //旧前虚拟节点let oldEndVnode = oldCh[oldEndIdx];     //旧后虚拟节点let newStartVnode = newCh[newStartIdx]; //新前虚拟节点let newEndVnode = newCh[newEndIdx];     //新后虚拟节点while( oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx){if( oldStartVnode == undefined){oldStartVnode = oldCh[++oldStartIdx];}if( oldEndVnode == undefined){oldEndVnode = oldCh[--oldEndIdx];}else if( sameVnode(oldStartVnode,newStartVnode)){// 第一种情况:旧前 和 新前console.log("1")patchVnode(oldStartVnode,newStartVnode);if(newStartVnode) newStartVnode.elm = oldStartVnode?.elm;oldStartVnode = oldCh[++oldStartIdx];newStartVnode = newCh[++newStartIdx];}else if(sameVnode(oldEndVnode,newEndVnode)){// 第二种情况:旧后 和 新后console.log("2")patchVnode(oldEndVnode,newEndVnode);if(newEndVnode) newEndVnode.elm = oldEndVnode?.elm;oldEndVnode= oldCh[--oldEndIdx];newEndVnode= newCh[--newEndIdx];}else if(sameVnode(oldStartVnode,newEndVnode)){// 第三种情况:旧前 和 新后console.log("3")patchVnode(oldStartVnode,newEndVnode);if(newEndVnode) newEndVnode.elm = oldStartVnode?.elm;// 把旧前指定的节点移动到旧后指向的节点的后面parentElm.insertBefore(oldStartVnode.elm,oldEndVnode.elm.nextSibling);oldStartVnode= oldCh[++oldStartIdx];newEndVnode= newCh[--newEndIdx];}else if(sameVnode(oldEndVnode,newStartVnode)){// 第四中情况:旧后 和 新前console.log("4")patchVnode(oldEndVnode,newStartVnode);if(newStartVnode) newStartVnode.elm = newEndVnode?.elm;// 把旧后指定的节点移动到旧前指向的节点的前面parentElm.insertBefore(oldEndVnode.elm,oldStartVnode.elm);oldEndVnode= oldCh[--oldEndIdx];newStartVnode= newCh[++newStartIdx];}else{// 第五种情况:以上都不满足条件 ===》 查找console.log('5');// 创建一个对象,存虚拟节点的(判断新旧有没有相同节点)const keyMap = {};for(let i = oldStartIdx;i<=oldEndIdx;i++){const key = oldCh[i]?.key;if( key ) keyMap[key] = i;}// 在旧节点中寻找新前指向的节点let idxInOld = keyMap[newStartVnode.key];// 如果有,说明数据在新旧虚拟节点中都存在if(idxInOld){const elmMove = oldCh[idxInOld];patchVnode(elMove,newStartVnode);// 处理过的节点,在旧虚拟节点的数组中,设置为undefinedoldCh[idxInOld] = undefined;parentElm.insertBefore(elMove.elm,oldStartVnode.elm);}else{// 如果没有找到 ==》 说明是一个新的节点【创建】parentElm.insertBefore( createElement(newStartVnode),oldStartVnode.elm);}// 新数据(指针) +1newStartVnode = newCh[++newStartIdx];}}// 结束while 只有两种情况 (新增和删除)// 1.oldStartIdx > oldEndIdx// 2.newStartIdx > newEndIdxif(oldStartIdx > oldEndIdx){// 进入新增操作const before = newCh[newEndIdx+1] ? newCh[newEndIdx+1].elm : null;for( let i=newStartIdx; i<=newEndIdx;i++){parentElm.insertBefore(createElement(newCh[i],before));} } else {// 进入删除操作for(let i=oldStartIdx;i<=oldEndIdx;i++){parentElm.removeChild(oldCh[i].elm);}}
}

9.谈一下MVVM框架

 web1.0时代

        文件全在一起,也就是前端和后端的代码全在一起

        问题:

                1.前端和后端都是一个人开发。(技术没有侧重点或者责任不够细分)

                2.项目不好维护

                3.html、css、js页面的静态内容没有,后端是没有办法工作的(没办法套数据)

web2.0时代

        ajax出现了,就可以:前端和后端数据分离了

        解决问题:后端不用等前端页面弄完没,后端做后端的事情(写接口),前端布局、特效、发送请求

        问题:

                1.html、css、js都在一个页面中,单个页面可能内容也比较多的(也会出现不好维护的情况)

出现前端框架MVC、MVVM

        解决问题:可以把一个“特别大”页面,进行拆分(组件化),单个组件进行维护

什么是MVVM

        Model-View-ViewModel的简写

        

view:视图【dom ==》 在页面中展示的内容】

model:模型【数据层:vue中的data数据】

viewModel:视图模型层【就是vue源码】

学了快一周的源码了,煎熬 

相关文章:

vue2面试题|[2024-12-5]

开题答辩终于结束了&#xff0c;又要开始我的前端面试学习啦&#xff01;&#xff01;&#xff01; 1.v-model双向绑定原理 class Vue{constructor(options){this.$options optionsthis.$watchEvent {}if(typeof options.beforeCreate function){options.beforeCreate.bind…...

共筑数字安全防线,2024开源和软件安全沙龙即将启幕

随着数字化转型进程的加快以及开源代码的广泛应用&#xff0c;开源凭借平等、开放、协作、共享的优秀创作模式&#xff0c;逐渐成为推动数字技术创新、加速传统行业转型升级的重要模式。但随着软件供应链日趋复杂多元&#xff0c;使得其安全风险不断加剧&#xff0c;针对软件供…...

目标跟踪领域经典论文解析

亲爱的小伙伴们&#x1f618;&#xff0c;在求知的漫漫旅途中&#xff0c;若你对深度学习的奥秘、JAVA 、PYTHON与SAP 的奇妙世界&#xff0c;亦或是读研论文的撰写攻略有所探寻&#x1f9d0;&#xff0c;那不妨给我一个小小的关注吧&#x1f970;。我会精心筹备&#xff0c;在…...

SQL DQL数据查询语言(后续)

SQL DQL数据查询语言&#xff08;后续&#xff09; 1.子查询 在查询语句中的WHERE条件子句中&#xff0c;又嵌套了另外一个查询语句在返回列中嵌套一个查询 where条件中嵌套 要求&#xff1a;查询课程为《高等数学-2》且分数不小于80分的学生的学号和姓名select a.StudentNo,a…...

Gitee配置SSH公钥

采用SSH协议同步Git仓库代码的好处就是高效。在配置好SSH公钥后&#xff0c;不需要每次操作都要输入用户名和密码&#xff08;主要针对命令行来说&#xff09;。 以我个人项目为例。 生成 SSH 公钥 1. 通过命令 ssh-keygen 生成 SSH Key&#xff1a; ssh-keygen -t ed25519…...

机器学习——感知机模型

文章目录 前言1.感知机模型介绍1.1基本概念1.2数学表达1.3几何解释1.4优缺点 2.二分类应用2.1应用介绍2.2准备数据集2.2.1环境检查2.2.2数据集介绍2.2.3获取数据2.2.4划分数据集 2.3可视化训练集2.4训练过程2.4.1首轮梯度下降2.4.2多轮梯度下降 2.5可视化分类结果2.6在验证集验…...

如何选择安全、可验证的技术?

澳大利亚信号局的澳大利亚网络安全中心 (ASD 的 ACSC) 发布了一份指导文件&#xff0c;题为《选择安全和可验证的技术》&#xff0c;旨在帮助组织在采购软件&#xff08;专有或开源&#xff09;、硬件&#xff08;例如物联网设备&#xff09;和云服务&#xff08;SaaS、MSP 服务…...

STL库中list的使用与迭代器的实现

STL库中list的使用与迭代器的实现 1.使用list中的部分函数assignspliceremoveuniquemeger 2.list的部分功能实现&#xff08;重点&#xff09;框架迭代器的实现 1.使用list中的部分函数 assign 功能一&#xff1a;当前链表的节点全部销毁&#xff0c;替换成迭代区间的值 功能二…...

android 常用三方框架

说实话&#xff0c; 我是比较讨厌三方框架的&#xff0c; 比如一个eventbus 底层逻辑就是个观察者模式&#xff0c;当然他的场景涵盖的比较丰富&#xff0c; 单从 单一原则来说&#xff0c; 还是一个简单的观察者模式就能解决问题&#xff0c; 何必要添加那么多文件到我们的项目…...

Browser.js断点续传上传

通过断点续传上传的方式将文件上传到OSS前&#xff0c;您可以指定断点记录点。上传过程中&#xff0c;如果出现网络异常或程序崩溃导致文件上传失败时&#xff0c;将从断点记录处继续上传未上传完成的部分。 attention&#xff1a; 1、 当您使用webpack或browserify等打包工具…...

Java项目实战II基于微信小程序的无中介租房系统(开发文档+数据库+源码)

目录 一、前言 二、技术介绍 三、系统实现 四、核心代码 五、源码获取 全栈码农以及毕业设计实战开发&#xff0c;CSDN平台Java领域新星创作者&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导。 一、前言 随着城市化进程的加速&#xff0c;租房市场日益繁荣&a…...

了解Cocoa Touch框架与主要组件

Cocoa Touch框架详解及其主要组件 一、Cocoa Touch框架概述 Cocoa Touch框架是苹果公司为iOS应用程序开发提供的一套完整的框架&#xff0c;它基于Cocoa框架&#xff0c;并专为触控设备如iPhone、iPad等设计。这套框架不仅包含了构建图形用户界面&#xff08;GUI&#xff09;…...

ISO45001职业健康安全管理体系涵盖了丰富的内容

范围与术语 适用范围&#xff1a;明确规定了该标准适用于任何有愿望建立、实施和保持职业健康安全管理体系的组织&#xff0c;旨在使组织能够通过管理体系的有效运行&#xff0c;预防和控制职业健康安全风险&#xff0c;持续改进职业健康安全绩效。术语定义&#xff1a;对职业…...

Spring Boot 整合 Druid 并开启监控

文章目录 1. 引言2. 添加依赖3. 配置数据源4. 开启监控功能5. 自定义 Druid 配置&#xff08;可选&#xff09;6. 访问监控页面7. 注意事项8. 总结 Druid 是一个由阿里巴巴开源的高性能数据库连接池&#xff0c;它不仅提供了高效的连接管理功能&#xff0c;还自带了强大的监控和…...

【JAVA高级篇教学】第一篇:Springboot对接通义千问大模型

博主今天打算讲解下Java如何对接阿里云的通义千问大模型&#xff0c;可以自己玩玩ai问答之类的&#xff01; 目录 一、发展历程 二、API-KEY的获取与配置 三、引用SDK 四、文本模型 1.代码 2.返回数据 3.官方代码案例 五、通义千问VL 1.计量计费 六、查看API-KEY调用额…...

【Windows 同时安装 MySQL5 和 MySQL8 - 详细图文教程】

卸载 MySQL 参考文章&#xff1a; 完美解决Mysql彻底删除并重装_怎么找到mysql并卸载-CSDN博客使用命令卸载mysql_卸载mysql服务命令-CSDN博客 先管理员方式打开 cmd &#xff0c;切换到 MySQL 安装目录的 bin 文件夹下&#xff0c;执行如下命令&#xff0c;删除 MySQL 服务 my…...

Next.js 系统性教学:深入理解缓存与数据优化策略

更多有关Next.js教程&#xff0c;请查阅&#xff1a; 【目录】Next.js 独立开发系列教程-CSDN博客 目录 前言 1. 缓存的基本概念 1.1 缓存的作用 1.2 Next.js 中的缓存策略 2. Next.js 的缓存机制 2.1 请求记忆化&#xff08;Request Memoization&#xff09; 2.1.1 什…...

JAVA数据结构

1.数组 (Array): 固定大小的容器,用于存储相同类型的元素,数组在内存中是连续存储的,支持通过索引快 速访问元素。 int[] numbers = new int[10]; numbers[0] = 1;2.Java Collections Framework (JCF) JCF提供了一组接口和类用于管理和操作集合(如列表,集合,…...

力扣第96题 不同的二叉搜索树

力扣第96题 - 不同的二叉搜索树 题目描述 给定一个整数 n&#xff0c;求以 1 到 n 为节点组成的所有 不同的二叉搜索树&#xff08;BST&#xff09; 的个数。 题目分析 二叉搜索树的性质 对于一个二叉搜索树&#xff0c;以 i 为根节点&#xff1a; 左子树的节点值来自 [1, i…...

在Ubuntu上使用IntelliJ IDEA:开启你的Java开发之旅!

你好&#xff0c;年轻的学徒&#xff01;&#x1f9d1;‍&#x1f4bb; 是时候踏上进入Java开发世界的史诗之旅了&#xff0c;我们的得力助手将是强大的IntelliJ IDEA。准备好了吗&#xff1f;出发吧&#xff01; 在我们开始之前&#xff0c;我们需要下载这个工具。但是&#…...

【C语言】18. 自定义类型:结构体类型

文章目录 前言&#xff1a;一、结构体类型的声明1、结构体回顾1&#xff09;结构的声明2&#xff09;结构体变量的创建和初始化 2、结构的特殊声明3、结构的⾃引⽤ 二、结构体变量的创建和初始化1、对⻬规则2、为什么存在内存对⻬?3、修改默认对⻬数 三、结构成员访问操作符1、…...

智能租赁管理系统助力规范化住房租赁市场提升用户体验

内容概要 在当今的住房租赁市场中&#xff0c;智能租赁管理系统应运而生&#xff0c;为房东和租客带来了前所未有的便利。这套系统就像一位全能助手&#xff0c;将租赁信息、监管机制以及在线签约功能集成在一起&#xff0c;让整个过程变得流畅而高效。换句话说&#xff0c;您…...

ERROR: KeeperErrorCode = NoNode for /hbase/master

原因分析 通过上面的情景模拟&#xff0c;我们可以看到报错的原因在于zookeeper中出现问题&#xff0c;可能是zookeeper中的/hbase/master被删除&#xff0c;或者是在hbase集群启动之后重新安装了zookeeper&#xff0c;导致zookeeper中的/hbase/master节点数据异常。 1. 停止…...

springboot第84集:Java进阶之路, Netty

# kafka-map文件夹 cd /usr/local/kafka-map # 根据需求自行修改配置 vi application.yml # 启动 java -jar kafka-map.jar byte minByte -128; byte maxByte 127; 用于表示一个 8 位&#xff08;1 字节&#xff09;有符号整数。它的值范围是 -128&#xff08;-2^7&#xff0…...

DevOps持续集成

DevOps流程 第一步安装git 关闭防火墙 systemctl stop firewalld cd /usr/loacl vim docker-compose.yml docker search gitlab 拉取gitlab镜像 2.33GB docker pull gitlab/gitlab-ce:latestvim docker-compose.yml修改docker-compose.yml version: 3.1 services:gitlab:i…...

sql server log文件

确定 SQL Server 实例中具有大量 VDF 的数据库 SELECT [name], COUNT(l.database_id) AS vlf_count FROM sys.databases AS s CROSS APPLY sys.dm_db_log_info(s.database_id) AS l GROUP BY [name] HAVING COUNT(l.database_id) > 100; 在收缩日志文件之前确定事务日志中…...

pip install报错 Missing dependencies for SOCKS support的正确解决办法:离线安装pysocks

今天准备开发python项目的时候&#xff0c;发现在pip install 的时候报错了&#xff0c;提示&#xff1a;Missing dependencies for SOCKS support&#xff0c;查遍csdn所有的回答都统一是只需要执行&#xff1a; unset all_proxy unset ALL_PROXY 然后再执行 pip install p…...

嵌入式学习(15)-stm32通用GPIO模拟串口发送数据

一、概述 在项目开发中可能会遇到串口不够用的情况这时候可以用通过GPIO来模拟串口的通信方式。 二、协议格式 按照1位起始位8位数据位1位停止位的方式去编写发送端的程序。起始位拉低一个波特率的时间&#xff1b;发送8位数据&#xff1b;拉高一个波特率的时间。 三、代码 …...

AKE 安全模型:CK, CK+, eCK

参考文献&#xff1a; [BCK98] Mihir Bellare, Ran Canetti, Hugo Krawczyk. A Modular Approach to the Design and Analysis of Authentication and Key Exchange Protocols (Extended Abstract). STOC 1998: 419-428.[CK01] Ran Canetti, Hugo Krawczyk. Analysis of Key-E…...

【Linux】通过crond服务设置定时执行shell脚本,实际执行时间却延迟了8小时

一、问题描述 通过使用crond服务设置定时任务&#xff0c;在每天凌晨的2:00执行脚本&#xff0c;但检查结果时发现&#xff0c;实际执行时间却在上午10点。 检查shell脚本执行结果发现&#xff0c;实际执行脚本时间在上午10:00&#xff0c;延迟了8小时。 检查系统时间&#xf…...

什么是云原生数据库 PolarDB?

云原生数据库 PolarDB 是阿里云推出的一款高性能、兼容性强、弹性灵活的关系型数据库产品。它基于云原生架构设计&#xff0c;结合分布式存储和计算分离的技术优势&#xff0c;为用户提供强大的计算能力、卓越的可靠性以及高性价比的数据库解决方案。PolarDB 适合各种业务场景&…...

(6)JS-Clipper2之ClipperOffset

1. 描述 ClipperOffset类封装了对打开路径和关闭路径进行偏移(膨胀/收缩)的过程。 这个类取代了现在已弃用的OffsetPaths函数&#xff0c;该函数不太灵活。可以使用不同的偏移量(增量)多次调用Execute方法&#xff0c;而不必重新分配路径。现在可以在一次操作中对开放和封闭路…...

基于51单片机64位病床呼叫系统设计( proteus仿真+程序+设计报告+原理图+讲解视频)

基于51单片机病床呼叫系统设计( proteus仿真程序设计报告原理图讲解视频&#xff09; 仿真图proteus7.8及以上 程序编译器&#xff1a;keil 4/keil 5 编程语言&#xff1a;C语言 设计编号&#xff1a;S0095 1. 主要功能&#xff1a; 基于51单片机的病床呼叫系统proteus仿…...

工业智能网关如何为企业实现智能制造赋能?

在数字化转型的浪潮中&#xff0c;工业智能网关作为连接物理世界与数字世界的桥梁&#xff0c;正逐步成为智能制造领域的核心组件。本文将通过一个实际使用案例&#xff0c;深入剖析工业智能网关如何助力企业实现生产流程的优化、数据的高效采集与分析&#xff0c;以及智能化决…...

【Spring项目】表白墙,留言板项目的实现

阿华代码&#xff0c;不是逆风&#xff0c;就是我疯 你们的点赞收藏是我前进最大的动力&#xff01;&#xff01; 希望本文内容能够帮助到你&#xff01;&#xff01; 目录 一&#xff1a;项目实现准备 1&#xff1a;需求 2&#xff1a;准备工作 &#xff08;1&#xff09;…...

Java-WebSocket

文章目录 WebSocket概念SpringBoot实现一个WebSocket示例STOMP消息订阅和发布后端主动发送消息 跨域 WebSocket概念 应用层协议&#xff0c;底层采用TCP&#xff0c;特点&#xff1a;持续连接&#xff0c;有状态&#xff0c;双向通信 当客户端想要与服务器建立WebSocket连接时…...

C#请求https提示未能为 SSL/TLS 安全通道建立信任关系

System.Net.WebException: 基础连接已经关闭: 未能为 SSL/TLS 安全通道建立信任关系 &#xff0c;这个错误通常表明你的应用程序在尝试建立一个安全的 SSL/TLS 连接时遇到了问题。这通常是由于证书验证失败引起的。证书验证失败可能有几个原因&#xff1a; 证书不受信任&#…...

pdf转word/markdown等格式——MinerU的部署:2024最新的智能数据提取工具

一、简介 MinerU是开源、高质量的数据提取工具&#xff0c;支持多源数据、深度挖掘、自定义规则、快速提取等。含数据采集、处理、存储模块及用户界面&#xff0c;适用于学术、商业、金融、法律等多领域&#xff0c;提高数据获取效率。一站式、开源、高质量的数据提取工具&…...

人工智能与机器学习:真实案例分析及其在各行业的应用前景

目录 引言 人工智能与机器学习的基础概念 人工智能的历史与演变 机器学习的算法分类 深度学习与传统机器学习的区别 行业应用案例分析 医疗健康 疾病预测与诊断 影像识别的运用 案例&#xff1a;IBM Watson在肿瘤治疗中的应用 金融服务 风险评估与欺诈检测 投资预测…...

再谈多重签名与 MPC

目录 什么是 MPC 钱包以及它们是如何出现的 多重签名和智能合约钱包已经成熟 超越 MPC 钱包 关于小队 多重签名已经成为加密货币领域的一部分&#xff0c;但近年来&#xff0c;随着 MPC&#xff08;多方计算&#xff09;钱包的出现&#xff0c;多重签名似乎被掩盖了。MPC 钱包之…...

(长期更新)《零基础入门 ArcGIS(ArcMap) 》实验二----网络分析(超超超详细!!!)

相信实验一大家已经完成了&#xff0c;对Arcgis已进一步熟悉了&#xff0c;现在开启第二个实验 ArcMap实验--网络分析 目录 ArcMap实验--网络分析 1.1 网络分析介绍 1.2 实验内容及目的 1.2.1 实验内容 1.2.2 实验目的 2.2 实验方案 2.3 实验流程 2.3.1 实验准备 2.3.2 空间校正…...

Python、R循环神经网络RNN、指数平滑ETS、ARIMA模型预测网络流量、ATM机取款、旅游需求时间序列数据...

全文链接&#xff1a;https://tecdat.cn/?p38496 分析师&#xff1a;Pengyuan Wen 在当今经济研究与商业决策领域&#xff0c;精准的时间序列预测具有极为关键的意义。社会消费品零售总额作为反映人民消费水平以及国民经济状况的核心指标&#xff0c;其发展趋势的精准把握对中…...

通过PS和Unity制作2D动画之二:IK的使用

一、IK的概念 IK&#xff1a;Inverse Kinematics&#xff0c;反向动力学。 &#xff08;1&#xff09;正向动力学 在骨骼动画中&#xff0c;构建骨骼的方法被称为正向动力学。它的表现形式是&#xff1a;子骨骼&#xff08;关节&#xff09;的位置根据父骨骼&#xff08;关节…...

如何防御ARP欺骗 保护IP安全

在数字化浪潮席卷全球的今天&#xff0c;网络安全威胁如同暗流涌动&#xff0c;时刻考验着我们的防范能力。其中&#xff0c;ARP欺骗攻击作为一种隐蔽性强、成本低廉且危害严重的网络攻击手段&#xff0c;成为众多网络安全事件中的一颗“毒瘤”。那么我们究竟是如何防御ARP欺骗…...

浏览器插件上架流程

上回说到&#xff0c;用 AI 开发上架了个浏览器插件。有朋友想知道上架扩展有需要哪些步骤。这里做个整理&#xff0c;这篇文章结构和步骤都是 AI 整理的。里面的文案细节、推荐的工具是我自己总结&#xff0c;以前发布扩展时直接用的。有需要的话下面的文案可以直接拷贝使用。…...

Android13 USB 串口默认授权

一&#xff09;场景需求 项目中使用到了can协议控制车机功能&#xff0c;can 直接转串口&#xff0c;用USB转串口工具 cn.wch.uartlib 来实现. can 板子通过usb 插入到机器上面&#xff0c;那就是usb 协议了。 遇到问题&#xff1a; 插入can 板的usb 到机器上后&#xff0c;直…...

word实践:正文/标题/表图等的共用模板样式设置

说在前面 最近使用word新建文件很多&#xff0c;发现要给大毛病&#xff0c;每次新建一个word文件&#xff0c;标题/正文的字体、大小和间距都要重新设置一遍&#xff0c;而且每次设置这些样式都忘记了参数&#xff0c;今天记录一下&#xff0c;以便后续方便查看使用。现在就以…...

docker安装kettle(PDI)并实现web访问

我是MAC电脑M1版本&#xff0c;希望把软件交给docker进行管理&#xff0c;最近公司同事都通过kettle来实现外部数据对接&#xff0c;所以我本地也有安装kettle需求&#xff0c;在网上找到了这个解决方案操作很简单&#xff0c;但出现了无法访问的情况。我的排查方式是&#xff…...

vue自定义弹窗点击除了自己区域外关闭弹窗

这里使用到vue的自定义指令 <div class"item" v-clickoutside"clickoutside1"><div click"opencity" class"text":style"{ color: popup.iscitypop || okcitylist.length ! 0 ? #FF9500 : #000000 }">选择地区…...

【vue2】封装自定义的日历组件(三)之基础添加月份的加减定位到最新月份的第一天

我们在切换月份的时候&#xff0c;希望高亮显示在每个月的第一天上面&#xff0c;这样的效果我们要怎么来实现&#xff0c;其实也很简单&#xff0c;我们先看下实现的效果 实现效果 代码实现 原理就是获取到每月的第一天日期&#xff0c;然后再跟整个的数据进行对比&#xff…...