8月vue高频面试题汇总
v-model可以用在自定义组件中吗?如果是这样,如何使用它?能。v-model其实就是一个语法糖,比如:复制代码其实等价于:复制代码在自定义组件中的使用方式相同:复制代码等同于:复制代码显然,custom-input与父组件的交互如下:父组件将searchText变量传递给custom-input组件,使用的prop命名为value;custom-input组件向父组件发送名为input的事件,父组件将接收到的值赋值给searchText;因此,自定义输入组件的实现应该类似于:Vue.component('custom-input',{props:['value'],template:``})复制代码前端高级面试题详解Vue模板编译原理Vue编译过程就是转换将模板转换为渲染函数。该过程分为以下三个步骤。第一步是将模板字符串转换为元素AST(解析器)。第二步是AST上的静态节点标记,主要用于虚拟DOM渲染优化(optimizer)。第三步是使用元素AST生成渲染函数代码字符串(代码生成器)。相关代码如下:导出函数compileToFunctions(template){//我们需要将html字符串转为render函数//1.将html代码转为ast语法树ast用于描述代码本身,形成一个树状结构,不仅可以描述html还有css和js语法//很多库都用ast,比如webpackbabeleslint等letast=parse(template);//2.优化静态节点//有兴趣的可以去看源码,不影响核心功能就不实现了//if(options.optimize!==false){//optimize(ast,选项);//}//3.通过ast重新生成代码//我们最终生成的代码需要和render函数一样//类似于_c('div',{id:"app"},_c('div',undefined,_v("hello"+_s(name)),_c('span',undefined,_v("world"))))//_c代表创建元素_v代表创建文本_s代表文本Json.stringify--将对象解析为文本letcode=generate(ast);//使用with语法将范围更改为this,然后调用渲染函数。为了方便可以使用call来改变this代码中变量的值letrenderFn=newFunction(`with(this){return${code}}`);returnrenderFn;}详解模板编译原理PortalVue中的key有什么用?key是在Vue中为vnode标记的唯一id。通过这把钥匙,我们的diff操作可以更准确、更快速。在diff算法的过程中,会先进行新旧节点的第一次和最后一次交叉比较。当没有匹配时,将使用新的。该节点的key与旧节点进行比较,然后超出差值。diff过程可以概括为:oldCh和newCh各有两个头尾变量StartIdx和EndIdx,它们的两个变量相互比较。一共有4种比较方式如果四个比较都不匹配,如果设置了key,则使用key进行比较。在比较过程中,变量会向中间倾斜。一旦StartIdx>EndIdx表示至少遍历了oldCh和newCh中的一个,就结束比较,这四种比较方式分别是head,tail,oldtailnewhead,oldheadnewtail。准确:如果没有添加key,那么Vue会选择复用节点(Vue的就地更新策略),导致之前节点的状态如果被保留,就会产生一系列的bug。快速:键的唯一性可以被Map数据结构充分利用。与遍历搜索的时间复杂度O(n)相比,Map的时间复杂度仅为O(1)。虚拟DOM的优缺点是什么?优点:保证性能下限:VirtualDOM可以通过diff找到最小差异,然后批量patch。这种操作虽然不如手动优化,但比粗略的DOM操作性能要好很多。因此,虚拟DOM可以在不手动操作DOM的情况下保证性能的下限:虚拟DOM的diff和patch在一次更新中自动进行,我们不需要手动操作DOM,大大提高了开发效率跨平台:虚拟DOM本质上是一个JavaScript对象,DOM与平台强相关。相比之下,虚拟DOM可以更方便的跨平台操作,比如服务端渲染、移动端开发等。缺点:无法优化到极致:在一些对性能要求极高的应用中,虚拟DOM无法进行针对性的极致优化,比如VScode采用直接手动操作DOM进行极致的性能优化。用VNode来描述一个DOM结构虚拟节点就是用一个对象来描述一个真实的DOM元素。首先将template(realDOM)转换成ast,ast树通过codegen生成render函数,render函数中的_c方法将其转换成虚拟dom。什么是MVVM?Model–View–ViewModel(MVVM)是由MicrosoftWPF和Silverlight的架构师KenCooper和TedPeters开发的一种软件架构设计模式,是一种简化用户界面的事件驱动编程方法。由JohnGossman(也是WPF和Silverlight的架构师)于2005年在他的博客上发布。MVVM源自经典的模型-视图-控制器(MVC)模式。MVVM的出现促进了前端开发和后端业务逻辑。前端开发分离,大大提高了前端开发的效率。MVVM的核心是ViewModel层,它就像一个中转站(值转换器),负责转换Model中的数据对象,使数据更易于管理和使用。本层向上与视图层进行双向数据绑定,向下通过接口请求与Model层进行数据交互,起到连接上下层的作用(1)视图层View为视图层,即,用户界面。前端主要使用HTML和CSS构建。(2)模型层模型是指数据模型,泛指后端进行的各种业务逻辑处理和数据操作。对于前端来说,就是后端提供的api接口。(3)ViewModel层ViewModel是由前端开发人员生成和维护的视图数据层。在这一层,前端开发人员对从后端获取的Model数据进行转换处理,并进行二次包装,生成满足View层预期的视图数据模型。需要注意的是,ViewModel封装的数据模型包括视图的状态和行为,而Model层的数据模型只包括状态,比如页面这部分显示什么,页面打开时会发生什么loaded,clickhere一块会发生什么,滚动这一块会发生什么,这些都属于视图行为(交互),视图状态和行为都封装在ViewModel中。这样的封装使得ViewModel可以完整地描述View层。MVVM框架实现了双向绑定,使得ViewModel的内容会实时显示在View层。前端开发人员不再需要通过低效和麻烦地操作DOM来更新视图。MVVM框架已经完成了最肮脏和最累的部分,我们的开发者只需要处理和维护ViewModel,更新数据视图会自动随之更新。这样View层显示的不是Model层的数据,而是ViewModel的数据。ViewModel负责和Model层交互,完全解耦了View层和Model层。这种脱钩至关重要。它是前后端分离方案实施的重要一环。下面用一个Vue的例子来说明一下MVVM的具体实现。有Vue开发经验的同学应该一目了然:(1)视图层{{message}}
点我
(2)ViewModel层varapp=newVue({el:'#app',data:{//用于描述视图状态消息:'HelloVue!',},methods:{//用于描述视图行为showMessage(){letvm=this;alert(vm.message);}},created(){letvm=this;//Ajax获取Model层ajax的数据({url:'/your/server/data/api',success(res){vm.message=res;}});}})(3)模型层{"url":"/your/server/data/api","res":{"success":true,"name":"IoveC","domain":"www.cnblogs.com"}}父子组件生命周期调用顺序(简单)渲染顺序:firstparent,thenchild,completionorder:firstchild,thenparentupdateorder:parentupdateleadstochildupdate,child更新完成后parentdestructionorder:firstfather,thenchild,completionorder:firstchild,thenparentvue和react的区别=>相同点:1.数据驱动页面,提供响应式视图组件2.均有虚拟DOM,基于组件开发,通过props参数在父子组件间传递数据,实现webComponents规范3.数据流向是单向的,都支持服务端渲染SSR4。两者都支持native方法,react有Reactnative,vue有wexx=>区别:1.数据绑定:Vue实现双向数据绑定,react是单向数据流2.数据渲染:大规模数据渲染,react更快3.使用场景:React和Redux架构适用于大型多人协作和复杂的项目,Vue适用于小而快的项目4.开发风格:react推荐做法是html和css都写在带有jsx+内联样式的js。Vue采用webpack+vue-loader单文件组件格式。HTML、js、css在同一个文件中。比较Proxy和Object.defineProperty的优缺点。Proxy的优缺点如下:Proxy可以直接监控对象而不是属性;Proxy可以直接监听数组的变化;Proxy有多达13种拦截方法,不限于apply、ownKeys、deleteProperty、has等,Object.defineProperty没有;Proxy返回一个新的Objects,我们只能操作新的对象来达到目的,而Object.defineProperty只能遍历对象属性,直接修改;Proxy作为新标准,浏览器厂商会不断优化,这是传说中新标准的性能加成;Object.defineProperty的优点是:兼容性好,支持IE9,而Proxy存在浏览器兼容性问题,无法用polyfill平滑,所以Vue的作者声明需要等到下一个大版本(3.0)再次使用Proxy写$nextTick什么?Vue对响应式的实现不会在数据发生后立即更新DOM。使用vm.$nextTick是在下一个DOM更新周期结束后立即执行延迟回调。修改数据后使用,可以在回调中获取更新后的DOM。为什么data必须是vue组件中的一个函数?该对象是引用类型。组件复用时,由于数据对象都指向同一个数据对象,当一个组件中的数据被修改时,其他复用组件中的数据也会同时被修改;而使用返回对象的函数,由于每次都返回一个新的对象(Object的实例),引用地址不同,所以不会出现这个问题。Vue3.0和2.0的响应式原理的区别在于Vue3.x使用Proxy而不是Object.defineProperty。因为Proxy可以直接监听对象和数组的变化,拦截方式多达13种。相关代码如下import{mutableHandlers}from"./baseHandlers";//代理相关逻辑import{isObject}from"./util";//工具方法exportfunctionreactive(target){//根据不同的参数创建不同的响应objectreturncreateReactiveObject(target,mutableHandlers);}functioncreateReactiveObject(target,baseHandler){if(!isObject(target)){}constobserved=newProxy(target,baseHandler);返回观察;}constget=createGetter();constset=createSetter();functioncreateGetter(){returnfunctionget(target,key,receiver){//辐射得到的值constres=Reflect.get(target,key,receiver);安慰。log("财产收购",key);if(isObject(res)){//如果获取的值是对象类型,则返回当前对象的代理对象returnreactive(res);}返回资源;};}functioncreateSetter(){returnfunctionset(target,key,value,receiver){constoldValue=target[key];常量hadKey=hasOwn(target,key);constresult=Reflect.set(target,key,value,receiver);if(!hadKey){console.log("添加新属性",key,value);}elseif(hasChanged(value,oldValue)){console.log("属性值已修改",key,value);}返回结果;};}exportconstmutableHandlers={get,//获取属性集时调用此方法,//修改属性时调用此方法};Vue中组件生命周期调用顺序先说说组件的调用顺序先父后子,渲染完成的顺序是先子后父。组件的销毁操作是先父后子,销毁顺序是先子后父。加载渲染进程parentbeforeCreate->parentcreated->parentbeforeMount->childbeforeCreate->childcreated->childbeforeMount->childmounted->parentmounted子组件更新流程parentbeforeUpdate->childbeforeUpdate->childupdated->parentupdatedparentcomponentupdateprocessparentbeforeUpdate->parentupdateddestructionprocessparentbeforeDestroy->childbeforeDestroy->childdestroyed->parentdestroyedv-show和v-if有什么区别?v-if是真正的条件渲染,因为它将确保条件块内的事件侦听器和子组件在切换期间被适当地销毁和重建;也很懒惰:如果在初始渲染时条件为假,什么也不做-条件块不会开始渲染,直到条件第一次变为真。v-show更简单-无论初始条件如何,元素始终呈现,并且根据CSS“显示”属性简单地切换。因此,v-if适用于运行时很少改变条件,不需要频繁切换条件的场景;v-show适用于需要非常频繁切换条件的场景。你写过自定义指令吗?原理是什么?Instructions本质上是装饰器,是Vue对HTML元素的扩展,为HTML元素添加自定义函数。vue在编译DOM时,会找到指令对象,并执行指令的相关方法。自定义指令有五个生命周期(也叫钩子函数),分别是bind、inserted、update、componentUpdated和unbind1。bind:只调用一次,当指令第一次绑定到一个元素时。可以在这里进行一次性的初始化设置。2.inserted:绑定元素插入到父节点时调用(只保证父节点存在,不一定插入到文档中)。3.update:绑定元素的模板更新时调用,不管绑定值是否变化。通过比较更新前后的绑定值,可以忽略不必要的模板更新。4.componentUpdated:当绑定元素所在的模板完成一个更新周期时调用。5.unbind:只调用一次,当指令与元素解除绑定时。原理1.生成ast语法树时,遇到指令会在当前元素上添加一个directives属性2.通过genDirectives生成指令代码3.打补丁前将指令的hook提取到cbs中,在打补丁的时候调用对应的hookpatchingprocess4.当指令对应的钩子函数执行时,调用对应指令定义的方法。diff算法时间复杂度:一棵树的完整diff算法时间复杂度为O(n*3),Vue优化为O(n)。理解:最小更新,key很重要。这可以是这个节点的唯一标识符,告诉diff算法它们在变化前后是同一个DOM节点。为什么扩展名v-for有一个键?如果没有key,就会被暴力复用。比如只说一个比如移动一个节点或者添加一个节点(ModifytheDOM),添加一个key只会移动和减少对DOM的操作。只有相同的虚拟节点才会进行精细比较,否则,旧的会被暴力删除,新的会被插入。只进行同层比较,不进行跨层比较。diff算法的优化策略:四种hitlookups,四种指针先旧后新(开始后插入删除节点的情况)和旧后新后(插入或删除节点的情况)beforeend)oldfrontandnewback(headandtailratio,这个发生了,涉及移动节点,然后新front指向的节点,移动到oldback之后)oldbackandnewfront(tailandheadratio),这个事情发生了,涉及到移动节点,然后新前端指向的节点,移动到旧前端)vue修饰符有哪些事件修饰符.stop阻止事件继续传播。prevent阻止标签的默认行为。capture采用事件捕获方式,即元素本身触发的事件先在这里处理,然后交给内部元素处理。self只有在event.target是当前元素本身时才会触发handler。一旦事件发生就会只被触发一次。被动告诉浏览器你不想阻止模型的事件v修饰符的默认行为。lazy通过这个修饰符,转化为在change事件中重新同步。number自动将用户输入的值转换为数字类型。trim自动过滤用户输入的第一个和最后一个空格的键盘事件修饰符。输入.tab。delete(捕获“Delete”和“Backspace”键).esc.space.up.down.left.rightsystemmodifierkeys.ctrl.alt.shift.metamousebuttonmodifiers.left.right.middle谈谈Vue和的思想React组件化1.我们在开发每个页面的时候,都会有很多重复的功能,比如element中的xxxx。像这样的纯非页面UI成为了我们常用的UI组件,而原来的前端组件只指UI组件2。随着业务逻辑越来越多,我们希望我们的组件能够处理很多事情,这就是我们常说的组件化。该组件不是UI组件,而是包含特定服务的业务组件。3、这种开发思路就是分而治之。将开发难度和维护成本的影响降到最低。而且可以多人协作,每个人写不同的组件,最后像搭积木一样组成页面