1.vue的生命周期有哪些以及每个生命周期做了什么以及应用场景?
下图为思维导图
下面是详细的表格
| 生命周期 | 功能 | 应用场景 |
|---|---|---|
| beforeCreate | 是new Vue()之后的第一个钩子,此时data、methods、computed以及watch中的数据都不可以访问 | 1.根据一些信息的判断,决定是否向下渲染,如果不需要,就跳转到其他的组件。2.权限校验,根据有没有权限,决定是否渲染此组件(但是这个是路由中的导航守卫更适合干这个事情) |
| created | 实例创建完成后发生,当前阶段已经完成了数据监测,也就是可以使用数据以及修改数据。这里修改数据不会触发updated函数。在这个阶段,可以进行一些数据的初始化,但是此时还不能和dom进行交互,如果一定要交互,就是用vm.$nextTick来访问dom | 提前初始化数据(为了让了页面展示数据快一些,提前获取数据,发送异步请求数据) |
| beforeMount | 实例已经完成了模版编译,但是还没有挂载到页面中。当前阶段虚拟dom已经创建完成,即将开始渲染。此时也可以对数据进行更改,但不会触发updated | 基本上不做什么,也有人在这里发送异步请求数据 |
| mounted | 将编译好的模版,挂载到页面当中显示。此时阶段,真实的dom已经挂载完毕,数据已经完成双向绑定。可以访问到真实的dom节点,可以对dom进行操作, | 做一些对dom的处理以及监听工作 |
| beforeUpdate | 发生在响应式数据更新之前,虚拟dom被重新渲染之前触发,你可以在当前阶段进行更改数据,不会造成重新渲染 | 基本不做什么 |
| updated | 发生在更新之后,当前的dom已经更新完毕。要注意的是,不能在此时更改数据,容易造成无限循环的更新 | 和mouted类似,也是对dom一些处理。注意:在这里不要对响应式数据进行修改,容易造成死循环 |
| beforeDestroy | 在实例销毁之前触发,此时实例还没有没销毁,还可以做一些善后工作,比如清除计时器 | 做一些数据的保存,或者提示,要不然没机会了 |
| destroyed | 发生在实例销毁之后,组件已经被拆卸,双向绑定被销毁,监听被移除,子实例被销毁 |
2.vue的响应式原理是什么?vue3的有何不同?
-
Vue 2 的响应式原理主要通过数据劫持(Object.defineProperty)和发布订阅模式实现的。
通过Object.defineProperty来劫持data中的各个属性的setter、getter,监听数据变化。在数据发生变化时发布消息给订阅者(watcher),订阅者触发响应的回调(update)更新视图
-
Vue3.x改用proxy替代Object.defineProperty,因为proxy可以直接监听对象和数组的变化,有13中拦截方法get set ownKeys has deleteProperty apply defineproperty constructor
注意:proxy只会代理对象的第一层,vue3中怎么做的呢?
在reflect.get中判断是不是object,如果是,再通过reactive做代理,这样就实现了深度监听
注意:检测数组的时候可能触发多次get和set,那么如何防止多次触发呢?
可以在自定义 setter 中添加逻辑,首先判断属性是否是目标对象自身的属性,然后再判断旧值和新值是否相等。只有满足这两个条件中的一个时,才执行更新操作,避免不必要的触发。
--javascripttypescriptbashsqljsonhtmlcssccppjavarubypythongorustmarkdown
import { reactive } from 'vue';
const target = reactive({
// 响应式对象
count: 0,
name: 'Vue'
});
const handler = {
set(target, key, value) {
if (key in target && target[key] === value) {
return true; // 值没有变化,不执行更新
}
return Reflect.set(target, key, value); // 执行更新操作
}
};
const proxy = new Proxy(target, handler);
proxy.count = 1; // 更新 count 属性,会触发更新
proxy.count = 1; // 由于值没有变化,不会触发更新
proxy.name = 'Vue'; // 更新 name 属性,会触发更新
3.vue3和vue2的区别?
-
生命周期上
2.响应式原理上,vue3利用proxy可以对整个对象以及数组进行监听
3.vue3支持Composition Api,使用更灵活。vue2是option Api
4.vue3打包体积更小,采用webpack中的treeShaking,剔除没有用到的代码
5.vue3模版支持多节点,vue2只支持单节点
6.vue3重写虚拟dom的实现
7.vue3更好对ts进行支持
4.讲下vue3怎么重写虚拟dom实现的?
vue3里面是在初始化的时候给没一个虚拟节点添加一个patchFlag,也就是一个优化标识,只会比较patchFlag发生改变的节点,进行视图更新。而对于pathchFlag没有发生改变的节点,做一个静态标记,在渲染的时候直接复用
vue2的时候是遍历每个虚拟节点,对比出不一样的,得到一个patch对象,边对比变更新
5.谈一谈对MVVM的理解?
MVVM是Model-View-ViewModel缩写,model代表数据层,view代表视图层,viewModel是数据和视图层的中间桥梁,数据自动绑定到viewModel上,发生改变自动更新视图,视图发生变化会通知viewModel更新数据
6.在vue2.x是如何检测到数组的变化的?
使用了函数劫持的方式,重新写了数组的方法,vue将data中的数组进行了原型链重写,指向了自己定义的数组原形方法,这样当调用数组api时,就通知依赖更新。如果数组中包含引用类型,就递归遍历进行监控,达到深度检测数组变化的效果
下面是自己写的重新数组方法的mini demo
--javascripttypescriptbashsqljsonhtmlcssccppjavarubypythongorustmarkdown
var originArr = [1, 2, 3];
var customArr = [];
customArr.push = function(params) {
console.log('custom 1');
Array.prototype.push.apply(this,arguments);
console.log('custom 2', customArr);
}
customArr.push(4);
customArr.push(5);
console.log('finally', customArr)
7.v-model双向绑定的原理是什么?
v-model本质上是一个语法糖,是value属性+input方法的结合语法糖,
使得在表单元素和应用状态之间建立了一个双向的数据绑定。
--javascripttypescriptbashsqljsonhtmlcssccppjavarubypythongorustmarkdown
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Custom v-model Example</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
</head>
<body>
<div id="app">
<input v-bind:value="myValue" v-on:input="updateMyValue($event.target.value)" type="text" >
</input>
<p>Input Value (Custom v-model): {{ myValue }}</p>
</div>
<script>
new Vue({
el: '#app',
data: {
myValue:''
},
methods: {
updateMyValue(value) {
this.myValue = value;
}
}
});
</script>
</body>
</html>
8.vue2.x和vue3.x的diff算法分别说下
简单来说diff算法
-
同级比较,再比较子节点
-
先判断一方有子节点一方没有子节点的情况
-
比较都有子节点的情况
-
递归比较子节点
vue2.x的diff算法,就是对遍历虚拟节点的进行逐一对比,得到一个patch对象,然后更新到视图上,边对比变更新。采用双端比较算法,循环向中间靠拢
vue3.x diff算法,就是给每个虚拟节点添加一个patchFlag,也就是一个优化标识,发生变化的pathflag节点,会更新到视图,没发生变化的会进行静态标记,渲染会复用这个节点
9.vue组件通信方式有哪些以及原理?
-
通过props 父->子
-
通过$emit 子->父
-
通过ref 父拿子里面的数据
-
通过$$root
-
通过provide inject
-
通过event Bus
-
通过$attrs
-
vuex
| 组件通信方式 | 适用场景 | 例子 |
|---|---|---|
| props | 父组件传数据给子组件 | |
| $emit | 子组件传值给父组件 | |
| ref | 父组件拿子组件数据 | |
| eventBus | 兄弟组件传值 | |
| $$root | 兄弟组件,拥有共同祖辈 | 兄弟组件:this.$parent.on('add', this.handleAdd) |
| listeners | 祖先传递数据给子孙 | |
| provide和inject | 祖先传递给子孙 | |
| vuex | 用于复杂的数据关系,用来共享变量 |
10.vue的路由实现,hash路由和history路由原理说一下?
hash路由模式原理:window.loaction.hash
-
当用户访问一个hash(带有#)页面时,浏览器不会像普通页面那样直接请求服务器跳转,而是dom之前的切换
-
通过监听hashChange监听路由的变化,从而触发路由的变化,来实现页面的切换
-
会根据哈希部分的内容匹配到响应的路由配置,然后渲染对应的组件到页面
history路由原理
-
HTML5 history Api中的pushState和repaceState允许修改浏览器的历史记录,而不造成页面的重新渲染
-
当用户在history路由模式下进行跳转时,会利用pushState在历史记录中添加新的记录,通过popSate事件监听路由变化
-
根据新路由匹配路由配置,并渲染对应组件到页面中
注意:这个需要服务端配置
--javascripttypescriptbashsqljsonhtmlcssccppjavarubypythongorustmarkdown
server {
listen 80;
server_name example.com;
location / {
try_files $uri $uri/ /index.html;
}
}
11.vue的v-show和v-if区别
v-show就当于css里面的display,但是dom元素依旧存在, 不涉及生命周期的改变
v-if切换就有一个编译/卸载的过程,涉及到组件的重建以及销毁
v-if有更高的切换消耗,v-show有更高的初始化渲染消耗
如果非常频繁的切换v-show比较好,如果在运行时条件很少改变用v-if比较好
12.keep-alive的常用属性有哪些以及原理?
keep-alive可以实现组件缓存,当组件切换时不会对当前组件卸载
常用属性include/exclude,允许组件有条件的缓存
两个生命周期activated,deactivated
13.nextTick的作用是什么?他的实现原理是什么?
nextTick的作用就是在下次DOM更新循环之后执行延迟回调。主要就是让我们在数据更新之后并且dom渲染完成之后执行一些操作,就是确保这些操作完成在执行
原理:nextTick主要使用了宏任务和微任务
主要过程就是promise mutactionObserver setImmedidate settimeout
定义了一个异步方法,多次调用nextTick会将方法存入队列中,通过这个异步方法清空当前队列
14.说一下vue ssr的实现原理?
SSR也就是服务端渲染,也就是降Vue在客户端把标签渲染成HTML的工作放在服务端上,然后再把html直接返回客户端
SSR有着更好的SEO,并且首屏加载速度更快
也有缺点: 服务端渲染应用程序也要处于Node.js的运行环境,对于服务器有更大的负载需求
15.Vue组件的data为什么必须是函数?
Vue中都是组件化组成,复用一次组件就创建一个实例,复用多个,就创建多个实例,函数可以保证数据的隔离性,如果是对象的话,对象属于引用类型,会影响到所有的实例,造成数据污染,达不到数据隔离的效果,所以data必须是一个函数
16.说一下vue中的compiler的实现原理是什么?
vue中的compiler主要就是将template转换成render,render再生成vnode
简单来说三步
-
parse 将模版template转换成抽象语法树ast
-
optimize 遍历ast的节点进行静态标记,这样就知道那部分是改变的,通过diff算法,如果有不一样的进行更新,提升性能
-
generate 将前两步完善好的ast树,组成render字符串,然后将render字符串通过new Function的方式转换成渲染函数
17.vue和React的区别是什么?
相同点
-
都有组件化思想
-
都有服务端渲染
-
都有数据驱动视图
-
都有构建工具 vue vue-cli react create React App
-
都有native vue是weex react是react native
-
都使用了virtual DOM
不同点
-
vue是双向绑定 数据更变视图也会更新,视图改变也会通知数据更新 react是单项数据流,数据驱动视图
-
diff算法不同 react是使用diff队列保存需要更新那些DOM,得到一个patch树,再统一操作批量更新dom vue是两端对比,循环向中间靠拢,边对比,变更新
-
通信方式不同 react是通过回调函数进行传递的,vue是通过事件和回调函数
18说一下watch和computed区别是什么?
-
都是监测数据变化的
-
watch是一对多,监测一个数据改变,对其做一些逻辑处理和操作。computed是多对一,监听属性依赖其他属性
-
watch支持异步,computed不支持
-
computed有缓存,只有依赖的值变了,才会重新计算。watch没有
-
watch有两个参数,第一个最新值,第二个输入之前的值
-
computed将会混入到vuede 实例中去,所以需要监听自变量。而watch监听data、props里面变化的数据
19.computed的工作原理?
computed本质是一个惰性的观察者。如果属性存在props或者data中会给警告
vue初次运行会对computed的属性做初始化处理(initComputed),初始化的时候会对computed的每一个属性用watcher包装起来,这里面会生成一个dirty为true,然后执行defineComputed函数来计算,计算完成之后把dirty变为false,会根据dirty的值来看是否需要重新计算。如果依赖发生改变,需要重新计算,再把dirty变为true
20.说一下知道的vue修饰符有哪些?
-
事件修饰符
-
按键修饰符
-
表单修饰符
事件修饰符
-
.stop阻止冒泡事件
-
.prevent阻止默认事件
-
.capture使用时间捕获模式
-
.self只在当前元素本身触发
-
.once只触发一次
-
.passive默认行为将立即触发
按键修饰符
- .enter回车
-
.left 左键
-
.right右键
-
.up上
-
.down下
-
.middle滚轮
-
.space 空格
表单修饰符
- .trim去空格过滤首尾
- .lazy在文本框失去焦点才触发
- .number将文本框输入的内容转为number类型
21.如何实现vue项目中的性能优化
编码阶段
-
尽量减少data中的数据
-
v-if和v-for不能连用
-
循环需要用key,并且保证key的唯一性
-
使用路由懒加载,异步组件
-
防抖和节流
-
图片懒加载
-
第三方模版按需导入
-
长列表滚动到可视区域加载
seo优化
- 服务端渲染
- 预渲染
打包优化
-
使用cdn加载第三方模块
-
多线程打包happypack
-
spiltChunks抽离公共组件
-
sourceMap优化
-
tree SShaking
22.vue中的spa应用如何优化首页加载速度?
1.cdn加载第三方模块
2.缓存。将长时间不会变的第三方库和静态资源设置为强缓存,max-age设置为最大。再将访问路径加上hash值,等哈希值变了之后保证获取到最新资源
3.gzip压缩
4.懒加载。路由懒加载。当url匹配到响应的路径是你,会通过import动态的加载组件,从而提升首屏加载速度。webpack会把动态加载的组件分离成一个单独的chunk文件
5.预渲染。由于浏览器在渲染页面之前,首先要加载html、css、js文件。为此可能会有一段白屏的时间,可以加loading,或者骨架屏
6.http2.如果系统首屏要同时加载很多静态资源文件,但是浏览器对同域名的tcp链接数目是有限制的(chrome6个),超过规定的tcp连接数,要等到之前发送的请求有响应了之后,才能继续发送。然而http2对tcp连接并发多个请求是没限制的,在网络较差的情况下开启http2优化较为明显
7.封装。封装公共组件,对utils,插件,过滤器以及指令
8.图片懒加载。减少http请求
9.使用svg图标
10.压缩图片 image-webpack-loader
11.合理使用第三方库。尽量使用按需加载
12.使用可视化工具分析打包模块 webpack-bundle-analyzer
23.vue中key的作用?
key的作用更搞笑的更新虚拟dom
24.说下vue中的ref?
ref是用来给元素和子组件注册引用信息。引用信息将会注册在父组件的$refs上
-
如果在普通的dom上,引用指向的就是dom元素
-
如果在子组件上,引用就指向组件实例
使用场景
-
本页获取dom元素
-
或者子组件的数据以及方法