【面试题】2024前端vue面试题及答案__c(‘div‘,{domprops {"innerhtml" _vm

}
复制代码


Style 也可以通过对象语法和数组语法进行动态绑定


对象语法:



data: {
activeColor: ‘red’,
fontSize: 30
}
复制代码


数组语法:



data: {
styleColor: {
color: ‘red’
},
styleSize:{
fontSize:‘23px’
}
}
复制代码


#### 了解history有哪些方法吗?说下它们的区别



> 
>  history 这个对象在html5的时候新加入两个api history.pushState() 和 history.repalceState() 这两个API可以在不进行刷新的情况下,操作浏览器的历史纪录。唯一不同的是,前者是新增一个历史记录,后者是直接替换当前的历史记录。 
>  


从参数上来说:



window.history.pushState(state,title,url)
//state:需要保存的数据,这个数据在触发popstate事件时,可以在event.state里获取//title:标题,基本没用,一般传null//url:设定新的历史纪录的url。新的url与当前url的origin必须是一样的,否则会抛出错误。url可以时绝对路径,也可以是相对路径。//如 当前url是 https://www.baidu.com/a/,执行history.pushState(null, null, ‘./qq/’),则变成 https://www.baidu.com/a/qq/,//执行history.pushState(null, null, ‘/qq/’),则变成 https://www.baidu.com/qq/window.history.replaceState(state,title,url)
//与pushState 基本相同,但她是修改当前历史纪录,而 pushState 是创建新的历史纪录复制代码


另外还有:


* window.history.back() 后退


* window.history.forward()前进


* window.history.go(1) 前进或者后退几步


从触发事件的监听上来说:


* pushState()和replaceState()不能被popstate事件所监听


* 而后面三者可以,且用户点击浏览器前进后退键时也可以


#### 在Vue中使用插件的步骤


* 采用ES6的import ... from ...语法或CommonJS的require()方法引入插件


* 使用全局方法Vue.use( plugin )使用插件,可以传入一个选项对象Vue.use(MyPlugin, { someOption: true })


#### $route和$router的区别


* $route是“路由信息对象”,包括path,params,hash,query,fullPath,matched,name等路由信息参数。


* 而$router是“路由实例”对象包括了路由的跳转方法,钩子函数等


#### 为什么要使用异步组件


1. 节省打包出的结果,异步组件分开打包,采用jsonp的方式进行加载,有效解决文件过大的问题。


2. 核心就是包组件定义变成一个函数,依赖import() 语法,可以实现文件的分割加载。



components:{
AddCustomerSchedule:(resolve)=>import(“…/components/AddCustomer”) // require([])
}
复制代码


原理



exportfunction ( Ctor: Class | Function | Object | void, data: ?VNodeData, context: Component, children: ?Array, tag?: string ): VNode | Array | void {
// async component let asyncFactory
if (isUndef(Ctor.cid)) {
asyncFactory = CtorCtor = resolveAsyncComponent(asyncFactory, baseCtor) // 默认调用此函数时返回 undefiend // 第二次渲染时Ctor不为undefined if (Ctor === undefined) {
returncreateAsyncPlaceholder( // 渲染占位符 空虚拟节点
asyncFactory,
data,
context,
children,
tag
)
}
}
}
functionresolveAsyncComponent ( factory: Function, baseCtor: Class ): Class | void {
if (isDef(factory.resolved)) {
// 3.在次渲染时可以拿到获取的最新组件 return factory.resolved
}
const resolve = once((res: Object | Class) => {
factory.resolved = ensureCtor(res, baseCtor)
if (!sync) {
forceRender(true) //2. 强制更新视图重新渲染
} else {
owners.length = 0
}
})
const reject = once(reason => {
if (isDef(factory.errorComp)) {
factory.error = trueforceRender(true)
}
})
const res = factory(resolve, reject)// 1.将resolve方法和reject方法传入,用户调用 resolve方法后
sync = falsereturn factory.resolved
}
复制代码


#### 函数式组件优势和原理


函数组件的特点


1. 函数式组件需要在声明组件是指定 functional:true


2. 不需要实例化,所以没有this,this通过render函数的第二个参数context来代替


3. 没有生命周期钩子函数,不能使用计算属性,watch


4. 不能通过$emit 对外暴露事件,调用事件只能通过context.listeners.click的方式调用外部传入的事件


5. 因为函数式组件是没有实例化的,所以在外部通过ref去引用组件时,实际引用的是HTMLElement


6. 函数式组件的props可以不用显示声明,所以没有在props里面声明的属性都会被自动隐式解析为prop,而普通组件所有未声明的属性都解析到$attrs里面,并自动挂载到组件根元素上面(可以通过inheritAttrs属性禁止)


优点


7. 由于函数式组件不需要实例化,无状态,没有生命周期,所以渲染性能要好于普通组件


8. 函数式组件结构比较简单,代码结构更清晰


使用场景:


* 一个简单的展示组件,作为容器组件使用 比如 router-view 就是一个函数式组件


* “高阶组件”——用于接收一个组件作为参数,返回一个被包装过的组件


例子



Vue.component(‘functional’,{ // 构造函数产生虚拟节点的functional:true, // 函数式组件 // data={attrs:{}}render(h){
returnh(‘div’,‘test’)
}
})
const vm = newVue({
el: ‘#app’
})
复制代码


源码相关



// functional componentif (isTrue(Ctor.options.functional)) { // 带有functional的属性的就是函数式组件returncreateFunctionalComponent(Ctor, propsData, data, context, children)
}

// extract listeners, since these needs to be treated as// child component listeners instead of DOM listenersconst listeners = data.on// 处理事件// replace with listeners with .native modifier// so it gets processed during parent component patch.
data.on = data.nativeOn// 处理原生事件// install component management hooks onto the placeholder nodeinstallComponentHooks(data) // 安装组件相关钩子 (函数式组件没有调用此方法,从而性能高于普通组件)复制代码


### Vue.set的实现原理




---


* 给对应和数组本身都增加了dep属性


* 当给对象新增不存在的属性则触发对象依赖的watcher去更新


* 当修改数组索引时,我们调用数组本身的splice去更新数组(数组的响应式原理就是重新了splice等方法,调用splice就会触发视图更新)


基本使用



> 
>  以下方法调用会改变原始数组:push(), pop(), shift(), unshift(), splice(), sort(), reverse(),Vue.set( target, key, value ) 
>  


* 调用方法:Vue.set(target, key, value )


* target:要更改的数据源(可以是对象或者数组)


* key:要更改的具体数据


* value :重新赋的值



<divid=“app”>{{user.name}} {{user.age}}<divid=“app”>复制代码


相关源码



// src/core/observer/index.js 44exportclassObserver { // new Observer(value)value: any;
dep: Dep;
vmCount: number; // number of vms that have this object as root $dataconstructor (value: any) {
this.value = value
this.dep = newDep() // 给所有对象类型增加dep属性
}
}
复制代码



// src/core/observer/index.js 201exportfunctionset (target: Array | Object, key: any, val: any): any {
// 1.是开发环境 target 没定义或者是基础类型则报错if (process.env.NODE_ENV !== ‘production’ &&
(isUndef(target) || isPrimitive(target))
) {
warn(Cannot set reactive property on undefined, null, or primitive value: ${(target: any)})
}
// 2.如果是数组 Vue.set(array,1,100); 调用我们重写的splice方法 (这样可以更新视图)if (Array.isArray(target) && isValidArrayIndex(key)) {
target.length = Math.max(target.length, key)
// 利用数组的splice变异方法触发响应式
target.splice(key, 1, val)
return val
}
// 3.如果是对象本身的属性,则直接添加即可if (key in target && !(key inObject.prototype)) {
target[key] = val // 直接修改属性值 return val
}
// 4.如果是Vue实例 或 根数据data时 报错,(更新_data 无意义)const ob = (target: any).__ob__if (target._isVue || (ob && ob.vmCount)) {
process.env.NODE_ENV !== ‘production’ && warn(
'Avoid adding reactive properties to a Vue instance or its root $data ’ +
‘at runtime - declare it upfront in the data option.’
)
return val
}
// 5.如果不是响应式的也不需要将其定义成响应式属性if (!ob) {
target[key] = val
return val
}
// 6.将属性定义成响应式的defineReactive(ob.value, key, val)
// 通知视图更新
ob.dep.notify()
return val
}
复制代码


## 大厂面试题分享 面试题库


#### 后端面试题库 (面试必备) 推荐:★★★★★


地址:[前端面试题库]( )




我们阅读以上源码可知,vm.$set 的实现原理是:


* 如果目标是数组 ,直接使用数组的 splice 方法触发相应式;


* 如果目标是对象 ,会先判读属性是否存在、对象是否是响应式,最终如果要对属性进行响应式处理,则是通过调用 defineReactive 方法进行响应式处理( defineReactive 方法就是 Vue 在初始化对象时,给对象属性采用 Object.defineProperty 动态添加 getter 和 setter 的功能所调用的方法)


#### Vue为什么没有类似于React中shouldComponentUpdate的生命周期


* 考点: Vue的变化侦测原理


* 前置知识: 依赖收集、虚拟DOM、响应式系统



> 
>  根本原因是Vue与React的变化侦测方式有所不同 
>  


* 当React知道发生变化后,会使用Virtual Dom Diff进行差异检测,但是很多组件实际上是肯定不会发生变化的,这个时候需要 shouldComponentUpdate 进行手动操作来减少diff,从而提高程序整体的性能


* Vue在一开始就知道那个组件发生了变化,不需要手动控制diff,而组件内部采用的diff方式实际上是可以引入类似于shouldComponentUpdate相关生命周期的,但是通常合理大小的组件不会有过量的diff,手动优化的价值有限,因此目前Vue并没有考虑引入shouldComponentUpdate这种手动优化的生命周期


#### vue-router中如何保护路由


分析


路由保护在应用开发过程中非常重要,几乎每个应用都要做各种路由权限管理,因此相当考察使用者基本功。


体验


全局守卫:



const router = createRouter({ … })

router.beforeEach((to, from) => {
// …// 返回 false 以取消导航returnfalse
})
复制代码


路由独享守卫:



const routes = [
{
path: ‘/users/:id’,
component: UserDetails,
beforeEnter: (to, from) => {
// reject the navigationreturnfalse
},
},
]
复制代码


组件内的守卫:



constUserDetails = {
template: ...,
beforeRouteEnter(to, from) {
// 在渲染该组件的对应路由被验证前调用
},
beforeRouteUpdate(to, from) {
// 在当前路由改变,但是该组件被复用时调用
},
beforeRouteLeave(to, from) {
// 在导航离开渲染该组件的对应路由时调用
},
}
复制代码


回答


* vue-router中保护路由的方法叫做路由守卫,主要用来通过跳转或取消的方式守卫导航。


* 路由守卫有三个级别:全局、路由独享、组件级。影响范围由大到小,例如全局的router.beforeEach(),可以注册一个全局前置守卫,每次路由导航都会经过这个守卫,因此在其内部可以加入控制逻辑决定用户是否可以导航到目标路由;在路由注册的时候可以加入单路由独享的守卫,例如beforeEnter,守卫只在进入路由时触发,因此只会影响这个路由,控制更精确;我们还可以为路由组件添加守卫配置,例如beforeRouteEnter,会在渲染该组件的对应路由被验证前调用,控制的范围更精确了。


* 用户的任何导航行为都会走navigate方法,内部有个guards队列按顺序执行用户注册的守卫钩子函数,如果没有通过验证逻辑则会取消原有的导航。


原理


runGuardQueue(guards)链式的执行用户在各级别注册的守卫钩子函数,通过则继续下一个级别的守卫,不通过进入catch流程取消原本导航



// 源码runGuardQueue(guards)
.then(() => {
// check global guards beforeEach
guards = []
for (const guard of beforeGuards.list()) {
guards.push(guardToPromiseFn(guard, to, from))
}
guards.push(canceledNavigationCheck)

returnrunGuardQueue(guards)

})
.then(() => {
// check in components beforeRouteUpdate
guards = extractComponentsGuards(
updatingRecords,
‘beforeRouteUpdate’,
to,
from
)

for (const record of updatingRecords) {
  record.updateGuards.forEach(guard => {
    guards.push(guardToPromiseFn(guard, to, from))
  })
}
guards.push(canceledNavigationCheck)

// run the queue of per route beforeEnter guardsreturnrunGuardQueue(guards)

})
.then(() => {
// check the route beforeEnter
guards = []
for (const record of to.matched) {
// do not trigger beforeEnter on reused viewsif (record.beforeEnter && !from.matched.includes(record)) {
if (isArray(record.beforeEnter)) {
for (const beforeEnter of record.beforeEnter)
guards.push(guardToPromiseFn(beforeEnter, to, from))
} else {
guards.push(guardToPromiseFn(record.beforeEnter, to, from))
}
}
}
guards.push(canceledNavigationCheck)

// run the queue of per route beforeEnter guardsreturnrunGuardQueue(guards)

})
.then(() => {
// NOTE: at this point to.matched is normalized and does not contain any () => Promise// clear existing enterCallbacks, these are added by extractComponentsGuards
to.matched.forEach(record => (record.enterCallbacks = {}))

// check in-component beforeRouteEnter
guards = extractComponentsGuards(
  enteringRecords,
  'beforeRouteEnter',
  to,
  from
)
guards.push(canceledNavigationCheck)

// run the queue of per route beforeEnter guardsreturnrunGuardQueue(guards)

})
.then(() => {
// check global guards beforeResolve
guards = []
for (const guard of beforeResolveGuards.list()) {
guards.push(guardToPromiseFn(guard, to, from))
}
guards.push(canceledNavigationCheck)

returnrunGuardQueue(guards)

})
// catch any navigation canceled
.catch(err =>isNavigationFailure(err, ErrorTypes.NAVIGATION_CANCELLED)
? err
: Promise.reject(err)
)
复制代码


[源码位置(opens new window)]( )


#### Vue-router 路由钩子在生命周期的体现


一、Vue-Router导航守卫


有的时候,需要通过路由来进行一些操作,比如最常见的登录权限验证,当用户满足条件时,才让其进入导航,否则就取消跳转,并跳到登录页面让其登录。 为此有很多种方法可以植入路由的导航过程:全局的,单个路由独享的,或者组件级的


1. 全局路由钩子


vue-router全局有三个路由钩子;


* router.beforeEach 全局前置守卫 进入路由之前


* router.beforeResolve 全局解析守卫(2.5.0+)在 beforeRouteEnter 调用之后调用


* router.afterEach 全局后置钩子 进入路由之后


具体使用∶


* beforeEach(判断是否登录了,没登录就跳转到登录页)



router.beforeEach((to, from, next) => {
let ifInfo = Vue.prototype.KaTeX parse error: Expected '}', got 'EOF' at end of input: … Vue.prototype.loginUrl;
}
} else {
returnnext();
}
})

复制代码


* afterEach (跳转之后滚动条回到顶部)



router.afterEach((to, from) => {
// 跳转之后滚动条回到顶部 window.scrollTo(0,0);
});

复制代码


2. 单个路由独享钩子


beforeEnter 如果不想全局配置守卫的话,可以为某些路由单独配置守卫,有三个参数∶ to、from、next



exportdefault [
{
path: ‘/’,
name: ‘login’,
component: login,
beforeEnter: (to, from, next) => {
console.log(‘即将进入登录页面’)
next()
}
}
]

复制代码


3. 组件内钩子


beforeRouteUpdate、beforeRouteEnter、beforeRouteLeave


这三个钩子都有三个参数∶to、from、next


* beforeRouteEnter∶ 进入组件前触发


* beforeRouteUpdate∶ 当前地址改变并且改组件被复用时触发,举例来说,带有动态参数的路径foo/∶id,在 /foo/1 和 /foo/2 之间跳转的时候,由于会渲染同样的foa组件,这个钩子在这种情况下就会被调用


* beforeRouteLeave∶ 离开组件被调用


注意点,beforeRouteEnter组件内还访问不到this,因为该守卫执行前组件实例还没有被创建,需要传一个回调给 next来访问,例如:



beforeRouteEnter(to, from, next) {
next(target => {
if (from.path == ‘/classProcess’) {
target.isFromProcess = true
}
})
}

复制代码


二、Vue路由钩子在生命周期函数的体现


4. 完整的路由导航解析流程(不包括其他生命周期)


* 触发进入其他路由。


* 调用要离开路由的组件守卫beforeRouteLeave


* 调用局前置守卫∶ beforeEach


* 在重用的组件里调用 beforeRouteUpdate


* 调用路由独享守卫 beforeEnter。


* 解析异步路由组件。


* 在将要进入的路由组件中调用 beforeRouteEnter


* 调用全局解析守卫 beforeResolve


* 导航被确认。


* 调用全局后置钩子的 afterEach 钩子。


* 触发DOM更新(mounted)。


* 执行beforeRouteEnter 守卫中传给 next 的回调函数


5. 触发钩子的完整顺序


路由导航、keep-alive、和组件生命周期钩子结合起来的,触发顺序,假设是从a组件离开,第一次进入b组件∶


* beforeRouteLeave:路由组件的组件离开路由前钩子,可取消路由离开。


* beforeEach:路由全局前置守卫,可用于登录验证、全局路由loading等。


* beforeEnter:路由独享守卫


* beforeRouteEnter:路由组件的组件进入路由前钩子。


* beforeResolve:路由全局解析守卫


* afterEach:路由全局后置钩子


* beforeCreate:组件生命周期,不能访问tAis。


* created;组件生命周期,可以访问tAis,不能访问dom。


* beforeMount:组件生命周期


* deactivated:离开缓存组件a,或者触发a的beforeDestroy和destroyed组件销毁钩子。


* mounted:访问/操作dom。


* activated:进入缓存组件,进入a的嵌套子组件(如果有的话)。


* 执行beforeRouteEnter回调函数next。


6. 导航行为被触发到导航完成的整个过程


* 导航行为被触发,此时导航未被确认。


* 在失活的组件里调用离开守卫 beforeRouteLeave。


* 调用全局的 beforeEach守卫。


* 在重用的组件里调用 beforeRouteUpdate 守卫(2.2+)。


* 在路由配置里调用 beforeEnteY。


* 解析异步路由组件(如果有)。


* 在被激活的组件里调用 beforeRouteEnter。


* 调用全局的 beforeResolve 守卫(2.5+),标示解析阶段完成。


* 导航被确认。


* 调用全局的 afterEach 钩子。


* 非重用组件,开始组件实例的生命周期:beforeCreate&created、beforeMount&mounted


* 触发 DOM 更新。


* 用创建好的实例调用 beforeRouteEnter守卫中传给 next 的回调函数。


* 导航完成


#### Vue-router 导航守卫有哪些


* 全局前置/钩子:beforeEach、beforeResolve、afterEach


* 路由独享的守卫:beforeEnter


* 组件内的守卫:beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave


#### Vue的diff算法详细分析


1. 是什么


diff 算法是一种通过同层的树节点进行比较的高效算法


其有两个特点:


* 比较只会在同层级进行, 不会跨层级比较


* 在diff比较的过程中,循环从两边向中间比较


diff 算法在很多场景下都有应用,在 vue 中,作用于虚拟 dom 渲染成真实 dom 的新旧 VNode 节点比较


2. 比较方式


diff整体策略为:深度优先,同层比较


1. 比较只会在同层级进行, 不会跨层级比较





![](https://img-blog.csdnimg.cn/img_convert/a52cdfbf04d74c828fbb44788c1ec532.webp?x-oss-process=image/format,png)



2. 比较的过程中,循环从两边向中间收拢


下面举个vue通过diff算法更新的例子:


新旧VNode节点如下图所示:





![](https://img-blog.csdnimg.cn/img_convert/4fc592ea86784f1db20bbbf17ea7d2ac.webp?x-oss-process=image/format,png)



第一次循环后,发现旧节点D与新节点D相同,直接复用旧节点D作为diff后的第一个真实节点,同时旧节点endIndex移动到C,新节点的 startIndex 移动到了 C





![](https://img-blog.csdnimg.cn/img_convert/a1285078a79d479ea264c363408bc819.webp?x-oss-process=image/format,png)



第二次循环后,同样是旧节点的末尾和新节点的开头(都是 C)相同,同理,diff 后创建了 C 的真实节点插入到第一次创建的 D 节点后面。同时旧节点的 endIndex 移动到了 B,新节点的 startIndex 移动到了 E





![](https://img-blog.csdnimg.cn/img_convert/43a24ebc1e7649f6bece4351db42f1de.webp?x-oss-process=image/format,png)



第三次循环中,发现E没有找到,这时候只能直接创建新的真实节点 E,插入到第二次创建的 C 节点之后。同时新节点的 startIndex 移动到了 A。旧节点的 startIndex 和 endIndex 都保持不动





![](https://img-blog.csdnimg.cn/img_convert/20e0cf1af51f45cd83a0e4c108171abc.webp?x-oss-process=image/format,png)



第四次循环中,发现了新旧节点的开头(都是 A)相同,于是 diff 后创建了 A 的真实节点,插入到前一次创建的 E 节点后面。同时旧节点的 startIndex 移动到了 B,新节点的startIndex 移动到了 B





![](https://img-blog.csdnimg.cn/img_convert/875f646d925e4403a12ed276f2b43ade.webp?x-oss-process=image/format,png)



第五次循环中,情形同第四次循环一样,因此 diff 后创建了 B 真实节点 插入到前一次创建的 A 节点后面。同时旧节点的 startIndex移动到了 C,新节点的 startIndex 移动到了 F





![](https://img-blog.csdnimg.cn/img_convert/184b8c51cbb940f3b8b1a539f65a5d2f.webp?x-oss-process=image/format,png)



新节点的 startIndex 已经大于 endIndex 了,需要创建 newStartIdx 和 newEndIdx 之间的所有节点,也就是节点F,直接创建 F 节点对应的真实节点放到 B 节点后面





![](https://img-blog.csdnimg.cn/img_convert/29ab1549406b4f38afb5743fa7d4f175.webp?x-oss-process=image/format,png)



3. 原理分析


当数据发生改变时,set方法会调用Dep.notify通知所有订阅者Watcher,订阅者就会调用patch给真实的DOM打补丁,更新相应的视图


源码位置:src/core/vdom/patch.js



functionpatch(oldVnode, vnode, hydrating, removeOnly) {
if (isUndef(vnode)) { // 没有新节点,直接执行destory钩子函数if (isDef(oldVnode)) invokeDestroyHook(oldVnode)
return
}

let isInitialPatch = falseconst insertedVnodeQueue = []

if (isUndef(oldVnode)) {
    isInitialPatch = truecreateElm(vnode, insertedVnodeQueue) // 没有旧节点,直接用新节点生成dom元素
} else {
    const isRealElement = isDef(oldVnode.nodeType)
    if (!isRealElement && sameVnode(oldVnode, vnode)) {
        // 判断旧节点和新节点自身一样,一致执行patchVnodepatchVnode(oldVnode, vnode, insertedVnodeQueue, null, null, removeOnly)
    } else {
        // 否则直接销毁及旧节点,根据新节点生成dom元素if (isRealElement) {

            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
                }
            }
            oldVnode = emptyNodeAt(oldVnode)
        }
        return vnode.elm
    }
}

}
复制代码


patch函数前两个参数位为oldVnode 和 Vnode ,分别代表新的节点和之前的旧节点,主要做了四个判断:


* 没有新节点,直接触发旧节点的destory钩子


* 没有旧节点,说明是页面刚开始初始化的时候,此时,根本不需要比较了,直接全是新建,所以只调用 createElm


* 旧节点和新节点自身一样,通过 sameVnode 判断节点是否一样,一样时,直接调用 patchVnode去处理这两个节点


* 旧节点和新节点自身不一样,当两个节点不一样的时候,直接创建新节点,删除旧节点


下面主要讲的是patchVnode部分



functionpatchVnode (oldVnode, vnode, insertedVnodeQueue, removeOnly) {
// 如果新旧节点一致,什么都不做if (oldVnode === vnode) {
return
}

// 让vnode.el引用到现在的真实dom,当el修改时,vnode.el会同步变化const elm = vnode.elm = oldVnode.elm// 异步占位符if (isTrue(oldVnode.isAsyncPlaceholder)) {
  if (isDef(vnode.asyncFactory.resolved)) {
    hydrate(oldVnode.elm, vnode, insertedVnodeQueue)
  } else {
    vnode.isAsyncPlaceholder = true
  }
  return
}
// 如果新旧都是静态节点,并且具有相同的key// 当vnode是克隆节点或是v-once指令控制的节点时,只需要把oldVnode.elm和oldVnode.child都复制到vnode上// 也不用再有其他操作if (isTrue(vnode.isStatic) &&
  isTrue(oldVnode.isStatic) &&
  vnode.key === oldVnode.key &&
  (isTrue(vnode.isCloned) || isTrue(vnode.isOnce))
) {
  vnode.componentInstance = oldVnode.componentInstancereturn
}

let i
const 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不是文本节点或者注释节点if (isUndef(vnode.text)) {
  // 并且都有子节点if (isDef(oldCh) && isDef(ch)) {
    // 并且子节点不完全一致,则调用updateChildrenif (oldCh !== ch) updateChildren(elm, oldCh, ch, insertedVnodeQueue, removeOnly)

    // 如果只有新的vnode有子节点

最后

技术是没有终点的,也是学不完的,最重要的是活着、不秃。零基础入门的时候看书还是看视频,我觉得成年人,何必做选择题呢,两个都要。喜欢看书就看书,喜欢看视频就看视频。最重要的是在自学的过程中,一定不要眼高手低,要实战,把学到的技术投入到项目当中,解决问题,之后进一步锤炼自己的技术。

技术学到手后,就要开始准备面试了,找工作的时候一定要好好准备简历,毕竟简历是找工作的敲门砖,还有就是要多做面试题,复习巩固。


))
) {
vnode.componentInstance = oldVnode.componentInstancereturn
}

let i
const 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不是文本节点或者注释节点if (isUndef(vnode.text)) {
  // 并且都有子节点if (isDef(oldCh) && isDef(ch)) {
    // 并且子节点不完全一致,则调用updateChildrenif (oldCh !== ch) updateChildren(elm, oldCh, ch, insertedVnodeQueue, removeOnly)

    // 如果只有新的vnode有子节点

最后

技术是没有终点的,也是学不完的,最重要的是活着、不秃。零基础入门的时候看书还是看视频,我觉得成年人,何必做选择题呢,两个都要。喜欢看书就看书,喜欢看视频就看视频。最重要的是在自学的过程中,一定不要眼高手低,要实战,把学到的技术投入到项目当中,解决问题,之后进一步锤炼自己的技术。

技术学到手后,就要开始准备面试了,找工作的时候一定要好好准备简历,毕竟简历是找工作的敲门砖,还有就是要多做面试题,复习巩固。

[外链图片转存中…(img-5sYosLoO-1719237493851)]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值