配色: 字号:
vuejs高频面试题
2021-01-15 | 阅:  转:  |  分享 
  
.什么是vue生命周期vue生命周期的理解vue实例有一个完整的生命周期,生命周期也就是指一个实例从开始创建到销毁的这个过程beforeCr
eate()?在实例创建之间执行,数据未加载状态created()?在实例创建、数据加载后,能初始化数据,dom渲染之前执行bef
oreMount()?虚拟dom已创建完成,在数据渲染前最后一次更改数据mounted()?页面、数据渲染完成,真实dom挂载完成
beforeUpadate()?重新渲染之前触发updated()?数据已经更改完成,dom?也重新?render?完成,更改数据
会陷入死循环beforeDestory()?和?destoryed()?前者是销毁前执行(实例仍然完全可用),后者则是销毁后执行作
用:生命周期中有多个事件钩子,在控制整个Vue实例的过程时更容易形成好的逻辑。beforeCreate:完成实例初始化,t
his指向被创建的实例,data,computed,watch,mothods方法和数据都不可以访问,数据观测之前(data
observer)被调用。created:实例创建完成,data,computed,watch,methods可被访问,未挂
载Dom,可对data进行操作,操作Dom需放到nextTick中。beforeMount:有了el,找到对应的
template编译成render函数mounted:完成挂载Dom和渲染,可对Dom进行获取节点等操作,可发起
后端请求拿到数据。beforeUpdate:数据更新时调用,发生在虚拟Dom重新渲染和打补丁之前之调用。updated:
组件Dom已完成更新,可执行依赖的Dom操作,不要操作数据会陷入死循环。beforeDestroy:实例销毁之前调用,
可进行优化操作,如销毁定时器,解除绑定事件。destroyed:组件已经被销毁,事件监听器和子实例都会被移除销毁。首次页面加载会
触发四个钩子函数:beforeCreate,created,beforeMount,mounted且DMO渲染在mo
unted中就已经完成了。可以使用$on(''hook:'')或$once(''hook:'')来简化生命周期的注册.谈谈MV
VM模式Model:代表数据模型,也可以在Model中定义数据修改和操作的业务逻辑。View:代表UI组件,
它负责将数据模型转化成UI展现出来。ViewModel:监听模型数据的改变和控制视图行为、处理用户交互,简单理解就是一个
同步View和Model的对象,连接Model和View。在MVVM架构下,View和Model之间并没有
直接的联系,而是通过ViewMode进行交互,Model和ViewModel之间的交互是双向自动的,因此View数
据的变化会同步到Model中,而Model数据的变化也会立即反应到View上。而开发者只需关注业务逻辑,不需要手动操作
DOM,不需要关注数据状态的同步问题,复杂的数据状态维护完全由MVVM来统一管理。MVVM和MVC区别?mvc和mv
vm其实区别并不大。都是一种设计思想。主要就是mvc中Controller演变成mvvm中的viewModel。mv
vm主要解决了mvc中大量的DOM操作使页面渲染性能降低,加载速度变慢,影响用户体验。和当Model频繁发生变化,开发
者需要主动更新到View。说下Vue实现数据双向绑定的原理Vue实现数据双向绑定主要是:采用数据劫持结合发布者-订阅者
模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布
消息给订阅者,触发相应监听回调。当把一个普通Javascript对象传给Vue实例来作为它的data选项时,Vue将
遍历它的属性,用Object.defineProperty()将它们转为getter/setter。用户看不到getter
/setter,但是在内部它们让Vue追踪依赖,在属性被访问和修改时通知变化。.请说一下Vue响应式数据的原理是什么?在V
ue初始化数据时,使用Object.defineProperty重新定义data中所有属性,增加了数据获取(gett
er)/设置(setter)的拦截功能。在获取/设置时可增加一些逻辑,这个逻辑交叫作依赖收集。当页面取到对应属性时
会进行依赖收集,如果属性发生变化,则会通知收集的依赖进行更新,而负责收集的就是watcher。如负责渲染的watcher
会在页面渲染的时候对数据进行取值,并把当前watcher先存起来对应到数据上,当更新数据的时候告诉对应的watcher去更
新,从而实现了数据响应式。data一般分为两大类:对象类型和数组:对象:在Vue初始化的时候,会调用initDat
a方法初始化data,它会拿到当前用户传入的数据。判断如果已经被观测过则不在观测,如果没有观测过则利用newObserve
r创建一个实例用来观测数据。如果数据是对象类型非数组的话会调用this.walk(value)方法把数据进行遍历,在内部使用
definReactive方法重新定义(definReactive是比较核心的方法:定义响应式),而重新定义采用的就是
Object.defineProperty。如当前对象的值还是个对象,会自动调用递归观测。当用户取值的时候会调用get方法
并收集当前的wacther。在set方法里,数据变化时会调用notify方法触发数据对应的依赖进行更新。数组:使用函数
劫持的方式重写了数组的方法,并进行了原型链重写。使data中的数组指向了自己定义的数组原型方法。这样的话,当调用数组API
时,可以通知依赖更新。如果数组中包含着引用类型,则会对数组中的引用类型进行再次监控。也就是当创建了Observer观测实例后,
如果数据是数组的话,判断是否支持自己原型链,如果不支持则调用protoAugment方法使目标指向arrayMethods
方法。arrayMethods就是重写的数组方法,包括push、pop、shift、unshift、splice、sort
和reverse共七个可以改变数组的方法,内部采用函数劫持的方式。在数组调用重写的方法之后,还是会调用原数组方法去更新数组。只
不过重写的方法会通知视图更新。如果使用push、unshift和splice等方法新增数据,会调用observeArra
y方法对插入的数据再次进行观测。如果数组中有引用类型,则继续调用observeArray方法循环遍历每一项,继续深度观测。前
提是每一项必须是对象类型,否则observe方法会直接return。.为何Vue采用异步渲染?如不采用异步更新,则每
次更新数据都会对当前组件进行重新渲染,因此为了性能考虑Vue在本轮数据更新结束后,再去异步更新视图。当数据变化之后,会调用
notify方法去通知watcher进行数据更新。而watcher会调用update方法进行更新(这里就是发布订
阅模式)。更新时并不是让wathcer立即执行,而是放在一个queueWatcher队列里进行过滤,相同的watche
r只存一个。最后在调用nextTick方法通过flushSchedulerQueue异步清空watcher队列。.n
extTick实现原理?nextTick方法主要是使用了宏任务和微任务定义了一个异步方法。多次调用nextTick
会将方法存入队列中,通过这个异步方法清空当前队列。所以nextTick方法就是异步方法。默认在内部调用nextTick时会
传入flushSchedulerQueue方法,存在一个数组里并让它执行。用户有时也会调用nextTick,调用时把用户传
过来的cb也放在数组里,都是同一个数组callbacks。多次调用nextTick只会执行一次,等到代码都执行完毕后
,会调用timerFunc这个异步方法依次进行判断所支持的类型:如支持Promise则把timerFunc包裹在了P
romise中并把flushCallbacks放在了then中,相当于异步执行了flushCallBacks。fl
ushCallBacks函数作用就是让传过来的方法依次执行。如不是IE、支持Mutationobserve并且是原生的
Mutationobserve。首先声明一个变量并创建一个文本节点。接着创建Mutationobserve实例并把flush
CallBacks传入,调用observe方法去观测每一个节点。如果节点变化会异步执行flushCallBacks方法。
如果支持setImmediate,则调用setImmediate传入flushCallBacks异步执行。以上都不支持
就只能调用setTimeout传入flushCallBacks。作用:$nextTick是在下次DOM更新循环结束之
后执行延迟回调,在修改数据之后使用$nextTick,则可以在回调中获取更新后的DOM。.请说一下Vue中Compute
d和watch?默认computed和watch内部都是用一个watcher实现的。computed有缓存功能
,不会先执行,只有当依赖的属性发生变化才会通知视图跟新。watcher没有缓存,默认会先执行,只要监听的属性发生变化就会更新视
图。computed调用initComputed方法初始化计算属性时,会获取到用户定义的方法,并创建一个watcher把用
户定义传进去,这个watcher有个标识:lazy=true,默认不会执行用户定义的函数。还有个标识dirty=
true默认去求值。watcher内部调用defineComputed方法将计算属性定义在实例上,其底层也是用的Obj
ect.defineProperty。并且传入了createComputedGetter方法定义一个计算属性。在用户取值时,调
用的是createComputedGetter返回函数computedGetter。判断当前的watcher.dirty
是否为true。如果为true则调用watcher.evaluate方法求值。在求值时是调用的this.get()方
法。其实this.get()就是用户传入的方法,执行时会把方法里的属性依次取值。而在取值前调用了pushTarget方法将
watcher放在了全局上,当取值时会进行依赖收集,把当前的计算属性的watcher收集起来。等数据变化则通知watch
er重新执行,也就是进入到了update方法中。update并没有直接让watcher执行,而是将dirty=t
rue。这样的好处就是,如果dirty=true,就进行求值,否则就返回上次计算后的值,从而实现了缓存的机制。watch调用
initWatch方法初始化watch的时候,内部传入用户定义的方法调用了createWatcher方法。在crea
teWatcher方法中比较核心的就是$watch方法,内部调用了newWatcher并传入了expOrFn和回
调函数。expOrFn如果是个字符串的话,会包装成一个函数并返回这个字符串。这时lazy=false了,则直接调用了
this.get()方法取属性的值。同computed在取值前也执行pushTarget方法将watcher放在了
全局上,当用户取值时就收集了watcher。因此当属性值发生改变时,watcher就会更新。如果监听的属性值是个对象,则
取对象里的值就不会更新了,因为默认只能对属性进行依赖收集,不能对属性值是对象的进行依赖收集。想要不管属性值是否是对象都能求值进行收
集依赖,可设置deep=true。如设置了deep=true,则会调用traverse方法进行递归遍历。.Vue
组件中data为什么必须是一个函数?因为js本身的特性带来的,同一个组件被复用多次,会创建多个实例。这些实例是同一个构造
函数。如果data是一个对象的话,那么所有组件都共享了同一个对象。为了保证组件中数据的独立性要求每个组件必须通过data函
数返回一个对象作为组件的状态。Vue通过extend创建子类之后,会调用mergeOptions方法合并父类和子类的选项
,选中就包括data。在循环完父类和子类之后调用mergeField函数的中的strat方法去合并data,如果da
ta不是函数而是个对象,则会报错提示data应该是个函数。.Vue中事件绑定原理Vue中事件绑定分为两种:原生事件绑定:
采用的是addEventListener实现组件事件绑定:采用的是$on方法实现以click事件为例,普通Dom
元素绑定事件是@click,编译出来是on和click事件,组件绑定事件是@click组件自定义事件和@cli
ck.native原生事件两种,编译出来分别是on和click事件,nativeOn和click事件。组件的na
tiveOn等价于普通元素的on,而组件的on单独处理。渲染页面时,普通Dom会调用updateDOMListen
ers方法,内部先把data.on方法拿出来,然后调用updateListeners方法来添加一个监听事件,同时会传入一
个add$1方法。内部调用addEventListener方法直接把事件绑定到元素上。而组件会调用updateCompo
nentListeners方法。内部也是调用updateListeners方法但传入的是add方法。这里的add方法
与普通元素的Dom的add$1方法略有不同,采用的是自己定义的发布订阅模式$on方法,解析的是on方法,组件内部通
过$emit方法触发的。还有click.native方法是直接把事件绑在了最外层元素上,用的也是updateListen
ers方法传入add$1方法。.v-model的实现原理是什么?通俗讲v-model可以看成是value+inpu
t的语法糖。组件的v-model也确实是这样。在组件初始化的时候,如果检测到有model属性,就会调用transf
ormModel方法转化model。如果没有prop属性和event属性,则默认会给组件prop为value
属性,给event为input事件。把prop的属性赋给了data.attrs并把值也给了它,即data.a
ttrs.value=''我们所赋的值''。会给on绑定input事件,对应的就是callback。如果在组件内自定义
model的prop和event,这样的话组件初始化的时候,接受属性和事件时不再是value和input
了,而是我们自定义的属性和事件。如果是普通的标签,则在运行时会自动判断标签的类型,生成不同的属性domProp和
事件on。还增加了指令directive,针对输入框的输入法加上了一些逻辑并做了校验和处理。.Vue中的v-show
和v-if是做什么用的,两者有什么区别?v-if:会在with方法里进行判断,如果条件为true则创建相应的虚拟节点
,否则就创建一个空的虚拟节点也就是不会渲染DOM。v-show:会在with方法里创建了一个指令就v-show,在运行的
时候处理指令,添加了style:display=none/originalDisplay。v-if才是“真正的”条件
渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当的被销毁和重建。v-if也是惰性的,如果在初次渲染时条件为假,
则什么也不做,一直到条件第一次变为真时,才会渲染条件块。相比之下,v-show就简单的多,不管初始条件是什么,元素总会被渲染
,并且只是简单的基于css进行切换。一般来说,v-if有更高的切换开销,v-show有更高的初始渲染开销。因此,如需要频
繁的切换则使用v-show较好,如在运行时条件不大可能改变则使用v-if较好。.v-if和v-for为什么不能连用
?v-for的优先级会比v-if要高,在调用with方法编译时会先进行循环,然后再去做v-if的条件判断,因此
性能不高。因此一般会把v-if提出来放在v-for外层,或者想要连用把渲染数据放在计算属性里进行过滤。.Vue中的v
-html会导致哪些问题v-html其原理就是用innerHtml实现的的,如果不能保证内容是完全可以被依赖的,则可能
会导致xxs攻击。在运行的时候,调用updateDOMProps方法或解析配置的属性,如果判断属性是innerHTM
L的话,会清除所有的子元素。.Vue中父子组件的调用顺序组件的调用都是先父后子,渲染完成的过程顺序都是先子后父组件的销毁操作
是先父后子,销毁完成的顺序是先子后父在页面渲染的时候,先执行父组件的beforeCreate->created->bef
roreMount,当父组件实例化完成的时候会调用rander方法,判断组件是不是有子组件,如果有子组件则继续渲染子组件以此类
推。当子组件实例化完成时候,会把子组件的插入方法先存起来放到instertedVNodeQueue队列里,最后会调用inv
okeIntertHook方法把当前的队列依次执行。更新也是一样,先父beforeUpdate->子beforeUpdate
再到子updated->父updated加载渲染过程父beforeCreate->父created->父befor
eMount->子beforeCreate->子created->子beforeMount->子mounted->父m
ounted子组件更新过程父beforeUpdate->子beforeUpdate->子updated->父updated父
组件更新过程父beforeUpdate->父updated销毁过程父beforeDestroy->子beforeDestro
y->子destroyed->父destroyed#Vue中父组件能监听到子组件的生命周期吗父组件通过@hook:能够监听
到子组件的生命周期,举个栗子://这里是父组件.Vue中组件怎么通讯?父子通讯:父→子props,子
→父$on/$emit通过eventsMixin方法中的$on方法维护一个事件的数组,然后将函数名传入$emi
t方法,循环遍历出函数并执行。获得父子组件实例的方式:$parent/$children在初始化的时候调用initLife
cycle方法初始化$parent和$children放在实例上在父组件中提供数据供子组件/孙子组件注入进来:Prov
ide/Inject。通过initProvide和initInjections方法分别把provide和reje
ct放在$options上。在调用reject的时候,调用resolveInject方法遍历,查看父级是否有此属性,
有则就直接return并把它定义在自己的实例上。Ref获得实例的方式调用组件的属性或方法ref被用来给元素或子组件注册引用
信息。引用信息将会注册在父组件的$refs对象上。用在DOM上就是DOM实例,用在组件上就是组件实例。Eventbu
s实现跨组件通讯实质上还是基于$on和$emit,因为每个实例都有$on和$emit并且事件的绑定和触发必须在同一
个实例,所以一般会专门定义一个实例去用于通信,如Vue.prototype.$bnts=newVue。Vuex状态管理实
现通讯$attrs和$Listeners实现数据和事件的传递,还有v-bind="$prop".为什么使用异步组件?可
使用异步的方式加载组件,减少打包体积,主要依赖import()语法,可实现文件的分割加载components:{testCp
t:(resove)=>import("../components/testCpt")或testCpt:r=>re
quire([''@/views/assetsInfo/assetsProofList''],r)}加载组件的时候,如果组件是个函数会
调用resolveAsyncComponent方法,并传入组件定义的函数asyncFactory,并让其马上执行。因为
是异步的所以执行后并不会马上返回结果。而返回的是一个promise,因此没有返回值,返回的是一个占位符。加载完成后,会执行f
actory函数并传入了成功/失败的回调。在回调resolve成功的回调时会调用forceRander方法,内部调用
$forceUpdate强制刷新。之后resolveAsyncComponent判断已经执行成功,就是去创建组件、初始化组件
和渲染组件。#Vue中的事件修饰符主要有哪些?分别是什么作用.stop:阻止事件冒泡.native:绑定原生事件.once:事
件只执行一次.self:将事件绑定在自身身上,相当于阻止事件冒泡.prevent:阻止默认事件.caption:用于事件捕获#
v-for里面数据层次太多,数据不刷新怎么办运用this.$forceUpdate()迫使Vue实例重新渲染。注意它仅仅
影响实例本身和插入插槽内容的子组件,而不是所有子组件。.说说对keep-alive的了解keep-alive是一个抽象组件,
可实现组件缓存。当组件切换时不会对当前组件进行卸载。算法:LRU→最近最久未使用法常用的生命周期:activated和
deactivated声明keep-alive时在函数里设置了几个属性:props,created,destroyed,mo
unted和rander等;props:调用keep-alive组件可设置的属性,共有三个属性如下:include:想
缓存的组件exclude:不想缓存的组件max:最多缓存多少个created:创建一个缓存列表destroyed:销毁时清
空所有缓存列表mounted:会监听include和exclude,动态添加或移除缓存rander:渲染时拿到第一
个组件,拿到第一个组件,判断是不是在缓存里.$route和$router的区别是什么?$router为VueRouter
实例,是个全局路由对象,包含路由跳转方法、钩子函数等。$route是路由信息对象||跳转的路由对象,每一个路由都会有一个
route对象,是一个局部对象,包含path,params,hash,query,fullPath,matched,name等路
由信息参数。.Vue路由的钩子函数首页可以控制导航跳转,beforeEach,afterEach等,一般用于页面title的修
改。一些需要登录才能调整页面的重定向功能。beforeEach主要有3个参数to,from,next:to:route即将进入的目
标路由对象,from:route当前导航正要离开的路由next:function一定要调用该方法resolve这个钩子。执行效果依
赖next方法的调用参数。可以控制网页的跳转。.vue-router有哪几种路由守卫?全局守卫(vue-router全局有
三个守卫)router.beforeEach全局前置守卫进入路由之前router.beforeResolve全局解析守卫(
2.5.0+)在beforeRouteEnter调用之后调用router.afterEach全局后置钩子进入路由之后//
main.js入口文件importrouterfrom''./router'';//引入路由router.before
Each((to,from,next)=>{next();});router.beforeResolve((to,
from,next)=>{next();});router.afterEach((to,from)=>{co
nsole.log(''afterEach全局后置钩子'');});路由独享守卫constrouter=newVueRo
uter({routes:[{path:''/foo'',component:Foo,beforeEnter:(to
,from,next)=>{//参数用法什么的都一样,调用顺序在全局前置守卫后面,所以不会被全局守卫覆盖}}]
})路由组件内的守卫beforeRouteEnter进入路由前,在路由独享守卫后调用不能获取组件实例this,组件实例
还没被创建beforeRouteUpdate(2.2)路由复用同一个组件时,在当前路由改变,但是该组件被复用时调用可以访问
组件实例thisbeforeRouteLeave离开当前路由时,导航离开该组件的对应路由时调用,可以访问组件实例this.
hash模式和history模式hash:在url中带有#,其原理是onhashchange事件。可以在win
dow对象上监听这个事件:window.onhashchange=function(event){...}history:
没有原#,其原理是popstate事件,需要后台配置支持。html5中新增两个操作历史栈的API:pushStat
e()和replaceState()方法。history.pushState(data[,title][,url]);//
向历史记录中追加一条记录history.replaceState(data[,title][,url]);//替换当前页在历
史记录中的信息。这两个方法也可以改变url,页面也不会重新刷新,在当前已有的back、forward、go的基础之上,它们提供
了对历史记录进行修改的功能。只是当它们执行修改时,虽然改变了当前的URL,但浏览器不会立即向后端发送请求。.Vuex是什么?
怎么使用它?哪种功能场景使用?Vuex是一个专为Vue.js应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件
的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex只能使用在vue上,因为其高度依赖于vue的双向绑定
和插件系统。调用了Vue.mixin,在所有组件的beforeCreate生命周期注入了设置this.$store这样
一个对象。场景有:单页应用中,组件之间的状态、音乐播放、登录状态、加入购物车state:Vuex使用单一状态树,存放的数据状态
,不可以直接修改里面的数据。mutations:定义方法动态修改Vuex的store中的状态或数据。getters:类
似vue的计算属性,主要用来过滤一些数据。actions:可以理解为通过将mutations里面处理数据的方法变成可异步
的方法,简单的说就是异步操作数据。view层通过store.dispath来分发action。modules:项目特别复
杂的时候,可以让每一个模块拥有自己的state、mutation、action、getters,使得结构非常清晰,方便管理。ac
tions和mutations的区别action主要处理的是异步的操作,mutation必须同步执行,而action既
可以处理同步,也可以处理异步的操作。action提交的是mutation,而不是直接变更状态。如果请求来的数据不是要被其他组件
公用,仅仅在请求的组件内使用,就不需要放入vuex的state里。如果被其他地方复用,请将请求放入action里方便复
用,并包装成promise返回。.assets和static的区别相同点:assets和static两个都是存放静
态资源文件。项目中所需要的资源文件图片,字体图标,样式文件等都可以放在这两个文件下。不相同点:assets中存放的静态资源文件在
项目打包时,会将assets中放置的静态资源文件进行打包上传,所谓打包简单点可以理解为压缩体积,代码格式化。而压缩后的静态资源
文件最终也都会放置在static文件中跟着index.html一同上传至服务器。static中放置的静态资源文件就不会要走打包
压缩格式化等流程,而是直接进入打包好的目录,直接上传至服务器。因为避免了压缩直接进行上传,在打包时会提高一定的效率,但是stat
ic中的资源文件由于没有进行压缩等操作,所以文件的体积也就相对于assets中打包后的文件提交较大点。在服务器中就会占据更大
的空间。建议:将项目中template需要的样式文件js文件等都可以放置在assets中,走打包这一流程。减少体积。而项目
中引入的第三方的资源文件如iconfoont.css等文件可以放置在static中,因为这些引入的第三方文件已经经过处理,我们
不再需要处理,直接上传。Vue中key的作用是什么?需要使用key给每一个节点做唯一标识,可让diff算法可以正确识
别此节点,以更高效的更新虚拟DOM。新旧children中的节点只有顺序是不同的时候,最佳的操作应该是通过移动元素的位置来达
到更新的目的需要在新旧children的节点中保存映射关系,以便能够在旧children的节点中找到可复用的节点。key也
就是children中节点的唯一标识.用vnode描述一个DOM结构虚拟节点就是用一个对象描述真实的dom元素会将templat
e先转换成ast树,ast通过代码生成codegen转成rander函数,rander函数内部调用$creat
eElement方法简称_c,传入tag(创建的元素),data(元素的属性),children(子元素).会判
断children是不是一个字符串,否则会做深度递归,最后返回的结果就是一个对象,可描述出DOM结构..简述Vue中
diff算法原理先同级比较,在比较子节点.判断出一方有子节点另一方没有子节点的情况.如果新的一方有子节点,老的没有,则把子节
点直接插入到老节点里即可.如果老的一方有子节点,新的没有,则把老的子节点直接删除.判断出都有子节点的情况,递归遍历子采用双指针(
头/尾指针)的方式比对节点.Vue.use与Vue.component的区别都用于注册全局组件/插件的Vue.compone
nt()每次只能注册一个组件,功能很单一。Vue.component(''draggable'',draggable)Vue.us
e()内部调用的仍是Vue.component()去注册全局组件/插件,但它可以做更多事情,比如多次调用Vue.compo
nent()一次性注册多个组件,还可以调用Vue.directive()、Vue.mixins()、Vue.prototype.
xxx=xxx等等,其第二个可选参数又可以传递一些数据Vue.use({install:function(Vue,opti
ons){//接收传递的参数:{name:''My-Vue'',age:28}console.log(option
s.name,options.age)Vue.directive(''my-directive'',{inserted(el,
binding,vnode){}})Vue.mixin({mounted(){}})Vue.component
(''draggable'',draggable)Vue.component(''Tree'',Tree)}},{name:
''My-Vue'',age:28})在main.js文件里动态注册全局组件时,或用到require.contextre
quire.context():一个Webpack的API,获取一个特定的上下文(创建自己的context),主要用来实现自
动化导入模块。它会遍历文件夹中的指定文件,然后自动化导入,而不需要每次都显式使用import/require语句导入模块!
在前端工程中,如果需要一个文件夹引入很多模块,则可以使用require.context()require.context(dir
ectory,useSubdirectories=false,regExp=/^\.\//)directory{St
ring}读取目录的路径useSubdirectories{Boolean}是否递归遍历子目录regExp{RegExp}
匹配文件的正则既然Vue通过数据劫持可以精准探测数据变化,为什么还需要虚拟DOM进行diff检测差异?现代前端框架有
两种方式侦测变化,一种是pull一种是pushpull:其代表为React,通常会用setStateAPI显式更新,然
后React会进行一层层的VirtualDomDiff操作找出差异,然后Patch到DOM上,React从一开
始就不知道到底是哪发生了变化,只是知道「有变化了」,然后再进行比较暴力的Diff操作查找「哪发生变化了」,另外一个代表就是A
ngular的脏检查操作。push:Vue的响应式系统则是push的代表,当Vue程序初始化的时候就会对数据dat
a进行依赖的收集,一但数据发生变化,响应式系统就会立刻得知,因此Vue是一开始就知道是「在哪发生变化了」,但是这又会产生一个
问题,如果你熟悉Vue的响应式系统就知道,通常一个绑定一个数据就需要一个Watcher,一但我们的绑定细粒度过高就会产生大
量的Watcher,这会带来内存以及依赖追踪的开销,而细粒度过低会无法精准侦测变化,因此Vue的设计是选择中等细粒度的方案,
在组件级别进行push侦测的方式,也就是那套响应式系统,通常我们会第一时间侦测到发生变化的组件,然后在组件内部进行Virtu
alDomDiff获取更加具体的差异,而VirtualDomDiff则是pull操作,Vue是push+pul
l结合的方式进行变化侦测的。Vue为什么没有类似于React中shouldComponentUpdate的生命周期?根
本原因是Vue与React的变化侦测方式有所不同React是pull的方式侦测变化,当React知道发生变化后,
会使用VirtualDomDiff进行差异检测,但是很多组件实际上是肯定不会发生变化的,这个时候需要用shouldCom
ponentUpdate进行手动操作来减少diff,从而提高程序整体的性能。Vue是pull+push的方式侦测变化的,在
一开始就知道那个组件发生了变化,因此在push的阶段并不需要手动控制diff,而组件内部采用的diff方式实际上是可以引
入类似于shouldComponentUpdate相关生命周期的,但是通常合理大小的组件不会有过量的diff,手动优化的价值
有限,因此目前Vue并没有考虑引入shouldComponentUpdate这种手动优化的生命周期。.Vue中常见的性能优化
编码优化(1).不要将所有的数据放在data里,data中的数据都会增加getter和setter,收收集对应的watche
r(2).在v-for时给每项元素绑定事件必须使用时间代理(3).SPA页面采用keep-alive缓存组件(4).
拆分组件(提高复用性,增加代码的可维护性,减少不必要的渲染)(5).v-if当值为false时内部指令不执行具有阻断功能,
很多情况下使用v-if代替v-show(6).使用key保证唯一性(7).使用Object.freeze冻结数据,
冻结后不再有getter和setter(8).合理使用路由懒加载和异步组件(9).数据持久化问题如:防抖、节流Vue
加载性能优化(1).第三方模块按需导入(babel-plugin-component)(2).滚动可视区域动态加载(vue-v
irtual-scroll-list/''vue-virtual-scroller'')--长列表优化(3).图片懒加载(v
ue-lazyload)用户体验(1).app-skeleton骨架屏(2).app-sheapp壳SEO优化(1).
预加载插件prerender-spa-plugin(2).服务端渲染ssr打包优化(1).使用CDN的方式加载第三方模
块(2).多线程打包(3).splitChunk抽离公共文件缓存压缩(1).客户端缓存和服务端缓存(2).服务端gzi
p压缩.什么是作用域插槽?插槽:创建组件虚拟节点时,会将组件儿子的虚拟节点先保存起来。初始化组件时,通过插槽属性将儿子进行分类。
(作用域为父组件)渲染组件时会拿对应的slot属性的节点进行替换操作。作用域插槽:在解析的时候不会作为组件的孩子节点。会解析
成函数,当子组件渲染时,会调用此函数进行渲染。(作用域为子组件)普通插槽编译时调用createElement方法创建组件,并把
子节点生成虚拟dom做好标识存起来。渲染时调用randerSlot方法循环匹配出对应的虚拟节点在父组件替换当前位置。而作用
域插槽在编译时会把子组件编译成函数,函数不调用就不会渲染。也就是说在初始化组件的时候并不会渲染子节点。渲染页面时调用rander
Slot方法执行子节点的函数并把对应的属性传过来。当节点渲染完成之后在组件内部替换当前位置。.Vue与Angular以及Reac
t的区别?1.与AngularJS的区别相同点:都支持指令:内置指令和自定义指令。都支持过滤器:内置过滤器和自定义过滤器。都支持双
向数据绑定。都不支持低端浏览器。不同点:AngularJS采用TypeScript开发,而Vue可以使用javasc
ript也可以使用TypeScript。在性能上,AngularJS依赖对数据做脏检查,所以Watcher越多越慢。Vue.j
s使用基于依赖追踪的观察并且使用异步队列更新,所有的数据都是独立触发的。对于庞大的应用来说,这个优化差异还是比较明显的。Angu
larJS社区完善,Vue的学习成本较小2.与React的区别相同点:React采用特殊的JSX语法,Vue.js在组件开发中也
推崇编写.vue特殊文件格式,对文件内容都有一些约定,两者都需要编译后使用。中心思想相同:一切都是组件,组件实例之间可以嵌套。都提
供合理的钩子函数,可以让开发者定制化地去处理需求。都不内置AJAX,Route等功能核心包,而是以插件的方式加载。在组件开发中都支
持mixins的特性。不同点:vue组件分为全局注册和局部注册,在react中都是通过import相应组件,然后模版中引
用;props是可以动态变化的,子组件也实时更新,在react中官方建议props要像纯函数那样,输入输出一致对应,而且不太
建议通过props来更改视图vue多了指令系统,让模版可以实现更丰富的功能,而React只能使用JSX语法react是
整体的思路的就是函数式,所以推崇纯组件,数据不可变,单向数据流,当然需要双向的地方也可以做到,比如结合redux-form,组件
的横向拆分一般是通过高阶组件。而vue是数据可变的,双向绑定,声明式的写法,vue组件的横向拆分很多情况下用mixin。Vu
e增加的语法糖computed和watch,而在React中需要自己写一套逻辑来实现。高精度全局权限处理权限控制由前端处理时,通常
使用v-if/v-show控制元素对不同权限的响应效果。这种情况下,就会导致很多不必要的重复代码,不容易维护,因此可以造一
个小车轮,挂在全局上对权限进行处理。//注册全局自定义指令,对底层原生DOM操作Vue.directive(''permiss
ion'',{//inserted→元素插入的时候inserted(el,binding){//获取到v-per
mission的值const{value}=bindingif(value){//根据配置的权限,去当前用户
的角色权限中校验consthasPermission=checkPermission(value)if(!hasPerm
ission){//没有权限,则移除DOM元素el.parentNode&&el.parentNode.removeCh
ild(el)}}else{thrownewError(`needkey!Likev-permission="[
''admin'',''editor'']"`)}}})//-->在组件中使用v-permissionpermission="[''admin'']">权限1'',''editor'']">权限2.对于vue3.0特性你有什么了解的吗?(1).监测机制的改变3.0基
于代理Proxy的observer实现,提供全语言覆盖的反应性跟踪。替代了Vue2采用defineProperty去定
义get和set,意味着彻底放弃了兼容IE,这也取消除了Vue2当中基于Object.defineProperty
的实现所存在的很多限制:=>只能监测属性,不能监测对象:=>检测属性的添加和删除;=>检测数组索引和长度的变更;=>支持Map、Set、WeakMap和WeakSet。新的observer还提供了以下特性:用于创建observable的公开API。这为中小规模场景提供了简单轻量级的跨组件状态管理解决方案。默认采用惰性观察。在2.x中,不管反应式数据有多大,都会在启动时被观察到。如果数据集很大,这可能会在应用启动时带来明显的开销。在3.x中,只观察用于渲染应用程序最初可见部分的数据。更精确的变更通知。在2.x中,通过Vue.set强制添加新属性将导致依赖于该对象的watcher收到变更通知。在3.x中,只有依赖于特定属性的watcher才会收到通知。不可变的observable:我们可以创建值的“不可变”版本(即使是嵌套属性),除非系统在内部暂时将其“解禁”。这个机制可用于冻结prop传递或Vuex状态树以外的变化。更好的调试功能:我们可以使用新的renderTracked和renderTriggered钩子精确地跟踪组件在什么时候以及为什么重新渲染。(2).模板模板方面没有大的变更,只改了作用域插槽,2.x的机制导致作用域插槽变了,父组件会重新渲染,而3.0把作用域插槽改成了函数的方式,这样只会影响子组件的重新渲染,提升了渲染的性能。同时,对于render函数的方面,vue3.0也进行一系列更改来方便习惯直接使用api来生成vdom。(3).对象式的组件声明方式vue2.x中的组件是通过声明的方式传入一系列option,和TypeScript的结合需要通过一些装饰器的方式来做,虽然能实现功能,但是比较麻烦。vue3.0修改了组件的声明方式,改成了类式的写法,这样使得和TypeScript的结合变得很容易。此外,vue的源码也改用了TypeScript来写。其实当代码的功能复杂之后,必须有一个静态类型系统来做一些辅助管理。现在vue3.0也全面改用TypeScript来重写了,更是使得对外暴露的api更容易结合TypeScript。静态类型系统对于复杂代码的维护确实很有必要。(4).其它方面的更改支持自定义渲染器,从而使得weex可以通过自定义渲染器的方式来扩展,而不是直接fork源码来改的方式。支持Fragment(多个根节点)和Protal(在dom其他部分渲染组建内容)组件,针对一些特殊的场景做了处理。基于treeshaking优化,提供了更多的内置功能。.Vue等单页面应用(spa)及其优缺点优点:Vue的目标是通过尽可能简单的API实现响应的数据绑定和组合的视图组件,核心是一个响应的数据绑定系统。MVVM、数据驱动、组件化、轻量、简洁、高效、快速、模块友好;即第一次就将所有的东西都加载完成,因此,不会导致页面卡顿。缺点:不支持低版本的浏览器,最低只支持到IE9;不利于SEO的优化(如果要支持SEO,建议通过服务端来进行渲染组件);第一次加载首页耗时相对长一些;不可以使用浏览器的导航按钮需要自行实现前进、后退。
献花(0)
+1
(本文系新用户1879F...原创)