前端-Vue篇

vue没有过多的介绍原理,不然篇幅巨大,由于时间较紧,所以只有简略提及,后面有时间我们来翻一波源码.

1、MVVM原理?

MVC、MVP、MVVM都是软件架构设计模式,而MVC、MVP、MVVM在MV(model-view)上都功能是相等的,也就是说,都做到了数据驱动视图.

1.1、MVC?(react)

为了实现用户输入事件驱动视图的更新,产生了controller,用于响应用户事件,从而驱动数据产生实现用户需求的变化.其中model层和view层使用了观察者模式,view在model中注册回调函数,当数据变化时,通知view变化,而controller则是控制view->model的中间人,用于响应在view层的事件.

缺点: view和model高度的耦合关系,使得view组件重用性价差,一般都是view和model绑定.

1.2 MVP?

为了解决在MVC中的高度耦合问题,实现多个view共用一个controller,出现了MVP,MVP解耦了view和model,将主要的业务逻辑放在了Presenter层,但是Presenter还需要负责view和model层数据同步的问题,维护起来比较困难.

缺点: Presenter层需要负责view和model的数据同步问题.

1.3 MVVM?(「vue」)

在MVVM中的model层不需要关系任何对于数据的处理,只是担任数据的传入功能,使用模版代码渲染view层,并且在VM层通过数据劫持的方式对model层和view层的数据实现了双向绑定,省去了在Presenter中的数据关联问题,在MVVM中,view和model感知不到地方的存在,这种低耦合的模式,提高了代码的重用性.

2、Vue2.X和Vue3.X的响应式原理?

vue2.X?

vue2.X主要是用了四个模块来实现数据的双向绑定.

  • 1、Observer
  • 2、Dep
  • 3、Watcher
  • 4、Scheduler

在vue2中主要是用到了Object.defineProperty()+观察者模式来实现数据的劫持(利用函数的方式来读写数据,可以实现更多的操作).

  • 1、首先对vue的data中的数据遍历,并且对return的数据进行「响应化处理」.(设置getter和setter)
  • 2、并且data中的每个数据都会生成一个new Dep()实例(实现依赖收集和分发的中转).
  • 3、依赖问题: 由于对data中的数据设置了劫持,所以在访问该数据时会触发getter函数,这时getter函数就会调用dep.depend()将对应的watcher放入用于在改变数据时,分发依赖的dep.subs数组中.
  • 4、watcher可以理解为拥有目标数据的回调函数的代理对象,watcher分为 1、用于render的rednerWatcher 2、在computed中的回调函数computed watcher 3、在watcher中的user watcher.
  • 5、当setter被触发时,就会遍历dep的subs数组,对其中watcher进行update调用.
  • 6、为了避免不必要的回调函数执行,例如一个函数中重复的对多个值进行修改,vue引入了sceduler,它会维护一个微任务队列,把watcher监听放入其中,类似set的作用,用于去重.

image.png

image.png

vue3.X?

vue3.x对vue2.x的改进就是解决了vue2.x无法对引用对象内容(不是本身)进行监听的问题,并且不支持Map、Set、WeakMap、WeakSet.

  • 1、在vue3中使用到了es6的proxy代理,通过proxy中的handle对数据读写进行拦截.
  • 2、vue3中将回调函数写在effect中,而effect会执行其中的回调函数,用于收集依赖(使用到proxy代理的数据).
  • 3、将effect放入effectStack栈顶,同时调用getter的track方法,建立targetMap.收集完依赖后精啊effect弹出.
  • 4、在targetMap中的key值代表被reactive化的数据,value则又是一个depsmap数据表,该表用于存放reactive对象中被effect依赖的属性和对应依赖的effect集合(用于去重).
  • 5、当调用setter函数时,就可以通过depsMap表来查找对应的effect回调,从而执行了.

image.png
图片来源:juejin.cn/post/684490…

3、vue中computed实现原理以及和watcher的区别?

computed是一种惰性的观察者,初始化时,会调用initComputed,对computed内的每一个属性进行数据劫持,并且标准化它们的get和set方法,同时它们都拥有dep(消息订阅器),用于分发和收集依赖(和data的数据类似),但是他们的最大的区别就是惰性,通过dirty属性来缓存数据,当该computed属性A使用了某个data-B时,data-B发生变化,使该computed属性A触发update,这时,如果该watcher对象是个computed并且依赖该computed的subs为空,就会触发惰性,仅仅将dirty设置为true(这个在后面起作用),这个true的目的是告诉get中的return函数evalute(),也许你会疑惑为啥要触发这个函数,而不是直接返回值,这时,我们提出的惰性就出来了,当dirty为true时,表示这个属性还没有被初始化过呢,就需要去调用真正的get方法计算并将dirty设置为false,反之,表示这个值被计算过了,那直接拿缓存不就好了吗,爷不想算了.

3.1、computed和watcher的区别?

  • computed表示一个计算的值,也会被挂载到vm实例上,而watcher则有监听挂载在vm上值的作用(也可以监听computed)
  • 两者都被做了响应式处理,computed有缓存机制,只有当第一次调用和依赖发生变化时会计算,而watcher则是当监听数据变化就执行.
  • computed更适合获取一个值,这个值被多个值所影响,watcher的适合一个值变化会带动一系列的变话.
  • watcher还适合做一些异步操作(访问API)和性能消耗大的操作.

4、Vue中key的作用?

key是给每一个vNode的唯一id,可以更快更高效的进行diff操作.
key值是为了更高效的更新虚拟dom,原理是在patch时,通过key值来精准表示两个节点是否相等,从而避免了频繁更新不同的元素的造成的性能消耗,减少dom操作(key默认为undefined,如果不设置那么按照顺序比较时,那么在进行插入操作时,diff算法认为被插入节点和其之后的节点都进行了变化从而增加了不必要的dom操作).

5、简述nextTick的原理?

Vue官方的描述: 在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。

以异步微任务的方式,在dom变化完成后需要对dom进行操作,就可以使用nextTick函数,补充一下,在对dom元素做赋值等操作的时候,会开启一个微任务队列,将数据依赖的watcher都放入该队列中(高效去重,避免不必要的计算),然后在宏任务执行完成后,进行清空队列,并且在dom更新完毕后触发nextTick.

6、Vue2是如何对数组的函数操作进行监听的?

vue2无法监听数组内容的变化,但是它却能在push、pop、unshift、shift等操作数组内容的变化之后触发监听,实际是Vue对这些函数进行了重写.这些问题在vue3中被解决.

7、Vue2中的data为什么是函数?

Vue中的组件有可能被重用,如果以对象的方式被子组件获取到,那么多个子组件公用一个父子间的data,这时如果不同的子组件需要对data进行操作时,就会造成当前使用的子组件污染其他组件的现象,所以使用函数的形式,让对象在独立作用域中.避免污染现象.

8、简述Vue的事件机制? on,on,off,emit,emit,once的原理?

  • 原理: 发布-订阅者模式
  • 使用on注册事件,on注册事件,emit分发事件,off删除注册的事件,off删除注册的事件,once分发一次后删除.

9、v-if和v-show的区别?

  • v-if是真正的条件渲染,会根据表示中内容的变化情况,对元素进行显示或者销毁的操作.
  • 同时v-if是惰性的,当表达示为false时,仅仅有comment占位符,不会进行元素的创建操作.
  • v-show则必然会创建元素,并且通过表达式的值来对display进行切换,从而切换布局时的显示情况.

所以v-if有着更高的切换开销,v-show有着更高的初始渲染开销.如果需要频繁切换使用v-show.

10、Vue中的路由传参?

Vue通过this.route.params(/path/:id),thisroute.params(/path/:id),thisroute.query(/path?id=1)的方式进行路由时的数据传递.

11、简述导航守卫?

  • vueRouter全局: beforeEach、beforeResolve、afterEach.
  • route: beforeEnter
  • component-Guards: beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave
  1. 导航被触发。
  2. 在失活的组件里调用 beforeRouteLeave 守卫。
  3. 调用全局的 beforeEach 守卫。
  4. 在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。
  5. 在路由配置里调用 beforeEnter
  6. 解析异步路由组件。
  7. 在被激活的组件里调用 beforeRouteEnter
  8. 调用全局的 beforeResolve 守卫 (2.5+)。
  9. 导航被确认。
  10. 调用全局的 afterEach 钩子。
  11. 触发 DOM 更新。
  12. 调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。

12、简述vDom?

具体过程非常繁琐,这里只是总结一下,可以通过对源码的阅读来加深理解.

vDom实际是一个VNode的实例对象,通过将template字符串化、paresHTML、生成AST抽象词法树、genarator转换,在vnode中定义了元素相关的属性、标签名、childrenNode、text、{staticClass、class}、directvie指令等,当发现有子节点时会递归的为子节点创建node挂载在父节点之上、当遇到组件节点时,通过createCompnent来创建组件节点,最终形成一个完整dom结构对象,接着进行patch和diff算法,diff算法会通过比较dom新旧节点的key值依旧节点的位置关系,对节点进行头头、尾尾、头尾、尾头的比较,从而进行patchVNode.最后替换domTree.

13、template编译过程?如果存在render函数,会怎样渲染?

在vm.$mounted()之后会检查是否存在render函数,如果存在则不进行parse Template.直接使用render.

14、template render过程

  • parseHTML生成抽象词法树.
  • 调用genarate对ast进行节点判断和指令转换等操作
  • 将转换后的code函数赋给render,返回.

15、keep-Alive的作用?

keep-Alive可以用来在切换组件时,保存(或者是缓存)被切换组件的渲染状态,当该组件被切换回来时,会保留当时的状态,可以有效减少组件重渲染的次数,提高性能.

16、组件中name的作用?

1、配合keep-Alive的include和exclude使用,表明可以被缓存的组件和不可以被缓存的组件.(keep-alive套router-view时).
2、可用于在自身组件中使用name的属性值作为标签名,递归的调用自身组件.

17、解释vue单向数据流的概念?

vue单向数据流表示在后代组件传值时,使用props从父组件流动到子组件,反过来则不行,这是为了防止子组件中意外的修改父组件的数据,从而引起数据混乱(一个父组件给多个子组件值时,子组件修改父组件的值意味着其他组件上的值也会被修改),vue会throw warn.当然,如果子组件需要修改父组件那么需要向父组件发送请求和监听.

18、assets和static的区别?

assets和static都是用于存放静态文件的,在assets中存放的文件会进行打包时,会经过体积压缩,减少资源占用,而static中的资源则不会进行压缩,所以css和js等文件可以方在assets中,可以有效减小文件体积,而图片和一些被压缩过的图标文件则可以放在static中.

19、为什么避免v-for和v-if一起使用?

v-for如果在前面,由于它的优先级高,那么它的directive会首先执行,将dom渲染出来,之后使用v-if则会去遍历元素,将不符合规则的摘掉,这就多了一个生成不必要元素的操作.

20、生命周期函数?

vue2.X?

初始化:

  • beforeCreate() 实例被创建之前,data数据和挂载元素都还没有.
  • created() 实例被创建之后调用,data已经初始化完成,但是dom操作还没启动.
  • beforeMounted() 实例被挂载之前调用,数据初始化完成,VDOM创建完成,即将实例开始渲染.
  • mounted() 实例被挂载完成,数据初始化完成,dom挂载完成,数据完成双向绑定.

更新:

  • beforeUpdate() 更新之前触发,还未更新,可以在该函数中继续更新,不会触发重复渲染.
  • updated() 更新完成时触发,避免使用更新操作,会造成死循环.

销毁操作

  • beforeDestroy() 在销毁之前触发,可以消除页面中的延时任务等操作.
  • destroy() 组件被销毁,指令解绑,事件监听移除,子实例销毁.

vue3.X?

beforeCreate()和create()都被放在了setup()中.
beforeMounted()-> onBeforeMounted()
mounted() -> Mounted()
beforeUpdate() -> onBeforeUpdate()
update() -> Update()
beforeDestroy -> onBeforeUnMounted()
destroy -> onUnMounted()

21、父子组件的执行顺序?

加载时:

  • 父: beforeCreated() // 加载父组件
  • 父: created()
  • 父: beforeMounted()
  • 子: beforeCreated() // 开始render时,发现子组件,开始创建子组件
  • 子: created()
  • 子: beforeMounted()
  • 子: mounted()
  • 父: mounted() // 父子间在整个vdom创建完毕后才触发mounted

更新时:

  • 父: beforeUpdate() // 由父组件开始更新到子组件更新完毕后,父组件再更新完毕
  • 子: beforeUpdate()
  • 子: updated()
  • 父: updated()

销毁时:

  • 父: beforeDestroy()
  • 子: beforeDestroy()
  • 子: destroy()
  • 父: destroy()

22、组件间的传值问题?

父向子传值?

  • 1、属性的方式,通过v-bind向子组件传值,在子组件中通过props来接受父组件传过来的值.
  • 2、通过在子组件上标记ref,通过父组件的this.$ref来获取子组件的数据,从而修改.
  • 3、通过this.$children来获取子组件数组,从而获取子组件并修改它上面的数据.

子向父传值?

  • 1、父组件在子组件上绑定函数,this.$emit(父组件函数,传入的值)触发父组件函数.
  • 2、通过this.$parent获取父组件.
  • 3、修饰符.sync,通过子组件this.$emit(‘update:par’,val),父组件给子组件: :par.sync=”fn”来触发.

后代组件传值?

  • 1、可以通过attrsattrs和listeners来获取父组件传来的值,再通过v-bind和v-on将父组件传给孙子组件.
  • 2、通过inject和provide来控制父组件和嵌套组件的传值问题,provide中return的值会向其所有嵌套子组件中传递,子组件可以通过provide来捕获数据,(并且子组件不可以直接修改数据,vue会报错,但是可以直接获取到父组件实例来修改数据.)

使用vueX数据管理工具和localStorage等缓存技术.

兄弟组件传值?

  • 1、通过新建一个bus,并且使用bus,并且使用bus.on(函数名,fn)监听,on(函数名,fn)监听,bus.$emit(函数名,data).

23、v-model实现方式?

v-model实现了input时的双向数据绑定,其原理就是利用了input时emitemit和on的语法糖,vue帮我们实现了相同功能但是语法更加简洁的写法

image.png

24、Vue单页面应用和多页面应用的区别?

  • 1、SPA 单页面应用
  • 2、MPA 多页面应用
  • 单页面应用中采用局部刷新的方式,多页面则采取整页刷新的方式,并且单页面采取组件化的开发策略,对于页面切换时的效率以及转场和组件之间的传值都优于多页面应用,并且对页面的可维护性也比多页面强.

25、Vue如何监听多个方法?

vue可以采用v-on={input: onInput, blur: onBlur}对象的方式来监听多个方法.

26、Vue-router的hash和history的实现原理?

vue采用了hash和history两种策略来实现单页面应用.

1、hash?

hash模式指的是使用url中的#后面部分的改变,并且进行window.onhashChange监听,来进行发送请求等操作,hash改变的操作会存放在window.history中,并且hash的改变并不会进行http请求,所有页面的跳转都是在客户端进行.

2、history?

history模式是利用了H5新特性的history.pushState和histrory.replaceSate的API来改变当前url地址(改变同源url)并且实现无刷新跳转,并且通过window.onpopstate来对页面的回退等操作进行监听与变化操作.

  • 问题: history对url敏感,当url变化后进行刷新时,会发送http请求,如果后端nginx没有对当前url做处理,则会返回404,降低用户体验.而由于hash的不敏感性,并不会发送请求,但是这并不有利于做SEO的优化.并且锚点功能不可用了.

根据受众群体来选择模式,当受众群体为普通用户时,history在SEO上优于hash.但是需要考虑服务端的url变化问题.

27、Vuex?

Vuex一种配合vue使用的单向数据流类型的状态管理工具.
主要通过view(监听事件)->actions(触发事件,改变vueX中的state)->state(state被view使用)->view.

  • state: 存储数据.
  • getters: 对state进行过滤,类似computed.
  • mutations: 对state进行操作,vueX中的函数函数.
  • actions: 用于发起异步请求.在vueX外部通过store.dispatch来分发,在vueX内可以调用commit通知mutation来进行后续的线性操作.

28、vue.use的内部原理?

vue.use在被初始化时,传入的参数为函数形式的plugin,并且可以添加额外的options参数,该plugin会被组册到vue的_installPlugins数组中,并且具有去重性,不会重复注册,之后,vue会判断plugin是否有install属性或者是否本身就是个函数,从而调用传入的install,并且被调用的函数中会传入Vue和options.

29、简述vue-loader的作用?

webpack中的loader可以用来打包或者编译js和css等文件.
vue-loader会将*.vue的HTML template、script、css文件提取出来交给其他loader.

30、axios的技术特点?

  • 1、axio是一种同于发起http请求的技术.
  • 2、可用于node端和浏览器端.
  • 3、支持promise.
  • 4、可以对请求和响应进行拦截.
  • 5、自动转换为json格式.

31、异步组件的使用方式?

组件可以同步加载和异步加载,并且同步加载还包括局部加载和全局加载.

  • 全局加载: Vue.component(tagName,options);

  • 局部加载: components: { tagName: options };

  • 函数异步加载:

    Vue.component(tagName,(resolve) => {
       require([path],(data) => {resolve(data)})
    })
    复制代码
  • promise异步加载:

    vue.component(tagName,() => import(path))
    复制代码
  • 高级异步组件:

    vue.component(tagName,() => (
       component,
       loading: component1,
       error: component2,
       delay, // 延迟多久渲染
       timeout // 最迟等待时间,超出渲染错误组件
    ))
    复制代码

32、$set的实现原理?

$set用于处理数组和object的属性不会触发响应式的问题,当操作为数组时,会调用被重写了的splice方法实现了对数组的操作,如果为对象时,并且对象身上没有该属性时,会调用defineReactive手动对属性实现响应化,并且调用notify实现依赖分发.

33、vue2.X性能优化的方式?

  • 1、区别使用v-if和v-show
  • 2、避免v-if和v-for的组合使用
  • 3、合理使用computed和watch.
  • 4、对图片可以实现懒加载.
  • 5、可以使用nuxt框架实现SSR服务端渲染.(优化SEO)
  • 6、对于延时任务的及时移除.
  • 7、为v-for设定合理的key值,在做patch和diff算法的时候提高交换效率.
  • 8、合理使用assets和static,对js和css文件压缩
  • 9、使用MixIn实现代码重用.
  • 10、使用webpack对图片进行压缩.
  • 11、做好cache-control缓存设置.
  • 12、可以使用CDN(内容分发网络),提高引入文件速度.

34、vue中routeroute和router的区别?

  • $route中存储了局部路由中信息,path、params、query等.
  • $router存储了全局路由的浏览记录和全局Vue、currentRoute等信息.

image.png

35、active-class的作用?

设置router-link组件的被选中时的类.

36、props中的值?

props可以是object或者array类型,如果是array则其中的值只能是string类型,如果是object类型,则可以指定更多的属性,type(指定数据类型)、validator(判断其中是否符合要求return false则报错)、required(必填)、default等.

37、vue获取dom元素的方式?

  • 1、通过document.getElement的方式获取元素.
  • 2、通过ref的方式获取(ref如果在普通的元素上直接获取该dom元素,如果在子组件上,则获取vueComponent实例)

38、keep-Alive的生命周期函数?

activated和deactivated是针对keep-alive的生命周期函数,在组件显示和隐藏是触发.

39、vue中的修饰符?

  • 1、事件修饰符?

    self只处理自身触发的事件
    once只触发一次的事件
    prevent阻止默认行为
    stop阻止冒泡
    capture 捕获时出发
    复制代码
  • 2、表单修饰符?

    lazy 配合v-model使用,可以只在change时触发,区别blur(change只有当值变化时blur则触发,而简单的blur则是无论值是否变化都触发)
    number 配合v-model,只能输入数字类型
    trim 配合v-model,对输入进行两边去空值
    复制代码
  • 3、key修饰符?

key.数字对应的ascii码从而触发事件.

  • 4、鼠标修饰符?

@click.left、@click.middle、@click.right

40、Vue中el的作用?

el可以为css选择器、也可以是dom元素,表示Vue实例的挂载目标.

41、Vue首页白屏问题和优化方式?

  • 1、网络问题?
  • 2、js文件较大,对js进行压缩.
  • 3、使用cdn,加速js文件加载.
  • 4、图片懒加载.
  • 5、路由懒加载,使用() => import(componentPath),webpack在进行打包时,会把他们作为一个个js文件,只有运行他们时才进行加载.提高加载性能.

42、Vue3的重要特性?

  • 1、支持typescript

  • 2、使用TreeShaking,按需加载,减少不必要的文件的加载.

  • 3、使用compositionAPI.

  • 4、由vue2中按类型分类转换为按功能分类,对于大型项目的代码功能关联性和可读性更好.

  • 5、支持hook函数,替代了mixin,复用性更强,去除了mixin会造成引入后,变量名冲突,函数意义不明等问题.

  • 6、响应式变为proxy方式,对于数组和对象等处理更优.

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享