文章目录
- 1.computed和watch的区别是什么?computed是怎么实现缓存的,底层实现原理是什么?
- 2.了解过哪些布局,flex布局说一下,宽高不定的div如何定位在屏幕中间,宽高为0能用flex实现吗?
- 3.cookie/localstorage/sessionstorage有什么区别?
- 4.数组有哪些常用的方法?说说他们的用途?
- [5.列表渲染时为什么要设置 key?他起什么作用?](#5.列表渲染时为什么要设置 key?他起什么作用?)
1.computed和watch的区别是什么?computed是怎么实现缓存的,底层实现原理是什么?
computed 是计算属性,用于从已有响应式数据中派生出一个新值.watch 是侦听器,用于监听数据变化并执行副作用逻辑.computed 有缓存,只有依赖变化时才会重新计算,watch 没有缓存,数据变化就会执行回调.computed 适合模板中直接使用. watch 适合异步请求、日志记录、手动操作 DOM、联动处理等副作用场景.总结就是,computed 更关注"得到一个值",watch 更关注"数据变了以后做一件事"
深挖 1:computed是怎么实现缓存的,底层实现原理是什么?
结论:computed 的缓存本质是依赖不变时直接返回上一次计算结果;只有依赖发生变化时,才会把缓存标记为失效并重新计算。
computed 之所以能缓存,本质上是因为它内部会维护一个"是否需要重新计算"的标记。第一次读取 computed 值时,会执行传入的 getter,并把结果保存下来;之后如果依赖项没有发生变化,再次读取时就直接返回上一次的结果,不会重复执行 getter。
当 computed 依赖的响应式数据发生变化时,Vue 不会立刻重新计算,而是先把这个 computed 标记成"脏值",也就是缓存失效。等到下次真正有人读取这个 computed 值时,才会重新执行 getter,得到新结果并再次缓存。
2.了解过哪些布局,flex布局说一下,宽高不定的div如何定位在屏幕中间,宽高为0能用flex实现吗?
第一问:了解过哪些布局?
我了解过的布局方式主要有普通文档流、浮动布局、定位布局、Flex 布局和 Grid 布局。早期页面中会比较多用浮动和定位来做布局,现在实际开发里更常用的是 Flex 和 Grid。其中 Flex 更适合一维布局,主要解决一行或者一列上的对齐和分布问题;Grid 更适合二维布局,可以同时从行和列两个维度去划分区域。
拓展了解:
普通文档流就是按照 html 的顺序,比如几个 div,就是从上到下 div 都占一行,浮动布局就是 float ,定位布局就是 postion:绝对定位相对定位
第二问:Flex 布局说一下
Flex 是一种一维弹性布局模型,主要用于控制子元素在主轴和交叉轴上的排列方式。
使用时父元素设置 display: flex,之后可以通过:
• flex-direction 控制主轴方向
• justify-content 控制主轴对齐
• align-items 控制交叉轴对齐
• flex-wrap 控制是否换行
• 子元素通过 flex、flex-grow、flex-shrink、flex-basis 控制伸缩
它比较适合做水平排列、垂直居中、两端对齐、等分布局这类场景。
拓展了解::
为什么不叫横轴纵轴?
因为不是固定横轴纵轴,flex-direction 决定主轴的方向,它是 row,那主轴就是横向,反之就是纵向的,交叉轴就是主轴垂直的那个方向.
第三问:宽高不定的 div 如何定位在屏幕中间
方式一:绝对定位 + transform
css
.parent {
position: relative;
width: 100vw;
height: 100vh;
}
.child {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
方式二:Flex 居中
css
.parent {
display: flex;
justify-content: center;
align-items: center;
width: 100vw;
height: 100vh;
}
如果父容器就是整个视口,其实更常用 flex 直接居中,实现会更简单。
第四问:宽高为 0 能用 flex 实现吗
可以。
因为 flex 控制的是flex item 在容器中的排列和对齐方式,并不要求子元素必须有宽高。
即使子元素宽高为 0,只要它是一个 flex item,仍然可以被放到父容器中心。
只是如果元素本身宽高为 0 且没有内容,视觉上看不到,所以看起来像"没有效果"。
css
.parent {
display: flex;
justify-content: center;
align-items: center;
width: 100vw;
height: 100vh;
}
.child {
width: 0;
height: 0;
}
3.cookie/localstorage/sessionstorage有什么区别?
它们都是浏览器提供的、在客户端(浏览器端)存储数据的技术,但它们在容量、生命周期、以及与服务器的通信这三个核心方面有着本质的区别。
cookie:
生命周期 :Cookie 的生命周期是可以手动设置的。我们可以给它设置一个过期时间,到时间后它就会自动失效。如果不设置,它默认在浏览器关闭时失效(这种也叫会话 Cookie)。如果设置了expires 或是 Max-age,就不是浏览器关闭就失效了,而是保存到指定时间.
容量:cookie 的容量比较小,一个域名下只能存储大约4KB 的数据
与服务器通信:每次发送 HTTP 请求到同个域名下时,浏览器都会自动把该域名下的所有 Cookie 都打包好,放在请求头里一起发送给服务器
缺点:
- 每次请求都会携带,如果 Cookie 里存了太多不必要的数据,会增加网络流量,影响性能。
- API 不够友好,需要手动封装
LocalStorage,属于Web Storage API:
生命周期:永久性存储,只要用户不手动清除浏览器缓存(或者代码里主动 localStorage.clear()),数据就永远不会过期,即使关闭浏览器、关机重启,数据依然存在。
容量: 非常大,通常在 5MB 到 10MB 之间
与服务器通信:纯粹的本地存储,它永远不会被自动发送到服务器
SessionStorage:
生命周期:"会话"级别的存储,单个浏览器标签页的生命周期,一旦这个标签页被关闭,它里面存储的所有 SessionStorage 数据都会被立即清除.
关键点: 即使是同一个网站,在两个不同的标签页里打开,它们的 SessionStorage 也是不互通、不共享的。
容量和与服务器通信和LocalStorage一样
4.数组有哪些常用的方法?说说他们的用途?
数组常用方法我一般按几类来记。
增删类:有 push、pop、shift、unshift,分别用于在数组头尾添加和删除元素;
遍历处理类:有 map、filter、reduce,其中 map 返回映射后的新数组,filter 用于筛选符合条件的元素,reduce 可以把数组累积计算成一个值;
截取类有: slice,用于截取数组的一部分且不改变原数组,splice 则可以在指定位置增删改元素,会修改原数组;
查找类:find、findIndex 用于查找元素,includes 判断数组中是否包含某个值,
其他的,join 可以把数组拼接成字符串,sort 用于排序,fill 用于用固定值填充数组。
拓展了解:
find 是找到第一个满足条件的元素本身,返回的是元素值,不是下标。参数是回调函数
findIndex 找到第一个满足条件的元素下标,返回的是索引。
javascript
const arr = [10, 20, 30]
const res = arr.find(item => item > 15)
console.log(res) // 20
const res = arr.findIndex(item => item > 15)
console.log(res) // 1
5.列表渲染时为什么要设置 key?他起什么作用?
列表渲染时设置 key,主要是为了让 Vue 在更新虚拟 DOM 时准确识别每个节点。因为 Vue 在 patch 过程中会尽量复用已有节点,如果不写 key,它通常会按位置进行就地复用;当列表发生新增、删除或顺序变化时,这种复用可能会出错,导致渲染异常,比如输入框内容错位或组件状态混乱。给每个节点设置稳定且唯一的 key 后,Vue 就能更准确地判断哪些节点应该复用、哪些需要新增、删除或移动,这样既能保证更新结果正确,也能提升 diff 的效率。
举例来看:
假如页面如下:
javascript
[
{ id: 1, name: '张三' },
{ id: 2, name: '李四' }
]
javascript
<div v-for="item in list">
<input />
<span>{{ item.name }}</span>
</div>

现在用户在第一个输入框里手动输入了:hello

然后再列表头部插入一个新数据:
javascript
[
{ id: 3, name: '王五' },
{ id: 1, name: '张三' },
{ id: 2, name: '李四' }
]
Vue 更新时,如果没有 key,会按照位置比较:
旧列表:
• 旧第 1 个:张三
• 旧第 2 个:李四
新列表:
• 新第 1 个:王五
• 新第 2 个:张三
• 新第 3 个:李四
因为没有 key,Vue 不知道"张三"是不是原来那个张三,vue的复用过程就变成了
第一步,把原来第一个 DOM 节点拿过来,改一改内容,让它现在显示成"王五".
第二步,把原来第二个 DOM 节点拿过来,改一改内容,让它现在显示成"张三"
第三步,再新建一个节点显示"李四"
问题来了,原来第一个节点里的 input,被用户输入过 hello,正常他的旁边应该显示的名字应该是张三,但是现在由于这种就地按位置复用,旁边展示的名字变成了王五这是不正确的,这就是节点按位置复用,导致 DOM 状态和数据身份错位。
加入 key 后恢复正常,为什么有key 时为什么不会乱?
javascript
<div v-for="item in list" :key="item.id">
<input />
<span>{{ item.name }}</span>
</div>
那 Vue 就知道:
• id=1 这一项还是原来的张三
• id=2 这一项还是原来的李四
• id=3 是新来的王五
这时候它不会再按"第几个位置"认人,而是按 id 认人。更像是新建一个王五节点,然后把张三和李四整体向后移
深挖 1:为什么不建议用index索引作为key?
其实上面的例子就能说明不能使用 index 作为索引,index 是列表中数据的位置,其实是不稳定的.当列表发生新增、删除或排序变化时,同一个元素的 index 会变化,导致 key 不稳定。这样 Vue 在 diff 的时候就可能错误复用节点,出现输入框内容错位、组件状态混乱等问题。
上述问题的最小复现:动态加入王五
javascript
<script setup lang="ts">
import { ref } from 'vue'
const list = ref([
{ id: 1, name: '张三' },
{ id: 2, name: '李四' }
])
const addItem = () => {
list.value.unshift({ id: 3, name: '王五' })
}
</script>
<template>
<div>
<button @click="addItem">头部插入</button>
<div v-for="item in list">
<input />
<span>{{ item.name }}</span>
</div>
</div>
</template>
