1.http 有哪些缓存?分别介绍一下这些缓存的优缺点。

HTTP 协议中的缓存主要分为四种类型:浏览器缓存、代理服务器缓存、网关缓存和 CDN 缓存。

浏览器缓存

浏览器缓存是指浏览器在本地缓存 HTTP 响应以便下次访问时可以快速返回,从而减少对服务器的请求次数和网络带宽的消耗。优点是可以提高页面加载速度,减轻服务器负载,缺点是如果网站更新频繁,会导致缓存数据不一致,需要手动清除缓存。

代理服务器缓存

代理服务器缓存是指在代理服务器上缓存 HTTP 响应,当下次请求相同的 URL 时,代理服务器可以直接返回缓存的响应,减少了请求的转发和网络传输时间。优点是可以加快响应速度,减少网络带宽消耗,缺点是缓存的数据可能会过期,需要定期更新缓存。

网关缓存

网关缓存是指在应用服务器前面部署的缓存服务器,可以缓存整个页面或动态生成的内容。优点是可以加快页面响应速度,减轻后端服务器负载,缺点是需要额外的硬件和软件支持,需要维护和管理。

CDN 缓存

CDN 缓存是指在全球各地部署的缓存服务器,可以缓存静态资源如图片、视频、JavaScript 和 CSS 等,通过 DNS 解析可以将用户请求转发到离用户最近的 CDN 服务器,从而减少网络传输时间和带宽消耗。优点是可以加快页面响应速度,减轻源站服务器负载,缺点是需要额外的费用支持,需要对 CDN 缓存进行管理和监控。

2.vue 的 hooks 有哪些?

Vue.js 是一个流行的 JavaScript 框架,提供了一些用于构建用户界面的常用工具和特性。在 Vue.js 3 中,使用了新的 Composition API,它提供了一组基于函数的 API,称为 Vue Hooks,用于组件中的逻辑复用。以下是 Vue Hooks 的一些常见用法:

reactive

该函数接受一个对象并返回一个响应式代理对象,可用于创建响应式数据。响应式数据意味着数据发生更改时,视图也将自动更新。

ref

该函数用于创建一个响应式数据引用,可以使用.value 属性访问值,该值可以是任何 JavaScript 类型。

computed

该函数用于创建计算属性,接受一个计算函数作为参数并返回一个响应式的计算结果。当依赖的响应式数据更改时,计算属性将自动重新计算。

watch

该函数用于监听响应式数据的更改,并在数据更改时执行指定的回调函数。

onMounted

该函数用于在组件挂载后执行指定的回调函数。

onUpdated

该函数用于在组件更新后执行指定的回调函数。

onUnmounted

该函数用于在组件卸载时执行指定的回调函数。

provide/inject

provide 和 inject 是用于父子组件之间共享数据的方法。provide 可以接受一个值,inject 可以接受一个提供者列表,组件可以通过 inject 访问到 provide 提供的值。

3.useContext

该函数用于获取上下文中提供的值,上下文是一个包含 provide 和 inject 的组件树。

4.useRoute/useRouter

useRoute 和 useRouter 是用于在 Vue Router 中访问路由信息的方法。useRoute 用于获取当前路由信息,而 useRouter 则用于获取路由实例和导航方法。

useStore

该函数用于在 Vuex 中访问 store 实例,可以用于获取状态和触发 actions。
Vue Hooks 的优点在于它们提供了更加灵活和可组合的方式来管理组件逻辑,使得组件逻辑复用更加容易。同时,它们也能更好地支持 TypeScript 等类型检查工具,帮助开发者更早地捕获代码中的问题。但是,在使用 Hooks 时需要注意,它们有可能会导致代码更加抽象和难以理解,也可能会导致更多的渲染开销,需要根据具体场景来进行选择。

5.v-for 为什么要写 key?

在 Vue 中使用 v-for 指令循环渲染 DOM 元素时,必须为每个被渲染的元素提供一个唯一的 key 属性。
key 属性的作用是帮助 Vue 识别每个被渲染的元素,以便在重新渲染列表时尽可能地复用已有的元素,而不是每次都创建新的元素,从而提高性能。
如果不提供 key 属性,Vue 将无法识别每个元素,可能会导致渲染出不正确的结果。而如果提供了相同的 key,可能会导致 Vue 无法正确地判断两个元素的差异,从而影响渲染结果和性能。
因此,在使用 v-for 渲染列表时,建议始终为每个元素提供一个唯一的 key 属性,可以使用任何唯一标识符,例如 ID、索引或其他唯一值。同时也需要注意,key 值应该是稳定的,不应该随着列表的重新排序或更新而发生变化。

6.线上的跨域问题

CORS

线上的跨域问题通常涉及跨域资源共享(CORS)策略。CORS 是浏览器的一种安全机制,用于限制不同域名之间的 HTTP 请求。
当浏览器发送跨域请求时,服务器需要在响应头中设置 Access-Control-Allow-Origin 字段,指定允许跨域请求的源。例如,以下响应头可以允许任何域名的请求:

1
Access-Control-Allow-Origin: *

除了允许跨域请求的源之外,还可以设置其他的 CORS 相关的响应头字段,如:

  • Access-Control-Allow-Methods: 允许跨域请求的 HTTP 方法;
  • Access-Control-Allow-Headers: 允许跨域请求的自定义请求头;
  • Access-Control-Max-Age: 指定 CORS 预检请求(OPTIONS 请求)的有效期限;

如果服务器未设置正确的 CORS 响应头,浏览器会拒绝跨域请求并报错。
此外,还可以使用 JSONP、WebSocket、代理等方式来解决跨域问题。但是,需要注意的是,这些方式也存在一定的限制和安全风险,需要根据具体情况进行选择和使用。

Nginx

Nginx 是一款高性能的 Web 服务器和反向代理服务器,可以通过配置来实现跨域请求的转发和代理。
以下是使用 Nginx 配置实现跨域请求的示例:

安装 Nginx

在 Linux 系统中使用包管理器进行安装,例如:

1
sudo apt-get install nginx

配置 Nginx

在 Nginx 的配置文件中,添加以下代码:

1
2
3
4
5
6
7
8
9
10
11
server {
listen 80;
server_name your_domain.com;

location / {
proxy_pass http://backend_server.com;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}

其中,your_domain.com 为前端域名,backend_server.com 为后端域名。通过配置 proxy_pass 字段,将前端请求转发至后端服务器,从而实现跨域请求。

重启 Nginx

在完成配置后,需要重启 Nginx 服务器使其生效:

1
sudo systemctl restart nginx

这样就可以使用 Nginx 来解决线上跨域问题了。当然,具体的配置需要根据实际情况进行调整。

7.用 mixin 混入你封装那些东西?具体用到了什么地方。

在 Vue.js 中,mixin 是一种可以跨组件复用代码的方式,用于封装和共享组件逻辑。下面是我在项目中使用 mixin 的一些封装和具体应用。

  1. 封装了一个 mixin,用于处理异步请求状态

在项目中,经常需要进行异步请求,并在请求结束后根据状态更新数据和 UI。为了减少重复的代码和提高复用性,我封装了一个 mixin,用于处理异步请求状态,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
export default {
data() {
return {
loading: false,
error: null
}
},
methods: {
async request(handler) {
this.loading = true
this.error = null

try {
await handler()
} catch (error) {
this.error = error
}

this.loading = false
}
}
}

该 mixin 包含一个 data 对象和一个方法。其中,data 对象包含了两个属性:loading 表示异步请求是否正在进行中,error 表示异步请求是否发生了错误。request 方法用于处理异步请求,接收一个异步处理函数作为参数,该函数在异步请求完成时进行调用。

  1. 在组件中使用异步请求状态 mixin

在具体的组件中,我可以使用 mixin 来共享异步请求状态的逻辑,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import requestMixin from '@/mixins/requestMixin'

export default {
mixins: [requestMixin],
data() {
return {
data: null
}
},
methods: {
async fetchData() {
await this.request(async () => {
this.data = await api.fetchData()
})
}
}
}

通过在组件中使用 mixins 属性,将 mixin 引入到组件中。在组件的 data 对象中定义了一个 data 属性,用于存储请求返回的数据。fetchData 方法通过调用 request 方法来处理异步请求状态,同时在异步处理函数中调用 api.fetchData() 方法来发起请求,并将返回的数据赋值给 data 属性。
除了上述例子,我还使用 mixin 封装了一些其他的逻辑,如表单验证、全局错误处理等。通过使用 mixin,可以减少代码的重复性,提高代码的复用性和可维护性。

8.用 echarts 图你是怎么去适应他的大小的?

在使用 ECharts 图表时,为了适应不同的设备尺寸,需要对图表大小进行适配。下面介绍一些适应 ECharts 图表大小的方法。

监听窗口变化

使用 window 对象的 resize 事件监听窗口大小变化,然后通过 ECharts 提供的实例方法 resize() 来重新调整图表大小。示例代码如下:

1
2
3
window.addEventListener('resize', () => {
chart.resize()
})

在监听到窗口大小变化时,调用 chart.resize() 方法重新调整图表大小,从而实现适应不同尺寸的设备。

动态设置容器大小

在使用 ECharts 时,可以动态设置容器的大小,从而适应不同尺寸的设备。示例代码如下:

1
2
3
4
5
6
7
8
9
10
const chartDom = document.getElementById('chart')
const chart = echarts.init(chartDom)

const width = chartDom.clientWidth || window.innerWidth
const height = chartDom.clientHeight || window.innerHeight

chart.resize({
width,
height
})

通过获取容器的宽度和高度,然后调用 chart.resize() 方法来重新调整图表大小,从而实现适应不同尺寸的设备。

自适应容器大小

ECharts 也提供了自适应容器大小的功能,即在初始化图表时,设置 autoResize 属性为 true,这样图表会自动调整大小以适应容器。示例代码如下:

1
2
3
4
5
6
7
8
9
10
const chartDom = document.getElementById('chart')
const chart = echarts.init(chartDom)

const width = chartDom.clientWidth || window.innerWidth
const height = chartDom.clientHeight || window.innerHeight

chart.resize({
width,
height
})

通过将 autoResize 属性设置为 true,图表会自动监听容器的大小变化,然后自动调整大小以适应容器。
以上是一些适应 ECharts 图表大小的方法,具体使用方法可以根据实际情况进行选择和调整。

9.你知道为什么 vue 中的 data 要以方法 return 的形式返回吗

在 Vue 组件中,data 属性是用来存储组件的数据的。由于每个 Vue 组件都是一个独立的实例,每个实例都有自己的 data 属性,当多个实例共享同一个对象时,会导致数据的混乱和不可预期的结果。
因此,在 Vue 中,data 属性必须是一个函数,而不是一个对象。当 Vue 创建实例时,会调用 data 函数,并将返回值作为实例的 data 属性。这样做的好处是,每个实例都会返回一个独立的 data 对象,从而保证了数据的独立性,避免了多个实例之间数据混乱的问题。
示例代码如下:

1
2
3
4
5
6
7
8
export default {
name: 'MyComponent',
data() {
return {
message: 'Hello Vue!'
}
}
}

在上面的示例中,data 属性是一个返回对象的函数。当创建 MyComponent 实例时,会调用 data 函数,返回一个包含 message 属性的对象,从而设置实例的 data 属性为 { message: ‘Hello Vue!’ }。
需要注意的是,如果将 data 属性设置为一个对象,而不是一个函数,这个对象会被所有实例共享,这会导致数据的混乱和不可预期的结果。因此,在 Vue 中,必须将 data 属性设置为一个返回对象的函数,以确保数据的独立性和正确性。

10.在 vue 新增的 provide 和 inject 的跨组件传参方式中,如果 A 组件是父组件,B 组件是 A 组件的子组件,c 组件是 B 组件的子组件,如果我在 A 组件中 provide 中写了一个 reload 属性,B 组件中 provide 中写了一个 reload 属性,那么在 C 组件中的 inject 取的 reload 是 A 组件中的还是 B 组件的值,为什么?

在 Vue 的 provide 和 inject API 中,provide 可以向下传递数据,inject 可以向上寻找数据。如果一个子组件的 inject 对象中包含了多个键名相同的属性,那么在子组件中获取这个属性时会优先取到离子组件最近的那个父级组件提供的值。
因此,在你的示例中,如果 A 组件提供了一个名为 reload 的属性,B 组件也提供了一个名为 reload 的属性,那么在 C 组件中的 inject 对象中取得的 reload 属性值将会是 B 组件提供的值。因为 B 组件离 C 组件更近,根据 Vue 的注入规则,Vue 会优先从离子组件最近的那个父级组件中获取值。
需要注意的是,如果多个组件都提供了相同的属性,那么应该在各个组件中为这个属性取不同的名字,以避免冲突和不可预期的结果。

11.在 promise 中我用.then().then().catch()的方法 我在第一个.then 里报一个错那么它是在第二个 then 还是在 catch 接受这个错误,为什么?

在 Promise 中,当一个异常被抛出时,会立即被传递给该 Promise 的 catch 方法。如果在 Promise 对象链中有多个 then 方法,则前面的 then 方法返回的 Promise 对象会成为后面的 then 方法的参数,这样就可以实现多个异步操作的串联。
如果在第一个 then 方法中抛出了一个异常,则该异常会被传递到该 Promise 对象链中最近的 catch 方法。如果该 Promise 对象链中没有 catch 方法,则异常会被传递到全局的 uncaughtException 事件中。
因此,在你的示例中,如果在第一个 then 方法中抛出了一个异常,则该异常会被传递到该 Promise 对象链中最近的 catch 方法。如果后面还有多个 then 方法,则会继续执行后面的 then 方法,直到该 Promise 对象链中最近的 catch 方法被调用为止。如果该 Promise 对象链中没有 catch 方法,则异常会被传递到全局的 uncaughtException 事件中。
示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
somePromise()
.then(() => {
// do something
throw new Error('Something went wrong')
})
.then(() => {
// this will not be executed if an error is thrown in the previous then method
console.log('Second then method')
})
.catch(error => {
// this will be executed if an error is thrown in the previous then method
console.error(error)
})

在上面的示例中,如果在第一个 then 方法中抛出了一个异常,则该异常会被传递到最近的 catch 方法中,而第二个 then 方法将不会被执行。如果没有异常被抛出,则第一个 then 方法将执行完毕,然后执行第二个 then 方法。如果第二个 then 方法中抛出了异常,则该异常会被传递到最近的 catch 方法中。

12.vue3 是怎么实现双向绑定的?

在 Vue3 中,双向绑定是通过 v-model 指令实现的。与 Vue2 不同的是,Vue3 中使用了 Proxy 对象来实现响应式系统。当数据发生变化时,会触发 Proxy 对象上的 set 方法,从而实现数据的响应式更新。
具体实现流程如下:

  1. 在模板中使用 v-model 指令,绑定一个表单元素的值到某个数据属性上。
  2. 在组件实例化时,对数据属性使用 reactive 方法进行响应式处理。
  3. 对表单元素使用 onInput 事件监听用户输入,同时将用户输入的值赋值给响应式的数据属性。
  4. 当数据属性的值发生变化时,会触发 Proxy 对象上的 set 方法,从而触发视图的更新。

需要注意的是,在 Vue3 中,使用 v-model 绑定的数据属性必须是响应式的数据。如果是一个普通的对象或者数组,需要使用 refreactive 方法进行处理后才能使用。

13.能具体说说 proxy 吗?

Proxy是 ES6 引入的一个新特性,用于创建一个代理对象,可以在该对象上进行读取、设置、函数调用等操作,并且可以在这些操作之前或之后添加拦截器,从而实现对目标对象的代理控制。
使用Proxy可以实现对对象或数组的劫持,可以监听到对象或数组的变化,从而实现双向绑定、响应式等功能。
具体使用方式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const obj = {
name: '张三',
age: 18
}

const proxy = new Proxy(obj, {
get(target, propKey, receiver) {
console.log(`正在获取 ${propKey} 属性`)
return Reflect.get(target, propKey, receiver)
},
set(target, propKey, value, receiver) {
console.log(`正在设置 ${propKey} 属性为 ${value}`)
return Reflect.set(target, propKey, value, receiver)
}
})

proxy.name // 正在获取 name 属性,返回 '张三'
proxy.age = 20 // 正在设置 age 属性为 20

在上面的代码中,我们使用new Proxy创建了一个代理对象proxy,并在proxy上定义了getset拦截器,分别用于拦截对象属性的读取和设置操作。在读取或设置对象属性时,会先触发拦截器中的操作,再执行实际的读取或设置操作。