Vue3
Vue3用的什么状态管理工具:
Pinia 和 Vuex 的区别在哪里:
- 架构设计:Vuex是Vue.js官方提供的状态管理库,而Pinia是由Vue作者维护的另一个状态管理库。Vuex采用了集中式的架构,将所有的状态存储在一个单一的全局状态树中,通过mutations和actions来修改和处理状态。而Pinia采用了去中心化的架构,将状态分布在多个模块中,每个模块拥有自己的状态、mutations和actions。
- 体积和复杂性:由于Vuex是Vue.js的官方状态管理库,它在Vue.js项目中广泛使用,并拥有庞大的生态系统。相比之下,Pinia是一个相对较新的库,较小且更简单。这使得Pinia在一些小型或简单的项目中可能更容易上手,而Vuex则更适合大型和复杂的项目。
- TypeScript 支持:在类型安全性方面,Vuex从Vue 2.x版本开始引入了对TypeScript的支持,但需要使用额外的插件来实现类型检查。而Pinia在设计之初就对TypeScript提供了原生的支持,提供了更好的类型推导和类型检查的支持。
- 代码风格和语法:由于架构的不同,Vuex和Pinia在代码风格和语法上也存在一些差异。Vuex使用了更传统的mutations和actions的方式来修改和处理状态,而Pinia更加倾向于直接操作状态。
需要注意的是,Vuex在Vue.js生态系统中非常成熟且广泛使用,有大量的插件和工具支持。如果你正在使用Vue.js,并且项目规模较大或需要与其他插件集成,那么Vuex可能是更好的选择。而如果你更喜欢较简单的状态管理方案,并且对TypeScript有较高的要求,那么Pinia可能是一个更合适的选择。
Vue3新建项目的工具:
npm init vite@latest
Vue3中响应式的方法:
ref 和 reactive
Vue3中 ref 和 reactive 的区别:
- ref 常用于创建响应式的基本类型数据,也可以创建包装对象或者数组,对于基本类型数据,ref返回一个带有 .value 的属性,需要通过 .value 来访问或者修改值,对于ref,创建对象或者数组,ref会包装这个对象或数组,变成响应式;也就是说,ref创建对象或者数组的方法,实际上是自动转化成了 reactive 的代理对象
import { ref } from 'vue';
// 创建响应式的基本类型数据
const count = ref(0);
console.log(count.value); // 输出 0
// 创建响应式的对象
const obj = ref({ name: 'John', age: 30 });
console.log(obj.value); // 输出 { name: 'John', age: 30 }
// 修改响应式数据
count.value++;
obj.value.age = 31;
- reactive 常用于创建复杂数据类型的数据,创建的数据直接调用,不用添加 .value 获取。
import { reactive } from 'vue';
// 创建响应式的复杂数据结构
const state = reactive({
name: 'John',
age: 30,
friends: ['Jane', 'Doe']
});
// 访问响应式数据
console.log(state.name); // 输出 'John'
console.log(state.friends); // 输出 ['Jane', 'Doe']
// 修改响应式数据
state.age = 31;
state.friends.push('AnotherFriend');
区别总结:
-
定于数据角度对比:ref 用来定义:基本类型数据
reactive 用来定义:对象、或数组类型的数据
备注:ref也可以用来定义对象或数组类型数据,它内部会自动通过 reactive 转为代理对象 -
原理角度对比:ref 通过 Object.defineProperty() 的 get 与 set 来实现响应式的(数据劫持)
reactive 通过使用 Proxy 来实现响应式(数据劫持),并通过Reflect 操作源对象内部的数据。 -
使用角度对比:ref 定义的数据:操作数据需要 .value,读取数据时模版中直接读取不需要 .valuereactive 定义的数据:操作数据与读取数据,均不需要 .value
Vue3中取消响应式的方法:
利用 Object.freeze() 方法来取消响应式
import {reactive} from 'vue';
const data = reactive({ name: 'John', age: 25 }); //创建一个响应式的对象(data)
const nonReactiveData = Object.freeze(data);
console.log(nonReactiveData); // 输出结果不会再随着name或age属性变化而改变
Vue3 中 setup 语法糖相当于 vue2 的哪几个生命周期:
其实 setup 是在在 vue2的 beforecreate 和 created 之前,相当于 beforecreate 和 created
Vue3有什么特有组件:
- Fragment:在Vue2中,组件中必须有一个根组件,Vue3组件可以没有根标签,内部会将多个标签包裹在一个 fragment 虚拟元素中,这样可以减少标签层级,减少内存占用。
<template>
<div>
<!-- Fragment 的使用,直接返回多个元素 -->
<header>
<h1>这是标题</h1>
</header>
<main>
<p>这是主要内容</p>
</main>
<footer>
<p>这是页脚</p>
</footer>
</div>
</template>
<script>
export default {
name: 'MyComponent',
// ... 组件的其他选项
};
</script>
- Teleport: 可将部分DOM移动到Vue app之外的位置。主要作用是移除JavaScript上下文中未引用的代码。因此,在Vue3中全局API现在只能作为ES模块构建的命名导出进行访问。
// Vue 应用中定义一个 <Teleport> 目标。这通常是一个具有唯一 ID 的 DOM 元素
<body>
<!-- ... 其他内容 ... -->
<div id="modal-target"></div>
<!-- ... 其他内容 ... -->
</body>
// 可以使用 <Teleport> 来将内容“传送”到这个目标位置
<template>
<div>
<!-- 触发模态框的按钮 -->
<button @click="showModal = true">显示模态框</button>
<!-- 使用 Teleport 将模态框内容传送到指定位置 -->
<Teleport to="#modal-target">
<div v-if="showModal" class="modal">
<div class="modal-content">
<span>这是一个模态框内容</span>
<button @click="showModal = false">关闭</button>
</div>
</div>
</Teleport>
</div>
</template>
- Suspense: 在等待异步组件时渲染一些额外的内容,提高用户体验。
<template>
<div>
<Suspense>
<template #fallback>
<div>加载中...</div>
</template>
<AsyncComponent />
</Suspense>
</div>
</template>
<script>
import AsyncComponent from './AsyncComponent.vue';
export default {
components: {
AsyncComponent
}
}
</script>
在上面的示例中,当AsyncComponent异步组件加载完成前,会显示"加载中…"的占位符内容。当AsyncComponent加载完成后,会替换掉占位符内容并显示异步组件本身。
Vue3 数组重写的方法
在 Vue 3 中,如果想对数组进行重写方法(即重写数组的内置方法),通常需要这样做是因为你想在修改数组时触发视图更新。Vue 3 的响应式系统对于原始数组的更改并不总是反应性的,尤其是当你使用某些内置数组方法(如 push、pop、shift、unshift、splice、sort 和 reverse)时。
为了解决这个问题,Vue 3 提供了一种方法来创建响应式数组,这样当你使用这些内置方法时,视图会自动更新。你可以使用 reactive 或 ref 函数来创建响应式数组。
以下是如何使用 reactive 和 ref 创建响应式数组,并重写其方法的示例:
Reactive
import { reactive } from 'vue';
const state = reactive({
list: []
});
// 重写 push 方法
state.list.push = function (...args) {
const result = Array.prototype.push.call(this, ...args);
// 触发依赖更新,如果需要的话
// 注意:通常不需要手动触发更新,因为 Vue 的响应式系统会自动处理
return result;
};
// 现在你可以使用重写的 push 方法,并且视图会自动更新
state.list.push('new item');
Ref
import { ref } from 'vue';
const list = ref([]);
// 重写 push 方法
list.value.push = function (...args) {
const result = Array.prototype.push.call(this, ...args);
// 触发依赖更新,如果需要的话
// 注意:通常不需要手动触发更新,因为 Vue 的响应式系统会自动处理
return result;
};
// 现在你可以使用重写的 push 方法,并且视图会自动更新
list.value.push('new item');
请注意,通常你不需要手动重写这些方法以触发更新,因为 Vue 3 的响应式系统已经为这些内置数组方法做了特殊处理。只有在某些特殊情况下,例如你需要拦截数组方法的调用或添加自定义逻辑时,你才需要重写这些方法。
另外,直接修改原型(如上面的示例所示)通常是不推荐的,因为它可能会导致与其他代码的冲突。更好的做法是使用计算属性或方法来封装数组操作,并返回一个新的响应式数组。
例如,使用计算属性来封装 push 操作:
import { reactive, computed } from 'vue';
const state = reactive({
list: []
});
const pushItem = (item) => {
state.list.push(item);
// 可以在这里添加其他逻辑
};
const updatedList = computed(() => {
// 返回一个新的数组副本,如果需要的话
return [...state.list];
});
// 使用 pushItem 函数来添加新项,并通过 updatedList 来访问更新后的数组
pushItem('new item');
在这个例子中,pushItem 函数封装了对 state.list 的 push 调用,并且你可以在这个函数中添加任何你需要的额外逻辑。updatedList 是一个计算属性,它返回 state.list 的一个新副本,确保视图总是反应最新的数组状态。
讲一下 hooks:
Vue3 中的 Hooks 是一种特殊的函数,以 “use” 作为开头,用于提供组件复用、状态管理等开发能力的方法1。
Hooks 的出现主要是为了解决 Vue2 中 Mixins 的问题,如 Mixins 逻辑互相嵌套、数据来源不明、不能互相传递状态等1。
在 Vue3 中,Hooks 的使用通常是在项目的 src 目录中创建一个 hooks 文件夹,用来存放 Hook 文件。根据功能或方法需要,可以在 hooks 文件夹中新建一个以 “use” 开头的文件,并在其中编写相应的逻辑代码。然后在需要使用该 Hook 的组件中,通过 import 语句引入并使用该 Hook,以实现代码复用和状态管理等功能2。
了解 Vue3 的热更新吗?
在 Vue 3 中实现热更新(Hot Module Replacement,简称 HMR)主要依赖于 webpack 的热更新功能。热更新允许你在应用程序运行时替换、添加或删除模块,而无需进行完全刷新。这对于开发过程中的即时反馈和效率提升非常有帮助。
要在 Vue 3 项目中实现热更新,你需要确保 webpack 配置正确,并且使用 Vue Loader。以下是一些步骤来设置和实现热更新:
安装依赖:
确保你的项目中已经安装了 webpack 和 vue-loader。如果你使用 Vue CLI 创建的项目,这些应该已经包含在内了。
配置 webpack:
在你的 webpack 配置文件中(通常是 webpack.config.js),确保启用了热更新插件:
const webpack = require('webpack');
module.exports = {
// ... 其他配置
plugins: [
// 添加热更新插件
new webpack.HotModuleReplacementPlugin(),
],
// 确保开发模式下启用热更新
mode: 'development',
devServer: {
hot: true, // 启用 webpack-dev-server 的热更新
},
};
VueRouter 的底层实现原理:
Vue Router 是 Vue.js 的官方路由器,用于构建单页面应用 (SPA)。它的底层实现原理主要包括以下几个方面:
- 路由映射
Vue Router 使用路由映射来将 URL 路径映射到对应的组件。这通常在路由配置文件中定义,例如:
const routes = [
{ path: '/home', component: Home },
{ path: '/about', component: About }
];
-
路由模式
Vue Router 支持两种模式:hash 和 history。
Hash 模式:使用 URL 的 hash 部分 (例如 #about) 来模拟完整的 URL,这样服务器不会收到请求。Vue Router 默认使用此模式。
History 模式:使用 HTML5 的 History API 来实现完整的 URL,服务器需要配置以支持这种模式。 -
路由组件渲染
当 URL 发生变化时,Vue Router 会根据当前的 URL 路径找到对应的组件,并使用 Vue 的组件系统来渲染该组件。这通常是通过 组件来实现的。 -
导航守卫
Vue Router 提供了导航守卫功能,允许你在路由改变前后执行某些逻辑,例如身份验证、权限检查等。 -
动态路由匹配
你可以使用动态路由匹配来定义带有参数的路由,例如:
{ path: '/user/:id', component: User }
在这个例子中,:id 是一个动态部分,可以在 User 组件中通过 $route.params.id 来访问。
-
嵌套路由
Vue Router 支持嵌套路由,允许你定义一个路由的子路由,这在构建复杂的页面布局时非常有用。 -
编程式导航
除了通过点击 进行导航外,Vue Router 还提供了编程式导航的方法,如 router.push()、router.replace() 和 router.go()。 -
路由元信息
你可以在每个路由配置中定义元信息 (meta),并在导航守卫中使用这些信息,例如进行权限控制。
底层实现技术
在底层,Vue Router 使用 Vue 的响应式系统来监听 URL 的变化,并相应地更新视图。当 URL 变化时,Vue Router 会更新 $route 对象,这个对象是响应式的,所以任何依赖于它的代码都会自动更新。
此外,Vue Router 还使用了 Vue 的组件系统来渲染路由对应的组件,以及使用了 Vue 的生命周期钩子和自定义指令等功能。
总的来说,Vue Router 的底层实现原理主要依赖于 Vue 的核心特性,如响应式系统、组件系统、生命周期钩子等,以及 HTML5 的 History API 和 URL 解析等技术。
HTTP1.0 和 HTTP2.0区别:
连接 。HTTP1.0每次连接只发送一个请求,只支持非持久性连接,HTTP2.0支持多路复用,可做到同一个连接并发处理多个请求。
传输 。HTTP1.0对HTTP消息体的数据类型支持不够,只支持text/html格式的消息体;HTTP2.0通过使用HPACK算法对header的数据压缩,提高了传输的效率。
安全 。HTTP1.0不支持加密传输;HTTP2.0支持加密传输,提高了安全性。
JS
js 中 map 和 weakmap 区别:
在 JavaScript 中,Map 和 WeakMap 都是用于存储键值对的集合,但它们之间存在一些关键的区别:
垃圾回收的影响:
Map:存储在 Map 中的对象不会被垃圾回收机制自动回收,即使没有其他引用指向该对象。
WeakMap:WeakMap 的键必须是对象,当该键不再被其他地方引用时,WeakMap 中的这个键值对会自动被删除,释放内存。这意味着它不会阻止其键对象被垃圾回收。
键的类型:
Map:可以使用任何类型作为键(包括原始类型如字符串、数字等和对象类型)。
WeakMap:键只能是对象,不能是原始类型。
遍历:
Map 和 WeakMap 都有类似的方法用于遍历,如 forEach、keys、values 和 entries。
用途:
Map 通常用于存储需要长期保留的键值对,或者当你希望即使没有其他引用时也能保留对象。
WeakMap 非常适合于存储与对象生命周期相关的数据,或者当你希望对象被垃圾回收时自动删除相关数据。
一个常见的使用场景是,当你想在 DOM 元素上存储数据时,可能会使用 WeakMap,因为 DOM 元素是对象,并且当它们从 DOM 中移除时,你希望与它们关联的数据也被自动删除。
总之,选择使用 Map 还是 WeakMap 主要取决于你的具体需求和希望如何处理与键值对相关的对象的生命周期。
map 和 weakmap 用法:
Map和WeakMap在JavaScript中都是用于存储键值对的集合,但它们的用法和特性有所不同。
Map的用法:
创建一个Map对象:const map = new Map();
添加键值对:map.set(key, value);
获取键对应的值:map.get(key);
删除键值对:map.delete(key);
遍历Map:map.forEach((value, key) => { console.log(key, value); });
Map对象可以记住键的原始插入顺序,并且任何对象或原始值都可以作为键或值。
WeakMap的用法:
创建一个WeakMap对象:const weakMap = new WeakMap();
添加键值对:weakMap.set(obj, value);,其中obj必须是对象。
获取键对应的值:weakMap.get(obj);
删除键值对:weakMap.delete(obj);
WeakMap的键是弱引用的,必须是对象,而值可以是任意对象或原始值。当键对象不再被其他地方引用时,WeakMap会自动删除该键值对,释放内存。
需要注意的是,由于WeakMap的键是弱引用的,因此它不能用于遍历操作,也没有size属性。
在实际应用中,Map常用于需要长期保留的键值对,而WeakMap则适用于与对象生命周期相关的数据存储,例如将DOM元素与相关数据关联起来,当DOM元素被移除时,相关数据也会自动被删除。
说一下http缓存策略,有什么区别,分别解决了什么问题
HTTP缓存策略主要包括强制缓存、协商缓存、禁止缓存、条件缓存等策略。它们之间的区别以及解决的问题如下:
强制缓存。浏览器在访问资源之前会检查本地是否已经存在该资源的缓存副本,如果存在且未过期,则直接从缓存加载,不会向服务器发送任何请求。这可以减少网络请求,提高页面加载速度。
协商缓存。浏览器会向服务器发送请求,服务器会返回资源的元数据(例如Last-Modified或ETag),浏览器根据元数据判断资源是否发生了变化,如果没有变化,则可以直接从缓存加载。这可以避免传输未修改的资源,节省带宽。
禁止缓存。==禁止浏览器缓存页面资源,所有资源都需要重新向服务器请求。==这可以确保每次获取的都是最新的资源,适用于需要实时更新的内容。
条件缓存。根据特定条件来判断是否使用缓存,例如根据用户的设备、地理位置等因素来决定是否使用缓存。这可以提供更个性化的用户体验。
强制缓存、协商缓存、禁止缓存、条件缓存他们怎么设置
在HTTP中,缓存策略通常通过设置响应头来实现。以下是针对强制缓存、协商缓存、禁止缓存和条件缓存的HTTP响应头设置方法:
- 强制缓存
强制缓存可以通过设置以下两个响应头来实现:
Expires:指定一个日期/时间,浏览器会在该时间之前使用缓存的副本。
Cache-Control:更灵活,可以设置多种缓存指令。例如,Cache-Control: max-age=3600 表示资源可以被缓存1小时。
- 协商缓存
协商缓存通常通过设置以下响应头来实现:
Last-Modified:服务器返回资源最后修改的时间。
ETag:服务器返回资源的唯一标识符。
当浏览器需要验证资源是否更新时,它会发送一个请求,包含以下请求头之一:
If-Modified-Since:浏览器发送上次从服务器获取资源的时间。
If-None-Match:浏览器发送上次从服务器获取资源的ETag。
如果资源没有变化,服务器将返回一个304状态码,告诉浏览器可以使用缓存的副本。
- 禁止缓存
要禁止浏览器缓存资源,可以设置以下响应头:
Cache-Control:设置为no-cache或no-store。no-cache表示必须向服务器验证缓存的有效性,而no-store表示不得存储任何版本的资源。
Pragma:设置为no-cache(注意这不是标准的HTTP/1.1头,但在某些浏览器中仍然有效)。
- 条件缓存
条件缓存实际上是一种协商缓存的形式,它允许浏览器在满足特定条件时使用缓存的资源。这通常通过组合使用Last-Modified、ETag、If-Modified-Since和If-None-Match等头来实现。
- 示例
强制缓存(使用Cache-Control)
Cache-Control: max-age=3600
协商缓存(使用Last-Modified和ETag)
Last-Modified: Tue, 12 Jan 2021 12:00:00 GMT
ETag: "12345"
禁止缓存
Cache-Control: no-store, no-cache, must-revalidate, proxy-revalidate
Pragma: no-cache
Expires: 0
条件缓存(浏览器在请求时使用If-None-Match或If-Modified-Since)
浏览器在请求时会自动添加这些头,如果之前缓存了资源的话。服务器会根据这些头来决定是否返回304状态码。
请注意,这些头应该由服务器设置,而不是由客户端(如JavaScript)设置。客户端可以请求特定的缓存行为,但最终的决定权在服务器。
实现链式调用方法
在JavaScript中,链式调用通常是通过在每个函数的末尾返回this来实现的。这样,你可以在一个对象上连续调用多个方法,每个方法都会返回对象本身,以便进行下一次调用。这种设计模式也被称为流畅接口或流畅API。
function Person(name) {
this.name = name;
// 返回this以实现链式调用
return this;
}
Person.prototype.sayHello = function() {
console.log(`Hello, my name is ${this.name}`);
// 返回this以实现链式调用
return this;
};
Person.prototype.setAge = function(age) {
this.age = age;
// 返回this以实现链式调用
return this;
};
// 使用链式调用
let person = new Person('Alice')
.sayHello()
.setAge(25);