摸鱼小记
Dec 21, 2023
摸鱼小记
一、promise.all 和 promise.allSettled
同样是接受多个promise,返回一个新的 Promise 实例。
promise.all 只要一个promise失败就会直接返回
promise.allsettled无论成功失败
,都会把所有promise的结果返回
当需要获取所有异步操作的结果时,无论是成功还是失败,应该使用 Promise.allSettled()。如果只关心所有操作都成功的情况,可以使用 Promise.all()。
二、watch、watchEffect
1、watch的理解
-
注意事项
避免监听大对象或数组:监听大对象或数组可能导致性能问题。
避免频繁的数据更新:不要在
watch
中进行过多的数据更新操作,以免导致无限循环。注意内存泄漏:当组件卸载时,确保清除
watch
监听器。 -
副作用
- 性能开销:频繁的数据变化可能导致
watch
回调函数被频繁执行,从而影响性能。 - 代码复杂性增加:如果
watch
的回调函数逻辑复杂,可能会导致代码难以维护。 - 不易调试:
watch
中的异步操作和副作用可能增加调试难度。 - 状态不一致:如果
watch
中有多个异步操作,可能会导致状态不一致的问题。
为了减少这些副作用,建议在watch
中进行简单的数据处理和状态更新,复杂的逻辑和副作用可以移到其他地方,如methods
或computed
中。此外,合理使用watchEffect
和watch
可以帮助你更有效地管理数据侦听和副作用。
2、watchEffect和watch的区别
watchEffect
和watch
都可以用于响应式数据的侦听,但它们在实现和用法上有所不同。
watch
和watchEffect
都是用于监听数据变化的重要函数。可以根据具体情况选择使用哪个函数。如果需要明确指定要监测的数据源,或需要访问新旧值的参数,则应该使用watch
;如果希望代码更简洁,并且不需要获得新旧值的参数,则应该使用watchEffect
。
三、计算属性
计算属性允许你基于响应式数据进行计算,并且当依赖数据变化时自动更新。
- 功能上:用于派生或转换数据,依赖于其他响应式数据。
- 使用上:在Vue组件中使用
computed
定义,然后可以像普通数据属性那样在模板中使用。 - 注意事项:
- 依赖数据应是响应式的。
- 不应在计算属性内部进行副作用操作。
- 计算属性是基于依赖缓存的,只在依赖变化时重新计算。
计算属性和方法的主要区别在于性能和调用方式:计算属性有缓存,自动更新;而方法每次调用都会执行。
四、封装组件的思路?
五、vuex定义了哪些属性和方法
Vuex定义了这几个主要的属性和方法:
属性:
- state:存储状态。
- getters:派生状态。
- mutations:同步改变state。
- actions:异步改变state。
- modules:模块化的store。
方法:
- commit(type, payload?):提交mutation。
- dispatch(type, payload?):分发action。
六、js数组遍历方法
1. forEach
forEach
方法用于遍历数组的每个元素,并对每个元素执行提供的函数。
javascriptconst arr = [1, 2, 3]; arr.forEach((item) => { console.log(item); });
2. map
map
方法会创建一个新数组,其结果是调用提供函数对原数组的每个元素进行处理后的值。
javascriptconst arr = [1, 2, 3]; const newArr = arr.map((item) => item * 2); console.log(newArr); // [2, 4, 6]
3. filter
filter
方法用于创建一个新数组,包含所有通过测试的元素。
javascriptconst arr = [1, 2, 3, 4, 5]; const filteredArr = arr.filter((item) => item > 2); console.log(filteredArr); // [3, 4, 5]
4. reduce
reduce
方法对数组的每个元素执行提供的函数,以生成单个值。
javascriptconst arr = [1, 2, 3]; const sum = arr.reduce((acc, item) => acc + item, 0); console.log(sum); // 6
5. some
some
方法用于检测数组中是否有至少一个元素通过了函数的测试。
javascriptconst arr = [1, 2, 3]; const hasEven = arr.some((item) => item % 2 === 0); console.log(hasEven); // true
6. every
every
方法用于检测数组中是否所有元素都通过了函数的测试。
javascriptconst arr = [1, 2, 3]; const allEven = arr.every((item) => item % 2 === 0); console.log(allEven); // false
七、前端存储数据有哪些方法
localStorage
、sessionStorage
、Cookie
- 状态管理库:
Pinia
、Vuex
八、为什么用虚拟dom
1、框架设计:vue、react框架设计 ,最小颗粒度只能到组件,组件全量更新问题
2、解藕运行环境:同框架代码跨端使用(移动端没有真实dom)
九、vue自定义指令
全局注册、局部注册
Vue2:
js// 全局注册自定义指令 Vue.directive('my-directive', { // 指令的定义 bind(el, binding, vnode) { // 绑定时执行的逻辑 }, // 可选,当绑定元素的父组件更新时调用 // 指令的所在组件已经更新,但是还没有渲染新的节点时,就会被调用 update(el, binding, vnode, oldVnode) { // 更新时执行的逻辑 }, // 可选,指令与元素解绑时调用 unbind(el, binding, vnode) { // 解绑时执行的逻辑 } }) // 局部注册 export default { directives: { 'my-directive': { ...同上 } } };
Vue3 (生命周期不同)
js// 注册自定义指令 export const myDirective = directive('my-directive', { // 指令的定义 mounted(el, binding, vnode, prevVNode) { // 绑定时执行的逻辑 }, // 可选,当绑定元素更新时调用 updated(el, binding, vnode, prevVNode) { // 更新时执行的逻辑 }, // 可选,指令与元素解绑时调用 unmounted(el, binding, vnode, prevVNode) { // 解绑时执行的逻辑 } }); // 全局注册自定义指令 app.directive('my-directive', myDirective); // 局部自定义指令(v开头) const vFocus = { //必须以 vNameOfDirective 的形式来命名本地自定义指令,以使得它们可以直接在模板中使用。 beforeMount: (el) => { // beforeUpdate: (el) => { //需要实时更新的时候用 // 在元素上做些操作 console.log(el); console.log(el.value); nextTick(()=>{ el.focus(); //获取焦点 }) } }
example:
typescriptimport type { DirectiveBinding } from "vue" const vHighlightBrackets = { mounted(el: HTMLElement, binding: DirectiveBinding) { hightLightContent(el, binding) }, updated(el: HTMLElement, binding: DirectiveBinding) { hightLightContent(el, binding) } } const hightLightContent = (el: HTMLElement, binding: DirectiveBinding) => { const regex = /【[^】]+】/g // 匹配所有【内容】 // 替换带有括号的内容为带有 span 标签的内容 el.innerHTML = binding.value.replace(regex, (match: any) => { return `<span class="text-tip-red">${match}</span>` }) } export default vHighlightBrackets
十、Vue reactive解构后失去响应性
原因分析
reactive内部的实现是创建一个代理对象Proxy,以及进行了一系列处理。 reactive失去效应是不在于Vue而在于Proxy对象本身。
解构相当于将该变量重新赋给(基础数据传值,引用类型传地址)了一个新变量,所以解构之后是一个基础数据则会响应式丢失
比如解构之后是基础类型的数据,那么解构相当于copy了一个值。访问时直接访问的这个copy值,跳过了代理,所以不会触发get
和set
。
当解构出来的是一个引用对象类型时,它是响应式的,当是基本数据类型时,响应式会丢失。
如何结构reactive?
使用toRefs()
十一、ref为什么需要.value
ref
接受的值是单值时,可以是一个数字,也可以是布尔值,字符串,那如何知道它被get
了,有何时被set
了?Proxy
的拦截是针对于对象,这种情况下就行不通了,实现的方案就是通过对象包裹,使用class
类来实现,在类中可以定义value
值,可以实现get
set
方法,也就可以知道了何时触发get
,set
了,也就是可以进行依赖收集和触发依赖。
十二、vue3为什么删除过滤器,用什么代替
Vue 3中删除了过滤器主要是因为它们的功能可以通过计算属性或者方法来轻松实现,从而简化了Vue的代码和维护。
Vue 2中的过滤器是一种全局功能,可以在模板中对数据进行格式化或者修改。但是,过滤器存在一些问题。
首先,过滤器会在每个组件实例的渲染过程中被调用,无论它们是否被实际使用。这会导致无用的计算,从而降低性能。
其次,过滤器是全局的,过多的全局过滤器会导致命名冲突和代码混乱。
最后,过滤器的语法和使用方式与常规JavaScript不同, 这会给Vue的开发和维护造成困扰。
为了解决上述问题,Vue 3提供了更好的替代方案:
- 计算属性:Vue 3鼓励将数据处理逻辑放在计算属性中。计算属性是根据响应式数据进行计算,并在数据变化时自动更新。这样可以避免无用的计算,提高性能。计算属性的使用方式更加直观和符合JavaScript的语法。
- 方法:Vue 3还推荐使用方法来进行数据处理。方法可以通过调用函数来实现对数据的修改或者格式化,并且可以在模板中直接调用。和计算属性一样,方法也是根据响应式数据进行更新,确保数据的一致性。
通过使用计算属性和方法,Vue 3提供了一个更加清晰、直观和灵活的方式来处理数据。这种方式比过滤器更符合JavaScript的习惯,并且提供了更好的性能和可维护性。因此,Vue 3决定删除过滤器功能,并鼓励开发者使用计算属性和方法来替代
十三、vue2、vue3中的v-if和v-for的优先级问题
- 在vue2中,v-for的优先级高于v-if. 每次循环迭代都会重新计算条件
- 在vue3中,v-if的优先级高于v-for. 在外面新增template加v-for可以解决
- 两种混在一起写法均不被官方推荐
十四、vuex与pinia
- Pinia使用类似Vue组件的API来创建和使用store,而Vuex使用一个全局对象来访问store
- 在Pinia中,状态是响应式的,这意味着当状态发生变化时,组件会自动更新。在Vuex中,我们需要手动触发更新。
- Pinia的store是模块化的,这意味着每个store可以包含自己的状态、操作和插件。在Vuex中,store是全局的,这意味着所有的状态和操作都在同一个store中。
十五、vue2响应式缺陷?
-
无法响应新增属性:在Vue2中,只有在实例被创建时就存在的属性才会被观察。这意味着如果你在已经创建的实例上动态添加新的属性,Vue无法检测到这些属性的变化。为了解决这个问题,你需要使用Vue.set或Vue.$set方法来添加响应式属性。
-
无法响应数组索引和length的变化:Vue2无法检测到通过索引设置数组元素的变化,以及直接修改数组的length属性。为了解决这个问题,你需要使用特定的数组方法,如push,pop,splice等,或者通过使用Vue.set方法来修改数组。
-
需要使用$watch来监听嵌套属性:Vue2中,如果你需要监听一个嵌套属性的变化,你需要使用$watch方法,并指定要观察的属性路径。这样会导致代码变得冗长,并且需要手动管理观察者。
-
性能问题:Vue2使用了基于对象的依赖追踪系统,它在某些情况下可能导致性能下降。当数据发生变化时,Vue会遍历整个组件的依赖关系图来更新相关的组件。对于大型项目或包含大量数据和组件的应用程序,这可能会导致性能问题。
-
深度监听的开销:Vue2中提供了深度监听对象的选项,但这种深度监听是递归的,会遍历整个对象树来检查变化。对于深层次、嵌套复杂的对象,这种深度监听可能会导致性能问题。