Vue2 PK Vue3
- 速度更快
- 体积更小
- 更易维护 生命周期
| Vue2 | Vue3 |
|---|---|
| beforeCreate | |
| creted | |
| beforeMount | onBeforeMount |
| mounted | onMounted |
| beforeUpdate | onBeforeUpdate |
| updated | onUpdated |
| beforeDestroy | onBeforeUnmount |
| destroyed | onUnmounted |
说下Vue的生命周期
- beforeCreate:在实例创建之间执行,数据是未加载状态。
- created:在实例创建、数据加载后,能初始化数据,DOM渲染之前执行。
- beforeMount:虚拟DOM已创建完成,在数据渲染前最后一次更改数据。el未挂载。
- mounted:页面、数据渲染完成。el挂载完毕。可以访问DOM节点。
- beforeUpdate:重新渲染之前触发。不会造成重渲染。
- Updated:数据已经更新完成,DOM也重新render完成,更改数据会陷入死循环。
- beforeDestroy:实例销毁前执行,实例仍然完全可用。
- destroyed:实例销毁后执行,这时候只剩下DOM空壳。
第一次页面加载会触发哪几个钩子?
beforeCreate, created, beforeMount, mounted
简述每个周期具体适合哪些场景
-
beforeCreate:其他的东西都还没创建。在beforeCreate生命周期执行的时候,data和methods中的数据都还没有初始化。不能在这个阶段使用data中的数据和methods中的方法
-
create: data 和 methods都已经被初始化好了,如果要调用 methods 中的方法,或者操作 data 中的数据,最早可以在这个阶段中操作
-
beforeMount:执行到这个钩子的时候,在内存中已经编译好了模板了,但是还没有挂载到页面中,此时,页面还是旧的
-
mounted:Vue实例已经初始化完成了。此时组件进入到了运行阶段。如果我们想要通过插件操作页面上的DOM节点,最早可以在和这个阶段中进行
-
beforeUpdate:当执行这个钩子时,页面中的显示的数据还是旧的,data中的数据是更新后的, 页面还没有和最新的数据保持同步
-
updated:页面显示的数据和data中的数据已经保持同步了,都是最新的
-
beforeDestory:Vue实例从运行阶段进入到了销毁阶段,这个时候上所有的 data 和 methods , 指令, 过滤器 ……都是处于可用状态。还没有真正被销毁
-
destroyed:这个时候上所有的 data 和 methods , 指令, 过滤器 ……都是处于不可用状态。组件已经被销毁了。
vue获取数据在哪个周期函数?
一般 created/beforeMount/mounted皆可, 比如如果你要操作 DOM , 那肯定mounted时候才能操作
Fragment碎片化,支持对个根节点
Composition 组合式API
- Vue2是选择式API(Options API),一个逻辑会散乱在不同的位置(data、peops、computed、watch、生命周期钩子函数),导致代码可读性变差,修改某个逻辑,需要在文件上跳来跳去
- Vue3组合式API(Composition API)可以将同一逻辑的内容写到一起,增强了代码的可读性,内聚性
异步组件
Vue3提供Suspense组件,允许程序等待加载完成前呈现设置的内容,包括两个命名插槽:default和fallback,fallback作为插槽加载状态
Teleport
Vue3.0 新增了一个Teleport组件,开发者可以使用它将其所在组件模板的部分内容移动到特定的DOM位置,譬如body或者其他任意位置。
v-for和v-if哪个优先级高?
- 在vue2中,v-for的优先级是高于v-if
- vue3中相反,v-if的优先级高于v-for
谈谈你对Vuex的理解
Vuex是实现组件全局状态管理的一种机制,可以方便的实现组件之间数据的共享。
其中共有五大属性分别是,actions,state,getters,mutations,modules
state: 相当于是存储库,里面会对数据进行缓存
getters: 其实就是相当于vue里面的计算属性,可以通过getters访问store中的内存数据,通过this.$store.getters访问
actions: 是进行异步操作的,通过this.$store.dispatch访问
mutations: 是进行同步操作的,通过this.$store.commit访问
modules: 就是进行模块化,这样会使代码更加整洁
在项目中通常通过axios返回的后端数据使this.$store.commit 往state里面存值,之后通过
this.$store.getters 取值
弊端:因为vuex相当于是本地缓存,因此当页面强制F5刷新的时候会有数据丢失的现象,因此一般配合localStorage做成持久化存储或者使用sessionStorage来进行浏览器存储解决,但是localStorage存储过多数据会导致页面变卡,因为localStorage的本质是对字符串的读取,具体使用方案还是需要根据具体项目情况具体分析。
谈谈你对Vue依赖收集的理解
通过遍历所有data中的属性,使用Object.defineProperty为其添加getter和setter,在getter中每个属性会new Dep来被记录为一个依赖,每一个依赖都有若干个wachter进行监视,当data中数据改变时,通知给相应的dep,dep再通知给wachter,wachter就从对应的data中拿到值后渲染到页面。
谈谈你对虚拟DOM的理解
由于在浏览器中操作 DOM 是很昂贵的,如果直接使用真实DOM的话,会对性能造成比较大的浪费,因此引入了虚拟DOM这个概念,使用虚拟DOM的话,可以对比虚拟DOM和真实DOM的差异,从而进行局部渲染来达到优化性能的目的。
- 优点:无需手动操作DOM
- 缺点:页面初始化 DOM 的时候,由于多了一层虚拟 DOM 的计算,所以初始显示会慢一些
diff算法
- sameVnode方法判断是否为同一类型节点,不是就直接替换,如果是接着执行
- 进行patchVnode判断是否为同一节点,相同返回,不同按情况分支
- 如果新节点和旧节点都有文本节点,那么新文本替换旧文本
- 如果旧节点没子节点,则新增子节点
- 如果新节点没有子节点,旧节点有,那么删除子节点
- 如果两者都有子节点,执行updataChildren
- updateChildren方法用于新旧虚拟节点的子节点对比,主要采用首尾指针法进行对比,每次比较后start和end点向中间靠拢,当旧节点中有一个start跑到end点右侧时终止比较,以上均不满足则采用key做映射。
Vue2和Vue3的原理和区别
- 2.0通过Object.defineProperty来劫持各个属性的setter和getter,在数据变动的时候发布消息给消息订阅者,触发相应的监听回调
- 3.0实现基于ES6的Proxy
差异
- 2.0基于Object.defineProerty,不及备监听数组的能力,需要重新定义数组原型来达到响应式
- Object.defineProperty无法监听到数组的添加和删除(需使用Vue.set,Vue。delete),深度监听需要一次性递归,对性能影响大。
- 3.0基于Proxy和Reflect,可以监听数组,可以监听对象属性的添加和删除
- 不需要一次遍历data属性,可以显著提高性能。
为什么在vue3中采用了Proxy而抛弃了Object.defineProperty?
- Object.defineProperty只能劫持对象的属性,因此我们需要对每个对象的每个属性进行遍历。在Vue2里是通过递归+遍历data对象来实现对数据的监控的。
- Proxy可以劫持整个对象,并返回一个新的对象,Proxy还可以代理数组,还可以代理动态增加的属性。
Vue中的$nextTick原理?
- 把回调函数放入callbacks等待执行
- 将执行函数放到微任务或者宏任务中
- 事件循环到了微任务或者宏任务,执行函数依次执行callbacks中的回调
template渲染过程
- 把模板编译为render函数
- 实例进行挂载, 根据根节点render函数的调用,递归的生成虚拟dom
- 通过diff算法对比虚拟dom,渲染到真实dom(类同react的虚拟DOM渲染过程)
- 组件内部data发生变化,组件和子组件引用data作为props重新调用render函数,生成虚拟dom, 返回到步骤3
Pinia和Vuex的区别
接口更简单,代码更简洁
- 舍弃了mutation,减少了很多不必要的代码
- 可以直接对数据进行读写,直接调用action,不再需要commit,dispatch
更好的TypeScript支持
- VueX中缺少很多类型支持,需要开发者自行模块类型声明
- pinia中尽可能的利用了类型推断
你是如何结局跨域的
- 发生原因是因为前台和后台得到服务器网址不一致产生的
- 在根目录下的Vue.config.js文件中配置
- 主要就是配置target,changeOrigin和pathRewrite这三个参数
-
- target参数指向后台的真实接口
-
- changeOrigin参数配置为true允许跨域
-
- pathRewrite参数会重写路径
params和query的区别
用法:query要用path来引入,params要用name来引入,接收参数都是类似的,分别是 this.route.params.name 。url地址显示:query更加类似于我们ajax中get传参,params则类似于post,说的再简单一点,query在浏览器地址栏中显示参数,params不显示
父子组件通信
- 父组件向子组件传值使用props属性
- 子组件向父组件传值使用自定义方法通过$emit触发
- 父组件访问子组件refs(获取指定的)
- 子组件访问父组件root(根组件)
兄弟组件通信
- 事件总线 Vue.prototype.$bus = new Vue()
- Vuex
单页面应用的优缺点
优点:
- 良好的交互体验。
- 良好的前后端工作分离模式。
- 减轻服务器压力。
缺点:
- SEO难度较高。
- 前进、后退管理。
- 初次加载耗时多。
$route和$router的区别?
$router为VueRouter实例,$route为当前路由
语义化
- 代码结构清晰,易于阅读
- 利于开发和维护
- 有利于搜索引擎优化(SEO)
SEO
- Title:掘金 - 代码不止,掘金不停
- Keywords:掘金,稀土,Vue.js,前端面试题
- description: 描述内容
回流和重绘
- 重绘:元素外观改变,改变布局
- 重排/回流:重新计算元素,重新生成布局
避免回流或重构
定位\集中改变样式,不要一条一条地修改 DOM 的样式
Flex 布局
容器的属性:
- flex-direction:决定主轴的方向 flex-direction: row|row-reverse|column|column-reverse;
- flex-wrap:决定换行规则 flex-wrap: nowrap | wrap | wrap-reverse;
- flex-flow:是flex-direction属性和flex-wrap属性的简写形式,默认值为row nowrap。
- justify-content:对其方式,水平主轴对齐方式(X轴)
- align-items:对齐方式,竖直轴线方向(横向)
- align-content(Y轴纵向)
项目的属性(元素的属性):
- order 属性:定义项目的排列顺序,顺序越小,排列越靠前,默认为 0
- flex-grow 属性:定义项目的放大比例,即使存在空间,也不会放大
- flex-shrink 属性:定义了项目的缩小比例,当空间不足的情况下会等比例的缩小
- flex-basis 属性:定义了在分配多余的空间,项目占据的空间。
- flex:是 flex-grow 和 flex-shrink、flex-basis 的简写,默认值为 0 1 auto。
- align-self:允许单个项目与其他项目不一样的对齐方式,可以覆盖
es6新增语法
let 和 const、解构赋值、展开运算符、模板字⾯量、箭头函数、Promises、Generators、Iterator、 for ... in、for ... of、Map 和 Set、Proxy、Class、es module
JS中的数据类型
- 基本类型(值类型):Number、String、Boolean、Symbol,null,undefined 栈内存储
- 引用类型(复杂数据类型):Object、Function、Array
作用域和作用域链
简单来说作用域就是变量与函数的可访问范围
- 全局作用域:代码在程序的任何地方都能被访问,window
- 函数作用域:在固定的代码片段才能被访问,function内
- 新增块级作用域,大括号内{}
一般情况下查找变量会在当前作用域先找。但是如果在当前作用域中没有查到,就会向上级作用域去查 直到查到全局作用域,这么一个查找过程形成的链条就叫做作用域链。
原型 && 原型链
- 构造函数 通过new得到实例对象,内部prototype指向原型对象
- 实例对象 __proto__指向原型对象
- 原型对象 constructor指向构造函数
在对象上查找一个属性或者方法时,如果找不到就会去原型对象找,再找不到再去原型对象的原型对象, 最后找到object的原型对象为null时就表示没有
JavaScript对象是通过引用来传递的。当我们修改原型时,与之相关的对象也会改变
EventLoop
js: 先会执行栈中的内容 - 栈中的内容执行后执行微任务 - 微任务清空后再取出一个宏任务压入执行栈执行 - 再去执行微任务 - 然后在取宏任务清微任务这样不停的循环。 复制代码
宏任务:ajax、定时器、一些浏览器api、script... 微任务:promise.then、mutationObersve
双向数据绑定
Vue 使用 Object.defineProperty 遍历和递归对data中的所有属性做数据劫持,把这些 属性 全部转为 getter/setter,当获取的时候会触发getter,设置的时候会触发setter
发布者-订阅者模式
- 监听(Observer): 遍历和递归对data中的所有属性做数据劫持,当获取的时候会触发get,设置的时候会触发set
- 模版编译(Compile): 解析模板指令,将模板中变量替换成数据,然后渲染初始化的页面视图。并且给所有的 变量添加监听数据的 订阅者,一旦数据有变动,收到通知,更新试图
- 依赖收集(Dep): 收集者,每个变量会有一个收集者,收集变量所对应的所有的订阅者,当数据发生变化的时候通知订阅者更新
- 订阅者(Watcher):数据监听 和 模版编译之间通信的桥梁,当收到通知更新时更新页面