Vue视图渲染原理解析,从构建VNode到生成真实节点树
前言
在 Vue
核心中除了响应式原理外,视图渲染也是重中之重。我们都知道每次更新数据,都会走视图渲染的逻辑,而这当中牵扯的逻辑也是十分繁琐。
本文主要解析的是初始化视图渲染流程,你将会了解到从挂载组件开始,Vue
是如何构建 VNode
,又是如何将 VNode
转为真实节点并挂载到页面。
挂载组件($mount)
Vue
是一个构造函数,通过 new
关键字进行实例化。
// src/core/instance/index.js
function Vue (options) {if (process.env.NODE_ENV !== 'production' &&!(this instanceof Vue)) {warn('Vue is a constructor and should be called with the `new` keyword')}this._init(options)
}
复制代码
在实例化时,会调用 _init
进行初始化。
// src/core/instance/init.js
Vue.prototype._init = function (options?: Object) {const vm: Component = this// ...if (vm.$options.el) {vm.$mount(vm.$options.el)}}
复制代码
_init
内会调用 $mount
来挂载组件,而 $mount
方法实际调用的是 mountComponent
。
// src/core/instance/lifecycle.js
export function mountComponent (vm: Component,el: ?Element,hydrating?: boolean
): Component {vm.$el = el// ...callHook(vm, 'beforeMount')let updateComponent/* istanbul ignore if */if (process.env.NODE_ENV !== 'production' && config.performance && mark) {// ...} else {updateComponent = () => {vm._update(vm._render(), hydrating) // 渲染页面函数}}// we set this to vm._watcher inside the watcher's constructor// since the watcher's initial patch may call $forceUpdate (e.g. inside child// component's mounted hook), which relies on vm._watcher being already definednew Watcher(vm, updateComponent, noop, { // 渲染watcherbefore () {if (vm._isMounted && !vm._isDestroyed) {callHook(vm, 'beforeUpdate')}}}, true /* isRenderWatcher */)hydrating = false// manually mounted instance, call mounted on self// mounted is called for render-created child components in its inserted hookif (vm.$vnode == null) {vm._isMounted = truecallHook(vm, 'mounted')}return vm
}
复制代码
mountComponent
除了调用一些生命周期的钩子函数外,最主要是 updateComponent
,它就是负责渲染视图的核心方法,其只有一行核心代码:
vm._update(vm._render(), hydrating)
复制代码
vm._render
创建并返回 VNode
,vm._update
接受 VNode
将其转为真实节点。
updateComponent
会被传入 渲染Watcher
,每当数据变化触发 Watcher
更新就会执行该函数,重新渲染视图。updateComponent
在传入 渲染Watcher
后会被执行一次进行初始化页面渲染。
所以我们着重分析的是 vm._render
和 vm._update
两个方法,这也是本文主要了解的原理——Vue
视图渲染流程。
构建VNode(_render)
首先是 _render
方法,它用来构建组件的 VNode
。
// src/core/instance/render.js
Vue.prototype._render = function () {const { render, _parentVnode } = vm.$optionsvnode = render.call(vm._renderProxy, vm.$createElement)return vnode
}
复制代码
_render
内部会执行 render
方法并返回构建好的 VNode
。render
一般是模板编译后生成的方法,也有可能是用户自定义。
// src/core/instance/render.js
export function initRender (vm) {vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true)
}
复制代码
initRender
在初始化就会执行为实例上绑定两个方法,分别是 vm._c
和 vm.$createElement
。它们两者都是调用 createElement
方法,它是创建 VNode
的核心方法,最后一个参数用于区别是否为用户自定义。
vm._c
应用场景是在编译生成的 render
函数中调用,vm.$createElement
则用于用户自定义 render
函数的场景。就像上面 render
在调用时会传入参数 vm.$createElement
,我们在自定义 render
函数接收到的参数就是它。
createElement
// src/core/vdom/create-elemenet.js
export function createElement (context: Component,tag: any,data: any,children: any,normalizationType: any,alwaysNormalize: boolean
): VNode | Array<VNode> {if (Array.isArray(data) || isPrimitive(data)) {normalizationType = childrenchildren = datadata = undefined}if (isTrue(alwaysNormalize)) {normalizationType = ALWAYS_NORMALIZE}return _createElement(context, tag, data, children, normalizationType)
}
复制代码
createElement
方法实际上是对 _createElement
方法的封装,它允许传入的参数更加灵活。
export function _createElement (context: Component,tag?: string | Class<Component> | Function | Object,data?: VNodeData,children?: any,normalizationType?: number
): VNode | Array<VNode> {if (isDef(data) && isDef(data.is)) {tag = data.is}if (!tag) {// in case of component :is set to falsy valuereturn createEmptyVNode()}// support single function children as default scoped slotif (Array.isArray(children) &&typeof children[0] === 'function') {data = data || {}data.scopedSlots = { default: children[0] }children.length = 0}if (normalizationType === ALWAYS_NORMALIZE) {children = normalizeChildren(children)} else if (normalizationType === SIMPLE_NORMALIZE) {children = simpleNormalizeChildren(children)}let vnode, nsif (typeof tag === 'string') {let Ctorns = (context.$vnode && context.$vnode.ns) || config.getTagNamespace(tag)if (config.isReservedTag(tag)) {// platform built-in elementsvnode = new VNode(config.parsePlatformTagName(tag), data, children,undefined, undefined, context)} else if (isDef(Ctor = resolveAsset(context.$options, 'components', tag))) {// componentvnode = createComponent(Ctor, data, context, children, tag)} else {// unknown or unlisted namespaced elements// check at runtime because it may get assigned a namespace when its// parent normalizes childrenvnode = new VNode(tag, data, children,undefined, undefined, context)}} else {// direct component options / constructorvnode = createComponent(tag, data, context, children)}if (Array.isArray(vnode)) {return vnode} else if (isDef(vnode)) {if (isDef(ns)) applyNS(vnode, ns)if (isDef(data)) registerDeepBindings(data)return vnode} else {return createEmptyVNode()}
}
复制代码
_createElement
参数中会接收 children
,它表示当前 VNode
的子节点,因为它是任意类型的,所以接下来需要将其规范为标准的 VNode
数组;
// 这里规范化 children
if (normalizationType === ALWAYS_NORMALIZE) {children = normalizeChildren(children)
} else if (normalizationType === SIMPLE_NORMALIZE) {children = simpleNormalizeChildren(children)
}
复制代码
simpleNormalizeChildren
和 normalizeChildren
均用于规范化 children
。由 normalizationType
判断 render
函数是编译生成的还是用户自定义的。
// 1. When the children contains components - because a functional component
// may return an Array instead of a single root. In this case, just a simple
// normalization is needed - if any child is an Array, we flatten the whole
// thing with Array.prototype.concat. It is guaranteed to be only 1-level deep
// because functional components already normalize their own children.
export function simpleNormalizeChildren (children: any) {for (let i = 0; i < children.length; i++) {if (Array.isArray(children[i])) {return Array.prototype.concat.apply([], children)}}return children
}// 2. When the children contains constructs that always generated nested Arrays,
// e.g. <template>, <slot>, v-for, or when the children is provided by user
// with hand-written render functions / JSX. In such cases a full normalization
// is needed to cater to all possible types of children values.
export function normalizeChildren (children: any): ?Array<VNode> {return isPrimitive(children)? [createTextVNode(children)]: Array.isArray(children)? normalizeArrayChildren(children): undefined
}
复制代码
simpleNormalizeChildren
方法调用场景是 render 函数当函数是编译生成的。normalizeChildren
方法的调用场景主要是 render 函数是用户手写的。
经过对 children
的规范化,children
变成了一个类型为 VNode
的数组。之后就是创建 VNode
的逻辑。
// src/core/vdom/patch.js
let vnode, ns
if (typeof tag === 'string') {let Ctorns = (context.$vnode && context.$vnode.ns) || config.getTagNamespace(tag)if (config.isReservedTag(tag)) {// platform built-in elementsvnode = new VNode(config.parsePlatformTagName(tag), data, children,undefined, undefined, context)} else if (isDef(Ctor = resolveAsset(context.$options, 'components', tag))) {// componentvnode = createComponent(Ctor, data, context, children, tag)} else {// unknown or unlisted namespaced elements// check at runtime because it may get assigned a namespace when its// parent normalizes childrenvnode = new VNode(tag, data, children,undefined, undefined, context)}
} else {// direct component options / constructorvnode = createComponent(tag, data, context, children)
}
复制代码
如果 tag
是 string
类型,则接着判断如果是内置的一些节点,创建一个普通 VNode
;如果是为已注册的组件名,则通过 createComponent
创建一个组件类型的 VNode
;否则创建一个未知的标签的 VNode
。
如果 tag
不是 string
类型,那就是 Component
类型, 则直接调用 createComponent
创建一个组件类型的 VNode
节点。
最后 _createElement
会返回一个 VNode
,也就是调用 vm._render
时创建得到的VNode
。之后 VNode
会传递给 vm._update
函数,用于生成真实dom。
生成真实dom(_update)
// src/core/instance/lifecycle.js
Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {const vm: Component = thisconst prevEl = vm.$elconst prevVnode = vm._vnodeconst prevActiveInstance = activeInstanceactiveInstance = vmvm._vnode = vnode// Vue.prototype.__patch__ is injected in entry points// based on the rendering backend used.if (!prevVnode) {// initial rendervm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */)} else {// updatesvm.$el = vm.__patch__(prevVnode, vnode)}activeInstance = prevActiveInstance// update __vue__ referenceif (prevEl) {prevEl.__vue__ = null}if (vm.$el) {vm.$el.__vue__ = vm}// if parent is an HOC, update its $el as wellif (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) {vm.$parent.$el = vm.$el}// updated hook is called by the scheduler to ensure that children are// updated in a parent's updated hook.
}
复制代码
_update
里最核心的方法就是 vm.__patch__
方法,不同平台的 __patch__
方法的定义会稍有不同,在 web 平台中它是这样定义的:
// src/platforms/web/runtime/index.js
import { patch } from './patch'
// install platform patch function
Vue.prototype.__patch__ = inBrowser ? patch : noop
复制代码
可以看到 __patch__
实际调用的是 patch
方法。
// src/platforms/web/runtime/patch.js
import * as nodeOps from 'web/runtime/node-ops'
import { createPatchFunction } from 'core/vdom/patch'
import baseModules from 'core/vdom/modules/index'
import platformModules from 'web/runtime/modules/index'// the directive module should be applied last, after all
// built-in modules have been applied.
const modules = platformModules.concat(baseModules)export const patch: Function = createPatchFunction({ nodeOps, modules })
复制代码
而 patch
方法是由 createPatchFunction
方法创建返回出来的函数。
// src/core/vdom/patch.js
const hooks = ['create', 'activate', 'update', 'remove', 'destroy']export function createPatchFunction (backend) {let i, jconst cbs = {}const { modules, nodeOps } = backendfor (i = 0; i < hooks.length; ++i) {cbs[hooks[i]] = []for (j = 0; j < modules.length; ++j) {if (isDef(modules[j][hooks[i]])) {cbs[hooks[i]].push(modules[j][hooks[i]])}}}// ...return function patch (oldVnode, vnode, hydrating, removeOnly){}
}
复制代码
这里有两个比较重要的对象 nodeOps
和 modules
。nodeOps
是封装的原生dom操作方法,在生成真实节点树的过程中,dom相关操作都是调用 nodeOps
内的方法。
modules
是待执行的钩子函数。在进入函数时,会将不同模块的钩子函数分类放置到 cbs
中,其中包括自定义指令钩子函数,ref 钩子函数。在 patch
阶段,会根据操作节点的行为取出对应类型进行调用。
patch
// initial render
vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */)
复制代码
在首次渲染时,vm.$el
对应的是根节点 dom 对象,也就是我们熟知的 id 为 app 的 div。它作为 oldVNode
参数传入 patch
:
return function patch (oldVnode, vnode, hydrating, removeOnly) {if (isUndef(vnode)) {if (isDef(oldVnode)) invokeDestroyHook(oldVnode)return}let isInitialPatch = falseconst insertedVnodeQueue = []if (isUndef(oldVnode)) {// empty mount (likely as component), create new root elementisInitialPatch = truecreateElm(vnode, insertedVnodeQueue)} else {const isRealElement = isDef(oldVnode.nodeType)if (!isRealElement && sameVnode(oldVnode, vnode)) {// patch existing root nodepatchVnode(oldVnode, vnode, insertedVnodeQueue, null, null, removeOnly)} else {if (isRealElement) {// mounting to a real element// check if this is server-rendered content and if we can perform// a successful hydration.if (oldVnode.nodeType === 1 && oldVnode.hasAttribute(SSR_ATTR)) {oldVnode.removeAttribute(SSR_ATTR)hydrating = true}if (isTrue(hydrating)) {if (hydrate(oldVnode, vnode, insertedVnodeQueue)) {invokeInsertHook(vnode, insertedVnodeQueue, true)return oldVnode} else if (process.env.NODE_ENV !== 'production') {warn('The client-side rendered virtual DOM tree is not matching ' +'server-rendered content. This is likely caused by incorrect ' +'HTML markup, for example nesting block-level elements inside ' +'<p>, or missing <tbody>. Bailing hydration and performing ' +'full client-side render.')}}// either not server-rendered, or hydration failed.// create an empty node and replace itoldVnode = emptyNodeAt(oldVnode)}// replacing existing elementconst oldElm = oldVnode.elmconst parentElm = nodeOps.parentNode(oldElm)// create new nodecreateElm(vnode,insertedVnodeQueue,// extremely rare edge case: do not insert if old element is in a// leaving transition. Only happens when combining transition +// keep-alive + HOCs. (#4590)oldElm._leaveCb ? null : parentElm,nodeOps.nextSibling(oldElm))// update parent placeholder node element, recursivelyif (isDef(vnode.parent)) {let ancestor = vnode.parentconst patchable = isPatchable(vnode)while (ancestor) {for (let i = 0; i < cbs.destroy.length; ++i) {cbs.destroy[i](ancestor)}ancestor.elm = vnode.elmif (patchable) {for (let i = 0; i < cbs.create.length; ++i) {cbs.create[i](emptyNode, ancestor)}// #6513// invoke insert hooks that may have been merged by create hooks.// e.g. for directives that uses the "inserted" hook.const insert = ancestor.data.hook.insertif (insert.merged) {// start at index 1 to avoid re-invoking component mounted hookfor (let i = 1; i < insert.fns.length; i++) {insert.fns[i]()}}} else {registerRef(ancestor)}ancestor = ancestor.parent}}// destroy old nodeif (isDef(parentElm)) {removeVnodes([oldVnode], 0, 0)} else if (isDef(oldVnode.tag)) {invokeDestroyHook(oldVnode)}}}invokeInsertHook(vnode, insertedVnodeQueue, isInitialPatch)return vnode.elm
}
复制代码
通过检查属性 nodeType
(真实节点才有的属性), 判断 oldVnode
是否为真实节点。
const isRealElement = isDef(oldVnode.nodeType)
if (isRealElement) {// ...oldVnode = emptyNodeAt(oldVnode)
}
复制代码
很明显第一次的 isRealElement
是为 true
,因此会调用 emptyNodeAt
将其转为 VNode
:
function emptyNodeAt (elm) {return new VNode(nodeOps.tagName(elm).toLowerCase(), {}, [], undefined, elm)
}
复制代码
接着会调用 createElm
方法,它就是将 VNode
转为真实dom 的核心方法:
function createElm (vnode,insertedVnodeQueue,parentElm,refElm,nested,ownerArray,index
) {if (isDef(vnode.elm) && isDef(ownerArray)) {// This vnode was used in a previous render!// now it's used as a new node, overwriting its elm would cause// potential patch errors down the road when it's used as an insertion// reference node. Instead, we clone the node on-demand before creating// associated DOM element for it.vnode = ownerArray[index] = cloneVNode(vnode)}vnode.isRootInsert = !nested // for transition enter checkif (createComponent(vnode, insertedVnodeQueue, parentElm, refElm)) {return}const data = vnode.dataconst children = vnode.childrenconst tag = vnode.tagif (isDef(tag)) {vnode.elm = vnode.ns? nodeOps.createElementNS(vnode.ns, tag): nodeOps.createElement(tag, vnode)setScope(vnode)/* istanbul ignore if */if (__WEEX__) {// ...} else {createChildren(vnode, children, insertedVnodeQueue)if (isDef(data)) {invokeCreateHooks(vnode, insertedVnodeQueue)}insert(parentElm, vnode.elm, refElm)}if (process.env.NODE_ENV !== 'production' && data && data.pre) {creatingElmInVPre--}} else if (isTrue(vnode.isComment)) {vnode.elm = nodeOps.createComment(vnode.text)insert(parentElm, vnode.elm, refElm)} else {vnode.elm = nodeOps.createTextNode(vnode.text)insert(parentElm, vnode.elm, refElm)}
}
复制代码
一开始会调用 createComponent
尝试创建组件类型的节点,如果成功会返回 true
。在创建过程中也会调用 $mount
进行组件范围内的挂载,所以走的还是 patch
这套流程。
if (createComponent(vnode, insertedVnodeQueue, parentElm, refElm)) {return
}
复制代码
如果没有完成创建,代表该 VNode
对应的是真实节点,往下继续创建真实节点的逻辑。
vnode.elm = vnode.ns? nodeOps.createElementNS(vnode.ns, tag): nodeOps.createElement(tag, vnode)
复制代码
根据 tag
创建对应类型真实节点,赋值给 vnode.elm
,它作为父节点容器,创建的子节点会被放到里面。
然后调用 createChildren
创建子节点:
function createChildren (vnode, children, insertedVnodeQueue) {if (Array.isArray(children)) {if (process.env.NODE_ENV !== 'production') {checkDuplicateKeys(children)}for (let i = 0; i < children.length; ++i) {createElm(children[i], insertedVnodeQueue, vnode.elm, null, true, children, i)}} else if (isPrimitive(vnode.text)) {nodeOps.appendChild(vnode.elm, nodeOps.createTextNode(String(vnode.text)))}
}
复制代码
内部进行遍历子节点数组,再次调用 createElm
创建节点,而上面创建的 vnode.elm
作为父节点传入。如此循环,直到没有子节点,就会创建文本节点插入到 vnode.elm
中。
执行完成后出来,会调用 invokeCreateHooks
,它负责执行 dom 操作时的 create
钩子函数,同时将 VNode
加入到 insertedVnodeQueue
中:
function invokeCreateHooks (vnode, insertedVnodeQueue) {for (let i = 0; i < cbs.create.length; ++i) {cbs.create[i](emptyNode, vnode)}i = vnode.data.hook // Reuse variableif (isDef(i)) {if (isDef(i.create)) i.create(emptyNode, vnode)if (isDef(i.insert)) insertedVnodeQueue.push(vnode)}
}
复制代码
最后一步就是调用 insert
方法将节点插入到父节点:
function insert (parent, elm, ref) {if (isDef(parent)) {if (isDef(ref)) {if (nodeOps.parentNode(ref) === parent) {nodeOps.insertBefore(parent, elm, ref)}} else {nodeOps.appendChild(parent, elm)}}
}
复制代码
可以看到 Vue
是通过递归调用 createElm
来创建节点树的。同时也说明最深的子节点会先调用 insert
插入节点。所以整个节点树的插入顺序是“先子后父”。插入节点方法就是原生dom的方法 insertBefore
和 appendChild
。
if (isDef(parentElm)) {removeVnodes([oldVnode], 0, 0)
}
复制代码
createElm
流程走完后,构建完成的节点树已经插入到页面上了。其实 Vue
在初始化渲染页面时,并不是把原来的根节点 app
给真正替换掉,而是在其后面插入一个新的节点,接着再把旧节点给移除掉。
所以在 createElm
之后会调用 removeVnodes
来移除旧节点,它里面同样是调用的原生dom方法 removeChild
。
invokeInsertHook(vnode, insertedVnodeQueue, isInitialPatch)
复制代码
function invokeInsertHook (vnode, queue, initial) {// delay insert hooks for component root nodes, invoke them after the// element is really insertedif (isTrue(initial) && isDef(vnode.parent)) {vnode.parent.data.pendingInsert = queue} else {for (let i = 0; i < queue.length; ++i) {queue[i].data.hook.insert(queue[i])}}
}
复制代码
在 patch
的最后就是调用 invokeInsertHook
方法,触发节点插入的钩子函数。
至此整个页面渲染的流程完毕~
总结
初始化调用 $mount
挂载组件。
_render
开始构建 VNode
,核心方法为 createElement
,一般会创建普通的 VNode
,遇到组件就创建组件类型的 VNode
,否则就是未知标签的 VNode
,构建完成传递给 _update
。
patch
阶段根据 VNode
创建真实节点树,核心方法为 createElm
,首先遇到组件类型的 VNode
,内部会执行 $mount
,再走一遍相同的流程。普通节点类型则创建一个真实节点,如果它有子节点开始递归调用 createElm
,使用 insert
插入子节点,直到没有子节点就填充内容节点。最后递归完成后,同样也是使用 insert
将整个节点树插入到页面中,再将旧的根节点移除。
往期相关文章:
手摸手带你理解Vue响应式原理
手摸手带你理解Vue的Computed原理
手摸手带你理解Vue的Watch原理
Vue你不得不知道的异步更新机制和nextTick原理
作者:WahFung
链接:https://juejin.im/post/6858902304599310343
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
相关文章
- Python的zip和exe安装方法
我用#CSDN#这个app发现了有技术含量的博客,小伙伴们求同去《Python的zip和exe安装方法》, 一起来围观吧 https://blog.csdn.net/u012060033/article/details/105449881?utm_sourceapp...
2024/5/7 19:26:54 - android9.0 apk静默安装
封装一个类,调用系统的的方法进行静默安装 public class PackageManagerCompatP {private static final String TAG PackageManagerCompatP.class.getSimpleName();RequiresApi(api Build.VERSION_CODES.LOLLIPOP)public static void install(Context context, S…...
2024/5/8 8:48:25 - Echarts 实现仪表盘轴线颜色扇形渐变
1 使用详解 series.axisLine. lineStyle.color 说明:设置仪表轴线颜色。 2 原始效果图 3 实现代码(仪表板轴线颜色扇形渐变) <body><!-- 为ECharts准备一个具备大小(宽高)的Dom --><div id"ma…...
2024/5/7 18:36:46 - ubuntu 16.04使用vim时方向键变成了ABCD
原因:ubuntu预装的是vim tiny版本 解决办法:安装vim full版本 在终端依次执行以下两条命令即可 sudo apt-get remove vim-common sudo apt-get install vim...
2024/4/2 9:54:14 - 5.1 CPU的功能和基本结构
5.1.1 CPU的功能 1)指令控制 取指令、分析指令、执行指令。 2)操作控制 将操作信号送往相应部件。 3)时间控制 4)数据加工 算数和逻辑运算。 5)中断处理 对计算机异常情况处理。 5.1.2 CPU的结构 1.运算器 1…...
2024/4/9 6:06:20 - 线性代数(一)
行列式涉及到的相应的概念行列式排列n阶行列数行列式的性质行列式 行列式 主对角线:\ 次对角线:/ 排列 排列:1、2n的数随机排序的数组,其中不可缺少数字逆序:前面的数比后面的某一个数大逆序数:奇排列,偶排列&am…...
2024/5/8 6:36:14 - ubuntu将默认文件名修改为英文
export LANGen_US #切换语言到英文 xdg-user-dirs-gtk-update #更新语言 export LANGzh_CN.UTF-8 #切换语言回中文...
2024/5/7 14:43:00 - HyperLogLogs
HyperLogLog是什么? HyperLogLog 是用来做基数统计的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定 的、并且是很小的。 HyperLogLog不保存元素本身,故只能知道集合大…...
2024/5/8 1:20:56 - mysql数据库初学者简单操作
mysql数据库操作 mysql操作步骤: 在cmd中链接mysql服务器: mysql -u root -p entry password: 输入密码当进入到mysql服务器中,可以查看有哪些数据库 show databases;创建数据库: create database 数据库的名字;进入数据库&…...
2024/4/2 9:54:13 - 攻防世界no-strings-attached ida静态调试(做题记录)
首先下载文件 打开 Exeinfo PE拖进去 发现无壳 打开ida 找到主函数 F5 先看看c伪代码 挨个点进去观察 setlocale banner prompt_authentication() authenticate 在authenticticate函数里面发现了关键 decrypt解密关键 跟踪进去 v6存的是传入的s的长度 v4应该是初始为0 v…...
2024/4/25 0:05:25 - 网络安全-靶机dvwa之sql注入Low到High详解(含代码分析)
目录 SQL Injection-LOW Union注入 注入点判断 字段判断 获取数据库名 获取表名 获取列名 获取数据 Error注入 获取表名 获取列名 获取数据 源码解析 主要步骤 漏洞原因 SQL Injection-MIDIUM Union 注入点判断 获取表名 Error注入 源码分析 步骤 漏洞原…...
2024/5/7 17:17:44 - gdb调试应用程序
前言 gdb是GNU开源组织发布的一款调试器,提供了丰富的功能。gdb不仅能调试普通的应用程序,还能调试正在运行的进程线程,甚至linux内核。gdb不仅能调试C语言,还可以调试C,Java等语言编写的程序。有一个不足就是gdb是一…...
2024/4/30 11:14:44 - JavaScript表达式与运算符
表达式与运算符 1.表达式的三个重要组成部分:变量,常量,运算符 举例来演示表达式的使用: //假设初始账户余额为1000,经过第一次支付后检测当前余额能否再次进行第二次支付,不能则发出提示信息,…...
2024/4/26 9:46:06 - Leetcode198:打家劫舍
文章目录题目描述思路分析代码实现题目描述 思路分析 详细解析请转自此观看 代码实现 if (nums.length 0) {return 0;}int N nums.length;int[] dp new int[N 1];dp[0] 0;dp[1] nums[0];for (int k 2; k < N; k) {dp[k] Math.max(dp[k - 1], nums[k - 1] dp[k -…...
2024/4/7 17:56:07 - springboot 访问静态文件遇到的一个坑
spring: mvc:static-path-pattern: /show/**resources:static-locations: file:/data/ppmicro/upload file:upload:basePath: /data/ppmicro/upload 在/data/ppmicro/upload下面放文件process1.png,然后通过下面的地址访问 http://localhost:19900/show/process…...
2024/4/3 13:07:06 - 蓝桥杯真题 15校1-单词计数 输入一个字符串,求它包含多少个单词。单词间以一个或者多个空格分开。 第一个单词前,最后一个单词后也可能有0到多个空格。 比如:“ abc xyz“ 包含两个单
问题描述 输入一个字符串,求它包含多少个单词。单词间以一个或者多个空格分开。 第一个单词前,最后一个单词后也可能有0到多个空格。 比如:" abc xyz" 包含两个单词,"ab c xyz " 包含3个单词。如下…...
2024/4/27 8:39:15 - 利用RFID来完成签到
RFID签到 通过STM32F103核心板作为主控,通过连接外设rc522,iic-oled等,实现完整的管理系统。 代码功能 注册学号 注册学号采用随机生成五位数字作为学生学号进行使用,代码如下 //注册学号 void Registered_Class(void) { u16…...
2024/4/25 16:54:52 - 【计算机体系结构】学习笔记 第一章 计算机系统设计基础
文章目录1.1 计算机系统的基本概念1.1.1 计算机系统的多级层次结构1.1.2 系统结构、组成与实现1. 计算机系统结构的定义System Architecture2. 计算机组成Computer Organization3. 计算机实现Computer Implementation1.1.3 计算机系统的分类1. 采用的基本器件分类2. 计算机系统…...
2024/4/26 13:25:21 - Python编程基础
numpy.random.randn()用法 pythonnumpy:基本矩阵操作 直接定义一个具体的矩阵 import numpy as np a np.array([[1,2,3],[4,5,6],[7,8,9]]) print(a)需要注意的是array的最外面是有一个小括号,然后里面还要有一个中括号,中括号里面才是每一…...
2024/4/3 14:38:33 - Android Socket
skb->truesize,len,datalen,size,等的区别?...
2024/4/3 14:41:38
最新文章
- centos系统等保测评操作明细
CentOS系统进行等保测评的操作明细通常包括以下几个关键步骤,这些步骤依据了提供的搜索结果: 1. 身份鉴别 - 检查是否每个用户都有唯一身份标识,密码是否具有复杂度要求并定期更换。 - 使用命令 more /etc/shadow 检查系统是否存在空口令账户…...
2024/5/8 10:46:45 - 梯度消失和梯度爆炸的一些处理方法
在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言,在此感激不尽。 权重和梯度的更新公式如下: w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...
2024/5/7 10:36:02 - Linux学习-网络UDP
网络 数据传输,数据共享 网络协议模型 OSI协议模型 应用层 实际发送的数据 表示层 发送的数据是否加密 会话层 是否建立会话连接 传输层 数据传输的方式(数据报、流式&#…...
2024/5/7 16:07:29 - 使用 Kafka 保证消息不丢失的策略及原理解析
✨✨祝屏幕前的小伙伴们每天都有好运相伴左右,一定要天天开心!✨✨ 🎈🎈作者主页: 喔的嘛呀🎈🎈 目录 一、引言 二. 持久化存储 2.1持久化存储原理: 2.2使用示例: …...
2024/5/5 8:26:54 - Github 2024-03-30 开源项目日报 Top10
根据Github Trendings的统计,今日(2024-03-30统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Python项目5TypeScript项目2C项目1Jupyter Notebook项目1Go项目1非开发语言项目1Mojo项目1开源 iOS 应用合作列表 创建周期:3351 天协议类型:…...
2024/5/7 17:01:32 - 416. 分割等和子集问题(动态规划)
题目 题解 class Solution:def canPartition(self, nums: List[int]) -> bool:# badcaseif not nums:return True# 不能被2整除if sum(nums) % 2 ! 0:return False# 状态定义:dp[i][j]表示当背包容量为j,用前i个物品是否正好可以将背包填满ÿ…...
2024/5/7 19:05:20 - 【Java】ExcelWriter自适应宽度工具类(支持中文)
工具类 import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellType; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet;/*** Excel工具类** author xiaoming* date 2023/11/17 10:40*/ public class ExcelUti…...
2024/5/7 22:31:36 - Spring cloud负载均衡@LoadBalanced LoadBalancerClient
LoadBalance vs Ribbon 由于Spring cloud2020之后移除了Ribbon,直接使用Spring Cloud LoadBalancer作为客户端负载均衡组件,我们讨论Spring负载均衡以Spring Cloud2020之后版本为主,学习Spring Cloud LoadBalance,暂不讨论Ribbon…...
2024/5/8 1:37:40 - TSINGSEE青犀AI智能分析+视频监控工业园区周界安全防范方案
一、背景需求分析 在工业产业园、化工园或生产制造园区中,周界防范意义重大,对园区的安全起到重要的作用。常规的安防方式是采用人员巡查,人力投入成本大而且效率低。周界一旦被破坏或入侵,会影响园区人员和资产安全,…...
2024/5/7 14:19:30 - VB.net WebBrowser网页元素抓取分析方法
在用WebBrowser编程实现网页操作自动化时,常要分析网页Html,例如网页在加载数据时,常会显示“系统处理中,请稍候..”,我们需要在数据加载完成后才能继续下一步操作,如何抓取这个信息的网页html元素变化&…...
2024/5/8 1:37:39 - 【Objective-C】Objective-C汇总
方法定义 参考:https://www.yiibai.com/objective_c/objective_c_functions.html Objective-C编程语言中方法定义的一般形式如下 - (return_type) method_name:( argumentType1 )argumentName1 joiningArgument2:( argumentType2 )argumentName2 ... joiningArgu…...
2024/5/7 16:57:02 - 【洛谷算法题】P5713-洛谷团队系统【入门2分支结构】
👨💻博客主页:花无缺 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 本文由 花无缺 原创 收录于专栏 【洛谷算法题】 文章目录 【洛谷算法题】P5713-洛谷团队系统【入门2分支结构】🌏题目描述🌏输入格…...
2024/5/7 14:58:59 - 【ES6.0】- 扩展运算符(...)
【ES6.0】- 扩展运算符... 文章目录 【ES6.0】- 扩展运算符...一、概述二、拷贝数组对象三、合并操作四、参数传递五、数组去重六、字符串转字符数组七、NodeList转数组八、解构变量九、打印日志十、总结 一、概述 **扩展运算符(...)**允许一个表达式在期望多个参数࿰…...
2024/5/7 1:54:46 - 摩根看好的前智能硬件头部品牌双11交易数据极度异常!——是模式创新还是饮鸩止渴?
文 | 螳螂观察 作者 | 李燃 双11狂欢已落下帷幕,各大品牌纷纷晒出优异的成绩单,摩根士丹利投资的智能硬件头部品牌凯迪仕也不例外。然而有爆料称,在自媒体平台发布霸榜各大榜单喜讯的凯迪仕智能锁,多个平台数据都表现出极度异常…...
2024/5/7 21:15:55 - Go语言常用命令详解(二)
文章目录 前言常用命令go bug示例参数说明 go doc示例参数说明 go env示例 go fix示例 go fmt示例 go generate示例 总结写在最后 前言 接着上一篇继续介绍Go语言的常用命令 常用命令 以下是一些常用的Go命令,这些命令可以帮助您在Go开发中进行编译、测试、运行和…...
2024/5/8 1:37:35 - 用欧拉路径判断图同构推出reverse合法性:1116T4
http://cplusoj.com/d/senior/p/SS231116D 假设我们要把 a a a 变成 b b b,我们在 a i a_i ai 和 a i 1 a_{i1} ai1 之间连边, b b b 同理,则 a a a 能变成 b b b 的充要条件是两图 A , B A,B A,B 同构。 必要性显然࿰…...
2024/5/7 16:05:05 - 【NGINX--1】基础知识
1、在 Debian/Ubuntu 上安装 NGINX 在 Debian 或 Ubuntu 机器上安装 NGINX 开源版。 更新已配置源的软件包信息,并安装一些有助于配置官方 NGINX 软件包仓库的软件包: apt-get update apt install -y curl gnupg2 ca-certificates lsb-release debian-…...
2024/5/7 16:04:58 - Hive默认分割符、存储格式与数据压缩
目录 1、Hive默认分割符2、Hive存储格式3、Hive数据压缩 1、Hive默认分割符 Hive创建表时指定的行受限(ROW FORMAT)配置标准HQL为: ... ROW FORMAT DELIMITED FIELDS TERMINATED BY \u0001 COLLECTION ITEMS TERMINATED BY , MAP KEYS TERMI…...
2024/5/8 1:37:32 - 【论文阅读】MAG:一种用于航天器遥测数据中有效异常检测的新方法
文章目录 摘要1 引言2 问题描述3 拟议框架4 所提出方法的细节A.数据预处理B.变量相关分析C.MAG模型D.异常分数 5 实验A.数据集和性能指标B.实验设置与平台C.结果和比较 6 结论 摘要 异常检测是保证航天器稳定性的关键。在航天器运行过程中,传感器和控制器产生大量周…...
2024/5/7 16:05:05 - --max-old-space-size=8192报错
vue项目运行时,如果经常运行慢,崩溃停止服务,报如下错误 FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory 因为在 Node 中,通过JavaScript使用内存时只能使用部分内存(64位系统&…...
2024/5/8 1:37:31 - 基于深度学习的恶意软件检测
恶意软件是指恶意软件犯罪者用来感染个人计算机或整个组织的网络的软件。 它利用目标系统漏洞,例如可以被劫持的合法软件(例如浏览器或 Web 应用程序插件)中的错误。 恶意软件渗透可能会造成灾难性的后果,包括数据被盗、勒索或网…...
2024/5/8 1:37:31 - JS原型对象prototype
让我简单的为大家介绍一下原型对象prototype吧! 使用原型实现方法共享 1.构造函数通过原型分配的函数是所有对象所 共享的。 2.JavaScript 规定,每一个构造函数都有一个 prototype 属性,指向另一个对象,所以我们也称为原型对象…...
2024/5/7 11:08:22 - C++中只能有一个实例的单例类
C中只能有一个实例的单例类 前面讨论的 President 类很不错,但存在一个缺陷:无法禁止通过实例化多个对象来创建多名总统: President One, Two, Three; 由于复制构造函数是私有的,其中每个对象都是不可复制的,但您的目…...
2024/5/8 9:51:44 - python django 小程序图书借阅源码
开发工具: PyCharm,mysql5.7,微信开发者工具 技术说明: python django html 小程序 功能介绍: 用户端: 登录注册(含授权登录) 首页显示搜索图书,轮播图࿰…...
2024/5/8 1:37:29 - 电子学会C/C++编程等级考试2022年03月(一级)真题解析
C/C++等级考试(1~8级)全部真题・点这里 第1题:双精度浮点数的输入输出 输入一个双精度浮点数,保留8位小数,输出这个浮点数。 时间限制:1000 内存限制:65536输入 只有一行,一个双精度浮点数。输出 一行,保留8位小数的浮点数。样例输入 3.1415926535798932样例输出 3.1…...
2024/5/7 17:09:45 - 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...
解析如下:1、长按电脑电源键直至关机,然后再按一次电源健重启电脑,按F8健进入安全模式2、安全模式下进入Windows系统桌面后,按住“winR”打开运行窗口,输入“services.msc”打开服务设置3、在服务界面,选中…...
2022/11/19 21:17:18 - 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。
%读入6幅图像(每一幅图像的大小是564*564) f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...
2022/11/19 21:17:16 - 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...
win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面,在等待界面中我们需要等待操作结束才能关机,虽然这比较麻烦,但是对系统进行配置和升级…...
2022/11/19 21:17:15 - 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...
有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows,请勿关闭计算机”的提示,要过很久才能进入系统,有的用户甚至几个小时也无法进入,下面就教大家这个问题的解决方法。第一种方法:我们首先在左下角的“开始…...
2022/11/19 21:17:14 - win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...
置信有很多用户都跟小编一样遇到过这样的问题,电脑时发现开机屏幕显现“正在配置Windows Update,请勿关机”(如下图所示),而且还需求等大约5分钟才干进入系统。这是怎样回事呢?一切都是正常操作的,为什么开时机呈现“正…...
2022/11/19 21:17:13 - 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...
Win7系统开机启动时总是出现“配置Windows请勿关机”的提示,没过几秒后电脑自动重启,每次开机都这样无法进入系统,此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一:开机按下F8,在出现的Windows高级启动选…...
2022/11/19 21:17:12 - 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...
有不少windows10系统用户反映说碰到这样一个情况,就是电脑提示正在准备windows请勿关闭计算机,碰到这样的问题该怎么解决呢,现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法:1、2、依次…...
2022/11/19 21:17:11 - 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...
今天和大家分享一下win7系统重装了Win7旗舰版系统后,每次关机的时候桌面上都会显示一个“配置Windows Update的界面,提示请勿关闭计算机”,每次停留好几分钟才能正常关机,导致什么情况引起的呢?出现配置Windows Update…...
2022/11/19 21:17:10 - 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...
只能是等着,别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚,只能是考虑备份数据后重装系统了。解决来方案一:管理员运行cmd:net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...
2022/11/19 21:17:09 - 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?
原标题:电脑提示“配置Windows Update请勿关闭计算机”怎么办?win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢?一般的方…...
2022/11/19 21:17:08 - 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...
关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!关机提示 windows7 正在配…...
2022/11/19 21:17:05 - 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...
钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...
2022/11/19 21:17:05 - 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...
前几天班里有位学生电脑(windows 7系统)出问题了,具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面,长时间没反应,无法进入系统。这个问题原来帮其他同学也解决过,网上搜了不少资料&#x…...
2022/11/19 21:17:04 - 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...
本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法,并在最后教给你1种保护系统安全的好方法,一起来看看!电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中,添加了1个新功能在“磁…...
2022/11/19 21:17:03 - 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...
许多用户在长期不使用电脑的时候,开启电脑发现电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机。。.这要怎么办呢?下面小编就带着大家一起看看吧!如果能够正常进入系统,建议您暂时移…...
2022/11/19 21:17:02 - 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...
配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!配置windows update失败 还原更改 请勿关闭计算机&#x…...
2022/11/19 21:17:01 - 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...
不知道大家有没有遇到过这样的一个问题,就是我们的win7系统在关机的时候,总是喜欢显示“准备配置windows,请勿关机”这样的一个页面,没有什么大碍,但是如果一直等着的话就要两个小时甚至更久都关不了机,非常…...
2022/11/19 21:17:00 - 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...
当电脑出现正在准备配置windows请勿关闭计算机时,一般是您正对windows进行升级,但是这个要是长时间没有反应,我们不能再傻等下去了。可能是电脑出了别的问题了,来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...
2022/11/19 21:16:59 - 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...
我们使用电脑的过程中有时会遇到这种情况,当我们打开电脑之后,发现一直停留在一个界面:“配置Windows Update失败,还原更改请勿关闭计算机”,等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢࿰…...
2022/11/19 21:16:58 - 如何在iPhone上关闭“请勿打扰”
Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...
2022/11/19 21:16:57