- vue2与vue3的区别:
1、生命周期:vue3在大部分生命周期名称上加上on,功能是类似的,不过vue3在组合式API中使用生命周期需要事先引入。
2、vue3允许多个根节点,vue2如果在模板中使用多个根节点会报错
3、vue2是optionsAPI,vue3是ComponentAPI,可以将同一逻辑的内容写到一起,增强代码的可读性内聚性
4、异步组件:vue3提供Suspense组件,允许程序在等待异步组件加载完成前渲染兜底的内容,比如loding使用户体验更平滑。
5、vue3提供Teleport组件可将部分DOM移动到Vue app之外的位置,比如弹窗组件
6、数据响应式原理不同,vue2是数据劫持加上发布订阅模式,vue3是使用proxyAPI
- 说说路由?
路由route就是URL到函数的一个映射,router就是管理多个路由的一个容器,当接收到一个URL需要去路由映射表中查找相应的函数。
服务器端的路由就是当接收到客户端发来的HTTP请求时会根据请求的URL找到相对应的映射函数并执行,然后将返回值发送给客户端。
客户端路由通常就是进行一些DOM的显示与隐藏,当访问不同的路径时,会显示不同的页面组件,客户端实现路由有两种基本方式:
基于Hash:就是URL中#及其后面的部分为hash,hash仅仅是客户端的一个状态,当发送请求时hash部分不会发送出去
基于History API:就是在不刷新页面的情况下,直接改变当前URL,需要后端人员的支持
在Vue中路由的使用,第一步需要先安装vue-router,第二步就是使用该插件,然后在router配置项里编写路由的相关信息,最后指定路由展示位置实现切换。
- vue2响应式实现
vue2采用数据劫持结合发布订阅模式,通过Object.defineproperty来劫持各个属性的setter和getter,在数据变动时发布消息给订阅者,触发响应的监听回调。
当创建Vue实例时,vue会遍历data的所有选项的属性,利用Object,defineproperty为属性添加getter和setter对数据的读取进行劫持,并且在内部追踪依赖,在属性被访问和修改时通知变化。
1 | //订阅器模型 |
- 说说对Vue的理解:
Vue是一个构建数据驱动的渐进性框架,它的目标是通过API实现响应数据绑定和视图的更新
- 说说Vue的优缺点:
优点:
1、数据驱动视图,对真实DOM进行抽象出虚拟DOM(本质就是一个js对象),并配合diff算法、响应式和观察者、异步队列等手段以最小的代价更新DOM,渲染页面
2、组件化,组件用单文件的形式进行代码的组织编写,使得我们可以在一个文件中编写html/css/js并且配合Vue-loader之后支持更强大的预处理器功能
3、强大且丰富的API提供一系列的api能满足业务开发中各类需求
4、生命周期构子函数,选项式的代码组织方式,
5、生态好,社区活跃
缺点:
1、由于底层是使用Object.defineProperty实现响应式,而这个api本身不支持IE8及以下浏览器
2、由于百度等引擎爬虫无法爬取js中的内容,故单页面应用先天就对seo优化不友好
- 什么是虚拟DOM,为什么需要虚拟DOM:
虚拟DOM是相对于浏览器所渲染出来的真实DOM的,在React、vue等技术出现之前,我们要改变页面展示的内容只能通过遍历查询dom树的方式来找到需要修改的dom然后修改样式行为或结构,来达到更新ui的目的。
这种方式相当 消耗计算资源,因为每次查询几乎需要遍历整棵DOM树,如果建立一个与DOM树对应的虚拟DOM对象,以对象嵌套的方式表示DOM树,那么每次DOM的更改就变成了js对象属性的更改,这样一来查找js对象的属性变化要比查询DOM树性能开销小。
理由:
- 简要概述Vue的生命周期:
生命周期就是Vue从创建到销毁的过程,分为四大步,分别是创建、挂载、更新和销毁。每一步又分为两小步。
beforeCreate:会挂载data,绑定事件;
created:在实例创建完成后被立即调用,挂载阶段还没开始。
beforeMount:render函数被调用,根据template或outerHTML渲染页面;
mounted:将vm.$el替换掉页面元素el,mounted将虚拟DOM挂载到真实页面,此时页面已经全部渲染完成;
beforeUpdate:数据更新时调用,这里适合在更新之前访问现有的DOM比如手动移除已经添加的事件监听器,该构子在服务器端渲染期间不被调用,因为只有初次渲染会在服务端进行。
updated:由于数据更改导致的虚拟DOM重新渲染与打补丁,在这之后调用该钩子。
beforeDestory:实例销毁之前调用,在这一步实例仍然完全可用,可以手动撤销监听事件,计时器等;
destoryed:仅存在DOM节点,其他所有东西已经自动销毁。
- 删除数组成员用delete和Vue.delete的区别:
delete删除数组成员只是被删除的元素变为empty/undefined,其他元素键值不变
Vue.delete是直接删除数组元素,改变了数组的键值
- vue的nexttick:
vue实现响应式并不是在数据变化之后DOM立即变化,而是等同一事件循环中的所有数据变化完成之后再同一进行视图更新。
vue的nexttick是在下次DOM更新循环结束之后执行延迟回调,在修改数据之后立即使用这个方法,可以获取更新后的DOM。
- 说说nextTick的源码:
1、vue把nextTick源码单独放到一个next-tick.js中,在nextTick函数外层定义三个变量callbacks,pending,timeFunc,一个是callbacks就是队列,每次调用$nextTick函数就是在向callbacks中新增回调函数的过程。
callbacks新增回调函数后又执行了timeFunc函数,pending用来标识同一个时间只能执行一次,在timeFunc函数中使用isNative函数用来判断所传参数是否在当前环境原生就支持。vue在代码中做了四个判断,对当前环境进行了不断的降级处理,尝试使用原生的Promise.then,MutationObserver和setImmediate,上述三个都不支持最后使用serTimeout。降级处理的目的都是将flushCallbacks函数放入到微任务(判断1和2)或者是宏任务(判断3,4),等待下一次事件循环时执行。
flushCallbacks就是把callbacks数组复制一份之后把callbacks置为空,最后用for循环把数组中的函数依次执行一遍。
使用vue的时候,在style标签加上scoped就可以实现样式隔离,只会作用在当前组件。
- vue和react的区别:
原理上:vue是通过发布订阅模式加数据劫持监听值的变化,从而实现响应式的,vue是双向数据流,使用v-model这一语法糖即可轻松实现数据的双向绑定。react是需要依赖”onchange/setState”模式来实现数据的双向绑定,因为他是单向数据流。
模板渲染:react在JSX中使用原生的js语法实现插值,条件渲染,循环等。vue则需要依赖指令进行更容易上手,但封装程度更高,调试成本更大。
- 计算属性(computed)和监听器(watch)的区别:
计算属性:计算属性是一个函数,最后函数返回的结果就是计算属性得到的结果,当我们第一次使用计算属性时会执行计算属性并进行计算然后将结果缓存起来,当第二次使用插值表达式或其他方式使用该计算属性时,就会检查计算属性中用到的数据是否发生了变化,如果没有则在直接获取缓存中的值,有则重新计算然后进行缓存。计算属性的应用场景是计算的内容需要依赖多个属性的情况。
监听器:一个监听器对应data中的一个属性,当属性发生变化时触发监听器的执行。监听器的应用场景是计算的内容依赖一个属性的情况。
如果数据反复发生变化计算很多次的情况下使用计算属性的开销更大,适合使用监听器;当一个数据反复使用但是它依赖的内容很少发生变化时由于计算属性会缓存计算结果所以更加合适。
- vue2为什么直接对对象新增和删除属性监听不到:
vue对对象属性实现响应式是只会对组件实例中data里面的对象通过遍历为每个属性用Object.defineProperty绑定getter和setter来实现属性数据的监听。
- 实现浏览器多文件并发上传怎么设置,怎么批量上传
使用elementUI默认是每保存一个文件就会立即向服务器发送上传请求,我们可以设置auto-upload来取消这个默认行为,设置limit可以控制最多上传文件个数,如果想要一次性上传多个文件可以将文件保存在一个数组中,然后发送一个请求。
- hash和history有哪些差异:
hash模式会在url中携带#,#及其后面的内容就是hash,当我们发送http请求时hash是不会被包括在url中的,所以hash模式可以在前端很好的实现路由跳转,即使刷新页面也没事。
history模式利用了HTML5historyAPI新增的pushState()和replaceState(),这两个方法应用于浏览器的历史记录栈,提供对历史记录进行修改的功能。history模式下的url是不带#号的,所以当刷新页面时,会向服务器发送请求该url的页面,如果没有在后端设置响应的资源会出现 404的情况。
父子组件如何进行通信:
父组件向子组件通信:
1、通过props属性实现父组件向子组件通信
1 | <template> |
1 | <template> |
2、使用$emit和$on传递父组件方法
1 | <template> |
1 | <template> |
3、通过$parent获取父组件然后使用父组件中的数据
4、vue依赖注入provide - inject实现子组件调用父组件的方法
1 | //父组件提供provide,允许我们指定我们想要提供给后代组件的数据/方法 |
子组件向父组件通信:
1、使用$emit和$on
1 | <template> |
1 | <template> |
2、在子组件添加ref属性,通过ref属性获取到子组件,与$parent类似
补充:消息发布订阅(适用于任意组件间通信):
1 | 1、安装pubsub |
全局事件总线(适用于任意组件间通信):
1 | //全局事件总线本质上就是一个对象,所有的组件都能够找到他,可以使用$on与$emit绑定与触发事件 |
- 拥抱vue3:
1、性能的提升,使用vite打包文件更小,初次渲染更快,更新更快,内存使用减少
2、更好的ts支持,可以在命令行直接配置
3、新增加proxyAPI可以对对象新增属性进行监听,proxy是真正意义上给对象包上一层代理从而去完成数据监听劫持的操作,总体来说复杂度比vue2是减少一个数量级的。只要对这个代理对象访问或修改都会被代理监听到进而动态决定返回什么东西,并且也不再需要把选项的东西重复挂载到组件实例的this上,因为访问的时候可以知道你访问的东西时属于props还是data还是其他的,vue只要根据这个信息去对应的数据结构中拿出来即可,所以会减少组件的内存。
- vite和webpack的比较:
vite特性:轻量;按需打包;HMR(热渲染更新)
webpack在启动时需要先build一遍,而这个过程是将所有模块提前编译、打包进bundle里,会消耗很多时间,而vite在启动时内部直接启动了web server,并不会编译所有的代码文件,它的打包原理是基于es新特性Dynamic imports实现的,而且在编译ts文件时引用ESbuild通过go对ts语言的支持编译速度比tsc还快几十倍。
- 说说ES6的proxy:
proxy是代理器的意思,proxy在目标对象外层搭建了一层拦截,外界对目标对象的某些操作必须通过这层拦截。
1、proxy可以直接监听对象而非属性
2、proxy可以直接监听数组的变化
3、proxy有多达13中的拦截方法
4、proxy返回的是一个新对象,我们可以只操作新的对象达到目的
1 | var proxy = new Proxy(target,handler) |
- 对vue3中set up函数的理解:
set up是为了使用组合式API,组件中所有用到的数据方法都要配置在set up中,set up返回的对象中所有的属性和方法在模板中都可以直接使用。在set up中定义响应式数据可以使用ref函数,定义一个对象类型的响应式数据可以使用reactive函数,ref函数底层是Object.defineProperty,reactive函数底层是proxy
- 说说vue的mixin:
mixin本质是一个js对象,他可以包含我们组件中任意功能选项,比如data,methods等,组件可以使用mixin对象将mixin选项混入到该组件本身的选项中,实现代码复用。当组件选项与mixin对象发生冲突时会覆盖mixin选项,如果相同选项为生命周期函数时会合并为一个数组,先执行mixin的函数再执行组件的。
- 详细说说vue2的响应式原理:
Vue是采用数据劫持加发布订阅模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。
1、需要给监听的数据对象observer进行递归遍历,包括子属性对象的属性,都加上setter和getter,这样给这个对象的某个属性赋值就会触发setter,就能监听到数据变化了。
2、compile解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知更新视图。
3、watcher是observer与compile之间通信的桥梁,主要的事情就是:
- 在自身实例化时往属性订阅器(dep)里面添加自己
- 自身必须有一个update()方法
- 待属性变动dep.notive()通知时,能调用自身的update方法,并触发compile中绑定的回调。
4、MVVM通过observer来监听自己model数据变化,通过compile来解析编译模板指令,最终利用watcher作为通信桥梁,达到数据变化->视图更新;视图交互变化->数据变更的双向绑定效果。
当执行new Vue时,vue就进入了初始化阶段,vue会对指令进行解析(初始化视图,增加订阅者、绑定更新函数),同时通过Observe遍历数据,对每一个属性是对象的添加__ob__,它是一个observer类,通过递归以及Object.defineProperty的getter与setter实现对多层数据,包括数组的监听。在observer实例中,都会有一个dep实例对象,它使用发布订阅的模式。当数据发生变化时,Observer中的setter方法会被触发,会调用Dep.notify(),循环依赖列表通知所有的watcher,并调用watcher的update方法,通过回调函数通知视图进行更新。(多看源码)
对于数组的监听,vue是对数组的七个方法,即push/pop/shift/unshift/splice/reverse/sort进行了改写,通过在observer实例对象与Array.prototype之间的原型链上添加了一个新的对象,然后在该对象中对数组方法进行了改写,让数组通过这七个方法的增删后的数组也可以被监听到。
- 理清Vue响应式系统中的Watcher和Dep的关系:
Dep何时创建:初始化时给data的属性进行数据劫持时创建的
Dep有几个:与data中的属性一一对应
Dep结构是什么:id标识、subs存放watcher的容器
Watcher何时创建:初始化解析大括号表达式/一般指令时创建
Watcher有几个:与模板表达式一一对应
Dep与watcher是多对多的关系:
一个data属性对应一个Dep,一个Dep可能对应n个watcher(属性多次在模板中被使用)
一个表达式对应一个Watcher,一个watcher可能对应n个Dep(多层表达式,如a.b.c)
- v-router传参方式:
1、使用router的name属性也就是params来传递参数
1 | //首先在router的index.js中配置每个路由的路径,name属性 |
2、通过query传参
1 | //在传值页面写法: |
3、使用vue里的<router-link>标签来传参
1 | //在传值页面呢 |
- 说说路由守卫:
路由守卫主要用来通过跳转或取消的方式对路由进行权限控制
全局守卫:
1 | //全局前置守卫,初始化执行,每次路由切换执行 |
独享守卫(只对特定的路由起作用):
1 | //直接在路由配置上定义beforeEnter守卫 |
组件内守卫:
1 | //进入守卫:在渲染该组件的对应路由时被调用 |
- 说说vue的diff算法:
1、当数据发生变化时,vue是如何更新节点的?
首先先根据真实DOM生成一颗虚拟DOM,虚拟DOM其实就是一个对象,以对象的形式模拟树形结构,当虚拟DOM中某个节点的数据发生变化时会生成一个新的虚拟DOM,然后对新旧虚拟DOM进行对比,发现有不同的地方就直接修改在真实DOM,然后对虚拟DOM进行更新。
2、diff的比较方式?
在采取diff算法会根据唯一的key值比较新旧节点,比较只会在同层级进行,不会跨层级比较。
3、diff的流程?
当数据发生改变时,会调用Dep.notify方法通知所有订阅者Watcher,订阅者就会调用patch方法给真实的DOM打补丁,更新相应的视图。
patch函数接收两个参数,分别是新旧虚拟DOM:
(1)判断两节点是否值得比较(key值,标签名,是否为注释节点等),值得比较则执行PatchVnode。不值得比较则用新的虚拟DOM替换旧的虚拟DOM
(2)如果两个节点都是一样的,那么就深入检查他们的子节点,如果两个子节点不一样就说明新的虚拟DOM完全被改变了,直接替换掉旧的虚拟DOM。如果两个节点不一样但是子节点一样也是直接替换。
(3)patchVnode:首先找到对应的真实DOM,称为el;然后判断新旧虚拟DOM是否指向同一个对象,如果是的话直接return,如果他们都有文本节点但是不相等,那么将el的文本节点设置为新虚拟DOM的文本节点,如果旧虚拟DOM有子节点而新虚拟DOM没有则删除el的子节点,如果旧虚拟DOM没有而新的有则把新虚拟DOM的子节点添加到el上。如果两者都有子节点,那么需要执行updateChildren函数比较子节点,这个函数主要做了以下这些事:
将新旧虚拟DOM的子节点提取出来,各自都有两个头尾的变量,他们的两个变量相互比较,一共有四种比较方式,分别是新前与新后,新后与旧后,新后与旧前,新前与旧后。如果这四种比较都没有匹配如果设置了key就会用key进行比较 ,在比较的过程中变量会往中间靠,一旦前面的索引大于后面的就说明新旧虚拟DOM的子节点至少有一个遍历完了,就会结束比较。
就这样层层递归下去知道新旧虚拟DOM的所有子节点比对完也将DOM的补丁都打好了。
diff算法底层是通过四种命中查找的优化策略,即①新前节点与新后节点,②新后节点与旧后节点,③新后节点与旧前节点,④新前节点与旧后节点,当一种查找不满足之后会继续下一种查找,如果找到则中断该节点查找,进行相应的处理,例如更新索引值或者通过patch函数让虚拟节点上树。如果四个策略都不满足,会循环查找。当开始索引值大于结束索引值时,也会终止查找策略,根据新旧索引情况判定是删除旧节点还是新增新节点。
- vuex与vue的关系以及vuex的使用:
vuex是vue开发提供的状态管理工具,在一个项目只能够频发使用组件传参的方式来同步data的值,一旦项目比较庞大,数据管理与维护是一个棘手的问题。vuex就是一个统一的管理数据的工具,只要把数据存放在里面即可在vue组件中使用。
vuex安装:
1、使用npm安装vuex
2、在项目根目录下新建一个store文件夹在文件夹中新建一个index.js
3、初始化index.js中的内容
1 | import Vue from "vue" |
4、将store挂载到当前项目vue实例中,在main.js配置
vuex的使用:
在任意组件中,可以使用this.$store.state.属性名来获取数据,在模块中使用。
在组件中修改state使用mutations,通过this.$store.commit(“mutation事件名”,参数)可以修改state中的数据。
在getters中进一步对state中的数据进行加工得到新数据,通过return返回新数据。
在vuex中使用actions发送异步请求,在组件中通过this.$store.dispatch(“actions的名字”,参数)来调用actions中的方法,使用commit调用mutations来修改state,而不是直接变更状态。
当项目越来越大,vuex中的数据越来越多,可以在modules中对数据进行分配,让不同的组件共享特定的state。
可以使用mapState将state中的数据映射到组件内部的计算属性中,使用…mapState()对对象进行展开运算,整体上是对象的合并。
- 为什么vue-if与vue-for不能放在一起使用:
因为vue-for的优先级比vue-if高,放在一起使用意味着每一个循环都会运行一次vue-if,消耗性能。
解决方案:
1、在外部包裹一个template标签使用vue-if,这样就可以先执行vue-if再执行vue-for。
2、不使用vue-if,使用计算属性对需要循环遍历的列表或数组进行过滤得到想要的元素。
- 为什么vue组件中的data必须是函数而不能是对象:
如果使用对象来声明数据的话,会让不同组件的data数据其实是同一个引用地址,会产生数据污染。而使用函数对数据进行声明,因为函数返回的对象内存地址并不相同所以不会出现数据污染。
在组件根实例对象data可以是对象或函数,但是在组件实例对象中data必须是函数。
- vue的组件与插件有什么不同:
组件:就是把各种逻辑抽象成一个统一的组价来实现开发的模式,在vue中每一个.vue后缀的文件可以视为一个组件。使用组件可以降低系统的耦合度,我们可以通过替换不同组件来实现不同的需求;组件也方便我们调试,在出现bug的时候可以通过移除组件的方式来判断问题所在;同时组件还可以提高项目的可维护性,由于组件在系统中是被复用的,所以对组件代码的优化可以获得系统的整体升级。
组件的编写一般就是vue单文件的形式,我们在文件中进行组件代码的编写,然后通过Vue.component进行全局注册或者在需要用到的地方进行局部注册即可。
插件:插件通常用来添加全局功能,有添加全局方法或属性(Vue.全局方法 = function(){})、添加全局资源(Vue.directive("方法名",{bind(el,binding,vnode,oldVnode){}}))、通过全局混入添加组件选项(Vue.mixin({created:function(){}}))、添加Vue实例方法(Vue.prototype.$方法名 = function(){})等实现插件功能。
插件的编写需要新建一个js文件并在里面暴露一个install方法,在方法里面进行插件代码的编写,然后通过Vue.use()方法对插件进行注册即可。
- 说说vue的插槽:
vue的插槽就是如果在项目中有一个会被重复使用到的组件,但是这个组件在不同的地方会有少量的更改,此时如果我们重写组件实际上是很不明智的。所以可以使用slot元素在组件中进行占位,然后可以在不同的组件中自定义插槽中的内容,实现对组件的复用比如一些布局组件。
插槽分为默认插槽、具名插槽与作用域插槽,具名插槽相对于默认插槽,可以通过name属性来表示不同的插槽,然后在父组件中为不同的插槽填写不同的内容;作用域插槽就是子组件可以通过将自身数据作为slot标签的一个元素传递给父组件,然后在父元素中使用v-slot获取然后使用{{}}进行调用。(注:v-slot只能在template标签上使用)
- 了解过Vue.observable吗,说说看:
当我们需要在非父子组件中进行通信时,如果实现的功能并不复杂,由于全局事件总线与vuex比较繁琐,所以可以使用observerable来实现。observerable是可观察的意思,它可以让一个对象变为响应式数据,我们可以通过新建一个js文件,通过Vue.observable方法创建一个state对象来存储需要进行共享的数据,然后在mutations对象中可以创建对应的方法,最后直接在vue文件中引入就可以直接使用了。
- 用的比较多的element 组件:
el-button按钮、el-input输入框、el-upload上传、el-form表单、el-breadcrumb面包屑
- 说说vue的模板引擎:
模板引擎就是让数据变为视图的解决方案。vue在实现模板引擎中引入了mustache库,在这之前实现数据变为视图的方法有:纯DOM(手动根据data创建dom节点和对应赋值)、join方法(将模板HTML字符串每一行都写在数组中,数据进行相对应的填充,最后利用join方法生成HTML)、es6模板字符串(join的升级版,由于可以换行直接写在反引号中,用$填充数据最后遍历生成HTML)。
mustache库使用render函数根据模板字符串和数据生成相对应的HTML,实现的原来是将传入的模板字符串转成tokens,结合传入的data生成填充数据渲染后的HTML字符串。tokens是反映模板字符串结构和内容的数组,它的分割是基于双大括号,里面的内容为name类型,值为key,外面的字符串内容都为text,值就是字符串;当双大括号里面是#array数组时,会被解析成#类型,这时候会开辟索引2,存放tokens数组,知道遇到双大括号里面是/array会被解析成/类型,值为数组索引。
具体实现步骤:
1、Scanner类:一个扫描器类,用于扫描将指定字符串,最后生成tokens
2、tokens数组的折叠:使用到栈结构sections以及收集器collector,对token类型是#的token进行折叠。
3、tokens与data结合生成HTML:对tokens数组的每一项类型检查,如果为text则直接加进HTMLStr中,如果为name类型将数据值加进HTMLStr中,如果是#类型,则使用递归思想将这一项的子项tokens和封装了.属性的子对象传入参数中,最后返回整个HTMLStr。
- vue3实现响应式相比2有什么改进:
vue2是通过在new一个vue实例对象的时候对data选项中的数据通过Object.defineProperty进行数据劫持,对于对象,如果在后续添加删除属性无法进行劫持,可以使用vue.$set解决;同时对于数组的api也无法监听实现响应式,通过重写数组七个方法解决。但是如果存在深层次的嵌套对象关系,需要进行深层的监听造成极大的性能问题。
vue3的proxy直接劫持整个对象,相当于在对象外层添加了一层拦截,对对象属性的读取与修改都能被拦截到,不需要对对象的每个属性进行劫持。并且proxy返回一个新对象,可以只操作新对象达到响应式的目的,proxy也可以直接监听数组的变化,proxy本身也有多种拦截方法是Object.defineProperty不具备的。
- vue3的componentAPI与vue2的optionsAPI有什么区别:
在vue2中,是将data数据、method方法以及生命周期函数分开进行声明,当组件变得复杂的时候,导致对应属性的列表增长,会让组件难以阅读和理解。在vue3的componentAPI将一个功能所定义的所有API放在一起,更加的高内聚低耦合。
在vue2中使用多个mixin混入时,可能会存在命名冲突的问题,在vue3的componentAPI中可以避免这一点。
- 说说vue3的tree-shaking:
在vue2中,无论在项目中是否使用到一些功能,都会将所有功能代码进行打包进生产代码中。vue3的tree-shaking特性基于es6语法(import与export),借助es6模块化在编译时可以确定模块之间的依赖关系以及输入输出的变量,判断哪些模块与变量未被使用,进而删除相对应的代码。
- vue-router生命周期:
beforeRouteEnter:在渲染该组件路由前被调用,不能通过this获取组件实例
beforeRouteLeave:导航离开该组件路由时调用
beforeRouteUpdate:在当前路由改变但是该组件被复用时调用
- 父子组件中生命周期执行顺序:
父beforeCreate -> 父created -> 父beforeMount -> 子beforeCreate -> 子created -> 子beforeMount -> 子mounted -> 父mounted->父beforeUpdate->子beforeUpdate->子updated->父updated->父beforeDestroy->子beforeDestroy->子destroyed->父destroyed
- 说说keep-alive:
keep-alive是vue提供的一个抽象组件,用来对组件进行缓存,从而节省性能。被keep-alive包裹的动态组件或者是router-view会缓存不活动的实例,再次被调用这些被缓存的实例会被再次复用,对于不需要实时更新的页面来说大大减少了性能上的消耗。
当组件子keep-alive中被切换时组件的activated,deactivated这两个生命周期构子函数会被执行。
首次进入组件时:beforeRouterEnter > beforeCreate > created > beforeMount > mounted > actived > … > beforeRouterLeave > deactived
再次进入组件时:beforeRouterEnter > actived > … > beforeRouterLeave > deactived
由于被包裹的组件我们请求获取的数据不会再重新渲染页面,只会保持第一次请求数据的渲染结果,所以在某些特定环境下需要强制刷新组件:
1、利用include(字符串或者正则,只有名称匹配的组件会被缓存)、exclude属性(字符串或者正则,任何名称匹配的组件不会被缓存)
1 | <keep-alive exclude="indexLists"> |
2、利用meta属性
1 | export default[ |
1 | <keep-alive> |
3、使用路由守卫实现前进刷新,后退缓存资料
- Vue样式隔离是怎么实现的?
使用vueSFC的时候,在<style> 标签加上scoped就可以实现样式隔离,只会作用在当前组件。
- Vue实现样式隔离的原理:
vue通过在DOM结构以及css样式上加上唯一的标记,保证唯一,达到样式私有化,不污染全局的作用。主要做了以下处理:
1、给HTML的DOM节点加一个不重复属性标志唯一性
2、在添加了scpoed属性的组件的每个样式选择器后添加一个等同于‘不重复属性’相同的字段,实现类似于作用域的效果,不影响全局。
3、如果组件内部还有组件,只会给最外层的组件里的标签加上唯一属性字段,不影响组件内部引用的组件。
- 说说v-model语法糖以及具体绑定什么属性:
本质上,v-model就是@input与:value的结合,通过绑定value值当input改变子组件值的时候通过事件传递给父组件进行修改,达到v-model的绑定原理。通常绑定的元素有radio单选框、CheckBox多选框还有select标签。
- Object.defineProperty除了改set和get,还可以设置什么属性:
1、value:就是将要修改的属性的值
2、writable:是否只读
3、enumerable:对象中的值能否被枚举
4、configurable:该属性决定2,3,4这三个属性的值能否被配置,值true就表示不能被修改,默认为true
- Vue 中怎么自定义过滤器:
Vue.js 允许自定义过滤器,可被用于一些常见的文本格式化。
过滤器可以用在两个地方:双花括号插值和 v-bind 表达式。过滤器应该被添加在 JavaScript 表达式的尾部,由“管道”符号指示 。
可以用全局方法 Vue.filter() 注册一个自定义过滤器,它接收两个参数:过滤器 ID 和过滤 器函数。过滤器函数以值为参数,返回转换后的值
1 | Vue .filter( 'reverse' , function (value) { return value.split( '' ).reverse().join( '' ) }) //过滤器也同样接受全局注册和局部注册 |
- 路由之间是怎么跳转的?有哪些方式?
1、<router-link to="需要跳转到页面的路径">
2、this.$router.push()跳转到指定的 url,并在 history 中添加记录,点击回退返回到上一个页 面
3、this.$router.replace()跳转到指定的 url,但是 history 中不会添加记录,点击回退到上上个 页面
4、this.$touter.go(n)向前或者后跳转 n 个页面,n 可以是正数也可以是负数
- 单页面与多页面的区别:
