Vue
vue揭秘
1、生命周期
vue2:
总共分为8个阶段:创建前/后,载入前/后,更新前/后,销毁前/后。
- 创建前/后: 在beforeCreate阶段,由于还未初始化,vue实例的挂载元素el和数据对象data都为undefined。在created阶段,vue实例的数据对象data有了,el还没有。
- 载入前/后:在beforeMount阶段,vue实例的$el和data都初始化了,但挂载之前还是为虚拟的dom节点,data.message还未替换。在mounted阶段,vue实例挂载完成,data.message成功渲染。
- 更新前/后:当data变化时,会触发beforeUpdate和updated方法。
- 销毁前/后:在执行destroy方法后,对data的改变不会再触发周期函数,说明此时vue实例已经解除了事件监听以及和dom的绑定,但是dom结构依然存在。
vue3: - beforeCreate -> 使用 setup()
- created -> 使用 setup()
- beforeMount -> onBeforeMount
- mounted -> onMounted
- beforeUpdate -> onBeforeUpdate
- updated -> onUpdated
- beforeDestroy -> onBeforeUnmount
- destroyed -> onUnmounted
- errorCaptured -> onErrorCaptured
2、MVVM
mvvm是一种设计思想。Model代表数据模型,可以在model中定义数据修改和操作的业务逻辑;view表示ui组件,负责将数据模型转换为ui展现出来,它做的是数据绑定的声明、 指令的声明、 事件绑定的声明。;而viewModel是一个同步view和model的对象。在mvvm框架中,view和model之间没有直接的关系,它们是通过viewModel来进行交互的。mvvm不需要手动操作dom,只需要关注业务逻辑就可以了。mvvm 的核心是数据劫持、数据代理、数据编译和"发布订阅模式"。
mvvm和mvc的区别在于:mvvm是数据驱动的,而MVC是dom驱动的。mvvm的优点在于不用操作大量的dom,不需要关注model和view之间的关系,而MVC需要在model发生改变时,需要手动的去更新view。大量操作dom使页面渲染性能降低,使加载速度变慢,影响用户体验。
详情
3、双向绑定原理
vue2是利用ES5的Object.defineProperty,
局限:只能监听对象属性的变化;不能利用索引直接设置一个数组项,也不能修改数组的长度
Object.defineProperty(object, "key", {
get() { return bValue; },
set(newValue) { bValue = newValue; },
enumerable : true,
configurable : true
});
Vue3.0中的响应式采用了ES6中的 Proxy 方法,可以监听对象某个属性值的变化,还可以监听对象属性的新增和删除,而且还可以监听数组。
var proxy = new Proxy({}, {
get: function(target, propKey) {
return 35;
}
set: function(obj, prop, value) {
obj[prop] = value;
return true;
}
});
4、虚拟dom
虚拟dom详解
虚拟dom本质是将目标所需的 UI 通过数据结构“虚拟”地表示出来,保存在内存中,然后将真实的 DOM 与之保持同步。在渲染之前,会使用新生成的虚拟节点和上一次生成的虚拟节点进行对比,只渲染不同的部分。
虚拟DOM在vue中主要提供与真实节点对应的虚拟节点vnode,然后需要将vnode和oldVnode进行比对,然后更新视图,对比两个虚拟节点的算法是patch算法。(在 vue 里面diff 算法就是通过 patch 函数来完成的,所有有的时候也叫 patch 算法。)
5、v-for为什么要用key
Vue 默认按照“就地更新”的策略来更新通过 v-for 渲染的元素列表。当数据项的顺序改变时,Vue 不会随之移动 DOM 元素的顺序,而是就地更新每个元素,确保它们在原本指定的索引位置上渲染。
key能提高diff效率其实是需要分情况的。
不用 key:
就地复用节点。在比较新旧两个节点是否是同一个节点的过程中会判断成新旧两个节点是同一个节点,因为 a.key 和 b.key 都是 undefined。所以不会重新创建节点和删除节点,只会在节点的属性层面上进行比较和更新。所以可能在某种程度上(创建和删除节点方面)会有渲染性能上的提升;
无法维持组件的状态。由于就地复用节点的关系,可能在维持组件状态方面会导致不可预知的错误,比如无法维持改组件的动画效果、开关等状态;
也有可能会带来性能下降。因为是直接就地复用节点,如果修改的组件,需要复用的很多节点,顺序又和原来的完全不同的话,那么创建和删除的节点数量就会比带 key 的时候增加很多,性能就会有所下降;
用 key:
维持组件的状态,保证组件的复用。因为有 key 唯一标识了组件,不会在每次比较新旧两个节点是否是同一个节点的时候直接判断为同一个节点,而是会继续在接下来的节点中找到 key 相同的节点去比较,能找到相同的 key 的话就复用节点,不能找到的话就增加或者删除节点。
查找性能上的提升。有 key 的时候,会生成 hash,这样在查找的时候就是 hash 查找了,基本上就是 O(1) 的复杂度。
节点复用带来的性能提升。因为有 key 唯一标识了组件,所以会尽可能多的对组件进行复用(尽管组件顺序不同),那么创建和删除节点数量就会变少,这方面的消耗就会下降,带来性能的提升。
总结:性能提升不能只考虑一方面,不是 diff 快了性能就快,不是增删节点少了性能就快,不考虑量级的去评价性能,都只是泛泛而谈。
key也不能是li元素的index,因为假设我们给数组前插入一个新元素,它的下标是0,那么和原来的第一个元素重复了,整个数组的key都发生了改变,这样就跟没有key的情况一样了。
6、nextTick
vue 采用的异步更新策略,当监听到数据发生变化的时候不会立即去更新DOM,而是开启一个任务队列,并缓存在同一事件循环中发生的所有数据变更;这种做法带来的好处就是可以将多次数据更新合并成一次,减少操作DOM的次数,如果不采用这种方法,假设数据改变100次就要去更新100次DOM,而频繁的DOM更新是很耗性能的。
nextTick 接收一个回调函数作为参数,并将这个回调函数延迟到DOM更新后才执行;
使用场景:想要操作 基于最新数据生成的DOM 时,就将这个操作放在 nextTick 的回调中;
实现原理:将传入的回调函数包装成异步任务,异步任务又分微任务和宏任务,为了尽快执行所以优先选择微任务;
nextTick实现
7、v-show、v-if的区别
v-if 是“真实的”按条件渲染,因为它确保了在切换时,条件区块内的事件监听器和子组件都会被销毁与重建。
v-if 也是惰性的:如果在初次渲染时条件值为 false,则不会做任何事。条件区块只有当条件首次变为 true 时才被渲染。
相比之下,v-show 简单许多,元素无论初始条件如何,始终会被渲染,只有 CSS display 属性会被切换。
总的来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要频繁切换,则使用 v-show 较好;如果在运行时绑定条件很少改变,则 v-if 会更合适。
实现方式:v-if会调用addIfCondition方法,生成vnode的时候会忽略对应节点,render的时候就不会渲染;
v-show会生成vnode,render的时候也会渲染成真实节点,只是在render过程中会在节点的属性中修改show属性值,也就是常说的display;
8、vue单页面应用如何首页优化
VUE首页加载过慢,其原因是因为它是一个单页应用,需要将所有需要的资源都下载到浏览器端并解析。
- cdn加速
- 路由懒加载
- 首页加 loading 或 骨架屏 (仅仅是优化体验)
- 首页使用SSR,其他页面前端渲染
- 优化js体积,使用分包等策略减少chunk-vendors.js的体积
- 优化第三方库,按需引入,减少不必要的引入
- 图片压缩,使用字体图标等
- 开启gizp压缩;使网站的css、js 、xml、html 等静态资源在传输时进行压缩,经过Gzip压缩后资源可以变为原来的30%甚至更小,尽管这样会消耗一定的cpu资源,但是会节约大量的出口带宽来提高访问速度
9、Vue 的父组件和子组件生命周期钩子执行顺序是什么
加载渲染过程 父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted
子组件更新过程 父beforeUpdate->子beforeUpdate->子updated->父updated
父组件更新过程 父beforeUpdate->父updated
销毁过程 父beforeDestroy->子beforeDestroy->子destroyed->父destroyed
10、Vue 中的 computed 是如何实现的
实现原理
技术揭秘
这两篇文章说得挺清晰的,耐心多看几遍就懂啦
总结:computed本身是通过代理的方式代理到组件实例上的,所以读取计算属性的时候,执行的是一个内部的getter,而不是用户定义的方法。
computed内部实现了一个惰性的watcher,在实例化的时候不会去求值,其内部通过dirty属性标记计算属性是否需要重新求值。当computed依赖的任一状态(不一定是return中的)发生变化,都会通知这个惰性watcher,让它把dirty属性设置为true。所以,当再次读取这个计算属性的时候,就会重新去求值。
React
不同之处
vue和react的区别
1、vuex、redux
两者都是管理全局状态的工具库,总的来说都是让 View 通过某种方式触发 Store 的事件或方法,Store 的事件或方法对 State 进行修改或返回一个新的 State,State 改变之后,View 发生响应式改变。
Redux: view——>actions——>reducer——>state变化——>view变化(同步异步一样)
Vuex: view——>commit——>mutations——>state变化——>view变化(同步操作) view——>dispatch——>actions——>mutations——>state变化——>view变化(异步操作)
vuex:
对于简单应用来说,使用 Vuex 是繁琐冗余,可以使用用响应式 API 做简单状态管理,用 reactive() 来创建一个响应式对象,并在不同组件中导入它。详见
State
Vuex 使用单一状态树——是的,用一个对象就包含了全部的应用层级状态。至此它便作为一个“唯一数据源 (SSOT)”而存在。这也意味着,每个应用将仅仅包含一个 store 实例。单一状态树让我们能够直接地定位任一特定的状态片段,在调试的过程中也能轻易地取得整个当前应用状态的快照.Vuex通过 store 选项,把 state 注入到了整个应用中,这样子组件能通过 this.$store 访问到 state 了
Mutation
更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的事件类型 (type)和一个回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数。注意:mutation 必须是同步函数
Action
Action 类似于 mutation,不同在于:
Action 提交的是 mutation,而不是直接变更状态。
Action 可以包含任意异步操作
Action 通过 store.dispatch 方法触发