最新的Vue面试题大全含源码级回答,吊打面试官系列
前言
金三银四快过去了,抓紧这段时间再复习下vue,为了在面试官前突出自己,在回答的时候能带上源码的实现和理解往往更容易成功。这里整理了vue常见的面试题和相应的源码及解读,希望对大家有所帮助。篇幅较长,分为vue2篇和vue3篇。
自己整理手写的,有哪里不对或者说的不准确的欢迎指出来,我会虚心接收指教。
vue2
1.vue响应式原理
回答这个问题,首先要搞清楚什么叫响应式。通常vue中所说的响应式是指数据响应式,数据变化可以被检测并对这种变化做出响应的机制。而在Vue这种MVVM框架中,最重要的核心就是实现数据层和视图层的连接,通过数据驱动应用,数据变化,视图更新。Vue中的方案是数据劫持+发布订阅模式。
vue在初始化的时候会进行劫持,包括props,data,methods,computed,watcher,并根据数据类型来做不同处理.
如果是对象则采用Object.defineProperty()的方式定义数据拦截:
function defineReactive(obj, key, val) {Object.defineProperty(obj, key, {get() {return val},set(v) {val = vnotify()}})
}
如果是数组,则覆盖数组的7个变更方法实现变更通知:
const arrayProto = Array.prototype
const arrayMethods = Object.create(arrayProto)['push','pop','shift','unshift','splice','sort','reverse'].forEach(function (method) {const original = arrayProto[method]def(arrayMethods, method, function mutator (...args) {const result = original.apply(this, args)notify()return result})
})
这是数据劫持的部分,接下来说下视图更新的机制:
- 由于 Vue 执行一个组件的 render 函数是由 Watcher 去代理执行的,Watcher 在执行前会把 Watcher 自身先赋值给 Dep.target 这个全局变量,等待响应式属性去收集它。
- 在组件执行render函数时访问了响应式属性,响应式属性就会精确的收集到当前全局存在的 Dep.target 作为自身的依赖。
- 在响应式属性发生更新时通知 Watcher 去重新调用
vm._update(vm._render())
进行组件的视图更新,视图更新的时候会通过diff算法对比新老vnode差异,通过patch即时更新DOM。
2.v-if和v-for哪个优先级高
答案是v-for解析的优先级高,可以在源码的compiler/codegen/index.js 里的genElement函数找到答案
function genElement (el: ASTElement, state: CodegenState): string {if (el.parent) {el.pre = el.pre || el.parent.pre}if (el.staticRoot && !el.staticProcessed) {return genStatic(el, state)} else if (el.once && !el.onceProcessed) {return genOnce(el, state)} else if (el.for && !el.forProcessed) {return genFor(el, state)} else if (el.if && !el.ifProcessed) {return genIf(el, state)} else if (el.tag === 'template' && !el.slotTarget && !state.pre) {return genChildren(el, state) || 'void 0'} else if (el.tag === 'slot') {return genSlot(el, state)} else {// component or elementlet codeif (el.component) {code = genComponent(el.component, el, state)} else {let dataif (!el.plain || (el.pre && state.maybeComponent(el))) {data = genData(el, state)}const children = el.inlineTemplate ? null : genChildren(el, state, true)code = `_c('${el.tag}'${data ? `,${data}` : '' // data}${children ? `,${children}` : '' // children})`}// module transformsfor (let i = 0; i < state.transforms.length; i++) {code = state.transforms[i](el, code)}return code}
}
vue中的内置指令都有相应的解析函数,执行顺序是通过简单的if else-if语法来确定的。在genFor的函数里,最后会return一个自运行函数,再次调用genElement。
虽然v-for和v-if可以放一起,但我们要避免这种写法,在官网中也有明确指出,这会造成性能浪费。
3.key的作用
作用:用来判断虚拟DOM的某个节点是否为相同节点,用于优化patch性能,patch就是计算diff的函数。
先看下patch函数:
只提取了本次要分析的关键代码
function patch (oldVnode, vnode) {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 {// some code}}return vnode
}
patch函数接收oldVnode和vnode,也就是要比较的新旧节点对象。
首先会用isUndef函数判断传入的两个vnode是否为空对象再做相应处理。当两个都为节点对象时,再用sameVnode来判断是否为同一节点,再判断本次操作是新增、修改、还是移除。
function sameVnode (a, b) {return (a.key === b.key // key值&& (a.tag === b.tag && // 标签名a.isComment === b.isComment && // 是否为注释节点isDef(a.data) === isDef(b.data) && // 是否都定义了data,data包含一些具体信息,例如onclick , stylesameInputType(a, b) // 当标签是<input>的时候,type必须相同))
}
sameVnode通过判断key、标签名、是否为注释、data等是否相等,来判断是否需要进行比较。
值得比较则执行patchVnode,不值得比较则用Vnode替换oldVnode,再渲染真实dom。
patchVnode会对oldVnode和vnode进行对比,然后进行DOM更新。这个会在diff算法里再进行说明。
v-for通常都是生成一样的标签,所以key会是patch判断是否相同节点的唯一标识,如果不设置key,它的值就是undefined,则可能永远认为这是两个相同节点,就会去做pathVnode pdateChildren的更新操作,这造成了大量的dom更新操作,所以设置唯一的key是必要的。
4.双向绑定原理
vue中双向绑定是一个指令v-model,可以绑定一个动态值到视图,同时视图中变化能改变该值。v-model是语法糖,默认情况下相当于:value和@input。
通常在表单元素可以直接使用v-model,这是vue解析的时候对这些表单元素进行了处理,根据控件类型自动选取正确的方法来更新元素。
v-model 在内部为不同的输入元素使用不同的 property 并抛出不同的事件:
- text 和 textarea 元素使用 value property 和 input 事件;
- checkbox 和 radio 使用 checked property 和 change 事件;
- select 字段将 value 作为 prop 并将 change 作为事件。
如果是自定义组件的话要使用它需要在组件内绑定props value并在数据更新数据的时候用$emit(‘input’),也可以在组件里定义modal属性来自定义绑定的属性名和事件名称。
model: {prop: 'checked',event: 'change'
}
5.nextTick原理
先看下官方文档的说明:
Vue 在更新 DOM 时是异步执行的。只要侦听到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。如果同一个 watcher 被多次触发,只会被推入到队列中一次。
nextTick就是将回调函数放到队列里去,保证在异步更新DOM的watcher后面,从而获取到更新后的DOM。
结合src/core/util/next-tick源码再进行分析。
首先是定义执行任务队列方法
function flushCallbacks () {pending = falseconst copies = callbacks.slice(0)callbacks.length = 0for (let i = 0; i < copies.length; i++) {copies[i]()}
}
按照推入callbacks队列的顺序执行回调函数。
然后定义timerFunc函数,根据当前环境支持什么方法来确定调用哪个异步方法
判断的顺序是: Promise > MutationObserver > setImmediate > setTimeout
最后是定义nextTick方法:
export function nextTick (cb?: Function, ctx?: Object) {let _resolvecallbacks.push(() => {if (cb) {try {cb.call(ctx)} catch (e) {handleError(e, ctx, 'nextTick')}} else if (_resolve) {_resolve(ctx)}})if (!pending) {pending = truetimerFunc()}if (!cb && typeof Promise !== 'undefined') {return new Promise(resolve => {_resolve = resolve})}
}
其实nextTick就是一个把回调函数推入任务队列的方法。
6.data为什么是函数
如果组件里 data 直接写了一个对象的话,那么在模板中多次声明这个组件,组件中的 data 会指向同一个引用。
此时对 data 进行修改,会导致其他组件里的 data 也被修改。使用函数每次都重新声明一个对象,这样每个组件的data都有自己的引用,就不会出现相互污染的情况了。
7.组件通信方式
1. props和$on
、$emit
适合父子组件的通信,通过props传递响应式数据,父组件通过$on
监听事件、子组件通过$emit
发送事件。
on和emit是在组件实例初始化的时候通过initEvents
初始化事件,在组件实例vm._events赋值一个空的事件对象,通过这个对象实现事件的发布订阅。下面是事件注册的几个关键函数:
// 组件初始化event对象,收集要监听的事件和对应的回调函数
function initEvents (vm: Component) {vm._events = Object.create(null)vm._hasHookEvent = false// init parent attached eventsconst listeners = vm.$options._parentListenersif (listeners) {updateComponentListeners(vm, listeners)}
}
...
// 注册组件监听的事件
function updateComponentListeners (vm: Component,listeners: Object,oldListeners: ?Object
) {target = vmupdateListeners(listeners, oldListeners || {}, add, remove, createOnceHandler, vm)target = undefined
}
2. ref
、$parent
、$children
,还有$root
- ref: 在普通DOM元素上声明就是DOM元素的引用,组件就是指向组件实例。
- $parent:访问组件的父组件实例
- $children:访问所有的子组件集合(数组)
- $root: 指向root实例
3. Event Bus
通常是创建一个空的Vue实例作为事件总线(事件中心)
,实现任何组件在这个实例上的事件触发与监听。原理就是一个发布订阅的模式,跟$on``$emit
一样,在实例化一个组件的事件通过initEvents初始化一个空的event对象,再通过实例化后的这个bus(vue实例)手动的$on
、$emit
添加监听和触发的事件,代码在src/core/instance/events
:
Vue.prototype.$on = function (event: string | Array<string>, fn: Function): Component {const vm: Component = this// 传入的事件如果是数组,就循环监听每个事件if (Array.isArray(event)) {for (let i = 0, l = event.length; i < l; i++) {vm.$on(event[i], fn)}} else {// 如果已经有这个事件,就push新的回调函数进去,没有则先赋值空数组再push(vm._events[event] || (vm._events[event] = [])).push(fn)// instead of a hash lookupif (hookRE.test(event)) {vm._hasHookEvent = true}}return vm}...Vue.prototype.$emit = function (event: string): Component {const vm: Component = this...let cbs = vm._events[event]// 循环调用要触发的事件的回调函数数组if (cbs) {cbs = cbs.length > 1 ? toArray(cbs) : cbsconst args = toArray(arguments, 1)const info = `event handler for "${event}"`for (let i = 0, l = cbs.length; i < l; i++) {invokeWithErrorHandling(cbs[i], vm, args, vm, info)}}return vm}
4. attrs、listeners
$attrs
: 包含了父作用域没被props声明
绑定的数据,组件可以通过v-bind="$attrs"
继续传给子组件$listernes
: 包含了父作用域中的v-on
(不含 .native 修饰器的) 监听事件,可以通过v-on="$listeners"
传入内部组件
5. provide、inject
父组件通过provide注入一个依赖,其所有的子孙组件可以通过inject来接收。要注意的是官网有这一段话:
提示:provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的 property 还是可响应的。
所以Vue不会对provide中的变量进行响应式处理。要想 inject 接受的变量是响应式的,provide 提供的变量本身就需要是响应式的。实际上在很多高级组件中都可以看到组件会将this通过provide传递给子孙组件,包括element-ui、ant-design-vue等。
6. vuex 状态管理实现通信
vuex是专为vue设计的状态管理模式。每个组件实例都有共同的store实例,并且store.state是响应式的,改变state唯一的办法就是通过在这个store实例上commit一个mutation,方便跟踪每一个状态的变化,实现原理在下面的vuex原理里有讲。
8.computed、watch、method有什么区别
computed:有缓存,有对应的watcher,watcher有个lazy为true的属性,表示只有在模板里去读取它的值后才会计算,并且这watcher在初始化的时候会赋值dirty为true,watcher只有dirty为true的时候才会重新求值,重新求值后会将dirty置为false,false会直接返回watcher的value,只有下次watcher的响应式依赖有更新的时候,会将watcher的dirty再置为false,这时候才会重新求值,这样就实现了computed的缓存。
watch:watcher的对象每次更新都会执行函数。watch 更适用于数据变化时的异步操作。如果需要在某个数据变化时做一些事情,使用watch。
method: 将方法在模板里使用,每次视图有更新都会重新执行函数,性能消耗较大。
9.生命周期
官网对生命周期的说明:
每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会。
生命周期就是每个Vue实例完成初始化、运行、销毁的一系列动作的钩子。
基本上可以说8 个阶段创建前/后,载入前/后,更新前/后,销毁前/后。
- 创建前/后: 在 beforeCreate 阶段,vue 实例的挂载元素 el 还没有。
- 载入前/后:在 beforeMount 阶段,vue 实例的$el 和 data 都初始化了,但还是挂载之前为虚拟的 dom 节点,data.message 还未替换。在 mounted 阶段,vue 实例挂载完成,data.message 成功渲染。
- 更新前/后:当 data 变化时,会触发 beforeUpdate 和 updated 方法。
- 销毁前/后:在执行 destroy 方法后,对 data 的改变不会再触发周期函数,说明此时 vue 实例已经解除了事件监听以及和 dom 的绑定,但是 dom 结构依然存在
结合源码再理解,在源码中生命周期钩子是用callHook函数调用的。看下callHook函数:
function callHook (vm: Component, hook: string) {pushTarget()const handlers = vm.$options[hook]const info = `${hook} hook`if (handlers) {for (let i = 0, j = handlers.length; i < j; i++) {invokeWithErrorHandling(handlers[i], vm, null, vm, info)}}if (vm._hasHookEvent) {vm.$emit('hook:' + hook)}popTarget()
}
接收一个vm组件实例的参数和hook,取组件实例的$options传入的hook属性值,有的话会循环调用这个钩子的回调函数。在调用生命钩子的回调函数之前会临时pushTarget一个null值,也就是将Dep.target置为空来禁止在执行生命钩子的时候进行依赖收集。
vm.$emit(‘hook:’ + hook)则是用来给父组件监听该组件的回调事件。
接下来看每个生命钩子具体调用的时机。
1. beforeCreate、created:
Vue.prototype._init = function (options?: Object) {...initLifecycle(vm)initEvents(vm)initRender(vm)callHook(vm, 'beforeCreate')initInjections(vm) // resolve injections before data/propsinitState(vm)initProvide(vm) // resolve provide after data/propscallHook(vm, 'created')...if (vm.$options.el) {vm.$mount(vm.$options.el)}}
在执行beforeCreate之前调用了 initLifecycle、initEvents、initRender
函数,所以beforeCreate是在初始化生命周期、事件、渲染函数之后的生命周期。
在执行created之前调用了initInjections、initState、initProvide,这时候created初始化了data、props、watcher、provide、inject等,所以这时候就可以访问到data、props等属性。
2. beforeMount、mounted
3. beforeUpdate、updated
这两个钩子函数是在数据更新的时候进行回调的函数。在src/core/instance/lifecycle.js
找到beforeUpdate调用的代码:
...
new Watcher(vm, updateComponent, noop, {before () {if (vm._isMounted && !vm._isDestroyed) {callHook(vm, 'beforeUpdate')}}}, true /* isRenderWatcher */)
...
_isMounted为ture的话(DOM已经被挂载)会调用callHook(vm, ‘beforeUpdate’)方法,然后会对虚拟DOM进行重新渲染。然后在/src/core/observer/scheduler.js下的flushSchedulerQueue()函数中渲染DOM,flushSchedulerQueue会刷新watcher队列并执行,执行完所有watcher的run方法之后(run方法就是watcher进行dom diff并更新DOM的方法),再调用callHook(vm, ‘updated’),代码如下:
/*** Flush both queues and run the watchers.*/
function flushSchedulerQueue () {...for (index = 0; index < queue.length; index++) {watcher = queue[index]if (watcher.before) {watcher.before()}watcher.run()}...callUpdatedHooks(updatedQueue)...
}function callUpdatedHooks (queue) {let i = queue.lengthwhile (i--) {const watcher = queue[i]const vm = watcher.vmif (vm._watcher === watcher && vm._isMounted && !vm._isDestroyed) {callHook(vm, 'updated')}}
}
4. beforeDestroy、destroyed
这两个钩子是vue实例销毁的钩子,定义在Vue.prototype.$destroy中:
Vue.prototype.$destroy = function () {const vm: Component = thisif (vm._isBeingDestroyed) {return}callHook(vm, 'beforeDestroy')vm._isBeingDestroyed = true// remove self from parentconst parent = vm.$parentif (parent && !parent._isBeingDestroyed && !vm.$options.abstract) {remove(parent.$children, vm)}// teardown watchersif (vm._watcher) {vm._watcher.teardown()}let i = vm._watchers.lengthwhile (i--) {vm._watchers[i].teardown()}// remove reference from data ob// frozen object may not have observer.if (vm._data.__ob__) {vm._data.__ob__.vmCount--}// call the last hook...vm._isDestroyed = true// invoke destroy hooks on current rendered treevm.__patch__(vm._vnode, null)// fire destroyed hookcallHook(vm, 'destroyed')// turn off all instance listeners.vm.$off()// remove __vue__ referenceif (vm.$el) {vm.$el.__vue__ = null}if (vm.$vnode) {vm.$vnode.parent = null}}
}
在销毁之前执行callHook(vm, ‘beforeDestroy’),然后销毁的时候做了几件事:
- 如果有父元素,将父元素的$children中把该组件实例移除。
- 移除watchers,并在依赖订阅者中移除自己。
- 删除数据引用
5. activated、deactivated
剩下的还有activated、deactivated、errorCaptured
三个钩子函数。
activated、deactivated这两个钩子函数分别是在keep-alive 组件激活和停用之后的回调。
errorCaptured捕获到当子孙组件错误时会被调用,在源码中可以经常看到try catch中catch会调用handleError函数,handleError会向组件所有的父级组件抛出异常,
function handleError (err: Error, vm: any, info: string) {pushTarget()try {if (vm) {let cur = vmwhile ((cur = cur.$parent)) {const hooks = cur.$options.errorCapturedif (hooks) {for (let i = 0; i < hooks.length; i++) {try {const capture = hooks[i].call(cur, err, vm, info) === falseif (capture) return} catch (e) {globalHandleError(e, cur, 'errorCaptured hook')}}}}}globalHandleError(err, vm, info)} finally {popTarget()}
}
分析完源码再一下官网图示,会更清楚:
10.keep-aliva原理
keep-alive是Vue.js的一个内置组件。它能够将不活动的组件实例保存在内存中,而不是直接将其销毁,它是一个抽象组件,不会被渲染到真实DOM中,也不会出现在父组件链中。
include与exclude两个属性,允许组件有条件地进行缓存,max属性确定最多缓存多少组件实例。
keep-alive是一个组件,跟其他组件一样有生命周期和render函数,keep-alive包裹的分析keep-alive就是分析一个组件。
源码再src/core/components/keep-alive
,created声明了要缓存的组件对象,和存储的组件keys,keep-alive销毁的时候会用pruneCacheEntry将缓存的所有组件实例销毁,也就是调用组件实例的destroy方法。在挂载完成后监听include和exclude,动态地销毁已经不满足include的组件和满足exclude的组件实例:
created () {this.cache = Object.create(null) // 存储需要缓存的组件this.keys = [] // 存储每个需要缓存的组件的key,即对应this.cache对象中的键值
},// 销毁keep-alive组件的时候,对缓存中的每个组件执行销毁
destroyed () {for (const key in this.cache) {pruneCacheEntry(this.cache, key, this.keys)}
},
mounted () {this.$watch('include', val => {pruneCache(this, name => matches(val, name))})this.$watch('exclude', val => {pruneCache(this, name => !matches(val, name))})
},
接下来是render函数:
render () {const slot = this.$slots.defaultconst vnode: VNode = getFirstComponentChild(slot)// 如果vnode存在就取vnode的选项const componentOptions: ?VNodeComponentOptions = vnode && vnode.componentOptionsif (componentOptions) {// check pattern//获取第一个有效组件的nameconst name: ?string = getComponentName(componentOptions)const { include, exclude } = thisif (// not included(include && (!name || !matches(include, name))) ||// excluded(exclude && name && matches(exclude, name))) {return vnode// 说明不用缓存,直接返回这个组件进行渲染}// 匹配到了,开始缓存操作const { cache, keys } = this // keep-alive组件的缓存组件和缓存组件对应的key// 获取第一个有效组件的keyconst key: ?string = vnode.key == null// same constructor may get registered as different local components// so cid alone is not enough (#3269)? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : ''): vnode.keyif (cache[key]) {// 这个组件的实例用缓存中的组件实例替换vnode.componentInstance = cache[key].componentInstance// make current key freshest// 更新当前key在keys中的位置remove(keys, key)keys.push(key)} else {cache[key] = vnodekeys.push(key)// prune oldest entry// 如果缓存中的组件个数超过传入的max,销毁缓存中的LRU组件// LRU: least recently used 最近最少用,缓存淘汰策略if (this.max && keys.length > parseInt(this.max)) {pruneCacheEntry(cache, keys[0], keys, this._vnode)}}vnode.data.keepAlive = true}// 若第一个有效的组件存在,但其componentOptions不存在,就返回这个组件进行渲染// 或若也不存在有效的第一个组件,但keep-alive组件的默认插槽存在,就返回默认插槽的第一个组件进行渲染return vnode || (slot && slot[0])
}
代码做了详细的注释,这里再分析下render做了什么。
通过this.$slots.default拿到插槽组件,也就是keep-alive包裹的组件,getFirstComponentChild获取第一个子组件,获取该组件的name(存在组件名则直接使用组件名,否则会使用tag)。接下来会将这个name通过include与exclude属性进行匹配,匹配不成功(说明不需要进行缓存)则不进行任何操作直接返回vnode(vnode节点描述对象,vue通过vnode创建真实的DOM)
。
匹配到了就开始缓存,根据key在this.cache中查找,如果存在则说明之前已经缓存过了,直接将缓存的vnode的componentInstance(组件实例)覆盖到目前的vnode上面。否则将vnode存储在cache中。并且通过remove(keys, key),将当前的key从keys中删除再重新keys.push(key),这样就改变了当前key在keys中的位置。这个是为了实现max的功能,并且遵循缓存淘汰策略。
如果没匹配到,说明没缓存过,这时候需要进行缓存,并且判断当前缓存的个数是否超过max指定的个数,如果超过,则销毁keys里的最后一个组件,并从keys中移除,这个就是LRU(Least Recently Used :最近最少使用
)缓存淘汰算法。
最后返回vnode或者默认插槽的第一个组件进行DOM渲染。
12.虚拟dom和diff算法
虚拟DOM是对DOM的描述,用对象属性来描述节点,本质上是JavaScript对象。它有几个意义:
- 具备跨平台的优势
由于 Virtual DOM 是以 JavaScript 对象为基础而不依赖真实平台环境,所以使它具有了跨平台的能力,比如说浏览器、小程序、Node、原生应用、服务端渲染等等。
- 提升渲染性能
频繁变动DOM会造成浏览器的回流或者重回,而通过将大量的DOM操作搬运到Javascript中,运用patching算法来计算出真正需要更新的节点,可以减少真实DOM的操作次数,从而提高性能。
- 代码可维护性更高
通过虚拟 DOM 的抽象能力,可以用声明式写 UI 的方式,大大提高了我们的工作效率。
在vue中template最终会转成render函数,而render函数最终是执行的createElement,生成vnode,vnode正是 vue中用来表示虚拟DOM的类,看下vnode:
class VNode {tag: string | void;data: VNodeData | void;children: ?Array<VNode>;text: string | void;elm: Node | void;ns: string | void;context: Component | void; // rendered in this component's scopekey: string | number | void;componentOptions: VNodeComponentOptions | void;componentInstance: Component | void; // component instanceparent: VNode | void; // component placeholder node// strictly internalraw: boolean; // contains raw HTML? (server only)isStatic: boolean; // hoisted static nodeisRootInsert: boolean; // necessary for enter transition checkisComment: boolean; // empty comment placeholder?isCloned: boolean; // is a cloned node?isOnce: boolean; // is a v-once node?asyncFactory: Function | void; // async component factory functionasyncMeta: Object | void;isAsyncPlaceholder: boolean;ssrContext: Object | void;fnContext: Component | void; // real context vm for functional nodesfnOptions: ?ComponentOptions; // for SSR cachingdevtoolsMeta: ?Object; // used to store functional render context for devtoolsfnScopeId: ?string; // functional scope id supportconstructor (tag?: string,data?: VNodeData,children?: ?Array<VNode>,text?: string,elm?: Node,context?: Component,componentOptions?: VNodeComponentOptions,asyncFactory?: Function) {this.tag = tagthis.data = datathis.children = childrenthis.text = textthis.elm = elmthis.ns = undefinedthis.context = contextthis.fnContext = undefinedthis.fnOptions = undefinedthis.fnScopeId = undefinedthis.key = data && data.keythis.componentOptions = componentOptionsthis.componentInstance = undefinedthis.parent = undefinedthis.raw = falsethis.isStatic = falsethis.isRootInsert = truethis.isComment = falsethis.isCloned = falsethis.isOnce = falsethis.asyncFactory = asyncFactorythis.asyncMeta = undefinedthis.isAsyncPlaceholder = false}// DEPRECATED: alias for componentInstance for backwards compat./* istanbul ignore next */get child (): Component | void {return this.componentInstance}
}
看下其中关键的几个属性:
-
tag: 当前节点的标签名
-
data: 表示节点上的class,attribute,style以及绑定的事件
-
children: 当前节点的子节点,是一个数组
-
text: 当前节点的文本
-
elm: 当前虚拟节点对应的真实dom节点
-
key: 节点的key属性,被当作节点的标志,用以优化
-
componentOptions: 组件的option选项
-
componentInstance: 当前节点对应的组件的实例
-
parent: 当前节点的父节点
-
isStatic: 是否为静态节点
children和parent是指当前的vnode的子节点和父节点,这样一个个vnode就形成了DOM树。
diff算法发生在视图更新
的时候,也就是数据更新的时候,diff算法会将新旧虚拟DOM作对比,将变化的地方转换为DOM
。
当某个数据被修改的时候,依赖对应的watcher会通知更新,执行渲染函数会生成新的vnode,vnode再去与旧的vnode进行对比更新,这就是vue中的虚拟dom diff算法触发的流程。
看下组件更新的_update方法:
Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {const vm: Component = thisconst prevEl = vm.$elconst prevVnode = vm._vnodeconst restoreActiveInstance = setActiveInstance(vm)vm._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)}}...
vm.$el = vm._patch(),这个就是最终渲染的DOM元素,patch就是vue中diff算法的函数,在key的作用章节有提过。patch将新旧虚拟DOM节点比较后,最终返回真实的DOM节点。
patch
看下patch代码(部分):
function patch (oldVnode, vnode, hydrating, removeOnly, parentElm, refElm) {/*vnode不存在则直接调用销毁钩子*/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, parentElm, refElm)} else {/*标记旧的VNode是否有nodeType*//*Github:https://github.com/answershuto*/const isRealElement = isDef(oldVnode.nodeType)if (!isRealElement && sameVnode(oldVnode, vnode)) {// patch existing root node/*是同一个节点的时候直接修改现有的节点*/patchVnode(oldVnode, vnode, insertedVnodeQueue, removeOnly)...return vnode.elm
首先是判断是否有新的vnode,没有代表是要销毁旧的vnode,调用销毁组件的钩子。
然后判断是否有旧的vnode,没有代表是新增,也就是新建root节点。
接下来判断旧的vnode是否是真实的元素,而不是组件,如果是组件并且用someVnode判断新旧节点是否是相同的节点(sameVnode在key的作用章节有做解析),是进行patchVnode,这时候进行真正的新老节点的diff。只有相同的节点才会进行diff算法!!!
patchVnode
function patchVnode (oldVnode,vnode,insertedVnodeQueue,ownerArray,index,removeOnly) {// 两个vnode相同,说明不需要diff,直接返回if (oldVnode === vnode) {return}// 如果传入了ownerArray和index,可以进行重用vnode,updateChildren里用来替换位置if (isDef(vnode.elm) && isDef(ownerArray)) {// clone reused vnodevnode = ownerArray[index] = cloneVNode(vnode)}const elm = vnode.elm = oldVnode.elm// 如果oldVnode的isAsyncPlaceholder属性为true时,跳过检查异步组件,returnif (isTrue(oldVnode.isAsyncPlaceholder)) {if (isDef(vnode.asyncFactory.resolved)) {hydrate(oldVnode.elm, vnode, insertedVnodeQueue)} else {vnode.isAsyncPlaceholder = true}return}/*如果新旧VNode都是静态的,同时它们的key相同(代表同一节点),并且新的VNode是clone或者是标记了once(标记v-once属性,只渲染一次),那么只需要替换elm以及componentInstance即可。*/if (isTrue(vnode.isStatic) &&isTrue(oldVnode.isStatic) &&vnode.key === oldVnode.key &&(isTrue(vnode.isCloned) || isTrue(vnode.isOnce))) {vnode.componentInstance = oldVnode.componentInstancereturn}let iconst data = vnode.dataif (isDef(data) && isDef(i = data.hook) && isDef(i = i.prepatch)) {i(oldVnode, vnode)}const oldCh = oldVnode.childrenconst ch = vnode.childrenif (isDef(data) && isPatchable(vnode)) {for (i = 0; i < cbs.update.length; ++i) cbs.update[i](oldVnode, vnode)if (isDef(i = data.hook) && isDef(i = i.update)) i(oldVnode, vnode)}/*如果这个VNode节点没有text文本时*/if (isUndef(vnode.text)) {if (isDef(oldCh) && isDef(ch)) {// 两个vnode都定义了子节点,并且不相同,就对子节点进行diffif (oldCh !== ch) updateChildren(elm, oldCh, ch, insertedVnodeQueue, removeOnly)} else if (isDef(ch)) {// 如果只有新的vnode定义了子节点,则进行添加子节点的操作if (process.env.NODE_ENV !== 'production') {checkDuplicateKeys(ch)}if (isDef(oldVnode.text)) nodeOps.setTextContent(elm, '')addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue)} else if (isDef(oldCh)) {// 如果只有旧的vnode定义了子节点,则进行删除子节点的操作removeVnodes(oldCh, 0, oldCh.length - 1)} else if (isDef(oldVnode.text)) {nodeOps.setTextContent(elm, '')}} else if (oldVnode.text !== vnode.text) {nodeOps.setTextContent(elm, vnode.text)}if (isDef(data)) {if (isDef(i = data.hook) && isDef(i = i.postpatch)) i(oldVnode, vnode)}}
通过代码可知,patchVnode分为多种情况,分析下子节点的diff过程 (oldCh 为 oldVnode的子节点,ch 为 Vnode的子节点)
- oldCh、ch都定义了调用updateChildren再进行diff
- 若 oldCh不存在,ch 存在,首先清空 oldVnode 的文本节点,同时调用 addVnodes 方法将 ch 添加到elm真实 dom 节点当中
- 若 oldCh存在,ch不存在,则删除 elm 真实节点下的 oldCh 子节点
- 若 oldVnode 有文本节点,而 vnode 没有,那么就清空这个文本节点
updateChildren
是子节点diff的函数,也是最重要的环节。
updateChildren
function updateChildren (parentElm, oldCh, newCh, insertedVnodeQueue, removeOnly) {// 声明oldCh和newCh的头尾索引和头尾的vnode,let oldStartIdx = 0let newStartIdx = 0let oldEndIdx = oldCh.length - 1let oldStartVnode = oldCh[0]let oldEndVnode = oldCh[oldEndIdx]let newEndIdx = newCh.length - 1let newStartVnode = newCh[0]let newEndVnode = newCh[newEndIdx]let oldKeyToIdx, idxInOld, vnodeToMove, refElmconst canMove = !removeOnlyif (process.env.NODE_ENV !== 'production') {checkDuplicateKeys(newCh)}while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {if (isUndef(oldStartVnode)) {oldStartVnode = oldCh[++oldStartIdx] // Vnode has been moved left} else if (isUndef(oldEndVnode)) {oldEndVnode = oldCh[--oldEndIdx]// 判断两边的头是不是相同节点} else if (sameVnode(oldStartVnode, newStartVnode)) {patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue, newCh, newStartIdx)oldStartVnode = oldCh[++oldStartIdx]newStartVnode = newCh[++newStartIdx]// 判断尾部是不是相同节点} else if (sameVnode(oldEndVnode, newEndVnode)) {patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue, newCh, newEndIdx)oldEndVnode = oldCh[--oldEndIdx]newEndVnode = newCh[--newEndIdx]// 判断旧节点头部是不是与新节点的尾部相同,相同则把头部往右移} else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved rightpatchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue, newCh, newEndIdx)canMove && nodeOps.insertBefore(parentElm, oldStartVnode.elm, nodeOps.nextSibling(oldEndVnode.elm))oldStartVnode = oldCh[++oldStartIdx]newEndVnode = newCh[--newEndIdx]// 判断旧节点尾部是不是与新节点的头部相同,相同则把头部往左移} else if (sameVnode(oldEndVnode, newStartVnode)) { // Vnode moved leftpatchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue, newCh, newStartIdx)canMove && nodeOps.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm)oldEndVnode = oldCh[--oldEndIdx]newStartVnode = newCh[++newStartIdx]} else {/*生成一个key与旧VNode的key对应的哈希表*/if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx)idxInOld = isDef(newStartVnode.key)? oldKeyToIdx[newStartVnode.key]: findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx)if (isUndef(idxInOld)) { // New elementcreateElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx)} else {vnodeToMove = oldCh[idxInOld]if (sameVnode(vnodeToMove, newStartVnode)) {patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue, newCh, newStartIdx)oldCh[idxInOld] = undefinedcanMove && nodeOps.insertBefore(parentElm, vnodeToMove.elm, oldStartVnode.elm)} else {// same key but different element. treat as new elementcreateElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx)}}newStartVnode = newCh[++newStartIdx]}}// oldCh或者newCh遍历完,说明剩下的节点不是新增就是删除if (oldStartIdx > oldEndIdx) {refElm = isUndef(newCh[newEndIdx + 1]) ? null : newCh[newEndIdx + 1].elmaddVnodes(parentElm, refElm, newCh, newStartIdx, newEndIdx, insertedVnodeQueue)} else if (newStartIdx > newEndIdx) {removeVnodes(oldCh, oldStartIdx, oldEndIdx)}}
首先给startIndex和endIndex来作为遍历的索引,在遍历的时候会先判断头尾节点是否相同,没有找到相同节点后再按照通用方式遍历查找;查找结束再按情况处理剩下的节点;借助key通常可以非常精确找到相同节点。
当oldCh 或者 newCh 遍历完后(遍历完的条件就是 oldCh 或者 newCh 的 startIndex >= endIndex ),说明剩下的节点为新增或者删除,这时候停止oldCh 和 newCh 的 diff。
13.Vuex原理
vuex是什么,先看下官方的原话:
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化
这段话可以得出几个结论:Vuex是为vue.js服务的
,而像redux与react是解耦的,然后vuex是状态管理模式,所有的状态以一种可预测的方式发生变化。
设计思想:
Vuex的设计思想,借鉴了Flux、Redux,将数据存放到全局的store,再将store挂载到每个vue实例组件中,利用Vue.js的细粒度数据响应机制来进行高效的状态更新。
原理可以从使用方式开始分析。
Vue.use(Vuex); // 1\. vue的插件机制,安装vuex
let store = new Vuex.Store({ // 2.实例化store,调用install方法state,getters,modules,mutations,actions,plugins
});
new Vue({ // 3.注入store, 挂载vue实例store,render: h=>h(app)
}).$mount('#app');
Vue.use是vue中的插件机制,内部会调用插件的install方法,vuex的install方法:
export function install (_Vue) {if (Vue) {if (process.env.NODE_ENV !== 'production') {console.error('[vuex] already installed. Vue.use(Vuex) should be called only once.')}return}/*保存Vue,同时用于检测是否重复安装*/Vue = _Vue/*将vuexInit混淆进Vue的beforeCreate(Vue2.0)或_init方法(Vue1.0)*/applyMixin(Vue)
}
vuex是个全局的状态管理,全局有且只能有一个store实例,所以在install的时候会判断是否已经安装过了,这个就是单例模式,确保一个类只有一个实例。在第一次install的时候会applyMixin,applyMixin是/src/mixin
导入的方法:
function (Vue) {const version = Number(Vue.version.split('.')[0])if (version >= 2) {Vue.mixin({ beforeCreate: vuexInit })} else {// override init and inject vuex init procedure// for 1.x backwards compatibility.const _init = Vue.prototype._initVue.prototype._init = function (options = {}) {options.init = options.init? [vuexInit].concat(options.init): vuexInit_init.call(this, options)}}/*** Vuex init hook, injected into each instances init hooks list.*/function vuexInit () {const options = this.$options// store injectionif (options.store) {this.$store = typeof options.store === 'function'? options.store(): options.store} else if (options.parent && options.parent.$store) {this.$store = options.parent.$store}}
}
先是判断下vue的版本,这边分析vue2的逻辑。利用Vue.mixin混入的机制,在组件实例的beforeCreate调用vuexInit方法,首先判断options是否有store,没有代表是root节点,这时候要进行store初始化,没有的话就取父组件的$store赋值,这样就实现了全局共用唯一的store实例。
store实现的源码在src/store.js
,其中最核心的是响应式的实现,通过resetStoreVM(this, state)调用,看下这个方法:
function resetStoreVM (store, state, hot) {const oldVm = store._vm// bind store public gettersstore.getters = {}// reset local getters cachestore._makeLocalGettersCache = Object.create(null)const wrappedGetters = store._wrappedGettersconst computed = {}forEachValue(wrappedGetters, (fn, key) => {// use computed to leverage its lazy-caching mechanism// direct inline function use will lead to closure preserving oldVm.// using partial to return function with only arguments preserved in closure environment.computed[key] = partial(fn, store)Object.defineProperty(store.getters, key, {get: () => store._vm[key],enumerable: true // for local getters})})// use a Vue instance to store the state tree// suppress warnings just in case the user has added// some funky global mixinsconst silent = Vue.config.silentVue.config.silent = truestore._vm = new Vue({data: {$$state: state},computed})Vue.config.silent = silent// enable strict mode for new vmif (store.strict) {enableStrictMode(store)}if (oldVm) {if (hot) {// dispatch changes in all subscribed watchers// to force getter re-evaluation for hot reloading.store._withCommit(() => {oldVm._data.$$state = null})}Vue.nextTick(() => oldVm.$destroy())}
}
resetStoreVM首先会遍历wrappedGetters,使用Object.defineProperty方法对store.getters的每一个getter定义get方法,这样访问this.$store.getter.test就等同于访问store._vm.test。
state是通过new一个Vue对象来实现数据的“响应式化”,运用Vue的data属性来实现数据与视图的同步更新,computed实现getters的计算属性。最终访问store.state也就是访问store._vm.state。
最后
在面试前花了三个月时间刷了很多大厂面试题,最近做了一个整理并分类,主要内容包括html,css,JavaScript,ES6,计算机网络,浏览器,工程化,模块化,Node.js,框架,数据结构,性能优化,项目等等。
包含了腾讯、字节跳动、小米、阿里、滴滴、美团、58、拼多多、360、新浪、搜狐等一线互联网公司面试被问到的题目,涵盖了初中级前端技术点。
无偿分享给大家,算是一个感恩回馈吧,详细内容可以在文末自行获取哈!
HTML
- HTML5新特性,语义化
- 浏览器的标准模式和怪异模式
- xhtml和html的区别
- 使用data-的好处
- meta标签
- canvas
- HTML废弃的标签
- IE6 bug,和一些定位写法
- css js放置位置和原因
- 什么是渐进式渲染
- html模板语言
- meta viewport原理
CSS
- 盒模型,box-sizing
- CSS3新特性,伪类,伪元素,锚伪类
- CSS实现隐藏页面的方式
- 如何实现水平居中和垂直居中。
- 说说position,display
- 请解释*{box-sizing:border-box;}的作用,并说明使用它的好处
- 浮动元素引起的问题和解决办法?绝对定位和相对定位,元素浮动后的display值
- link和@import引入css的区别
- 解释一下css3的flexbox,以及适用场景
- inline和inline-block的区别
- 哪些是块级元素那些是行级元素,各有什么特点
- grid布局
- table布局的作用
- 实现两栏布局有哪些方法?
- css dpi
- 你知道attribute和property的区别么
- css布局问题?css实现三列布局怎么做?如果中间是自适应又怎么做?
- 流式布局如何实现,响应式布局如何实现
- 移动端布局方案
- 实现三栏布局(圣杯布局,双飞翼布局,flex布局)
- 清除浮动的原理
- overflow:hidden有什么缺点?
- padding百分比是相对于父级宽度还是自身的宽度
- css3动画,transition和animation的区别,animation的属性,加速度,重力的模拟实现
- CSS 3 如何实现旋转图片(transform: rotate)
- sass less
- 对移动端开发了解多少?(响应式设计、Zepto;@media、viewport、JavaScript 正则表达式判断平台。)
- 什么是bfc,如何创建bfc?解决什么问题?
- CSS中的长度单位(px,pt,rem,em,ex,vw,vh,vh,vmin,vmax)
- CSS 选择器的优先级是怎样的?
- 雪碧图
- svg
- 媒体查询的原理是什么?
- CSS 的加载是异步的吗?表现在什么地方?
- 常遇到的浏览器兼容性问题有哪些?常用的hack的技巧
- 外边距合并
- 解释一下“::before”和“:after”中的双冒号和单冒号的区别
JS
- js的基本类型有哪些?引用类型有哪些?null和undefined的区别。
- 如何判断一个变量是Array类型?如何判断一个变量是Number类型?(都不止一种)
- Object是引用类型嘛?引用类型和基本类型有什么区别?哪个是存在堆哪一个是存在栈上面的?
- JS常见的dom操作api
- 解释一下事件冒泡和事件捕获
- 事件委托(手写例子),事件冒泡和捕获,如何阻止冒泡?如何组织默认事件?
- 对闭包的理解?什么时候构成闭包?闭包的实现方法?闭包的优缺点?
- this有哪些使用场景?跟C,Java中的this有什么区别?如何改变this的值?
- call,apply,bind
- 显示原型和隐式原型,手绘原型链,原型链是什么?为什么要有原型链
- 创建对象的多种方式
- 实现继承的多种方式和优缺点
- new 一个对象具体做了什么
- 手写Ajax,XMLHttpRequest
- 变量提升
- 举例说明一个匿名函数的典型用例
- 指出JS的宿主对象和原生对象的区别,为什么扩展JS内置对象不是好的做法?有哪些内置对象和内置函数?
- attribute和property的区别
- document load和document DOMContentLoaded两个事件的区别
- === 和 == , [] === [], undefined === undefined,[] == [], undefined == undefined
- typeof能够得到哪些值
- 什么是“use strict”,好处和坏处
- 函数的作用域是什么?js 的作用域有几种?
- JS如何实现重载和多态
- 常用的数组api,字符串api
- 原生事件绑定(跨浏览器),dom0和dom2的区别?
- 给定一个元素获取它相对于视图窗口的坐标
- 如何实现图片滚动懒加载
- js 的字符串类型有哪些方法? 正则表达式的函数怎么使用?
- 深拷贝
- 编写一个通用的事件监听函数
- web端cookie的设置和获取
- setTimeout和promise的执行顺序
- JavaScript 的事件流模型都有什么?
- navigator对象,location和history
- js的垃圾回收机制
- 内存泄漏的原因和场景
- DOM事件的绑定的几种方式
- DOM事件中target和currentTarget的区别
- typeof 和 instanceof 区别,instanceof原理
- js动画和css3动画比较
- JavaScript 倒计时(setTimeout)
- js处理异常
- js的设计模式知道那些
- 轮播图的实现,以及轮播图组件开发,轮播10000张图片过程
- websocket的工作原理和机制。
- 手指点击可以触控的屏幕时,是什么事件?
- 什么是函数柯里化?以及说一下JS的API有哪些应用到了函数柯里化的实现?(函数柯里化一些了解,以及在* 函数式编程的应用,最后说了一下JS中bind函数和数组的reduce方法用到了函数柯里化。)
- JS代码调试
框架
- 使用过哪些框架?
- zepto 和 jquery 是什么关系,有什么联系么?
- jquery源码如何实现选择器的,为什么$取得的对象要设计成数组的形式,这样设计的目的是什么
- jquery如何绑定事件,有几种类型和区别
- 什么是MVVM,MVC,MVP
- Vue和Angular的双向数据绑定原理
- Vue,Angular组件间通信以及路由原理
- react和vue的生命周期
- react和vue的虚拟dom以及diff算法
- vue的observer,watcher,compile
- react和angular分别用在什么样的业务吗?性能方面和MVC层面上的区别
- jQuery对象和JS的Element有什么区别
- jQuery对象是怎么实现的
- jQuery除了它封装了一些方法外,还有什么值得我们去学习和使用的?
- jQuery的$(‘xxx’)做了什么事情
- 介绍一下bootstrap的栅格系统是如何实现的
浏览器相关
- 跨域,为什么JS会对跨域做出限制
- 前端安全:xss,csrf…
- 浏览器怎么加载页面的?script脚本阻塞有什么解决方法?defer和async的区别?
- 浏览器强缓存和协商缓存
- 浏览器的全局变量有哪些
- 浏览器同一时间能够从一个域名下载多少资源
- 按需加载,不同页面的元素判断标准
- web存储、cookies、localstroge等的使用和区别
- 浏览器的内核
- 如何实现缓存机制?(从200缓存,到cache到etag再到)
- 说一下200和304的理解和区别
- 什么是预加载、懒加载
- 一个 XMLHttpRequest 实例有多少种状态?
- dns解析原理,输入网址后如何查找服务器
- 服务器如何知道你?
- 浏览器渲染过程
- ie的某些兼容性问题
- session
- 拖拽实现
- 拆解url的各部分
ES6
- 谈一谈 promise
- 所有的 ES6 特性你都知道吗?如果遇到一个东西不知道是 ES6 还是 ES5, 你该怎么区分它
- es6的继承和es5的继承有什么区别
- promise封装ajax
- let const的优点
- es6 generator 是什么,async/await 实现原理
- ES6和node的commonjs模块化规范区别
- 箭头函数,以及它的this
计算机网络
- HTTP协议头含有哪些重要的部分,HTTP状态码
- 网络url输入到输出怎么做?
- 性能优化为什么要减少 HTTP 访问次数?
- Http请求的过程与原理
- https(对是https)有几次握手和挥手?https的原理。
- http有几次挥手和握手?TLS的中文名?TLS在哪一网络层?
- TCP连接的特点,TCP连接如何保证安全可靠的?
- 为什么TCP连接需要三次握手,两次不可以吗,为什么
- 为什么tcp要三次握手四次挥手?
- tcp的三次握手和四次挥手画图(当场画写ack 和 seq的值)?
- tcp与udp的区别
- get和post的区别?什么情况下用到?
- http2 与http1 的区别?
- websocket
- 什么是tcp流,什么是http流
- babel是如何将es6代码编译成es5的
- http2的持久连接和管线化
- 域名解析时是tcp还是udp
- 域名发散和域名收敛
- Post一个file的时候file放在哪的?
- HTTP Response的Header里面都有些啥?
工程化
- 对webpack,gulp,grunt等有没有了解?对比。
- webpack的入口文件怎么配置,多个入口怎么分割。
- webpack的loader和plugins的区别
- gulp的具体使用。
- 前端工程化的理解、如何自己实现一个文件打包,比如一个JS文件里同时又ES5 和ES6写的代码,如何编译兼容他们
模块化
- 对AMD,CMD,CommonJS有没有了解?
- 为什么要模块化?不用的时候和用RequireJs的时候代码大概怎么写?
- 说说有哪些模块化的库,有了解过模块化的发展的历史吗?
- 分别说说同步和异步模块化的应用场景,说下AMD异步模块化实现的原理?
- 如何将项目里面的所有的require的模块语法换成import的ES6的语法?
- 使用模块化加载时,模块加载的顺序是怎样的,如果不知道,根据已有的知识,你觉得顺序应该是怎么样的?
Nodejs
- 对nodejs有没有了解
- Express 和 koa 有什么关系,有什么区别?
- nodejs适合做什么样的业务?
- nodejs与php,java有什么区别
- Nodejs中的Stream和Buffer有什么区别?
- node的异步问题是如何解决的?
- node是如何实现高并发的?
- 说一下 Nodejs 的 event loop 的原理
数据结构
- 基本数据结构:(数组、队列、链表、堆、二叉树、哈希表等等)
- 8种排序算法,原理,以及适用场景和复杂度
- 说出越多越好的费波拉切数列的实现方法?
性能优化
- cdn的用法是什么?什么时候用到?
- 浏览器的页面优化?
- 如何优化 DOM 操作的性能
- 单页面应用有什么SEO方案?
- 单页面应用首屏显示比较慢,原因是什么?有什么解决方案?
其他
- 正则表达式
- 前端渲染和后端渲染的优缺点
- 数据库的四大特性,什么是原子性,表的关系
- 你觉得前端体系应该是怎样的?
- 一个静态资源要上线,里面有各种资源依赖,你如何平稳上线
- 如果要你去实现一个前端模板引擎,你会怎么做
- 知道流媒体查询吗?
- SEO
- mysql 和 mongoDB 有什么区别?
- restful的method解释
- 数据库知识、操作系统知识
- click在ios上有300ms延迟,原因及如何解决
- 移动端的适配,rem+媒体查询/meta头设置
- 移动端的手势和事件;
- unicode,utf8,gbk编码的了解,乱码的解决
完整版面试题资料免费分享,只需你点赞支持,动动手指点击此处就可免费领取了。
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
相关文章
- VS2015+AngularJS+Ionic开发
安装VS2015 Update2的过程是非常曲折的。还好经过不懈的努力,终于折腾成功了。 如果开发Cordova项目的话,推荐大家用一下ionic这个框架,效果还不错。对于Cordova、PhoneGap、ionic、AngularJS这些框架或库的关系,我个人理解是这样…...
2024/4/21 4:59:03 - Vue~~
链接: https://pan.baidu.com/s/1vF2m3nXzjiacwCWCH8r9pg 提取码: c68b ---------错误--------- 安装vue插件 没有Vue Component 安装成功后可以右键看看有没有Vue Component 解决 点击 file 打开设置 settings,展开 Editor 找到 file and code templates找到 …...
2024/4/21 4:59:02 - 工作记录笔记
面试题:http://www.importnew.com/22056.htmlhttp://marlonyao.iteye.com/blog/344876http://cache.baiducontent.com/c?m9f65cb4a8c8507ed4fece763104687270e54f72864879b5468d4e419ce3b46454762e0b82c3510738983233915ea141cbcff2102471453b08cb98b5daec885295f9…...
2024/4/21 4:59:01 - 杂乱手札 - LINUX, Apache, Mysql, PHP, HTML-JS-CSS, Redis 2014 to 2016
很乱,很乱,very乱...... 一分耕耘一分收获。 Begin SSH 基本ssh登录 ssh usernameIP // ssh rudon***.***.***.*** 无密码SSH登录 约定: 电脑A上的Ua用户,想要免密码ssh登录到远程的服务器B上的Ub用户 概括: 本地…...
2024/4/21 4:59:00 - Node.js前篇
1.Node.js介绍 1.1 node.js是什么 (1) Node.js is a JavaScript runtime built on Chrome’s V8 JavaScript engine. Node.js不是一门语言Node.js不是库、不是框架Node.js是一个Javascript运行时环境简单来讲就是Node.js可以解析和执行JavaScript代码以…...
2024/4/21 4:59:00 - nodejs笔记
目录 # Node.js 第1天 # Node.js 第2天课堂笔记 # Node.js 第3天课堂笔记 # Node.js 第4天课堂笔记 # Node.js 第5天课堂笔记 # Node.js 第6天课堂笔记 # Node.js 第7天课堂笔记 # Node.js 第1天 ## 上午总结 - Node.js 是什么 JavaScript 运行时 既不是语言&…...
2024/4/21 4:58:58 - 常见的前端vue面试题
常见的前端vue面试题 1、请讲述下VUE的MVVM的理解? MVVM 是 Model-View-ViewModel的缩写,即将数据模型与数据表现层通过数据驱动进行分离,从而只需要关系数据模型的开发,而不需要考虑页面的表现,具体说来如下&#x…...
2024/4/21 4:58:57 - 那些年我准备的前端面试题
commonJS相关: (1)在commonJS规范中,require,exports,module,__filename,__dirname都是通过动态编译后添加到模块的头部的,这样就不存在全局变量污染的问题 但是他们传入的require,exports,module都是一个空对象。而且必须弄清楚…...
2024/4/21 4:58:57 - ajax详解
一.前置知识 postman 1.上网的本质 通过网络给服务器要数据或把数据扔给服务器。 2.什么是URL URL(全称是UniformResourceLocator)中文叫统一资源定位符,用于标识互联网上每个资源的唯一存放位置。浏览器只有通过URL地址,才能…...
2024/4/21 4:58:56 - Java面试宝典Beta5.0
pdf下载地址:Java面试宝典 第一章内容介绍 20 第二章JavaSE基础 21 一、Java面向对象 21 1. 面向对象都有哪些特性以及你对这些特性的理解 21 2. 访问权限修饰符public、private、protected, 以及不写(默认)时的区别(2017-11-12) 22 3.…...
2024/4/21 4:58:54 - es2015学习笔记经典入门教程
es2015也称为ES6,是JavaScript语言的下一代标准,下面将分享如何一步步解开它的面纱,哟没有一种幸福感O(∩_∩)O哈哈~ 目录 1. 简介 2. 为什么要了解es6 3. ES-Checker 4. Babel 5. 开发工具 5.1. Sublime Text 5.2. WebS…...
2024/4/21 4:58:53 - 35道常见的前端vue面试题
对于前端来说,尽管css、html、js是主要的基础知识,但是随着技术的不断发展,出现了很多优秀的mv*框架以及小程序框架。因此,对于前端开发者而言,需要对一些前端框架进行熟练掌握。这篇文章我们一起来聊一聊VUE及全家桶的…...
2024/4/21 4:58:52 - 巧用Angular项目的get设置Angular class属性访问的别名
以this.host为例: 实现位置: /*** Helper method to return the host element for the directive* given by the elementRef.*/protected get host(): HTMLElement {return this.elementRef.nativeElement;}更多Jerry的原创文章,尽在…...
2024/4/21 4:58:51 - 使用Angular的http client发送请求,请求response总是被当成json类型处理
奇怪的问题,我的req.responseType字段没有显式赋值,而默认值为json: 这个默认值是在哪里填充的呢? 调试代码,http.js的第1669行有这个默认的逻辑: 解决方案:下图第26行 responseType: text a…...
2024/4/20 19:39:09 - Angular学习之旅-----get传值 路由跳转
新建shoplist组件 在app-routing.module.ts中配置路由 import { NgModule } from angular/core; import { Routes, RouterModule } from angular/router; // 引入组件 import { HomeComponent } from ./components/home/home.component import { NewsComponent } from ./comp…...
2024/4/20 19:39:07 - angular HttpClient get 方法获取数据
请先查看上一篇文章HttpClient配置,之后在进行。 使用 this.myhttp.get(http://192.168.2.139:9002/api/patients)方法,读取webapi。因为get方法是通过AJAX方法读取数据的,所以服务器要可以跨域访问,具体方法查询webapi文章1 impo…...
2024/4/20 19:39:06 - Angular4项目运行时URL自动加#方法
import {HashLocationStrategy , LocationStrategy} from angular/common;NgModule({declarations: [AppCmp],bootstrap: [AppCmp],imports: [BrowserModule, routes],providers: [{provide: LocationStrategy, useClass: HashLocationStrategy}] }); 这样设置后,访…...
2024/4/20 19:39:05 - Angular--使用Angular get 请求数据
1. Angular–使用Angular get 请求数据 1.1 准备工作 1.1.1 新建一个angular项目 ng new angularDemo041.1.2 在项目里面新建一个news组件 ng g component components/news1.2 在app.module.ts 中引入HttpclientModule并注入 app.module.ts import { NgModule } from angu…...
2024/4/21 4:58:52 - Angular HttpClient改为同步请求
1、示例代码: import { Component, OnInit } from angular/core; import { HttpClient, HttpParams } from angular/common/http;Component({selector: app-test,templateUrl: ./test.component.html,styleUrls: [./test.component.css] }) export class TestCompo…...
2024/4/21 4:58:49 - 关于angular $http 中的data传递参数(json字符串)后端没法接收的问题
使用的angular版本1.6.4,后台采用springMVC。项目要实行前后台分离,前后台交互都采用json,刚开始有些抗拒。不否认,angular确实好用,不用再花很多心思在繁琐的dom上了。不过小白我刚刚接触,废了半天劲&…...
2024/4/21 4:58:48
最新文章
- json解析大全
JSON解析案例1 将String转为JSONObject JSONObject res JSONObject.parseObject(result);获取documents JSONArray array res.getJSONObject("result").getJSONArray("documents");遍历JSONArray for (int i 0; i < array.size(); i) {JSONObject…...
2024/4/27 13:41:05 - 梯度消失和梯度爆炸的一些处理方法
在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言,在此感激不尽。 权重和梯度的更新公式如下: w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...
2024/3/20 10:50:27 - 3d representation的一些基本概念
顶点(Vertex):三维空间中的一个点,可以有多个属性,如位置坐标、颜色、纹理坐标和法线向量。它是构建三维几何形状的基本单元。 边(Edge):连接两个顶点形成的直线段,它定…...
2024/4/27 1:08:47 - 链表面试题
删除链表中等于给定值 val 的所有节点 /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}* ListNode(int val) { this.val val; }* ListNode(int val, ListNode next) { this.val val; this.next next; }* }*/ …...
2024/4/21 17:51:49 - 【外汇早评】美通胀数据走低,美元调整
原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...
2024/4/26 18:09:39 - 【原油贵金属周评】原油多头拥挤,价格调整
原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...
2024/4/26 20:12:18 - 【外汇周评】靓丽非农不及疲软通胀影响
原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...
2024/4/26 23:05:52 - 【原油贵金属早评】库存继续增加,油价收跌
原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...
2024/4/27 4:00:35 - 【外汇早评】日本央行会议纪要不改日元强势
原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...
2024/4/25 18:39:22 - 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响
原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...
2024/4/25 18:39:22 - 【外汇早评】美欲与伊朗重谈协议
原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...
2024/4/26 21:56:58 - 【原油贵金属早评】波动率飙升,市场情绪动荡
原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...
2024/4/27 9:01:45 - 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试
原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...
2024/4/26 16:00:35 - 【原油贵金属早评】市场情绪继续恶化,黄金上破
原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...
2024/4/25 18:39:16 - 【外汇早评】美伊僵持,风险情绪继续升温
原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...
2024/4/25 18:39:16 - 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势
原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...
2024/4/26 19:03:37 - 氧生福地 玩美北湖(上)——为时光守候两千年
原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...
2024/4/26 22:01:59 - 氧生福地 玩美北湖(中)——永春梯田里的美与鲜
原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...
2024/4/25 18:39:14 - 氧生福地 玩美北湖(下)——奔跑吧骚年!
原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...
2024/4/26 23:04:58 - 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!
原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...
2024/4/25 2:10:52 - 「发现」铁皮石斛仙草之神奇功效用于医用面膜
原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...
2024/4/25 18:39:00 - 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者
原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...
2024/4/26 19:46:12 - 广州械字号面膜生产厂家OEM/ODM4项须知!
原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...
2024/4/27 11:43:08 - 械字号医用眼膜缓解用眼过度到底有无作用?
原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...
2024/4/27 8:32:30 - 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...
解析如下: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