1 Pinia和Vuex的区别
支持选项式api和组合式api写法
pinia没有mutations,只有:state、getters、actions( state是一个对象返回一个对象和组件的data是一样的语法)
pinia分模块不需要modules(之前vuex分模块需要modules)
TypeScript支持很好
自动化代码拆分
pinia体积更小(性能更好)
2 vue2的组件通信
父子组件之间的通信
兄弟组件之间的通信
祖孙与后代组件之间的通信
非关系组件间之间的通信
通过 props 传递 //父传子
通过 $emit 触发自定义事件 //子传父
使用 ref //子传父
EventBus //兄弟组件传值
$parent 或$root //兄弟组件传值(共同祖辈)
attrs 与 listeners
Provide 与 Inject //祖先传递数据给子孙
Vuex //状态管理工具
3 js事件循环机制
调用栈(Call Stack):用于存储函数调用的信息,包括函数的参数、局部变量和返回地址等。
任务队列(Task Queue):用于存储待处理的任务,包括宏任务和微任务。 3.事件循环(Event Loop):不断检查任务队列中是否有任务可执行,如果有则按优先级取出任务放入调用栈执行。
事件循环的工作过程如下:
执行栈中现有的代码。
当遇到异步任务(如定时器、网络请求等)时,将其放入任务队列中,并继续执行栈中现有的代码。
当栈中现有的代码全部执行完毕后,事件循环检测到任务队列中存在任务,就按照以下规则处理:
如果有微任务,就先执行所有的微任务。
微任务执行完毕后再去检查宏任务,执行一个宏任务。
重复上述步骤,直到任务队列中没有任何任务。
当任务队列中的所有任务都处理完毕后,JavaScript线程就空闲出来了,可以执行其他任务或等待新的任务。
事件循环是一个循环的过程,会不断地检查任务队列和执行栈,直到所有任务都处理完毕。同时,js的执行是单线程的,即所有任务都是在主线程中执行,无法利用多线程并发执行。
主线程读取JS代码,此时为同步环境,形成相应的堆和执行栈,当主线程遇到异步操作的时候,将异步操作交给对应的API进行处理,当异步操作处理完成,推入任务队列中,主线程执行完毕后,查询任务队列,取出一个任务,并推入主线程进行处理,重复步骤以上步骤,就称之为事件循环(任务队列中又分为宏任务和微任务)
4 vue中nextTick()和setTimeout()
在Vue中,
nextTick()
方法会先于setTimeout()
方法执行。
nextTick()
方法是同步执行的,它将回调函数放入一个任务队列中,然后在当前任务完成后立即执行回调函数。而setTimeout()
方法是异步执行的,它不会等待当前任务完成,而是在指定的时间后执行回调函数。在Vue中使用
nextTick()
可以确保所有的渲染任务都已经完成,而DOM元素已经被更新。这种机制在处理DOM操作和组件通信时非常有用,因为它可以保证回调函数在DOM更新之后执行。因此,如果在Vue组件中使用
nextTick()
方法,回调函数将在DOM更新完成后立即执行,而使用setTimeout()
方法的回调函数则可能在DOM更新之前执行。所以nextTick()
方法会先于setTimeout()
方法执行。
5 vue路由懒加载
{ path: '/user/:id', component: () => import('@/views/User.vue') }在上面的示例中,
User.vue
组件将会在访问/user/:id
路由时才被加载和创建实例。另外,还可以使用
async
关键字修饰import
语句来实现异步加载组件{ path: '/user/:id', component: async () => await import('@/views/User.vue') }使用异步加载时,需要在组件的路由配置中添加
meta
属性,用来在异步加载时传递元数据。示例代码如下:{ path: '/user/:id', component: async () => await import('@/views/User.vue'), meta: { title: 'User' } }在异步加载时,还可以传递参数给组件,示例代码如下:
{ path: '/user/:id', component: function (resolve) { import('@/views/User.vue').then((module) => { resolve(module.default) }) }, meta: { title: 'User' } }在上面的示例中,通过
import()
函数动态导入组件,并使用.then()
方法处理导入成功后的结果,然后使用resolve()
方法将组件对象传递给路由组件的懒加载函数。
6 async和await的区别
async
是一个修饰符,用于标记一个函数为异步函数。一个异步函数可以使用await
关键字暂停执行,等待异步操作的结果。而await
只能在异步函数中使用。异步函数返回的是一个
Promise
,当异步函数中包含await
时,会等待异步操作的结果,然后将结果返回给调用者。如果异步函数中发生了异常,会将异常抛给调用者
7 闭包
闭包是指一个函数能够访问并操作其外部函数作用域中的变量,即使在外部函数已经执行完毕并返回之后,这些变量仍然能够被内部函数访问。 具体来说,当一个函数内部定义了另一个函数,并将该内部函数返回或赋值给一个变量时,就形成了一个闭包。内部函数可以访问外部函数的变量和参数,以及它自身的作用域中的变量,但是外部函数无法访问内部函数的变量和参数。 闭包的一个常见用途是创建私有变量和私有函数。由于内部函数可以访问外部函数的变量,但外部函数无法访问内部函数的变量,因此可以将变量定义在外部函数中,并通过内部函数来操作这些变量,从而实现对变量的封装和保护。
8 js中深拷贝和浅拷贝
浅拷贝是指创建一个新对象,将原对象的可枚举属性复制到新对象中,新对象和原对象共享同一类型的直接子属性。如果原对象的属性值是基本类型,则直接复制该属性值;如果原对象的属性值是指针类型(例如对象、数组等),则新对象和原对象的对应属性值仍指向同一块内存地址。
深拷贝是指创建一个新对象,将原对象的可枚举属性复制到新对象中,并且如果原对象的属性值是指针类型,则递归地对属性值进行深拷贝,使新对象和原对象互不影响,各自拥有独立的直接子属性。
浅拷贝的实现方式:
使用Object.assign()方法:
Object.assign(target, source1, source2, ...);
使用扩展运算符(...):
const copy = { ...source };
使用数组的slice方法:
const copy = arr.slice();深拷贝的实现方式:
使用递归:
function deepCopy(obj) { if (obj === null || typeof obj !== 'object') { return obj; } let copy; if (Array.isArray(obj)) { copy = obj.map(item => deepCopy(item)); } else { copy = {}; Object.keys(obj).forEach(key => { copy[key] = deepCopy(obj[key]); }); } return copy; }
使用序列化和反序列化:
const copy = JSON.parse(JSON.stringify(obj));
使用第三方库,如lodash的_.cloneDeep方法:
const copy = _.cloneDeep(obj);