1、vue的理解
渐进式框架(中图的可选技术方案选择多样),只关注视图层,
声明式渲染 => 组件系统 => 客户端路由 =>状态管理 =>构建工具
声明式框架、MVVM模式、虚拟dom、组件化(高内聚,低耦合)
2、spa的理解
单页面应用(spa):只有一个html页面,切换页面是通过监听路有变化,由于html中是空的html结构,无法做seo优化,所以产生首次白屏的问题。
多页面应用(mpa): 页面必须重新加载,复用组件不太方便,服务端返回的html页面,不好维护,页面跳转是需要刷新的,ssr(服务端渲染)。
seo优化的解决方案:
1⃣️静态的话可以用静态页面预渲染,打包的时候把页面放在浏览器中运行,把htmlb,比如官网(比较稳定的)中可以使用这种方案。
2⃣️ssr(首屏ssr服务端加载)+csr(客户端渲染)也就是NuxtJs去实现。
3、响应式数据的理解
响应式:用户的取值和修改都被用户检测到。
Vue2缺陷:
1⃣️ 对象内通过defineReactive方法,使用defineProperty进行数据劫持(只劫持已存在的属性),对属性进行重写加入get和set;数组则是通过重写数组方法,性能较低。全通过递归和遍历所有的属性,也导致性能变低下。
2⃣️新增和删除时(并未加入get和set ),通过delete去补充
3⃣️Es6中的Map和Set不支持
Vu3改进:
1⃣️通过Proxy代理,并不是重写属性,而是代理拦截了属性。
2⃣️Vue2中在get中通过搜集water,而Vue3中通过get去搜集effect。3⃣️vue2和vue3都是递归,Vue3是使用时递归,而Vue2是直接递归。
4、数组的劫持过程
let newArrayProto = Object.create(Array.prototype);
Let oldArrayProto = Ob
[‘push’, ’shift’,…..].forEach(method=>{
newArrayProto[method] = function(…arg){
// 进行函数劫持
oldArrayProtomethod
}
})
数组的缺点:数组的索引和长度是无法监控的。
5、Vue的依赖搜集
Vue2:这个流程:
1⃣️每个属性都有自己的dep属性,存放所依赖的watcher(即存放多个dep),当属性变化后会通知watcher更新
2⃣️默认在初始化会调用render函数,此时会触发属性依赖搜集dep
3⃣️当属性发生修改时,会触发watcher更新dep.notify()
当组件渲染的时候,会创建一个渲染watcher,渲染时调用render方法,并且会把watcher放在全局上,为了后续继续使用,开始进行页面渲染,访问响应式数据,因为响应式数据会配备dep属性,会记住哪个模板中使用了,即会记住当前的watcher,后面如果哪个属性改变了会通过dep去通知watcher,即组件重新更新了。
Vue3:这个流程:
1⃣️vue3中会通过Map结构将属性和effect映射起来
2⃣️默认在初始化会调用render函数,此时会触发属性依赖收集track
3⃣️当属性发生改变时会找到对应的effect列表依次执行trigger
6、Vue2中的Vue.set是如何实现的
总结:新增属性无法实现响应式的问题,因为defineproperty方法只会劫持已存在的属性,而且对于数组没有解决索引和长度问题;针对对象,Vue.set等价于defineproperty定义一个新的属性
函数参数是target、key、value,
数组是通过splice实现的;
对象是通过defineReactive(即defineproperty,定义响应式数据),并通知页面更新;
Vue3中已经删掉这个方法
7、v-if和v-show的实现原理
v-if:如果条件不成立,不会渲染当前指令所在节点的dom元素
v-show:只是切换dom的显示和隐藏,最终会被编译成css中的display(通过切换display:none和原来所拥有的display的值进行控制)
Vue2中:
v-if,在编译时条件满足时会创建一个div,而不满足时就会创建一个注释节点(空节点),可以阻断内部代码是否执行。
v-show,在编译时会变成一个自定义的指令。在自定义指令中的钩子:通过变量originalDisplay保留默认的el.style.display,如果这个值是none的话就就置为空;当更新update时,把这个originalDisplay赋给这个el.style.display。
Vue3中:
v-if,跟vue2原理类似
v-show,跟vue2原理类似,只是把方法分离成一个setDisplay的方法。
如果v-if和v-show同时写入,v-if还是会被编译,v-if的优先级更高。
(v-if="true" v-show="false”,会不显示出来)
8、watch和computed的区别:
vue2中三种watcher(渲染watcher、计算属性watcher、用户watcher);
vue2中三种effect(渲染effect、计算属性effect、用户effect);
Vue2中:
1⃣️computed(计算属性)是有缓存的,每一个计算属性内部维护一个dirty属性,值为true;当取值时执行用户的方法,取到值就缓存给一个value,并把dirty改为false,下一次再取值时根据dirty的false值直接返回value的值;当dirty为true时会触发更新。
1、计算属性给个计算属性watcher,给个默认的watcher一个lazy:true,不会立即执行;
2、计算属性通过一个方法变成了一个属性的原理:内部是通过defineProperty将计算属性定义到实例子上;
3、当用户取值时,触发getter,拿到计算属性对于的watcher,看dirty是否为true,如果是true就求值运算;
4、计算属性watcher中依赖的属性(即引入了别的值)去搜集模版的渲染(最外层的)watcher,通过dirty值得更新去触发页面的更新;
5、如果依赖的值没有变化,则使用缓存的值;
2⃣️watch(监听),initWatcher
底层new了一个watcher,
计算属性不支持异步逻辑。
Vue3中:
1⃣️computed(计算属性)与vue2的原理不同。computed,本身就具有收集的功能,收集组件渲染的effect,而不是像vue2中通过属性去收集外层的watcher;如果其中依赖的属性发生变化,会通知计算属性effect更新dirty,并且计算属性会触发自己收集的渲染effect执行。
2⃣️ doWatch方法,底层new了一个ReactiveEffect,多了个onCleanup函数,去让用户做一些清理操作(比如input框中,快速输入12,第一次接口响应比较慢2s,第二次接口响应会快1s,vue2中会通过慢的接口去覆盖,先显示12,再显示1;但是在vue3中可以多了第三个参数onCleanup默认的清理方法,可以拦截上一次存入的cleanup函数,在这个函数中更改状态上个锁,就直接显示12了,即在第二次监听时根据cleanup函数里更改的状态取消了之前未完成的请求)
9、vue3中的ref和reactive的区别:
reactive:只用于处理对象类型的数据响应式,底层采用new Proxy()
ref:通常用于处理单值的响应式,主要解决原始值的响应式问题,底层采用的是类似Objecet.defineProperty()方法实现的
reactive,主要是通过createReactiveObject,先判断传入的值是否是对象,不是对象的话,不做任何操作直接返回。如果是对象类型的数据,最后会通过new Proxy()做一个代理,返回一个代理对象。
ref的实现,传递一个任意类型的值,如果是ref不做处理,如果不是就通过new RefImpl()来创建一个ref,如果是对象,会通过reactive方法做处理,ref包对象,reactive可以包ref,reactive可以默认做一个拆包的操作,不需.value取值
9、vue3中的watch和watchEffect的区别:
watchEffect:立即运行一个函数,然后被动的追踪依赖,当依赖改变时重新执行函数。
Watch:侦听一个或多个响应式数据源,在变化时掉用这个函数
都会创建一个响应式函数,new ReactiveEffect(getter,scheduler),在watchEffect中当数据变化时,会调用scheduler,内部会触发effect.run()方法。而watch是数据变化后,会调用scheduler,内部会调用cb回调函数。
effect.run()
10、如何将template转化为render函数
1、将template模版转化成ast语法树
2、对静态语法做标记,对静态节点进行跳过diff操作
3、重新生成代码(在运行时,通过Vue.compile(‘<div><div>’))
11、new Vue()这个过程做了什么
1、内部会进行初始化操作
2、内部会初始化组件绑定的事件(initEvent),初始化父子关系(initLifecycle)
3、初始化响应式数据data、computed、props、watch、method。对provide和inject进行处理( initInjections和initProvide),也对数据进行劫持,对象采用defineProperty数组采用方法重写(initData)
4、内部挂载时会产生一个watcher,会调用render函数触发依赖搜集,内部会所有的响应式数据增加dep属性,让属性记录当前的watcher
5、vue更新的时候采用虚拟dom的方式进行diff算法更新。
12、Vue.observable()的原理
普通对象变成响应式对象,对数据劫持。类似pinia和vuex,不复杂时可以使用
13、v-if和v-for的区别
vue3:v-if的优先级高 ,不能在一起使用,创建一个template包裹
vue2:v-for的优先级高 编码时是:先解析v-for,再解析v-if。会导致先循环在判断,浪费性能,会把v-if判断掉的节点转化为一个注释节点
14、Vue的diff算法原理
核心:比较两个平级额度虚拟节点的差异,不考虑跨级比较的情况,内部采用深度递归的方式进行比较。
Vue2:
比较流程:
1、先比较是否是相同的节点
2、相同节点就比较属性,并将老的虚拟dom复用给新的虚拟节点
3、比较儿子节点,考虑老节点和新节点儿子的情况
4、优化比较,头头、尾尾、头尾、尾头,双指针的形式去遍历
5、对比查找进行复用
15、key的作用和原理
1、主要用在虚拟dom算法,用于比对新旧节点,如果不用key,会产生“就地修改”的问题
2、在使用v-for时,用index作为key在新增时,也会遇到“就地修改”的问题
一般来说,标签和key来区别是否时相同的节点,
16、Vue.use()是什么
概念:
安装vue.js的插件,如果是对象写法,必须提供install方法,如果是函数写法,会被作为instal
L方法。install方法调用时,会将Vue作为参数传入,这样插件中就不再需要依赖Vue了。
功能:
1、添加全局指令、全局过滤器,全局组件
2、全局混入
3、通过Vue.prototype添加vue实例方法
17、Vue.extend()方法的作用
参数是template和data,最后可以通过new来创建一个子类 ,vue内部的组件都是通过extend()来创建的
18、Vue中组件的data为什么是函数
Vue2:
1、根实例(new Vue()的实例对象)是单例的不涉及到共享,无论是对象和函数都可
2、防止多个组件实例对象公用一个dara,产生数据污染,所以需要工厂函数返回一个全新的data作为组件的数据源。
Vue是通过
Vue3是通过组件的形式
Vue2版本是命令方式使用,通过new Vue去创建Vue实例
Vue3版本将命令和功能API函数化,通过createApp去创建应用实例,Vue.createApp函数功能就是返回应用实例对象
19、函数式组件的优势
函数式组件:无状态、无生命周期、无this,但是相对性能较高,函数式组件就是普通的函数,没有new的过程
在vue2中是有优势的,在vue3中没有优势
20、v-once
内置指令,只渲染一次,随后被视为静态内容被跳过,以优化更新性能,本质上是通过缓存来实现
Vue3.2后,加入了v-memo=“[a, b]”
21、Vue.mixin的原理
1⃣️、替换,如:props、methods、computed、inject(比如,组件的props中的name会替换mixin中的name)
2⃣️、合并,如:data、provide;watch、生命周期合并成队列(比如,data的合并,以组件本生为主,它的权重更大)
3⃣️、叠加,如:component, directive, filter,会在原型链上叠加(比如,filter的钩子,会在原型链上一层一层叠加)
核心策略:
默认策略,defaultStrats函数中(用三目预算符,优先使用组件的options),组件的options > 组件mixin options > 全局options。通过mergeOptions、mergeAssets(如:component, directive, filter等)把全局属性进行合并,会进行判断子类的mixins进行递归合并,又如mergeWatch等很多合并操作。
22、Vue中的slot是如何实现的
定制化组件,拓展组件的功能,模版编译后进行标记,比如返回name的名字,attrs、slot的值放入一个函数中,根据映射表的映射关系,来渲染以替换占位符。
23、双向绑定的理解和原理
双向绑定靠v-model,数据和视图的修改,默认是:value+input的语法糖,
在不同的表单元素中解析的不一样,比如复选框会被解析成change事件
24、.sync的
v-model默认只能双向绑定一个属性,而.sync可以绑定多个变量。Vue3把.sync删除了,v-model:可以传多个
25、name选项的好处
1、比如递归组件中,可以通过name属性找到这个组件进行递归
2、在keep-live中通过name属性,中的include和exclude去筛选name
3、查找某个组件$children可以查找这个组件,去查找和调试
26、Vue异步组件的作用和原理
1、回调写法。
2、通过import导入,默认就是promise
3、对象写法,可以loading的选择,失败的时候渲染一个错误组件
原理:(与图片懒加载类类似)
默认渲染异步占位符的节点
组件加载完毕后调用$forceUpdate强制更新,渲染加载完毕后的组件
27、nextTick的理解
Vue2:
数据同步修改,视图异步更新的。
nextTick函数, 内部既有同步代码又有异步代码
nextTick 回调,可以理解为 99% 的场景下都是微任务
优雅降级,Promise.then**、MutationObserver 、 **setImmediate和setTimeout(fn, 0)优雅降级的过程
Vue3:
不考虑兼容性,直接使用的是Promise.then,变成了一个方法。实际底层更新的时候也是nextTick的策略,跟上面nextTick说的一样。
28、keep-alive的原理:
路由页面的渲染:使用可以在路由meta属性中加入,
动态组件:keep-alive标签和router-view标签搭配使用缓存组件
29、Vue的性能优化
1、数据嵌套层级过深,合理设置数据 .....
30、
减小文件体积,
1、路由懒加载,大组件用异步组件,以减少入口文件的体积
2、提取功共代码
3、静态资源缓存,本地存储
4、图片压缩,图片合并(雪碧图)
5、打包时开启gzip压缩,使用插件
6、使用ssr对首屏做服务端渲染
31、跨域问题
1、cors,由服务端设置,允许指定的客户端访问
2、构建工具设置反向代理,比如webpack中devServe设置proxy;或者上线时nginx做反向代理
3、websocket通信
….
32、Vue封装axios的细节
1、设置请求超时时间
2、根据项目设置环境变量设置请求路径
3、设置请求拦截,如添加token
4、设置响应拦截,对响应的状态码或数据进行格式化
5、添加请求loading效果,