教你Vue3 Compiler 优化细节,如何手写高性能渲染函数(下)
cac55 2024-10-26 08:14 7 浏览 0 评论
作者:HcySunYang
转发链接:https://mp.weixin.qq.com/s/cbLm56UcoV6DQI2jBMM8YQ
目录
教你Vue3 Compiler 优化细节,如何手写高性能渲染函数(上)
教你Vue3 Compiler 优化细节,如何手写高性能渲染函数(下)本篇
预字符串化
静态提升的 VNode 节点或节点树本身是静态的,那么能否将其预先字符串化呢?如下模板所示:
<div>
<p></p>
<p></p>
...20 个 p 标签
<p></p></div>
假设如上模板中有大量连续的静态的 p 标签,当采用了 hoist 优化时,结果如下:
cosnt hoist1 = createVNode('p', null, null, PatchFlags.HOISTED)
cosnt hoist2 = createVNode('p', null, null, PatchFlags.HOISTED)... 20 个 hoistx 变量
cosnt hoist20 = createVNode('p', null, null, PatchFlags.HOISTED)
render() {
return (openBlock(), createBlock('div', null, [
hoist1, hoist2, ...20 个变量, hoist20
]))}
预字符串化会将这些静态节点序列化为字符串并生成一个 Static 类型的 VNode:
const hoistStatic = createStaticVNode('<p></p><p></p><p></p>...20个...<p></p>')
render() {
return (openBlock(), createBlock('div', null, [
hoistStatic
]))}
这有几个明显的优势:
- 生成代码的体积减少
- 减少创建 VNode 的开销
- 减少内存占用
静态节点在运行时会通过 innerHTML 来创建真实节点,因此并非所有静态节点都是可以预字符串化的,可以预字符串化的静态节点需要满足以下条件:
- 非表格类标签:caption 、thead、tr、th、tbody、td、tfoot、colgroup、col
- 标签的属性必须是:
- 标准 HTML attribute: https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes
- 或 data-/aria- 类属性
当一个节点满足这些条件时代表这个节点是可以预字符串化的,但是如果只有一个节点,那么并不会将其字符串化,可字符串化的节点必须连续且达到一定数量才行:
- 如果节点没有属性,那么必须有连续 20 个及以上的静态节点存在才行,例如:<div><p></p><p></p>… 20 个 p 标签<p></p></div>
或者在这些连续的节点中有 5 个及以上的节点是有属性绑定的节点:
<div>
<p id="a"></p>
<p id="b"></p>
<p id="c"></p>
<p id="d"></p>
<p id="e"></p></div>
这段节点的数量虽然没有达到 20 个,但是满足 5 个节点有属性绑定。
这些节点不一定是兄弟关系,父子关系也是可以的,只要阈值满足条件即可,例如:
<div>
<p id="a">
<p id="b">
<p id="c">
<p id="d">
<p id="e"></p>
</p>
</p>
</p>
</p></div>
预字符串化会在编译时计算属性的值,例如:
<div>
<p :id="'id-' + 1">
<p :id="'id-' + 2">
<p :id="'id-' + 3">
<p :id="'id-' + 4">
<p :id="'id-' + 5"></p>
</p>
</p>
</p>
</p></div>
在与字符串化之后:
const hoistStatic = createStaticVNode('<p id="id-1"></p><p id="id-2"></p>.....<p id="id-5"></p>')
可见 id 属性值是计算后的。
Cache Event handler
如下组件的模板所示:
<Comp @change="a + b" />
这段模板如果手写渲染函数的话相当于:
render(ctx) {
return h(Comp, {
onChange: () => (ctx.a + ctx.b)
})}
很显然,每次 render 函数执行的时候,Comp 组件的 props 都是新的对象,onChange 也会是全新的函数。这会导致触发 Comp 组件的更新。
当 Vue3 Compiler 开启 prefixIdentifiers 以及 cacheHandlers 时,这段模板会被编译为:
render(ctx, cache) {
return h(Comp, {
onChange: cache[0] || (cache[0] = ($event) => (ctx.a + ctx.b))
})}
这样即使多次调用渲染函数也不会触发 Comp 组件的更新,因为 Vue 在 patch 阶段比对 props 时就会发现 onChange 的引用没变。
如上代码中 render 函数的 cache 对象是 Vue 内部在调用渲染函数时注入的一个数组,像下面这种:
render.call(ctx, ctx, [])
实际上,我们即使不依赖编译也能手写出具备 cache 能力的代码:
const Comp = {
setup() {
// 在 setup 中定义 handler
const handleChange = () => {/* ... */}
return () => {
return h(AnthorComp, {
onChange: handleChange // 引用不变
})
}
}}
因此我们最好不要写出如下这样的代码:
const Comp = {
setup() {
return () => {
return h(AnthorComp, {
onChang(){/*...*/} // 每次渲染函数执行,都是全新的函数
})
}
}}
v-once
这是 Vue2 就支持的功能,v-once 是一个“很指令”的指令,因为它就是给编译器看的,当编译器遇到 v-once 时,会利用我们刚刚讲过的 cache 来缓存全部或者一部分渲染函数的执行结果,例如如下模板:
<div> <div v-once>{{ foo }}</div></div>
会被编译为:
render(ctx, cache) {
return (openBlock(), createBlock('div', null, [
cache[1] || (cache[1] = h("div", null, ctx.foo, 1 /* TEXT */))
]))}
这样就缓存了这段 vnode。既然 vnode 已经被缓存了,后续的更新就都会读取缓存的内容,而不会重新创建 vnode 对象了,同时在 Diff 的过程中也就不需要这段 vnode 参与了,因此你通常会看到编译后的代码更接近如下内容:
render(ctx, cache) {
return (openBlock(), createBlock('div', null, [
cache[1] || (
setBlockTracking(-1), // 阻止这段 VNode 被 Block 收集
cache[1] = h("div", null, ctx.foo, 1 /* TEXT */),
setBlockTracking(1), // 恢复
cache[1] // 整个表达式的值
)
]))}
稍微解释一下这段代码,我们已经讲解过何为 “Block Tree”,而 openBlock() 和 createBlock() 函数用来创建一个 Block。而 setBlockTracking(-1) 则用来暂停收集的动作,所以在 v-once 编译生成的代码中你会看到它,这样使用 v-once 包裹的内容就不会被收集到父 Block 中,也就不参与 Diff 了。
所以,v-once 带来的性能提升来自两方面:
- 1、VNode 的创建开销
- 2、无用的 Diff 开销
但其实我们不通过模板编译,一样可以通过缓存 VNode 来减少 VNode 的创建开销:
const Comp = {
setup() {
// 缓存 content
const content = h('div', 'xxxx')
return () => {
return h('section', content)
}
}}
但这样避免不了无用的 Diff 开销,因为我们没有使用 Block Tree 优化模式。
这里有必要提及的一点是:在 Vue2.5.18+ 以及 Vue3 中 VNode 是可重用的,例如我们可以在不同的地方多次使用同一个 VNode 节点:
const Comp = {
setup() {
const content = h('div', 'xxxx')
return () => {
// 多次渲染 content
return h('section', [content, content, content])
}
}}
手写高性能渲染函数
接下来我们将进入重头戏环节,我们尝试手写优化模式的渲染函数。
几个需要记住的小点:
- 一个 Block 就是一个特殊的 VNode,可以理解为它只是比普通 VNode 多了一个 dynamicChildren 属性
- createBlock() 函数和 createVNode() 函数的调用签名几乎相同,实际上 createBlock() 函数内部就是封装了 createVNode(),这再次证明 Block 就是 VNode。
- 在调用 createBlock() 创建 Block 前要先调用 openBlock() 函数,通常这两个函数配合逗号运算符一同出现:render() {return (openBlock(), createBlock('div'))}
Block Tree 是灵活的:
在之前的介绍中根节点以 Block 的角色存在的,但是根节点并不必须是 Block,我们可以在任意节点开启 Block:
setup() {
return () => {
return h('div', [
(openBlock(), createBlock('p', null, [/*...*/]))
])
}}
这也是可以的,因为渲染器在 Diff 的过程中如果 VNode 带有 dynamicChildren 属性,会自动进入优化模式。但是我们通常会让根节点充当 Block 角色。
正确地使用 PatchFlags:
PatchFlags 用来标记一个元素需要更新的内容,例如当元素有动态的 class绑定时,我们需要使用 PatchFlags.CLASS 标记:
const App = {
setup() {
const refOk = ref(true)
return () => {
return (openBlock(), createBlock('div', null, [
createVNode('p', { class: { foo: refOk.value } }, 'hello', PatchFlags.CLASS) // 使用 CLASS 标记
]))
}
}}
如果使用了错误的标记则可能导致更新失败,下面列出详细的标记使用方式:
- PatchFlags.CLASS - 当有动态的 class 绑定时使用
- PatchFlags.STYLE - 当有动态的 style 绑定时使用,例如:createVNode(‘p’, { style: { color: refColor.value } }, ‘hello’, PatchFlags.STYLE)
- PatchFlags.TEXT - 当有动态的文本节点是使用,例如:createVNode(‘p’, null, refText.value, PatchFlags.TEXT)
- PatchFlags.PROPS - 当有除了 class 和 style 之外的其他动态绑定属性时,例如:createVNode(‘p’, { foo: refVal.value }, ‘hello’, PatchFlags.PROPS, [‘foo’])
这里需要注意的是,除了要使用 PatchFlags.PROPS 之外,还要提供第五个参数,一个数组,包含了动态属性的名字。
- PatchFlags.FULL_PROPS - 当有动态 name 的 props 时使用,例如:createVNode(‘p’, { [refKey.value]: ‘val’ }, ‘hello’, PatchFlags.FULL_PROPS)
实际上使用 FULL_PROPS 等价于对 props 的 Diff 与传统 Diff 一样。其实,如果觉得心智负担大,我们大可以全部使用 FULL_PROPS,这么做的好处是:
- 避免误用 PatchFlags 导致的 bug
- 减少心智负担的同时,虽然失去了 props diff 的性能优势,但是仍然可以享受 Block Tree 的优势。
当同时存在多种更新,需要将 PatchFlags 进行按位或运算,例如:PatchFlags.CLASS | PatchFlags.STYLE 。
NEED_PATCH 标识
为什么单独把这个标志拿出来讲呢,它比较特殊,需要我们额外注意。当我们使用 ref 或 onVNodeXXX 等 hook 时(包括自定义指令),需要使用该标志,以至于它可以被父级 Block 收集,详细原因我们在静态提升一节里面讲解过了:
const App = {
setup() {
const refDom = ref(null)
return () => {
return (openBlock(), createBlock('div', null,[
createVNode('p',
{
ref: refDom,
onVnodeBeforeMount() {/* ... */}
},
null,
PatchFlags.NEED_PATCH
)
]))
}
}}
该使用 Block 的地方必须用
在最开始的时候,我们讲解了有些指令会导致 DOM 结构不稳定,从而必须使用 Block 来解决问题。手写渲染函数也是一样:
- 分支判断使用 Block:
const App = {
setup() {
const refOk = ref(true)return () => {
return (openBlock(), createBlock('div', null, [
refOk.value
// 这里使用 Block
? (openBlock(), createBlock('div', { key: 0 }, [/* ... */]))
: (openBlock(), createBlock('div', { key: 1 }, [/* ... */]))
]))}
}
}
这里使用 Block 的原因我们在前文已经讲解过了,但这里需要强调的是,除了分支判断要使用 Block 之外,还需要为 Block 指定不同的 key 才行。
- 列表使用 Block:
当我们渲染列表时,我们常常写出如下代码:
const App = {
setup() {
const obj = reactive({ list: [ { val: 1 }, { val: 2 } ] })
return () => {
return (openBlock(), createBlock('div', null,
// 渲染列表
obj.list.map(item => {
return createVNode('p', null, item.val, PatchFlags.TEXT)
})
))
}
}}
这么写在非优化模式下是没问题的,但我们现在使用了 Block,前文已经讲过为什么 v-for 需要使用 Block 的原因,试想当我们执行如下语句修改数据:
obj.list.splice(0, 1)
这就会导致 Block 中收集的动态节点不一致,最终 Diff 出现问题。解决方案就是让整个列表作为一个 Block,这时我们需要使用 Fragment:
const App = {
setup() {
const obj = reactive({ list: [ { val: 1 }, { val: 2 } ] })
return () => {
return (openBlock(), createBlock('div', null, [
// 创建一个 Fragment,并作为 Block 角色
(openBlock(true), createBlock(Fragment, null,
// 在这里渲染列表
obj.list.map(item => {
return createVNode('p', null, item.val, PatchFlags.TEXT)
}),
// 记得要指定正确的 PatchFlags
PatchFlags.UNKEYED_FRAGMENT
))
]))
}
}}
总结一下:
- 对于列表我们应该始终使用 Fragment,并作为 Block 的角色
- 如果 Fragment 的 children 没有指定 key,那么应该为 Fragment 打上 PatchFlags.UNKEYED_FRAGMENT。相应的,如果指定了 key 就应该打上 PatchFlags.KEYED_FRAGMENT
- 注意到在调用 openBlock(true) 时,传递了参数 true,这代表这个 Block 不会收集 dynamicChildren,因为无论是 KEYED 还是 UNKEYED的 Fragment,在 Diff 它的 children 时都会回归传统 Diff 模式,因此不需要收集 dynamicChildren。
这里还有一点需要注意,在 Diff Fragment 时,由于回归了传统 Diff,我们希望尽快恢复优化模式,同时保证后续收集的可控性,因此通常会让 Fragment 的每一个子节点都作为 Block 的角色:
const App = {
setup() {
const obj = reactive({ list: [ { val: 1 }, { val: 2 } ] })
return () => {
return (openBlock(), createBlock('div', null, [
(openBlock(true), createBlock(Fragment, null,
obj.list.map(item => {
// 修改了这里
return (openBlock(), createBlock('p', null, item.val, PatchFlags.TEXT))
}),
PatchFlags.UNKEYED_FRAGMENT
))
]))
}
}}
最后再说一下稳定的 Fragment,如果你能确定列表永远不会变化,例如你能确定 obj.list 是不会变化的,那么你应该使用:PatchFlags.STABLE_FRAGMENT标志,并且调用 openBlcok() 去掉参数,代表收集 dynamicChildren:
const App = {
setup() {
const obj = reactive({ list: [ { val: 1 }, { val: 2 } ] })
return () => {
return (openBlock(), createBlock('div', null, [
// 调用 openBlock() 不要传参
(openBlock(), createBlock(Fragment, null,
obj.list.map(item => {
// 列表中的任何节点都不需要是 Block 角色
return createVNode('p', null, item.val, PatchFlags.TEXT)
}),
// 稳定的片段
PatchFlags.STABLE_FRAGMENT
))
]))
}
}}
如上注释所述。
- 使用动态 key 的元素应该是 Block
正如在静态提升一节中所讲的,当元素使用动态 key 的时候,即使两个元素的其他方面完全一样,那也是两个不同的元素,需要做替换处理,在 Block Tree 中应该以 Block 的角色存在,因此如果一个元素使用了动态 key,它应该是一个 Block:
const App = {
setup() {
const refKey = ref('foo')
return () => {
return (openBlock(), createBlock('div', null,[
// 这里应该是 Block
(openBlock(), createBlock('p', { key: refKey.value }))
]))
}
}}
这实际上是必须的,详情查看 https://github.com/vuejs/vue-next/issues/938 。
使用 Slot hint
我们在“稳定的 Fragment”一节中提到了 slot hint,当我们为组件编写插槽内容时,为了告诉 runtime:“我们已经能够保证插槽内容的结构稳定”,则需要使用 slot hint:
render() {
return (openBlock(), createBlock(Comp, null, {
default: () => [
refVal.value
? (openBlock(), createBlock('p', ...))
? (openBlock(), createBlock('div', ...))
],
// slot hint
_: 1
}))}
当然如果你不能保证这一点,或者觉得心智负担大,那么就不要写 hint 了。
使用 $stable hint
$stable hint 和之前讲的优化策略不同,前文中的策略都是假设渲染器在优化模式下工作的,而 $stable 用于非优化模式,也就是我们平时写的渲染函数。那么它有什么用呢?如下代码所示(使用 tsx 演示):
export const App = defineComponent({
name: 'App',
setup() {
const refVal = ref(true)
return () => {
refVal.value
return (
<Hello>
{
{ default: () => [<p>hello</p>] }
}
</Hello>
)
}
}})
如上代码所示,渲染函数中读取了 refVal.value 的值,建立了依赖收集关系,当修改 refVal 的值时,会触发 <Hello> 组件的更新,但是我们发现 Hello 组件从来没有 props 变化,二来它的插槽内容是静态的,因此不应该更新才对,这时我们可以使用 $stable hint:
export const App = defineComponent({
name: 'App',
setup() {
const refVal = ref(true)
return () => {
refVal.value
return (
<Hello>
{
{ default: () => [<p>hello</p>], $stable: true } // 修改了这里
}
</Hello>
)
}
}})
为组件正确地使用 DYNAMIC_SLOTS
当我们动态构建 slots 时,需要为组件的 VNode 指定 PatchFlags.DYNAMIC_SLOTS,否则将导致更新失败。什么是动态构建 slots呢?通常情况下是指:依赖当前 scope 变量构建的 slots,例如:
render() {
// 使用当前组件作用域的变量
const slots ={}
// 常见的场景
// 情况一:条件判断
if (refVal.value) {
slots.header = () => [h('p', 'hello')]
}
// 情况二:循环
refList.value.forEach(item => {
slots[item.name] = () => [...]
})
// 情况三:动态 slot 名称,情况二包含情况三
slots[refName.value] = () => [...]
return (openBlock(), createBlock('div', null, [
// 这里要使用 PatchFlags.DYNAMIC_SLOTS
createVNode(Comp, null, slots, PatchFlags.DYNAMIC_SLOTS)
]))}
如上注释所述。
以上,不知道到达这里的同学有多少,Don’t stop learning…
本篇已完结
推荐Vue学习资料文章:
《Deno将停止使用TypeScript,并公布五项具体理由》
《为什么Vue3.0不再使用defineProperty实现数据监听?》
《如何写出优秀后台管理系统?11个经典模版拿去不谢「干货」》
《一个由 Vue 作者尤雨溪开发的 web 开发工具—vite》
《提高10倍打包速度工具Snowpack 2.0正式发布,再也不需要打包器》
《大厂Code Review总结Vue开发规范经验「值得学习」》
《带你了解 vue-next(Vue 3.0)之 炉火纯青「实践」》
《「干货」Vue+高德地图实现页面点击绘制多边形及多边形切割拆分》
《细品pdf.js实践解决含水印、电子签章问题「Vue篇」》
《Vue仿蘑菇街商城项目(vue+koa+mongodb)》
《基于 electron-vue 开发的音乐播放器「实践」》
《「实践」Vue项目中标配编辑器插件Vue-Quill-Editor》
《「干货」Deno TCP Echo Server 是怎么运行的?》
《「实践」基于Apify+node+react/vue搭建一个有点意思的爬虫平台》
《「实践」深入对比 Vue 3.0 Composition API 和 React Hooks》
《前端网红框架的插件机制全梳理(axios、koa、redux、vuex)》
《深入学习Vue的data、computed、watch来实现最精简响应式系统》
《10个实例小练习,快速入门熟练 Vue3 核心新特性(一)》
《10个实例小练习,快速入门熟练 Vue3 核心新特性(二)》
《教你部署搭建一个Vue-cli4+Webpack移动端框架「实践」》
《尤大大细品VuePress搭建技术网站与个人博客「实践」》
《是什么导致尤大大选择放弃Webpack?【vite 原理解析】》
《带你了解 vue-next(Vue 3.0)之 小试牛刀【实践】》
《带你了解 vue-next(Vue 3.0)之 初入茅庐【实践】》
《一篇文章教你并列比较React.js和Vue.js的语法【实践】》
《深入浅出通过vue-cli3构建一个SSR应用程序【实践】》
《聊聊昨晚尤雨溪现场针对Vue3.0 Beta版本新特性知识点汇总》
《【新消息】Vue 3.0 Beta 版本发布,你还学的动么?》
《Vue + Koa从零打造一个H5页面可视化编辑器——Quark-h5》
《深入浅出Vue3 跟着尤雨溪学 TypeScript 之 Ref 【实践】》
《手把手教你深入浅出vue-cli3升级vue-cli4的方法》
《Vue 3.0 Beta 和React 开发者分别杠上了》
《手把手教你用vue drag chart 实现一个可以拖动 / 缩放的图表组件》
《Vue3 尝鲜》
《2020 年,Vue 受欢迎程度是否会超过 React?》
《手把手教你Vue解析pdf(base64)转图片【实践】》
《手把手教你Vue之父子组件间通信实践讲解【props、$ref 、$emit】》
《深入浅出Vue3 的响应式和以前的区别到底在哪里?【实践】》
《干货满满!如何优雅简洁地实现时钟翻牌器(支持JS/Vue/React)》
《基于Vue/VueRouter/Vuex/Axios登录路由和接口级拦截原理与实现》
《手把手教你D3.js 实现数据可视化极速上手到Vue应用》
《吃透 Vue 项目开发实践|16个方面深入前端工程化开发技巧【上】》
《吃透 Vue 项目开发实践|16个方面深入前端工程化开发技巧【中】》
《吃透 Vue 项目开发实践|16个方面深入前端工程化开发技巧【下】》
作者:HcySunYang
转发链接:https://mp.weixin.qq.com/s/cbLm56UcoV6DQI2jBMM8YQ
相关推荐
- 让组策略保护Windows XP的安全
-
默认安装完WindowsXP之后,我们的WindowsXP并不很安全。因此,我们有必要对系统进行一些修修补补,一般情况下我们都要动用到注册表。诚然,修改注册表是一种非常有效的方法,但是它需要一定的...
- 你造吗?十种方式保护你免受"零日攻击"
-
|责编:王迪WindowsXP的寿终正寝,数据安全问题又再一次成为人们关注的焦点。近日,微软透漏,一个基于InternetExplorer的“零日攻击”给用户带来了严重破坏。“零日攻击”一种利用...
- 特立独行——打造游戏专用独立系统
-
大部分人的电脑是为了学习和工作用的,所以,如果你是一个游戏迷,那么推荐你安装一个独立系统专用于游戏,做到工作娱乐两不相扰。方案1:游戏专用移动WindowsXP目的:解决游戏兼容性问题喜欢玩游戏的都...
- 驰为VX8 3G Win8入门教程篇
-
距离Win8.1的正式发布也将近1年了,凭借着Win8.1在移动便携以及娱乐办公上的优势,现在的Win8平板越来越受到消费者的追捧,而驰为VX83G就是其中一款,搭载了卓越的英特尔Z3735G四核芯,...
- 易淘收银软件说明
-
易淘收银系统,简称易淘收银,专为小型及连锁零售、餐饮行业打造。基于SaaS模式,智能便捷,无需维护,轻量级设计却功能强大,简约而不失专业,助力门店高效管理收银。1、前台系统:收银客户端;2、后台系...
- CAD打不开怎么办?原因可能是电脑中毒了,6步就能完美解决问题
-
一、问题描述我的CAD安装后无法打开,安装过程中没有出现任何问题,但是安装后打开就出现一个对话框“DBXCAS0”点击后又出现“FATALERROR:UnhandledAccessViola...
- 腾讯QQ6.1正式版发布更新
-
2014-07-2405:12:00作者:张林【中关村在线软件资讯】7月24日消息:腾讯QQ官网小幅更新了QQ6.1正式版,最新版本号升级至11905,继续主打扁平化、炫酷登录窗口、支持同步最近一...
- Win10等网页版OneDrive无法登陆怎么办?
-
IT之家(www.ithome.com):Win10等网页版OneDrive无法登陆怎么办?Win10之家报道,微软OneDrive云网盘是跨平台的数据同步和存储服务,支持WindowsPC(如Wi...
- 经典回顾:折戟沉沙的Windows Longhorn有着惊艳的登录屏幕
-
尽管微软原先计划让WindowsLonghorn继承WindowsXP操作系统的衣钵,但这个充满雄心壮志的操作系统项目最终还是未能迎来曙光,而是被微软用WindowsVista取而代...
- 电脑怎么优化
-
电脑配置和宽带流量也是硬件,但这些要求其实并不需要很高,关键还是怎么去安全使用电脑并进行有效的优化。电脑的应用和优化处理一、电脑的应用和优化处理二、目前,大家使用的个人电脑,配置方面均没多大问题,比如...
- 怎么安装usb驱动
-
USB驱动主要是针对WIN98时代的说法,如今WINXP已集成大部分USB驱动,通常都能识别。只有极少数情况下,例如手机、打印机或扫描仪等办公设备的USB驱动可能无法自动识别。1、USB驱动偶尔无法...
- 普通话考试多名考生信息被泄露,接投诉后涉事网站被限制访问
-
“陕西普通话成绩查询网(sxpth.cn)”泄露个人信息网站截图网传图片显示,407名普通话考试考生的姓名、身份证号码等个人信息疑似被泄露。9月26日下午,涉事网站sxpth.cn的域名注册商——成...
- 电脑伪技巧——个人电脑无需设置登录密码
-
默认情况下,我们每次登录系统都要输入登录账户对应的密码才能进入桌面。有些朋友觉得这样很麻烦,由于电脑只是自己使用,还不如不要设置密码,这样每次可以自动登录。大家知道,账户密码是系统验证用户合法性的唯一...
- Windows 10/11 自带远程桌面:实用技巧与操作指南
-
Windows10/11自带远程桌面:实用技巧与操作指南在当今快节奏的数字时代,远程访问和控制计算机的需求日益增长。微软在Windows10和Windows11中内置了远程桌面功能,为用户提供...
- 不升级系统的5大原因造吗?
-
2015-01-2405:54:00作者:陈占伟Windows10系统的发布,让人们重新将目光聚焦到生命力长久的Windows系统之上。如今操作系统越来越多,似乎Windows升级的获得的关注度...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- 如何绘制折线图 (52)
- javaabstract (48)
- 新浪微博头像 (53)
- grub4dos (66)
- s扫描器 (51)
- httpfile dll (48)
- ps实例教程 (55)
- taskmgr (51)
- s spline (61)
- vnc远程控制 (47)
- 数据丢失 (47)
- wbem (57)
- flac文件 (72)
- 网页制作基础教程 (53)
- 镜像文件刻录 (61)
- ug5 0软件免费下载 (78)
- debian下载 (53)
- ubuntu10 04 (60)
- web qq登录 (59)
- 笔记本变成无线路由 (52)
- flash player 11 4 (50)
- 右键菜单清理 (78)
- cuteftp 注册码 (57)
- ospf协议 (53)
- ms17 010 下载 (60)