Vue原理解析
文章目录
- 1. VUE的响应式原理
- 1.1 ViewModel
- 1.2 双向绑定的基本原理
- 1.3 什么是响应性
- 1.4 Vue 中的响应性是如何工作的
- 2. Vue 渲染机制
- 2.1 虚拟 DOM
- 2.2 渲染管线
- 2.3 带编译时信息的虚拟 DOM
- 2.3.1 静态提升
- 2.3.2 修补标记 Flags
- 2.3.3 树结构打平
- 2.3.4 对 SSR 激活的影响
1. VUE的响应式原理
响应式的基本原理:双向数据绑定
,就是把Model
绑定到View
,当我们用JavaScript
代码更新Model
时,View
就会自动更新,在单向绑定的基础上,如果用户更新了View
,Model
的数据也会自动更新。
双向绑定由三个重要部分构成:
数据层(Model
):应用数据及业务逻辑
视图层(View
):应用的展示效果,各类UI组件
业务逻辑层(ViewModel
):框架封装的核心,负责将数据与视图关联起来
1.1 ViewModel
作用:
- 数据变化更新视图
- 视图变化更新数据
它还有两个主要部分组成:
- 监听器(
Observer
):对所有数据的属性进行监听 - 解析器(
Compiler
):对每个节点的指令进行扫描跟解析,根据指令模板替换数据,以及绑定相应的更新函数
1.2 双向绑定的基本原理
在 JavaScript
中有两种劫持属性访问的方式:Object.defineProperty
和 Proxy
。
Vue 2
使用Object.defineProperty
完全由于需支持更旧版本浏览器的限制。- 在
Vue 3
中使用了Proxy
来创建响应式对象,将getter/setter
用于ref
。
首先要对数据(data)
进行劫持监听
。所以需要设置一个监听器Observer
,用来监听所有的属性。
每一个组件都有一个Watcher
实例。如果属性发生变化,需要通知订阅者Watcher
,看是否需要更新。因为订阅者有多个,所以需要一个消息订阅器(发布者)Dep
(订阅者集合的管理数组)来专门收集这些订阅者,在Observer
和Watcher
之间进行统一管理。
还需要一个指令解析器Compile
,对每个节点元素进行扫描和解析,将相关指令初始化为一个订阅者Watcher
,并替换模板数据或绑定相应的函数,此时当订阅者Watcher
接收到相应属性的变化,就会执行对应的更新函数,从而更新视图。
1、实现一个监听器
Observer
,用来劫持并监听所有属性,如果发生变化,就通知订阅者。
2、实现一个订阅者Watcher
,可以收到属性的变化通知并执行相应的函数,从而更新视图。
3、实现一个解析器Compile
,可以扫描和解析每个节点的相关指令,并据此初始化视图和订阅器Watcher。
1.3 什么是响应性
如果我们在 JavaScript 写类似的逻辑:
let A0 = 1
let A1 = 2
let A2 = A0 + A1console.log(A2) // 3A0 = 2
console.log(A2) // 仍然是 3
当我们更改 A0 后,A2 不会自动更新。
那么我们如何在 JavaScript 中做到这一点呢?首先,为了能重新运行计算的代码来更新 A2,我们需要将其包装为一个函数:
let A2function update() {A2 = A0 + A1
}
然后,我们需要定义几个术语:
- 这个
update()
函数会产生一个副作用
,或者就简称为作用
,因为它会更改程序里的状态。 A0
和A1
被视为这个作用的依赖
,因为它们的值被用来执行这个作用。因此这次作用也可以说是一个它依赖的订阅者
。
我们需要一个魔法函数,能够在 A0
或 A1
(这两个依赖) 变化时调用 update()
(产生作用)。
whenDepsChange(update)
这个 whenDepsChange()
函数有如下的任务:
- 当一个变量被读取时进行追踪。例如我们执行了表达式
A0 + A1
的计算,则A0
和A1
都被读取到了。 - 如果一个变量在当前运行的
副作用
中被读取了,就将该副作用
设为此变量
的一个订阅者
。例如由于A0
和A1
在update()
执行时被访问到了,则update()
需要在第一次调用之后成为A0
和A1
的订阅者。 - 探测一个变量的变化。例如当我们给
A0
赋了一个新的值后,应该通知其所有订阅了的副作用重新执行。
1.4 Vue 中的响应性是如何工作的
我们是可以追踪一个对象的属性
进行读和写的。
在 JavaScript
中有两种劫持属性访问的方式:getter/setters
和 Proxies
。Vue 2 使用 getter/setters
完全由于需支持更旧版本浏览器的限制。而在 Vue 3
中使用了 Proxy
来创建响应式对象,将 getter/setter
用于 ref
。下面的伪代码将会说明它们是如何工作的:
function reactive(obj) {return new Proxy(obj, {get(target, key) {track(target, key)return target[key]},set(target, key, value) {target[key] = valuetrigger(target, key)}})
}function ref(value) {const refObject = {get value() {track(refObject, 'value')return value},set value(newValue) {value = newValuetrigger(refObject, 'value')}}return refObject
}
- 当你将一个响应性对象的属性解构为一个局部变量时,响应性就会“断开连接”,因为对局部变量的访问不再触发
get / set
代理捕获。 - 从
reactive()
返回的代理尽管行为上表现得像原始对象,但我们通过使用===
运算符还是能够比较出它们的不同。
在 track()
内部,我们会检查当前是否有正在运行的副作用。如果有,我们会查找到一个所有追踪了该属性的订阅者,它们存储在一个 Set
中,然后将当前这个副作用添加到该 Set
中。
// 这会在一个副作用就要运行之前被设置
// 我们会在后面处理它
let activeEffectfunction track(target, key) {if (activeEffect) {const effects = getSubscribersForProperty(target, key)effects.add(activeEffect)}
}
副作用订阅将被存储在一个全局的 WeakMap<target, Map<key, Set<effect>>>
数据结构中。如果在第一次追踪时没有找到对相应属性订阅的副作用集合,它将会在这里新建。这就是 getSubscribersForProperty()
函数所做的事。为了简化描述,我们跳过了它其中的细节。
在 trigger()
之中,我们会再查找到该属性的所有订阅副作用。但这一次我们是去调用它们:
function trigger(target, key) {const effects = getSubscribersForProperty(target, key)effects.forEach((effect) => effect())
}
现在让我们回到 whenDepsChange()
函数中:
function whenDepsChange(update) {const effect = () => {activeEffect = effectupdate()activeEffect = null}effect()
}
它包装了原先的 update
函数到一个副作用中,并在运行实际的更新之前,将它自己设为当前活跃的副作用。而在更新期间开启的 track()
调用,都将能定位到这个当前活跃的副作用。
此时,我们已经创建了一个能自动跟踪其依赖关系的副作用,它会在依赖关系更改时重新运行。我们称其为响应式副作用
。
Vue 提供了一个 API 来让你创建响应式副作用 watchEffect()
。事实上,你会发现它的使用方式和我们上面示例中说的魔法函数 whenDepsChange()
非常相似。我们可以用真正的 Vue API
改写上面的例子:
import { ref, watchEffect } from 'vue'const A0 = ref(0)
const A1 = ref(1)
const A2 = ref()watchEffect(() => {// 追踪 A0 和 A1A2.value = A0.value + A1.value
})// 将触发副作用
A0.value = 2
使用一个响应式副作用来更改一个 ref 并不是最优解,事实上使用计算属性会更直观简洁:
import { ref, computed } from 'vue'const A0 = ref(0)
const A1 = ref(1)
const A2 = computed(() => A0.value + A1.value)A0.value = 2
在内部,computed
会使用响应式副作用来管理失效与重新计算的过程。
那么,常见的响应式副作用的用例是什么呢?自然是更新 DOM!我们可以像下面这样实现一个简单的“响应式渲染”:
import { ref, watchEffect } from 'vue'const count = ref(0)watchEffect(() => {document.body.innerHTML = `计数:${count.value}`
})// 更新 DOM
count.value++
实际上,这与 Vue 组件保持状态和 DOM 同步的方式非常接近。每个组件实例创建一个响应式副作用来渲染和更新 DOM。当然,Vue 组件使用了比 innerHTML
更高效的方式来更新 DOM。这会在渲染机制
一章中详细介绍。
ref()
、computed()
和 watchEffect()
这些 API 都是组合式 API
的一部分,如果你至今只使用过选项式 API,那么你需要知道的是组合式 API 更贴近 Vue 底层的响应式系统。事实上,Vue 3 中的选项式 API 正是基于组合式 API 建立的。对该组件实例 (this
) 所有的属性访问都会触发 getter/setter
的响应式追踪,而像 watch
和 computed
这样的选项也是在内部调用相应等价的组合式 API。
2. Vue 渲染机制
2.1 虚拟 DOM
const vnode = {type: 'div',props: {id: 'hello'},children: [/* 更多 vnode */]
}
这里所说的 vnode
即一个纯 JavaScript 的对象 (一个“虚拟节点”),它代表着一个 <div>
元素。它包含我们创建实际元素所需的所有信息。它还包含更多的子节点,这使它成为虚拟 DOM 树的根节点。
一个运行时渲染器将会遍历整个虚拟 DOM 树,并据此构建真实的 DOM 树。这个过程被称为挂载 (mount)
。
如果我们有两份虚拟 DOM 树,渲染器将会有比较地遍历它们,找出它们之间的区别,并应用这其中的变化到真实的 DOM 上。这个过程被称为修补 (patch)
,又被称为“比较差异 (diffing)
”或“协调 (reconciliation)
”。
虚拟 DOM 带来的主要收益是它赋予了开发者编程式地、声明式地创建、审查和组合所需 UI 结构的能力,而把直接与 DOM 相关的操作交给了渲染器。
2.2 渲染管线
- 编译:Vue 模板被编译为了渲染函数:即用来返回虚拟 DOM 树的函数。这一步骤可以通过构建步骤提前完成,也可以通过使用运行时编译器即时完成。
- 挂载:运行时渲染器调用渲染函数,遍历返回的虚拟 DOM 树,并基于它创建实际的 DOM 节点。这一步会作为
响应式副作用
执行,因此它会追踪其中所用到的所有响应式依赖。 - 修补:当一个依赖发生变化后,副作用会重新运行,这时候会创建一个更新后的虚拟 DOM 树。运行时渲染器遍历这棵新树,将它与旧树进行比较,然后将必要的更新应用到真实 DOM 上去。
2.3 带编译时信息的虚拟 DOM
虚拟 DOM 在 React 和大多数其他实现中都是纯运行时的:协调算法无法预知新的虚拟 DOM 树会是怎样,因此它总是需要遍历整棵树、比较每个 vnode 上 props 的区别来确保正确性。另外,即使一棵树的某个部分从未改变,还是会在每次重渲染时创建新的 vnode,带来了完全不必要的内存压力。这也是虚拟 DOM 最受诟病的地方之一:这种有点暴力的协调过程通过牺牲效率来换取可声明性和正确性。
但实际上我们并不需要这样。在 Vue 中,框架同时控制着编译器和运行时。这使得我们可以为紧密耦合的模板渲染器应用许多编译时优化。编译器可以静态分析模板并在生成的代码中留下标记,使得运行时尽可能地走捷径。与此同时,我们仍旧保留了边界情况时用户想要使用底层渲染函数的能力。我们称这种混合解决方案为带编译时信息的虚拟 DOM
。
下面,我们将讨论一些 Vue 编译器用来提高虚拟 DOM 运行时性能的主要优化:
2.3.1 静态提升
在模板中常常有部分内容是不带任何动态绑定的:
<div><div>foo</div> <!-- 需提升 --><div>bar</div> <!-- 需提升 --><div>{{ dynamic }}</div>
</div>
foo
和 bar
这两个 div 是完全静态的,没有必要在重新渲染时再次创建和比对它们。Vue 编译器自动地会提升这部分 vnode 创建函数到这个模板的渲染函数之外,并在每次渲染时都使用这份相同的 vnode,渲染器知道新旧 vnode 在这部分是完全相同的,所以会完全跳过对它们的差异比对。
此外,当有足够多连续的静态元素时,它们还会再被压缩为一个“静态 vnode”,其中包含的是这些节点相应的纯 HTML 字符串。这些静态节点会直接通过 innerHTML
来挂载。同时还会在初次挂载后缓存相应的 DOM 节点。如果这部分内容在应用中其他地方被重用,那么将会使用原生的 cloneNode()
方法来克隆新的 DOM 节点,这会非常高效。
2.3.2 修补标记 Flags
对于单个有动态绑定的元素来说,我们可以在编译时推断出大量信息:
<!-- 仅含 class 绑定 -->
<div :class="{ active }"></div><!-- 仅含 id 和 value 绑定 -->
<input :id="id" :value="value"><!-- 仅含文本子节点 -->
<div>{{ dynamic }}</div>
在为这些元素生成渲染函数时,Vue 在 vnode 创建调用中直接编码了每个元素所需的更新类型:
createElementVNode("div", {class: _normalizeClass({ active: _ctx.active })
}, null, 2 /* CLASS */)
最后这个参数 2 就是一个修补标记 (patch flag)。一个元素可以有多个修补标记,会被合并成一个数字。运行时渲染器也将会使用位运算来检查这些标记,确定相应的更新操作:
if (vnode.patchFlag & PatchFlags.CLASS /* 2 */) {// 更新节点的 CSS class
}
位运算检查是非常快的。通过这样的修补标记,Vue 能够在更新带有动态绑定的元素时做最少的操作。
Vue 也为 vnode 的子节点标记了类型。举个例子,包含多个根节点的模板被表示为一个片段 (fragment),大多数情况下,我们可以确定其顺序是永远不变的,所以这部分信息就可以提供给运行时作为一个修补标记。
export function render() {return (_openBlock(), _createElementBlock(_Fragment, null, [/* children */], 64 /* STABLE_FRAGMENT */))
}
2.3.3 树结构打平
再来看看上面这个例子中生成的代码,你会发现所返回的虚拟 DOM 树是经一个特殊的 createElementBlock()
调用创建的:
export function render() {return (_openBlock(), _createElementBlock(_Fragment, null, [/* children */], 64 /* STABLE_FRAGMENT */))
}
这里我们引入一个概念“区块”,内部结构是稳定的一个部分可被称之为一个区块。在这个用例中,整个模板只有一个区块,因为这里没有用到任何结构性指令 (比如 v-if
或者 v-for
)。
每一个块都会追踪其所有带修补标记的后代节点 (不只是直接子节点),举个例子:
<div> <!-- root block --><div>...</div> <!-- 不会追踪 --><div :id="id"></div> <!-- 要追踪 --><div> <!-- 不会追踪 --><div>{{ bar }}</div> <!-- 要追踪 --></div>
</div>
编译的结果会被打平为一个数组,仅包含所有动态的后代节点:
div (block root)
- div 带有 :id 绑定
- div 带有 {{ bar }} 绑定
当这个组件需要重渲染时,只需要遍历这个打平的树而非整棵树。这也就是我们所说的树结构打平,这大大减少了我们在虚拟 DOM 协调时需要遍历的节点数量。模板中任何的静态部分都会被高效地略过。
v-if
和 v-for
指令会创建新的区块节点:
<div> <!-- 根区块 --><div><div v-if> <!-- if 区块 -->...<div></div>
</div>
一个子区块会在父区块的动态子节点数组中被追踪,这为他们的父区块保留了一个稳定的结构。
2.3.4 对 SSR 激活的影响
修补标记和树结构打平都大大提升了 Vue SSR 激活
的性能表现:
- 单个元素的激活可以基于相应 vnode 的修补标记走更快的捷径。
- 在激活时只有区块节点和其动态子节点需要被遍历,这在模板层面上实现更高效的部分激活。
相关文章:
Vue原理解析
文章目录1. VUE的响应式原理1.1 ViewModel1.2 双向绑定的基本原理1.3 什么是响应性1.4 Vue 中的响应性是如何工作的2. Vue 渲染机制2.1 虚拟 DOM2.2 渲染管线2.3 带编译时信息的虚拟 DOM2.3.1 静态提升2.3.2 修补标记 Flags2.3.3 树结构打平2.3.4 对 SSR 激活的影响1. VUE的响应…...
C# Lambda表达式含义及各种写法
Lambda表达式在各个语言中的表达方式都不太相同,本文重点介绍C#的Lambda表达式。 首先,Lambda表达式就是一个匿名的方法/函数。 以下面的一个完整版作为例子,前面是参数,后面是返回值: 由于 Lambda表达式和委托常常一起…...
计算机组成原理:1. 计算机系统概论
更好的阅读体验\huge{\color{red}{更好的阅读体验}}更好的阅读体验 文章目录1.1 计算机系统简介1.1.1 计算机软硬件概念1.1.2 计算机的层次1.1.3计算机组成和计算机体系结构1.2 计算机的基本组成1.2.1 冯诺伊曼计算机的特点1.2.2 计算机的硬件框图1.2.3 计算机的工作步骤1.3 计…...
【c#】c#常用小技巧方法整理(5)—— 字符串操作类
1、GetStrArray(string str, char speater, bool toLower) 把字符串按照分隔符转换成 List 2、GetStrArray(string str) 把字符串转 按照, 分割 换为数据 3、GetArrayStr(List list, string speater) 把 List 按照分隔符组装成 string 4、GetArrayStr(List list) 得到数组列表以…...
用队列实现栈VS用栈实现队列
之前我们就讲过队列,栈的基础知识,笔者之前有过详细的介绍,感兴趣的可以根据笔者的个人主页进行查找:https://blog.csdn.net/weixin_64308540/?typelately225. 用队列实现栈请你仅使用两个队列实现一个后入先出(LIFO&…...
MY2480-16P语音模块的使用
MY2480-16P语音模块的使用开发环境:STM32CUBEMXKEIL5辅助软件:串口助手、迅捷文字转语音一、MY2480-16P语音模块引脚图及引脚定义二、选择触发方式三、使用串口控制MY2480-16P语音模块四、模块使用指南开发环境:STM32CUBEMXKEIL5 辅助软件&a…...
I/O 多路复用
。新到来一个 TCP 连接,就需要分配一个进程或者线程,那么如果要达到 C10K,意味着要一台机器维护 1 万个连接,相当于要维护 1 万个进程/线程,操作系统就算死扛也是扛不住的。 一个进程虽然任一时刻只能处理一个请求&…...
2023 最新版网络安全保姆级指南,从0到1,建议收藏!
一、网络安全学习的误区 1.不要试图以编程为基础去学习网络安全 不要以编程为基础再开始学习网络安全,一般来说,学习编程不但学习周期长,且过渡到网络安全用到编程的用到的编程的关键点不多。一般人如果想要把编程学好再开始学习网络安全往…...
力扣39.组合总数
文章目录力扣39.组合总数题目描述方法1:深搜回溯力扣39.组合总数 题目描述 给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可…...
sql的case when用法详解
简单CASE WHEN函数: CASE SCORE WHEN A THEN 优 ELSE 不及格 END CASE SCORE WHEN B THEN 良 ELSE 不及格 END CASE SCORE WHEN C THEN 中 ELSE 不及格 END等同于,使用CASE WHEN条件表达式函数实现: CASE WHEN SCORE A THEN 优WHEN SCORE …...
AtCoder Grand Contest 061(题解)
A - Long Shuffle 这道题本质是一个找规律的题 既然是打表题,我们先暴力把他打出来 (盗一张图.jpg) 接下来就是在这张图中挖掘答案 我们可以明显的看到偶数行是有一些规律的 要么是相邻对的互换,要么不变 不变和互换的位置也有讲究,在二进制…...
生成系列论文:文本控制的3d点云生成 TextCraft(一):论文概览
TextCraft: Zero-Shot Generation of High-Fidelity and Diverse Shapes from Text 论文原文: https://arxiv.org/abs/2211.01427 论文的研究动机 DALL2已经在文本控制的图像生成上取得很好的效果,但是基于文本控制的3d点云生成的研究还不太成熟&#…...
IDEA常用插件
常用IDEA插件 Codota 插件下载地址:Codota AI Autocomplete for Java and JavaScript - IntelliJ IDEs Plugin | Marketplace IDEA的自动补全功能已经很强大了,但是这个插件的自动补全功能更加强大,这是一个基于AI技术,学习了大量…...
Spring的事务传播机制
多个事务方法相互调用时,事务如何在这些方法之间进行传播,Spring中提供了七种不同的传播机制,来保证事务的正常执行: REQUIRED:默认的传播机制,如果存在事务,则支持/加入当前事务,如…...
Python:路径之谜(DFS剪枝)
题目描述 小张冒充 X 星球的骑士,进入了一个奇怪的城堡。 城堡里边什么都没有,只有方形石头铺成的地面。 假设城堡地面是 nn 个方格。如下图所示。 按习俗,骑士要从西北角走到东南角。可以横向或纵向移动,但不能斜着走…...
阿里巴巴在开源压测工具 JMeter 上的实践和优化
Apache JMeter [1] 是 Apach 旗下的开源压测工具,创建于 1999 年初,迄今已有超过 20 年历史。JMeter 功能丰富,社区(用户群体)庞大,是主流开源压测工具之一。 性能测试通常集中在新系统上线或大型活动前&…...
React Draggable插件实现拖拽功能
React Draggable插件实现拖拽功能1.下载Draggable插件2.引入Draggable插件3.设置一个div,并设置样式,并用Draggable包裹起来4.设置拖拽的范围5.Draggable常用props1.下载Draggable插件 npm install react-draggable2.引入Draggable插件 // 引入拖拽插件…...
MySQL-运算符
算术运算符: 加法运算-: 减法运算*: 乘法运算/: 除法运算,返回商%: 求余运算,返回余数例:创建n5表,插入数字100,查看数据表分别查看、-、*、/、%mysql> create table n5(-> num int); Query OK, 0 rows affected…...
Hudi-基本概念(时间轴、文件布局、索引、表类型、查询类型、数据写、数据读、Compaction)
文章目录基本概念时间轴(TimeLine)文件布局(File Layout)Hudi表的文件结构Hudi存储的两个部分Hudi的具体文件说明索引(Index)原理索引选项全局索引与非全局索引索引的选择策略对事实表的延迟更新对事件表的去重对维度表的随机更删…...
数据分享|中国各省、各市、各区县分年、分月、逐日平均气温数据(2000年~2019年)
今天分享给大家的是 2000 年~2019 年中国各省、各市、各县的分年、分月、逐日的平均气温数据(单位:摄氏度) 原始数据来源于国家气象科学数据共享服务平台-中国地面气候资料日值数据集(V3.0),原始数据是各个观测站点的日度数据,为了方便大家使用,我使用 Barnes 方法(…...
steam/csgo搬砖,2023年最暴利的项目
这个项目赚钱主要来源于两个地方: 1.比如说今天美元的汇率是1美元6.8人民币,那我们有特定的渠道能拿到1美元5.0-5.5左右人民币的价格,100美元的汇率差利润就有180元左右的利润,当然这个价格是根据国际的汇率上下会有浮动的。 2.…...
RDSDRDSPolarDBPolarDB-X的区别
RDS 阿里云关系型数据库(Relational Database Service,简称RDS),是一种稳定可靠、可弹性伸缩的在线数据库服务。 基于阿里云分布式文件系统和高性能存储,RDS支持MySQL、SQL Server、PostgreSQL和PPAS(Post…...
【Python学习笔记】30.Python3 命名空间和作用域
前言 本章介绍Python的命名空间和作用域。 命名空间 先看看官方文档的一段话: A namespace is a mapping from names to objects.Most namespaces are currently implemented as Python dictionaries。 命名空间(Namespace)是从名称到对象的映射,大…...
后量子 KEM 方案:Kyber
参考文献: Bos J, Ducas L, Kiltz E, et al. CRYSTALS-Kyber: a CCA-secure module-lattice-based KEM[C]//2018 IEEE European Symposium on Security and Privacy (EuroS&P). IEEE, 2018: 353-367.Avanzi R, Bos J, Ducas L, et al. Crystals-kyber[J]. NIST…...
2019年广东工业大学腾讯杯新生程序设计竞赛(同步赛)
同步赛链接 A-原初的信纸(最值,STL) 题意: 找 n 个数的最大值. 参考代码: void solve() {int n;std::cin >> n;std::vector<int> a(n);for (auto &c : a)std::cin >> c;std::cout << *max_element…...
生产Nginx现大量TIME-WAIT,连接耗尽,该如何处理?
背景说明: 在尼恩读者50交流群中,是不是有小伙伴问: 尼恩,生产环境 Nginx 后端服务大量 TIME-WAIT , 该怎么办? 除了Nginx进程之外,还有其他的后端服务如: 尼恩,生产环境…...
Linux服务器clang-13安装(环境变量配置)
1.从llvm的github网址选择合适的release合适的运行平台进行下载,下载官方预编译的二进制压缩包。 2.将下载好的压缩包进行本地上传。 使用scp命令进行上传 scp -r -P 端口号 本地文件路径 服务器ID等:服务器上目标地址 3.解压(tar命令) 4.环境变量配…...
【C++】C/C++内存管理模板初阶
文章目录一、 C/C内存管理1. C/C内存分布2. C内存管理方式3. operator new与operator delete函数4. new和delete的实现原理5. 定位new表达式6. 常见面试题malloc/free和new/delete的区别内存泄漏二、模板初阶1. 泛型编程2. 函数模板3. 类模板一、 C/C内存管理 1. C/C内存分布 …...
笙默考试管理系统-index展示
public class PageList<T> : List<T> { public int PageIndex { get; private set; } //页索引 public int PageSize { get; private set; }//页大小 public int TotalPage { get; private set; }//总页数 public int TotalCo…...
前端基础知识6
谈谈你对语义化标签的理解语义化标签就是具有语义的标签,它可以清晰地向我们展示它的作用和用途。 清晰的代码结构:在页面没有css的情况下,也能够呈现出清晰的代码内容 有利于SEO: 爬虫依赖标签来确定关键字的权重,因此可以和搜索…...
建设网企业沟通平台/搜狗seo软件
最初"\r"(return)表示“回车”即回到行首,“\n”(next)表示“换行”即定位到下一行;UNIX和Linux使用“\n”换行,而Windows用“\r\n”(不是\n\r,已验证),macOS用…...
51软件测试网站/广告设计自学教程
在 Windows Vista and Windows 7上, 启动ARM-Cortex-A8_RTSM 平台下的debug配置可能会导致如下错误:DS-5 Debugger 的debug 会话使用telnet(不是串行连接)控制你的计算机连接到RTSM。这个错误发生在: 当你试图从debugger 连接到R…...
b2b 网站制作/b站推广网站
网友们在小编的贴吧里面留言,他们说到windows7 raid1重装系统的问题,他们的windowsXP系统出现了很多人问题了,需要重装系统,所以对于那些电脑新手来说当然不会了,现在小编就分享一个win7raid1重装系统图文教程给大家&a…...
江苏企业网站建设公司/seo职业培训学校
一、值类型(基本类型): 数值型: 十进制:0~9 八进制:0~7 十六进制:0X、0x 浮点型数据:传统记数(1.2),科学记数(6e3) 特殊值Infinity:超出所表示的…...
优惠券网站是不是很难做/谷歌sem服务商
在学习过程中主要感觉在学习方法的选择上也要下功夫思考,找到一个比较合适的学习方法,以下是个人感觉有用的方法: 1) 看视频。 看视频时要认真看、记、体会每一个动作,拿视频中教练讲的跟自己实际的动作作对比,以找到…...
有专业做淘宝网站的美工吗/高端网站设计定制
Path类 提供静态方法,完成路径字符串的常见操作 例如在C盘的文件夹a下的b文件夹下的1.mp3文件 C:\a\b\1.mp3 一.获取信息的方法: 1.获得路径:Path.GetDirectoryName(路径); 结果:C:\a\b 获得文件名:Path.GetFileName(路…...