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

10 Vue 特性要点

Vue2 特性要点


Vue2 源码理解

Vue 双向数据绑定

先从单向绑定切入单向绑定非常简单,就是把Mode1绑定到view,当我们用Javascript代码更新Model时, view就会自动更新
双向绑定就很容易联想到了,在单向绑定的基础上,用户更新了View, Mode1的数据也自动被更新了

因为 Vue 是数据双向绑定的框架,而整个框架的由三个部分组成:

  • 数据层(Model):应用的数据及业务逻辑,为开发者编写的业务代码
  • 视图层(View):应用的展示效果,各类UI组件,由 template 和 css 组成的代码
  • 业务逻辑层(ViewModel):框架封装的核心,它负责将数据与视图关联起来
理解 ViewModel

它的主要职责就是

  • 数据变化后更新视图
  • 视图变化后更新数据

当然,它还有两个主要部分组成

  • 监听器(Observer):对所有数据的属性进行监听
  • 解析器(Compiler):对每个元素节点的指令进行扫描跟解析,根据指令模板替换数据,以及绑定相应的更新函数
Proxy (Vue3)
  • Proxy可以直接监听对象而非属性
  • Proxy 可以直接监听数组的变化
  • Proxy有多达13种拦截方法,不限于 apply, ownKeys, deleteProperty, has 等 Object.defineProperty 不具备的
  • Proxy 返回的是一个新对象,我们可以只操作新的对象达到目的,而 Object.defineProperty 只能遍历对象属性直接修改
  • Proxy作为新标准将受到浏览器厂商重点持续的性能优化,也就是传说中的新标准的性能红利
Object.defineProperty (Vue2,Vue3 ref())
  • 监听对象属性变化,通过 getter/setter 作出反应
  • 无法监听数组属性的变化,Vue 通过重写数组方法从而实现
  • 兼容性好,支持IE9,而 Proxy 的存在浏览器兼容性问题,而且无法用 polyfill 磨平
Vue 实现双向数据绑定
class Vue {constructor(options) {// * 数据备份this.$data = this._data = options.data// * 数据劫持Observe(this.$data)// * 数据代理Object.keys(this.$data).forEach(key => {Object.defineProperty(this, key, {enumerable: true, // 允许迭代configurable: true, // 允许修改get() {return this.$data[key]},set(newValue) {this.$data[key] = newValue}})})// * 模板编译Compile(options.el, this)}
}// * 数据劫持
function Observe(obj) {// 递归终止条件if (!obj || typeof obj !== 'object') returnconst dep = new Dep()// 通过Object.keys(obj)获取到当前odj上的每个属性Object.keys(obj).forEach(key => {//获取属性值let value = obj[key]// 把value子节点进行递归Observe(value)Object.defineProperty(obj, key, {enumerable: true, // 允许迭代configurable: true, // 允许修改get() {Dep.target && dep.addSubs(Dep.target)return value},set(newValue) {value = newValueObserve(value)// 通知每一个订阅者更新自己的文本dep.notify()}})})
}// * 模板编译
function Compile(el, vm) {// 获取el对应的DOM元素vm.$el = document.querySelector(el)// 创建文档碎片,提高DOM操作的性能const fragment = document.createDocumentFragment()while ((childNode = vm.$el.firstChild)) {fragment.appendChild(childNode)}// 模板编译replaceNode(fragment)// 重新渲染页面vm.$el.appendChild(fragment)// 负责对DOM模板进行编译的方法function replaceNode(node) {// 定义匹配差值表达式的正则const regMustache = /\{\{\s*(\S+)\s*\}\}/// 证明当前的node节点是一个文本子节点,需要进行正则替换if (node.nodeType === 3) {// 注意:文本子节点,也是一个DOM对象,如果要获取文本子节点的字符串内容,需要调用textContent属性获取// console.log(node.textContent);const text = node.textContent// 进行字符串的正则匹配与提取const execResult = regMustache.exec(text)// console.log(execResult);if (execResult) {const value = execResult[1].split('.').reduce((newObj, k) => newObj[k], vm)node.textContent = text.replace(regMustache, value)// 在这里,创建watcher的实例new Watcher(vm, execResult[1], newValue => {node.textContent = text.replace(regMustache, newValue)})}// 终止递归条件return}// 如果是DMO节点 并且是输入框if (node.nodeType === 1 && node.tagName.toUpperCase() === 'INPUT') {// 获取节点的所有属性const attrs = Array.from(node.attributes)// 判断属性中是否存在 v-modelconst findResult = attrs.find(attr => attr.name === 'v-model')if (findResult) {//获取到当前v-model的属性值 name info.aconst expStr = findResult.valueconst value = expStr.split('.').reduce((newObj, k) => newObj[k], vm)node.value = value// 在这里,创建watcher的实例new Watcher(vm, expStr, newValue => {node.value = newValue})// 监听文本框的input输入事件,拿到文本框最新的值,把最新的值,更新到vm上即可node.addEventListener('input', e => {const keyArr = expStr.split('.')const obj = keyArr.slice(0, keyArr.length - 1).reduce((newObj, k) => newObj[k], vm)console.log(obj)obj[keyArr[keyArr.length - 1]] = e.target.value})}}// 证明不是文本节点,可能是一个DOM元素,需要进行递归处理node.childNodes.forEach(child => replaceNode(child))}
}class Dep {constructor() {// 设置存放订阅者信息的数组this.subs = []}// 向subs数组中添加订阅者的信息addSubs(watcher) {this.subs.push(watcher)}// 发布通知的方法notify() {this.subs.forEach(watcher => watcher.update())}
}// 订阅者的类
class Watcher {// cb回调函数中,记录着当前Watcher如何更新自己的文本内容// 但是,只知道如何更新自己还不行,还必须拿到最新的数据,// 因此,还需要在new Watcher 期间,把Vm也传递进来(因为Vm中保存着最新的数据)// 除此之外,还需要知道,在Vm身上众多的数据中,哪个数据,才是当前自己所需要的数据,// 因此,必须在new Watcher期间,指定watcher 对应的数据的名字keyconstructor(vm, key, cb) {this.vm = vmthis.key = key// 实例的回调函数this.cb = cb// 下面三行负责把创建的Watcher 实例存到Dep实例的subs数组中Dep.target = thiskey.split('.').reduce((newObj, k) => newObj[k], vm)Dep.target = null}// 触发回调函数的方法 发布者通知我们更新update() {const value = this.key.split('.').reduce((newObj, k) => newObj[k], this.vm)this.cb(value)}
}

Vue 数据劫持

通过 vm 对象来代理 data 对象中属性的操作(读/写);能够更加方便的操作data中的数据

JS 实现: Object.defineProperty()
Vue2 实现: 通过 Object.defineProperty() 把data对象中所有属性添加到 vm 上,为每一个添加到 vm 上的属性,都指定一个 getter/setter ,在 getter/setter 内部去操作(读/写) data 中对应的属性
Vue3 实现: 通过 Proxy,Reflect 实现

Vue 数据监测(发布/订阅者模式)

Vue 实现数据监听主要使用 监视者设计模式 设置一个监视者,将数据设置为响应式数据后,添加到 Vue._data 中,Vue 再将 Vue._data 属性 代理到 vm 实例上,方便使用这就导致,如果在实例化过程中,没有传入数据,后续想要添加数据时,Vue 就不会对数据进行响应式处理,另外: 数组内元素在实例化时不做响应式处理
Vue 将被侦听的数组的变更方法进行了包裹,所以它们也将会触发视图更新

// 模拟 Vue 中属性监测
function Observer(obj){// 汇总对象中所有的属性形成一个数组const keys = Object.keys(obj)// 遍历keys.forEach((k)=>{Object.defineProperty(this,k,{get(){return obj[k]},set(val){console.log(`${k}被改了,我要去解析模板,生成虚拟DOM.....我要开始忙了`)obj[k] = val}})})
}

Vue.set() and vm.$set()

使用 Vue 所提供的方法动态添加属性并做响应式处理

Vue.set(object, propertyName, value)
// 还可以使用 vm.$set 实例方法,这也是全局 Vue.set 方法的别名
this.$set(this.someObject,'b',2)

Vue 数组包裹方法实现响应式

push()
pop()
shift()
unshift()
splice()
sort()
reverse()

实现 mustache 模板引擎

模板引擎是将数据转变为视图最优雅的方案
实现功能: 解析模板字符串,将模板字符串转为 JS的数组表示(tokens),然后转换为 domStr 并上树

数据变视图方法
  1. 纯 DOM 法: 通过createElement标签,在追加节点(上树)
  2. 数组 join 法: var str=[‘A’,‘B’,‘C’,‘D’].join(‘’) 输出结果:ABCD (可以实现换行)
  3. ES6 模板字符串: 反引号、变量
  4. 模板引擎: mustache: {{}}
mustache 模板引擎结构
  • mian.js: 程序主入口,调用各项功能
  • parseTemplateToTokens.js: 将模板字符串转换为 tokens
    • Scanner.js: 扫描器类,扫描模板字符串,返回匹配的字符串
    • nestTokens.js: 字符串转换为 tokens
  • renderTemplate.js: 判断 tokens 类型分别作出处理
    • lookup.js: 判断读取数据是否存在 ( . ),存在循环后返回数据,不存在直接调用返回数据
    • parseArray.js: 递归调用 renderTemplate 实现数组循环

mustache 模板引擎结构.jpeg

main.js
import parseTemplateToTokens from './parseTemplateToTokens'
import renderTemplate from './renderTemplate'/*** ! 程序主入口* * 1. 在 window 全局对象挂载 mustache 对象,并添加 render 方法* * 2. 调用 parseTemplateToTokens 方法将模板字符串转换为 tokens* * 3. 调用 renderTemplate 方法将 tokens 转换为 domStr* * 4. 将 domStr 上树*/
window.mustache = {render(templateStr, data) {let tokens = parseTemplateToTokens(templateStr) // 模板字符串转换 tokenslet domStr = renderTemplate(tokens, data) // tokens 转换为 domStrdocument.body.innerHTML = domStr // domStr 上树}
}

parseTemplateToTokens.js
import Scanner from './Scanner'
import nestTokens from './nestTokens'/*** ! 模板字符串 转换 tokens 函数* * 1. 创建 Scanner 扫描器对象,传入数据进行扫描返回对应数据* * 2. 对数据进行分类处理* * 3. 将数据传入 nestTokens 方法,将需要折叠的 token 进行折叠处理** @export* @param {String} templateStr* @return {Array}*/
export default function parseTemplateToTokens(templateStr) {const scanner = new Scanner(templateStr) // 创建扫描器对象const tokens = [] // 创建数组接收扫描的数据let words = '' // 临时存储字符串变量// 判断指针是否扫描到尾部while (scanner.eos()) {words = scanner.scanUtil('{{') // scanUtil 方法最后一次会返回空字符串if (words !== '') {tokens.push(['text', words])}scanner.scan('{{')words = scanner.scanUtil('}}')if (words !== '') {if (words[0] === '#') {tokens.push(['#', words.substring(1)])} else if (words[0] === '/') {tokens.push(['/', words.substring(1)])} else {tokens.push(['name', words])}}scanner.scan('}}')}return nestTokens(tokens)
}

Scanner.js
/*** ! 扫描器 (对特定数据扫描并返回)* * 1. scan() 传入特定字符,跳过此字符串长度,改变尾部字符串* * 2. scanUtil() 传入特定字符,直至到达指定字符处,返回指针指过的字符,改变尾部字符串* * 3. eos() 判断指针是否已到尾部,返回布尔值** @export* @class Scanner*/
export default class Scanner {constructor(templateStr) {this.templateStr = templateStrthis.pos = 0 // 指针this.tail = templateStr // 尾巴字符串,初始化为字符串原文}// * 跳过模板语法特定字符scan(tag) {if (this.tail.indexOf(tag) === 0) {this.pos += tag.lengththis.tail = this.templateStr.substring(this.pos)}}// * 指针进行扫描,遇到特定内容停止,并且返回扫描过得字符scanUtil(stopTag) {// 保存调用此函数时的指针值,以便返回扫描过得字符let pos_start = this.poswhile (this.eos() && this.tail.indexOf(stopTag) !== 0) {this.pos++ // 指针自增this.tail = this.templateStr.substring(this.pos) // 根据指针改变尾巴字符串(如果对this.tail进行截取操作, this.tail 一直被缩短,每次截取后的基准位置是不同的)}return this.templateStr.substring(pos_start, this.pos)}// * 判断指针指向是否已到尾部eos() {return this.pos !== this.templateStr.length}
}

nestTokens.js
/*** !  JS 字符串多维数组 token 化,折叠 tokens, 将 # 和 / 之间的 tokens 能够整合起来* * 1. 利用栈结构先进后出的特点,遇到 # 对栈顶进行压入操作,遇到 / 对栈顶进行出栈操作* * 2. 自我实现: 主要利用出栈时判断栈数组长度,为0 则直接压入返回数据容器中,不为 0 证明栈中还有数组存在,将此次出栈数据压入当前栈顶数据中(sections[sections.length - 1][2].push(section))* * 3. 改进实现: 利用一个收集器,默认指向返回数据容器,一旦出现特定字符,数据会被压入栈结构栈顶,而收集器指向此栈结构栈顶,出栈时改变指向,栈顶还有数据指向栈顶,没有则指向返回数据容器* * 4. 收集器本质上是存储了栈顶数据引用的变量,简化代码** @export* @param {String} tokens* @return {Array}*/
// ? 1. 自我实现
/* export default function nestTokens(tokens) {const nestedTokens = [] // 创建返回数据容器let sections = [] // 利用栈结构,创建一个栈顶数组确定现在所操作的数据属于哪个结构tokens.forEach((token, index) => {switch (token[0]) {case '#':token[2] = [] // 创建 token [2] 数组以便接收后续数据sections.push(token) // 压栈(入栈)breakcase '/':let section = sections.pop() // 弹栈// 出栈时判断长度,长度不为 0,证明栈顶还有数据,则将此数据压入倒指定栈顶数组中(可行),否则压入返回数据容器sections.length > 0 ? sections[sections.length - 1][2].push(section) : nestedTokens.push(section)breakdefault:// 长度不为 0,证明栈顶还有数据,则将此数据压入倒指定栈顶数组中(可行),否则压入返回数据容器sections.length > 0 ? sections[sections.length - 1][2].push(token) : nestedTokens.push(token)}})return nestedTokens // 返回数据容器
} */// ? 2. 收集器实现
export default function nestTokens(tokens) {const nestedTokens = [] // 创建返回数据数组let sections = [] // 利用栈结构,创建一个栈顶数组确定现在所操作的数据属于哪个结构let collector = nestedTokens // 创建一个收集器,默认指向返回数据容器(引用数据类型)tokens.forEach(token => {switch (token[0]) {case '#': // 特定字符处理collector.push(token) // 将数据放入收集器(默认返回数据容器,如改变指向则为栈顶元素)sections.push(token) // 压栈(入栈)collector = token[2] = [] // 创建 token [2] 数组以便接收后续数据,同时也改变收集器指向,以便后续数据压入breakcase '/': // 特定字符处理sections.pop() // 弹栈collector = sections.length > 0 ? sections[sections.length - 1][2] : nestedTokens // 重新指定收集器执行breakdefault:collector.push(token) // 向收集器中 push 数据}})return nestedTokens // 返回数据容器
}

renderTemplate.js
import lookup from './lookup'
import parseArray from './parseArray'/*** ! 判断 token[0] 项类型,针对类型作出相应操作* * 1. 对 text 类型直接添加* * 2. 对 name 类型进行数据查找返回* * 3. 对 # 类型进行 parseArray 方法调用,递归解析类型** @export* @param {String} tokens* @param {*} data* @return {String}*/
export default function renderTemplate(tokens, data) {let resultStr = '' // 创建字符串容器console.log(tokens)// 遍历 tokens 判断类型作出相应处理for (let i = 0; i < tokens.length; i++) {const token = tokens[i]if (token[0] === 'text') {resultStr += token[1]} else if (token[0] === 'name') {resultStr += lookup(data, token[1])} else if (token[0] === '#') {resultStr += parseArray(token, data)}}return resultStr
}

lookup.js
/*** ! 为传入字符串匹配对应数据* * 1. 对字符串中带 . 且自身不是 . 的字符串进行循环查找数据* * 2. 其他字符串直接数据引用返回** @export* @param {*} dataObj* @param {String} keyName* @return {*}*/
export default function lookup(dataObj, keyName) {// 判断是否有点符号且自身不能为点符号(防止 . 匹配被舍弃),如有则处理后返回,没有点符号直接访问对象属性返回if (keyName.indexOf('.') !== -1 && keyName !== '.') {let keyArr = keyName.split('.') // 将字符串转换为数组// 引用类型,需一层一层向下寻找let resultValue = dataObjkeyArr.forEach(item => {resultValue = resultValue[item] // 当前层数数据保存})return resultValue // 返回访问的数据}return dataObj[keyName] // 返回访问的数据
}

parseArray.js
import renderTemplate from './renderTemplate'
import lookup from './lookup'/*** ! 为传入的字符串进行循环操作(递归调用)* * 1. 获取 token[1] 的对应数据* * 2. 遍历数据,获奖递归调用完成后的 domStr** @export* @param {String} token* @param {*} data* @return {String}*/
export default function parseArray(token, data) {let v = lookup(data, token[1]) // 找到 token[1] 在 data 中的数据let resultStr = '' // 创建返回字符串容器// 遍历数据(次数取决于对象数组长度)v.forEach((item, index) => {resultStr += renderTemplate(token[2], {...v[index], // 展开数据中的值;每次递归调用时要把对应数组的数据传输过去'.': v[index] // 匹配 .})})return resultStr
}

VNode 是什么? 什么是虚拟 DOM

1. VNode是什么

VNode是JavaScript对象, VNode表示Virtual DOM,实际上它只是一层对真实DOM的抽象,以Javascript对象(VNode节点)作为基础的树,用对象的属性来描述节点,最终可以通过一系列操作使这棵树映射到真实环境上,通过render将template 模版描述成VNode,然后进行一系列操作之后形成真实的DOM进行挂载。

(name:Hello Kitty', age: 1,
children: null]

2. 为什么需要 VNode

因为真实 DOM 对象是非常大的,而使用原生 API 去操作 DOM 时,浏览器会从构建 DOM 树从头到尾执行一遍流程,而且更新多个DOM对象并渲染时,无法精确的控制渲染的时机,导致多次修改,多次渲染,大量频繁的DOM操作会使页面速度变慢

3. VNode的优点
  1. 兼容性强,不受执行环境的影响。VNode因为是JS对象,不管Node还是浏览器,都可以统一操作,从而获得了服务端渲染、原生渲染、手写渲染函数等能力
  2. Vue 通过在内存中实现文档结构的虚拟表示来解决此问题,其中虚拟节点(VNode)表示DOM树中的节点。当需要操纵时,可以在虚拟DOM的内存中执行计算和操作,而不是在真实DOM上进行操纵。这自然会更快,并且允许虚拟DOM算法计算出最优化的方式来更新实际DOM结构,一旦计算出,就将其应用于实际的DOM树,这就提高了性能,这就是为什么基于虚拟DOM的框架(例如Vue和React)如此突出的原因

Vue 中如何实现一个虚拟 DOM

  1. 首先要构建一个VNode的类, DOM元素上的所有属性在VNode类实例化出来的对象上都存在对应的属性。例如tag表示一个元素节点的名称,text表示个文本节点的文本, chlidren表示子节点等。将VNode类实例化出来的对象进行分类,例如注释节点、文本节点、元素节点、组件节点、函数式节点、克隆节点
  2. 然后通过编译将模板转成宣染函数render,执行宣染函数render,在其中创建不同类型的VNode类,最后整合就可以得到一个虚拟DOM(vnode)
  3. 最后通过patch将vnode和oldVnode进行比较后,生成真实DOM

Vue 的 diff 算法

Diff算法实现的是最小量更新虚拟DOM。这句话虽然简短,但是涉及到了两个核心要素:虚拟DOM、最小量更新

  • 首先比较一下新旧节点是不是同一个节点(可通过比较sel(选择器)和key(唯一标识)值是不是相同),不是同一个节点则进行暴力删除(注:先以旧节点为基准插入新节点,然后再删除旧节点)
  • 若是同一个节点则需要进一步比较
    1. 完全相同,不做处理
    2. 新节点内容为文本,直接替换完事
    3. 新节点有子节点,这个时候就要仔细考虑一下了:若老节点没有子元素,则直接清空老节点,将新节点的子元素插入即可;若老节点有子元素则就需要按照上述的更新策略进行执行

Vue2 面试题

Vue 页面第一次加载了那几个生命周期钩子

当页面第一次页面加载时会触发 beforeCreate, created, beforeMount,mounted这几个钩子函数

Vue 命名规则

  • 一种是使用链式命名 my-component
  • 一种是使用大驼峰命名 MyComponent
  • 在字符串模板中 和都可以使用
  • 在非字符串模板中最好使用,因为要遵循W3C规范中的自定义组件名(字母全小写且必须包含一个连字符),避免和当前以及未来的HTML元素相冲突(Vue 推荐)

v-if 和 v-show 的区别,使用场景

  • 两种指令可以在判断为 true 时占据页面位置,false 时不占据页面位置
  • 控制手段: v-show 本质上是通过 css-display: none 来控制元素的显示与隐藏,而 v-if 是直接控制删除/添加 DOM 元素的方式来控制显示与隐藏
  • 编译过程: v-if切换有一个局部编译/卸载的过程,切换过程中合适地销毁和重建内部的事件监听和子组件; v-show只是简单的基于css切换
  • 编译条件: v-if 是真正的条件宣染,它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建(触发生命周期函数)。只有渲染条件为 false 时,并不做操作,直到为 true 才渲染
  • 使用场景: v-if与v-show都能控制dom元素在页面的显示; v-if 相比v-show开销更大的(直接操作dom节点增加与删除); 如果需要非常频繁地切换,则使用v-show较好; 如果在运行时条件很少改变,则使用v-if较好

v-if 为什么不能和 v-for 一起使用

Vue2 处理指令时, v-for 比 v-if 具有更高的优先级; Vue3 处理指令时, v-if 比 v-for 的优先级更高

  1. 我们的使用意图是通过 v-if 来控制循环列表的显示与隐藏,但实际情况会更预想的有很大不同
  2. v-if和v-for同时用在同一个元素上,带来性能方面的浪费(每次渲染都会先循环再进行条件判断)
  3. 如果避免出现这种情况,则在外层嵌套template (页面渲染不生成dom节点) ,在这一层进行v-if判断,然后在内部进行v-for循环

为什么 data 属性必须返回一个函数

组件实例对象data必须为函数,目的是为了防止多个组件实例对象之间共用一个data,产生数据污染。采用函数的形式, initData 时会将其作为工厂函数都会返回全新 data 对象

动态给vue的data添加一个新的属性时会发生什么?怎样解决?

Vue2 响应式数据是通过 object.defineProperty 将我们 data 中的数据配置了 getter setter而来,我们动态添加的数据并没有添加 getter 和 setter ,此时数据当然不是响应式

解决方案

  1. Vue.set/$set: 通过这个方法可以为制定对象添加响应式数据(注意: 无法向根数据/data 和 组件实例上追加响应式数据)
  2. Object.assign: 直接使用是没有响应式的,这个的做法是合并对象属性,将响应式对象和我们追加的属性进行合并,此时就会触发更新追加数据成功
  3. $forceUpdate: 如果你发现你自己需要在vue中做一次强制更新,99.9%的情况,是你在某个地方做错了事,直接强制重新刷新数据

说说你对vue的mixin的理解,有什么应用场景

本质其实就是一个js对象,它可以包含我们组件中任意功能选项,如data、components、methods, created, computed 等等

全局混入
将对应数据混入到所有组件中,使用全局混入需要特别注意,因为它会影响到每一个组件实例(包括第三方组件) PS:全局混入常用于插件的编写
局部混入
将数据混入制定的组件
使用场景
日常开发中对一些相同或者相似的代码,多次出现,此时可以考虑使用将代码进行抽离,在需要使用此数据的组件进行混入,达到代码复用的目的
合并策略

  • 替换型: 替换型策略有props、methods, inject、computed,就是将新的同名参数替代旧的参数
  • 合并型: 合并型策略是data,通过set方法进行合并和重新赋值,重名数据为纯对象时递归合并数据,否则最终留存组件数据,舍弃混入数据
  • 队列型: 全部生命周期和watch,原理是将函数存入一个数组,然后正序遍历依次执行
  • 叠加型: 叠加型有component, directives, filters,通过原型链进行层层的叠加

说说你对keep-alive的理解是什么

keep-alive是vue中的内置组件,能在组件切换过程中将状态保留在内存中,防止重复渲染DOM
keep-alive包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们

keep-alive 可以设置以下props属性

  • include -字符串或正则表达式。只有名称匹配的组件会被缓存
  • exclude -字符串或正则表达式。任何名称匹配的组件都不会被缓存
  • max -数字。最多可以缓存多少组件实例

备注: 匹配首先检查组件自身的name选项,如果name选项不可用,则匹配它的局部注册名称(父组件components选项的键值),匿名组件不能被匹配
设置了keep-alive缓存的组件,会多出两个生命周期钩子(activated 组件激活 与 deactivated 组件失活)
缓存后如何获取数据

  1. beforeRouteEnter: 路由前置守卫,每次组件渲染的时候,都会执行beforeRouteEnter
  2. actived: 在keep-alive缓存的组件被激活的时候,都会执行actived 钩子

watch, methods, computed的执行顺序

  1. computed 是在HTML DOM加载后马上执行的,如赋值 (属性将被混入到Vue实例),创建一个新的变量进行使用
  2. methods 则必须要有一定的触发条件才能执行,如点击事件
  3. watch 在 Vue 实例初始化时绑定到 Vue 实例上,必须要有一定的触发条件才能执行,用于观察Vue实例上的数据变动,监听现有数据
  • 默认加载时: 先 computed 再 watch, 不执行 methods
  • 触发某一事件时: 先 computed 再 methods 再到 watch
  • computed vs methods: 计算属性可以缓存,以便复用,而方法不行

总结: 计算属性computed只有在它的相关依赖发生改变时才会重新求值,当有一个性能开销比较大的的计算属性A,它需要遍历一个极大的数组和做大量的计算,然后我们可能有其他的计算属性依赖于A,这时候,我们就需要缓存,每次确实需要重新加载;而不需要缓存时用methods

computed 中的属性名和 data 中的属性名可以相同吗

不能同名,因为不管是 computed 属性名还是 data 数据名还是 props 数据名都会被挂载在 vm 实例 上,因此这三个都不能同名

Vue 中 template 编译的原理

简而言之,就是先转化成AST树,再得到的render函数返回VNode (Vue的虚拟DOM节点)

  • 首先,通过compile编译器把template编译成AST 语法树(abstract syntaxtree 即源代码的抽象语法结构的树状表现形式), compile是createCompiler的返回值, createCompiler是用以创建编译器的。另外compile还负责合并option
  • 然后, AST 会经过generate(将AST 语法树转化成render funtion字符串的过程)得到render函数, render的返回值是VNode, VNode是Vue的虚拟DOM节点,里面有(标签名、子节点、文本等等)

Vue2 兼容什么版本的浏览器

不支持ie8及以下,部分兼容ie9 ,完全兼容10以上,因为Vue的响应式原理是基于 ES5 的 Object. defineProperty() 而这个方法不支持ie8及以下

Vue 解决 SPA 首页卡顿情况

卡顿原因

  • 网络延时问题
  • 资源文件体积过大
  • 资源重复发送请求加载
  • 加载脚本,渲染内容堵塞

解决方案

  1. 减小入口文件体积: 常用的手段是路由懒加载,把不同路由对应的组件分割成不同的代码块,待路由被请求的时候会单独打包路由,使得入口文件变小,加载速度大大增加,开启路由懒加载,像Vue这种单页面应用,如果没有应用懒加载,运用webpack打包后的文件将会异常的大,造成进入首页时,需要加载的内容过多,时间过长,会出现长时间的白屏,即使做了loading也是不利于用户体验,而运用懒加载则可以将页面进行划分,需要的时候加载页面,可以有效的分担首页所承担的加载压力,减少首页加载用时
  2. 静态资源本地存储: 前端多使用善用本地存储,对一些长期资源可以使用 localStorage 进行存储
  3. UI 框架按需加载: 根据 UI 框架文档使用按需加载
  4. 组件重复打包: 对多次引用的文件可以考虑抽取成公共文件,避免重复引用
  5. 图片资源的压缩: 图片资源可以考虑使用雪碧图,小图片转换 base64 的方式减少网络请求
  6. 开启 Gzip 压缩: 网络传输数据时进行压缩处理
  7. 使用 SSR: 服务器端渲染技术

<style> 中添加 scoped 属性的原理

1. scoped 是什么

在Vue组件中,为了使样式私有化(模块化),不对全局造成污染,可以在style标签上添加scoped属性以表示它的只属于当下的模块,局部有效。如果一个项目中的所有Vue组件style标签全部加上了scoped,相当于实现了样式的私有化。如果引用了第三方组件,需要在当前组件中局部修改第三方组件的样式,而又不想去除scoped属性造成组件之间的样式污染。此时只能通过穿透scoped的方式来解决

2. scoped 实现原理

Vue 中的 scoped 属性的效果主要通过 PostCSS 转译实现

即; PostCSS 给所有 dom 添加了一个唯一不重复的动态属性,然后,给CSS选择器额外添加一个对应的属性选择器来选择该组件中 dom ,这种做法使得样式私有化

对 SPA 单页面的理解

单页Web应用(single-page application简称为SPA)是一种特殊的Web应用。它将所有的活动局限于一个Web页面中,仅在该Web页面初始化时加载相应的HTML、JavaScript和CSS。一旦页面加载完成了, SPA不会因为用户的操作而进行页面的重新加载或跳转。取而代之的是利用JavaScript动态的变换HTML的内容,从而实现UI与用户的交互。由于避免了页面的重新加载,SPA可以提供较为流畅的用户体验。得益于ajax,我们可以实现无跳转刷新,又多亏了浏览器的histroy机制,我们用hash的变化从而可以实现推动界面变化。从而模拟客户端的单页面切换效果

SPA 的优点

1、无刷新界面,给用户体验原生的应用感觉
2、节省原生(android和ios)app开发成本(小程序)
3、提高发布效率,程序开发无需每次安装更新包
4、容易借助其他知名平台更有利于营销和推广
5、符合web2.0的趋势

SPA 的缺点

1、效果和性能确实和原生的有较大差距
2、各个浏览器的版本兼容性不一样
3、业务随着代码量增加而增加,不利于首屏优化
4、某些平台对hash有偏见,有些甚至不支持pushstate
5、不利于搜索引擎抓取

实现核心

通过监听 url 中 hash 值得变化来进行相应的跳转动作

SPA SEO
  1. SSR 服务器渲染

将组件或页面通过服务器生成 html,再返回给浏览器,如 index.js

  1. 静态化
  1. 一种是通过程序将动态页面抓取并保存为静态页面,这样的页面的实际存在于服务器的硬盘中
  2. 另外一种是通过WEB服务器的URL Rewrite的方式,它的原理是通过web服务器内部模块按一定规则将外部的URL请求转化为内部的文件地址,一句话来说就是把外部请求的静态地址转化为实际的动态页面地址,而静态页面实际是不存在的。这两种方法都达到了实现URL静态化的效果
  1. 使用Phantomjs针对爬虫处理

原理是通过Nginx配置,判断访问来源是否为爬虫,如果是则搜索引擎的爬虫请求会转发到一个node server,再通过 PhantomJs 来解析完整的HTML,返回给爬虫

Vue 中如何重置 data

要初始化data中的数据,可以使用Object.assign ()方法,实现重置data中的数据

1. Object.assign() 方法基本定义

Object. assign() 方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。
用法: Object.assign (target, … sources),第一个参数是目标对象,第二个参数是源对象,就是将源对象属性复制到目标对象,返回目标对象

2)具体使用方式
vm.$data // 获取当前状态下的 data
vm.$options.data(this) // 获取组件初始化状态下的 data
Object.assign(this.$data, this.$options.data(this)) // 将初始化状态的 data 数组赋值给现有 data 数组,实现覆盖

Vue 插件

插件通常用来为 Vue 添加全局功能

插件的功能范围没有严格的限制——一般有下面几种

  1. 添加全局方法或者 property;如: vue-custom-element
  2. 添加全局资源: 指令/过滤器/过渡等;如 vue-touch
  3. 通过全局混入来添加一些组件选项;如 vue-router
  4. 添加 Vue 实例方法,通过把它们添加到 Vue.prototype 上实现;
  5. 一个库,提供自己的 API,同时提供上面提到的一个或多个功能;如 vue-router
使用插件

通过全局方法 Vue.use() 使用插件;它需要在你调用 new Vue() 启动应用之前完成

// 调用 `MyPlugin.install(Vue)`
Vue.use(MyPlugin)new Vue({// ...组件选项
})

也可以传入一个可选的选项对象

Vue.use(MyPlugin, { someOption: true })

注意: Vue.use 会自动阻止多次注册相同插件,届时即使多次调用也只会注册一次该插件

开发插件

Vue.js 的插件应该暴露一个 install 方法;这个方法的第一个参数是 Vue 构造器,第二个参数是一个可选的选项对象

MyPlugin.install = function (Vue, options) {// 1. 添加全局方法或 propertyVue.myGlobalMethod = function () {// 逻辑...}// 2. 添加全局资源Vue.directive('my-directive', {bind (el, binding, vnode, oldVnode) {// 逻辑...}...})// 3. 注入组件选项Vue.mixin({created: function () {// 逻辑...}...})// 4. 添加实例方法Vue.prototype.$myMethod = function (methodOptions) {// 逻辑...}
}

Vue 过度与动画

在插入、更新或移除 DOM 元素时,在合适的时候给元素添加样式类名

元素进入的样式类

  1. v-enter: 进入的起点
  2. v-enter-active: 进入过程中
  3. v-enter-to: 进入的终点

元素离开的样式类

  1. v-leave: 离开的起点
  2. v-leave-active: 离开过程中
  3. v-leave-to: 离开的终点

使用方法

// 使用<transition>包裹要过度的元素,并配置name属性
<transition name="hello"><h1 v-show="isShow">你好啊!</h1>
</transition>

注意: 若配置了 name 属性,则样式指定类名时需将 v- 更换成 name
备注: 若有多个元素需要过度,则需要使用: <transition-group>,且每个元素都要指定key

Vue 组件化编程

解决问题

组件可以提升整个项目的开发效率。能够把页面抽象成多个相对独立的模块,解决了我们传统项目开发:效率低、难维护、复用性等问题。

需求实现
  1. 拆分静态组件: 组件要按照功能点拆分,命名不要与html元素冲突
  2. 实现动态组件: 考虑数据存放位置,是一个组件使用,还是众多组件使用
    • 一个组件: 放置在组件自身
    • 众多组件: 放置到它们共同的父组件中
  3. 实现交互: 针对功能进行方法封装实现
具体使用
  1. 使用 import 导入组件(导入)
  2. 在组件内使用 component 注册局部组件或者使用 Vue.component 注册全局组件(注册)
  3. 在组件内 template 以标签的形式使用(使用)
  4. 数据传递可以使用自定义属性,自定义方法,全局事件总线,Vuex;需要传递模板可以使用 solt 插槽

Vue-loader 的作用

Vue-loader 会解析文件,提取出每个语言块,如果有必要会通过其他 loader 处理,最后将他们组装成一个 commonjs 模块; module.exports 出一个 Vue.js 组件对象

<temlate> 模板
  1. 默认语言: html
  2. 每个 .Vue 文件最多包含一个 块
  3. 内容将被提取为字符串,将编译用作 Vue组件 的 template 选项
  4. 这个标签可以使用 comments 为 True 选项保留模板中的 HTML 注释
<script> 脚本
  1. 默认语言: JS(在监测到babel-loader或者buble-loader配置时,自动支持ES2015)
  2. 每个 .Vue 文件最多包含一个 块
  3. 该脚本在类 CommonJS 环境中执行(就像通过webpack打包的正常JS模块)。所以你可以require()其他依赖。在ES2015支持下,也可以使用import跟export语法
  4. 脚本必须导出 Vue.js 组件对象,也可以导出由 Vue. extend() 创建的扩展,对象;但是普通对象是更好的选择
<style> 样式

默认语言: css
1、一个.Vue文件可以包含多个
2、这个标签可以有 scoped 或者 module 属性来帮助你讲样式封装到当前组件;具有不同封装模式的多个
3、默认情况下,可以使用 style-loader 提取内容,并且通过

相关文章:

10 Vue 特性要点

Vue2 特性要点 Vue2 源码理解 Vue 双向数据绑定 先从单向绑定切入单向绑定非常简单,就是把Mode1绑定到view,当我们用Javascript代码更新Model时, view就会自动更新 双向绑定就很容易联想到了,在单向绑定的基础上,用户更新了View, Mode1的数据也自动被更新了 因为 Vue 是数据双向…...

ESP32和mDNS学习

目录 mDNS的作用mDNS涉及到的标准文件组播地址IPv4 多播地址IPv6 多播地址预先定义好的组播地址 mDNS调试工具例程mDNS如何开发和使用注册服务查询服务 mDNS的作用 mDNS 是一种组播 UDP 服务&#xff0c;用来提供本地网络服务和主机发现。 你要和设备通信&#xff0c;需要记住…...

学习SQL如何使用CASE语句查询分析设备状态

学习SQL如何使用CASE语句查询分析设备状态 一、前言1. 问题背景2. SQL查询分析3. SQL查询解析 二、结论 一、前言 在实际应用中&#xff0c;经常需要对设备的状态进行监控和分析。通过SQL查询&#xff0c;我们可以有效地从数据库中提取和计算设备的状态信息。本文将介绍如何编…...

Gartner发布2024年零信任网络技术成熟度曲线:20项零信任相关的前沿和趋势性技术

大多数组织都制定了零信任信息安全策略&#xff0c;而网络是零信任实施领域的顶级技术。此技术成熟度曲线可以帮助安全和风险管理领导者确定合适的技术&#xff0c;以将零信任原则嵌入其网络中。 战略规划假设 到 2026 年&#xff0c;15% 的企业将在企业拥有的局域网上用 ZTNA …...

React hook 之 useState

在组件的顶部定义状态变量&#xff0c;并传入初始值&#xff0c;确保当这些状态变量的值发生变化时&#xff0c;页面会重新渲染。 const [something,setSomething] useState(initialState); useState 返回一个由两个值组成的数组&#xff1a;1、当前的 state&#xff0c;在首次…...

jenkins中shell脚本中使用构建参数化Groovy变量的四种方式

jenkins中shell脚本中使用构建参数化Groovy变量的四种方式: 以字符变量为例&#xff1a; 流水线代码&#xff1a; pipeline {agent {//label "${server}"label "${28}"}stages {stage(Hello) {steps {echo Hello Worldecho "${28}"echo "…...

Robot Operating System——ParameterEventHandler监控Parameters的增删改行为

大纲 创建订阅"/parameter_events"的Node监控自身Node内部Parameter监控自身Node外部Parameter监听所有Node的所有Parameter的变动执行效果总结 在《Robot Operating System——AsyncParametersClient监控Parameters的增删改行为》一文中&#xff0c;我们通过AsyncPa…...

计算机网络(Wrong Question)

一、计算机网络体系结构 1.1 计算机网络概述 D 注&#xff1a;计算机的三大主要功能是数据通信、资源共享、分布式处理。&#xff08;负载均衡、提高可靠性&#xff09; 注&#xff1a;几段链路就是几段流水。 C 注&#xff1a;记住一个基本计算公式&#xff1a;若n个分组&a…...

Docker+consul容器服务的更新与发现

1、Consul概述 &#xff08;1&#xff09;什么是服务注册与发现 服务注册与发现是微服务架构中不可或缺的重要组件。起初服务都是单节点的&#xff0c;不保障高可用性&#xff0c;也不考虑服务的压力承载&#xff0c;服务之间调用单纯的通过接口访问。直到后来出现了多个节点…...

全网最详细!! Linux 安装、配置教程

一、下载安装包 首先去官网下载VMware最新版本&#xff0c;以及发行版CentOS -7&#xff0c;懒得下载的可以私信我&#xff0c;我给你发包 其中&#xff0c;CentOS&#xff08;Community Enterprise Operating System&#xff09;是一个基于Linux的开源操作系统&#xff0c;它是…...

cocos creator 3学习记录01——如何替换图片

一、动态加载本地图片 1、通过将图片关联到CCClass属性上来进行代码切换。 1、这种方法&#xff0c;需要提前在脚本文件中声明好代表图片的CCClass属性。 2、然后拖动图片资源&#xff0c;到脚本内声明好的属性上以进行关联。 3、然后通过程序&#xff0c;来进行切换展示。…...

【Android Compose】ListView效果

【Android Compose】ListView效果 1、Column、Row 和 Box2、LazyColumn和LazyRow3、Compose 中的状态4、ListView效果5、android-compose-codelabs Jetpack Compose 使用入门 Jetpack Compose 教程 Jetpack Compose 1、Column、Row 和 Box Compose 中的三个基本标准布局元素是 …...

【Pytorch实战教程】Pytorch中.detach()的详细介绍

detach() 是 PyTorch 中用于分离张量的计算图的一个方法。它在处理计算图时非常有用,尤其是在需要停止梯度传播的情况下。以下是 detach() 方法的详细介绍: 方法概述 detach() 方法返回一个新的张量,从当前计算图中分离出来,即返回的张量不会参与梯度计算。这在某些情况下…...

AR 眼镜之-充电动画定制-实现方案

目录 &#x1f4c2; 前言 AR 眼镜系统版本 充电动画 1. &#x1f531; 技术方案 1.1 方案介绍 1.2 实现方案 关机充电动画 亮屏/锁屏充电动画 2. &#x1f4a0; 关机充电动画 2.1 关机充电动画核心处理类与路径 2.2 实现细节 步骤一&#xff1a;1&#xff09;定制 …...

AJAX-XMLHttpRequest 详解

(创作不易&#xff0c;感谢有你&#xff0c;你的支持&#xff0c;就是我前行的最大动力&#xff0c;如果看完对你有帮助&#xff0c;请留下您的足迹&#xff09; 目录 前言 XMLHttpRequest 概述 主要用途 工作流程 示例代码 GET 请求示例 POST 请求示例 注意事项 工作…...

内容管理系统 Contentful 与 Baklib

对于希望管理其产品和服务的在线文档或知识库以支持其客户和员工的组织来说&#xff0c;市场上有太多的平台和工具。 遵循的做法之一是使用无头内容管理系统 (CMS)。 如果您是这样的组织之一&#xff0c;正在考虑使用无头 CMS - Contentful 之一来管理您的在线知识库&#xff0…...

[Mysql-视图和存储过程]

视图 视图是从一个或者几个基本表&#xff08;或视图&#xff09;导出的表。它与基 本表不同&#xff0c;是一个虚表。 创建使用视图 # 视图 -- 视图只能用来查询&#xff0c;不能做增删改 -- 创建视图 -- create view 视图名【view_xxx / v_xxx】 -- as 查询语句 create view…...

Linux下C++静态链接库的生成以及使用

目录 一.前言二.生成静态链接库三.使用静态链接库 一.前言 这篇文章简单讨论一下Linux下如何使用gcc/g生成和使用C静态链接库&#xff08;.a文件&#xff09;。 二.生成静态链接库 先看下目录结构 然后看下代码 //demo.h#ifndef DEMO_H #define DEMO_H#include<string&g…...

【8月EI会议推荐】第四届区块链技术与信息安全国际会议

一、会议信息 大会官网&#xff1a;http://www.bctis.nhttp://www.icbdsme.org/ 官方邮箱&#xff1a;icbctis126.com 组委会联系人&#xff1a;杨老师 19911536763 支持单位&#xff1a;中原工学院、西安工程大学、齐鲁工业大学&#xff08;山东省科学院&#xff09;、澳门…...

2024年【甘肃省安全员B证】考试资料及甘肃省安全员B证模拟试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年甘肃省安全员B证考试资料为正在备考甘肃省安全员B证操作证的学员准备的理论考试专题&#xff0c;每个月更新的甘肃省安全员B证模拟试题祝您顺利通过甘肃省安全员B证考试。 1、【多选题】5kW以上电动机开关箱中电…...

结合el-upload上传组件,验证文件格式及大小

结合el-upload上传组件&#xff0c;验证文件格式及大小 效果如下&#xff1a; 代码如下&#xff1a; upgradeFirmwareInfo.vue页面 <template><div><el-dialog title"新增固件升级包" :visible.sync"dialogFormVisible"top"7vh&qu…...

配置php-fpm服务

nginx(unix domain socket方式) server {listen 80;#root /test/php/publiclocation / {#URL重写 例如隐藏index.phpif (!-f $request_filename) {rewrite ^(.*)$ /index.php?s/$1 last;break;}}location ~ [^/]\.php(/|$) {#try_files $uri 404;fastcgi_index index.php;…...

科普文:Linux系统安全加固指南

本指南仅关注安全性和隐私性&#xff0c;而不关注性能&#xff0c;可用性或其他内容。 列出的所有命令都将需要root特权。以“$”符号开头的单词表示一个变量&#xff0c;不同终端之间可能会有所不同。 选择正确的Linux发行版 选择一个好的Linux发行版有很多因素。 避免分发…...

MFC开发,自定义消息

在MFC开发中&#xff0c;主要核心机制就是消息机制。QT与之类似的机制就是信号与槽。QT中的信号与槽是非常容易自定义的&#xff0c;MFC也是如此&#xff0c;自定义也是比较方便&#xff0c;况且自定义消息或者控件在整个GUI图形化界面开发中也是非常重要的部分&#xff0c;上篇…...

如何在 SpringBoot 中优雅的做参数校验?

一、故事背景 关于参数合法性验证的重要性就不多说了&#xff0c;即使前端对参数做了基本验证&#xff0c;后端依然也需要进行验证&#xff0c;以防不合规的数据直接进入服务器&#xff0c;如果不对其进行拦截&#xff0c;严重的甚至会造成系统直接崩溃&#xff01; 本文结合…...

Godot入门 03世界构建1.0版

在game场景&#xff0c;删除StaticBody2D节点&#xff0c;添加TileMap节点 添加TileSet图块集 添加TileSet源 拖动图片到图块&#xff0c;自动创建图块 使用橡皮擦擦除。取消橡皮擦后按住Shift创建大型图块。 进入选择模式&#xff0c;TileMap选择绘制&#xff0c;选中图块后在…...

GitHub每日最火火火项目(7.26)

1. 项目名称&#xff1a;meta - llama / llama3 项目介绍&#xff1a;这是 Meta Llama 3 的官方 GitHub 站点。目前尚不清楚该项目的具体功能和特点&#xff0c;但从名称推测&#xff0c;它可能与 Llama 3 模型相关&#xff0c;或许涉及到该模型的开发、训练或应用等方面。 项…...

微服务实践和总结

H5原生组件web Component Web Component 是一种用于构建可复用用户界面组件的技术&#xff0c;开发者可以创建自定义的 HTML 标签&#xff0c;并将其封装为包含逻辑和样式的独立组件&#xff0c;从而在任何 Web 应用中重复使用。 <!DOCTYPE html> <html><head…...

Spring Boot中的策略模式:优雅地处理不同商品类型的订单

引言 在开发复杂的业务系统时&#xff0c;我们经常会遇到需要根据不同条件执行不同逻辑的情况。例如&#xff0c;在电商平台中&#xff0c;可能需要根据商品的不同类型&#xff08;如电子产品、服装、食品等&#xff09;来执行不同的业务逻辑&#xff0c;比如不同的库存管理、…...

django_创建菜单(实现整个项目的框架,调包)

文章目录 前言代码仓库地址在线演示网址启动网站的时候出现错误渲染路径的一些说明文件结构网页显示一条错误路由顺序js打包出现问题的代码函数没有起作用关于进度开发细节显示不了图片梳理一下函数调用的流程修改一些宽度参数classjs 里面的一些细节让三个按钮可以点击设置按钮…...

最新全新UI异次元荔枝V4.4自动发卡系统源码

简介&#xff1a; 最新全新UI异次元荔枝V4.4自动发卡系统源码 更新日志&#xff1a; 1增加主站货源系统 2支持分站自定义支付接口 3目前插件大部分免费 4UI页面全面更新 5分站可支持对接其他分站产品 6分站客服可自定义 7支持限定优惠 图片&#xff1a; 会员中心截图&…...

PyTorch安装CUDA标准流程(可解决大部分GPU无法使用问题)

最近一段时间在研究PyTorch中的GPU的使用方法&#xff0c;之前曾经安装过CUDA&#xff0c;不过在PyTorch中调用CUDA时无法使用。考虑到是版本不兼容问题&#xff0c;卸载后尝试了其他的版本&#xff0c;依旧没有能解决问题&#xff0c;指导查阅了很多资料后才找到了解决方案。 …...

C++从入门到起飞之——初始化列表类型转换static成员 全方位剖析!

&#x1f308;个人主页&#xff1a;秋风起&#xff0c;再归来~&#x1f525;系列专栏&#xff1a;C从入门到起飞 &#x1f516;克心守己&#xff0c;律己则安 目录 1、初始化列表 2、 类型转换 3. static成员 4、完结散花 1、初始化列表 • 之前我们实现构造函数…...

PHP框架简介

PHP是一种广泛使用的开源脚本语言&#xff0c;主要用于Web开发&#xff0c;它可以创建动态交互式Web页面。而PHP框架则是一套用于开发Web应用程序的工具和库的集合&#xff0c;它可以帮助开发者更高效地编写PHP代码&#xff0c;提高开发速度和代码的可维护性。 理解PHP框架&am…...

微信小程序-粘性组件

再次完善&#xff1a;将区域设置为粘性时&#xff0c;会脱离原有文档&#xff0c;使得出现下方页面突然遮盖一部分&#xff0c;不平滑 解决&#xff1a;给出一个新的空白区域&#xff0c;宽高与粘性区域一致&#xff0c;wx:if 控制其显示 /****************/ 后续补充&#…...

微服务注册中心

目录 1.微服务的注册中心 1.1 注册中⼼的主要作⽤ 1.2 常⻅的注册中⼼ 2.nacos简介 2.1 nacos实战⼊⻔ 2.2.1 搭建nacos环境 2.2.2 将商品微服务注册到nacos 3.服务调⽤Ribbon⼊⻔ 3.1 Ribbon概述 3.1.1 什么是Ribbon 3.1.2 Ribbon的主要作⽤ 3.2.2 ⼯程改造 4.服务…...

HDU1032——The 3n + 1 problem,HDU1033——Edge,HDU1034——Candy Sharing Game

目录 HDU1032——The 3n 1 problem 题目描述 运行代码 代码思路 HDU1033——Edge 题目描述 运行代码 代码思路 HDU1034——Candy Sharing Game 题目描述 运行代码 代码思路 HDU1032——The 3n 1 problem 题目描述 Problem - 1032 运行代码 #include <iostr…...

内网对抗-隧道技术篇防火墙组策略HTTP反向SSH转发出网穿透CrossC2解决方案

知识点&#xff1a; 1、C2/C2上线-CrossC2插件-多系统平台支持 2、隧道技术篇-应用层-SSH协议-判断&封装&建立&穿透 3、隧道技术篇-应用层-HTTP协议-判断&封装&建立&穿透隧道技术主要解决网络通讯问题&#xff1a;遇到防火墙就用隧道技术&#xff0c;…...

实战案例:如何用ChatGPT生成适合不同领域的高质量文章

随着人工智能技术的快速进展&#xff0c;制作高质量文章已变得轻而易举。尤其是OpenAI推出的ChatGPT&#xff0c;极大地简化了写作任务。接下来&#xff0c;本文将通过具体案例&#xff0c;详解如何利用ChatGPT撰写不同领域的高品质文章。 背景&#xff1a;光辉AI交流-免费问答…...

多线程案例-单例模式

单例模式是设计模式之一&#xff0c;能保证某个类在程序中只存在唯一一份实例&#xff0c;而不会创建出多个实例 单例模式的具体实现方法有很多&#xff0c;最常见的是 “饿汉” 和 “懒汉” 两种。 饿汉模式 class Singlenton{private static Singlenton instance new Sin…...

P6 优化篇 - 数据折线图可视化步骤

增加新页面, 则需要在 page.json里面增加页面信息 2.添加目录, 和路径 同时也要添加目录了 , 新建目录LineChart , 添加文件LineChart.vue 4.LineChart.vue 直接复制黏贴 <template><view class"container"><!-- 图表显示区域 --><view cla…...

优选算法之二分查找(上)

目录 一、二分查找 1.题目链接&#xff1a;704. 二分查找 2.题目描述&#xff1a; 3.算法流程&#xff1a; 4.算法代码&#xff1a; 二、在排序数组中查找元素的第一个和最后一个位置 1.题目链接&#xff1a;34. 在排序数组中查找元素的第一个和最后一个位置 2.题目描述…...

JavaScript(16)——定时器-间歇函数

开启定时器 setInterval(函数,间隔时间) 作用&#xff1a;每隔一段时间调用这个函数&#xff0c;时间单位是毫秒 例如&#xff1a;每一秒打印一个hello setInterval(function () { document.write(hello ) }, 1000) 注&#xff1a;如果是具名函数的话不能加小括号&#xf…...

VUE中的重点*

1.MVC 和 MVVM的区别&#xff1f; MVC&#xff1a;M&#xff08;model数据&#xff09;、V&#xff08;view视图&#xff09;&#xff0c;C&#xff08;controlle控制器&#xff09; 缺点是前后端无法独立开发&#xff0c;必须等后端接口做好了才可以往下走&#xff1b; 前端没…...

rabbitmq生产与消费

一、rabbitmq发送消息 一、简单模式 概述 一个生产者一个消费者模型 代码 //没有交换机&#xff0c;两个参数为routingKey和消息内容 rabbitTemplate.convertAndSend("test1_Queue","haha");二、工作队列模式 概述 一个生产者&#xff0c;多个消费者&a…...

spring-boot3.x整合Swagger 3 (OpenAPI 3) +knife4j

1.简介 OpenAPI阶段的Swagger也被称为Swagger 3.0。在Swagger 2.0后&#xff0c;Swagger规范正式更名为OpenAPI规范&#xff0c;并且根据OpenAPI规范的版本号进行了更新。因此&#xff0c;Swagger 3.0对应的就是OpenAPI 3.0版本&#xff0c;它是Swagger在OpenAPI阶段推出的一个…...

SM2隐式证书用户公私钥生成python代码实现

GMT0130-2023具体描述基于SM2算法的隐式证书公钥机制&#xff0c;这里尝试Python代码实现密钥生成部分功能&#xff0c;具体如下&#xff0c;椭圆曲线计算实现使用python第三方包gmssl。 #生成用户私钥Da和公钥Pa&#xff0c;其中Da&#xff08;tAdA)mod N&#xff0c;Pa可以直…...

IEC104转MQTT网关快速实现了IEC104到MQTT的转换和数据交互

随着智能电网技术的不断进步&#xff0c;IEC 104&#xff08;IEC 60870-5-104&#xff09;协议作为电力系统中重要的远动通信标准&#xff0c;正逐步融入更广泛的物联网生态系统中。亚马逊AWS&#xff08;Amazon Web Services&#xff09;&#xff0c;作为全球领先的云计算服务…...

【OpenCV C++20 学习笔记】调节图片对比度和亮度(像素变换)

调节图片对比度和亮度&#xff08;像素变换&#xff09; 原理像素变换亮度和对比度调整 代码实现更简便的方法结果展示 γ \gamma γ校正及其实操案例线性变换的缺点 γ \gamma γ校正低曝光图片矫正案例代码实现 原理 关于OpenCV的配置和基础用法&#xff0c;请参阅本专栏的其…...

web UI自动化测试 浏览器模式设置

自动化之浏览器模式设置 做selenium UI自动化测试时&#xff0c;每次都需要启动浏览器、用例运行结束后再关闭浏览器&#xff0c;浏览器启动相当地耗费时间&#xff0c;在本机运行用例的话还得放开双手&#xff0c;可以使用chrome的headless模式&#xff0c;让浏览器在后台运行…...