前端笔面试查漏补缺
面试笔试的知识总结,查漏补缺
一、CSS样式隔离
CSS样式隔离用于确保组件或元素的样式不会与其他组件或元素的样式发生冲突。
1.scoped css -- <style scoped>
构建工具(vue-loader)会在编译阶段对css特殊处理,给当前组件的所有dom节点添加一个唯一的属性(通常是data-v-xxxxx,其中xxxxx是一个哈希值),并在css选择器末尾添加这个属性选择器,实现css样式隔离。
1)优点:
样式隔离,避免全局样式的冲突
允许创建可重用的组件,无需担心样式冲突
CSS代码更易于维护和理解,因为每个组件的样式都是独立的
2)限制
性能影响,可能会略微降低页面的加载速度
样式作用范围,scoped样式只能作用于组件及其后代元素,无法作用于组件外部的元素
有时第三方库的样式可能无法被scoped正确隔离,需要额外处理
二、修饰符对应js原生方法
1.事件冒泡
当一个元素上触发某个事件时,这个事件会按照从内到外的顺序逐级传递给父元素,直到传递到文档的根元素(通常是window对象)
1)vue对应修饰符:.stop
2)原生js:event.stopPropagation()
方法会在调用时修改事件对象(event)内部状态,状态通常是一个标志位,标记事件是否应该继续传播
2.默认行为
.prevent用于阻止元素的默认事件行为。例如,当点击一个提交按钮时,浏览器默认会提交表单,但使用了.prevent会阻止默认行为,允许开发者自定义逻辑(如在提交前验证表单数据)。
1)vue对应修饰符:.prevent
2)原生js:event.preventDefault()
与event.stopPropagation()类似,浏览器会检查发生的事件类型(例如,点击、提交、键盘事件等)。不同的事件类型有不同的默认行为。浏览器会在内部标记该事件为“已取消默认行为”,通常通过一个内部标志来实现。
但是,event.preventDefault()只会取消事件的默认行为,它不会阻止事件在DOM树中的传播。
3.扩展补充
1).capture:阻止事件捕获关键字。
(事件捕获:)
原生js:addEventListener,第三个参数设为true,在捕获阶段监听。
element.addEventListener('click', function(event) {
// 捕获阶段处理代码
}, true); // 第三个参数为true,表示在捕获阶段监听
2).self:只在点击到元素自身的时候才执行对应函数。
原生js:添加监听器,然后检查event.target是否等于监听器附加的元素。
element.addEventListener('click', function(event) {
if (event.target === this) {
// 仅当事件目标为当前元素时执行代码
}
});
3).once:事件只执行一次,再次点击不会再执行
原生js:手动移除监听器,removeEventListener
function handleClick(event) {
// 事件处理代码
// 移除监听器,防止再次触发
element.removeEventListener('click', handleClick);
}
element.addEventListener('click', handleClick);
三、display
1.block
元素显示为块级元素。
块级元素会独占一行,宽度默认为其父容器的100%。
常见的块级元素有 <div>,<p>,<h1> 等。
2.inline
元素显示为内联元素。
内联元素不会独占一行,只占据其内容所需的宽度。
常见的内联元素有 <span>,<a>,<img> 等。
3.inline-block
结合了块级元素和内联元素的特点。
元素像内联元素一样不会独占一行,但可以设置宽度和高度。
4.none
元素不会被显示。
元素从文档布局中完全移除,就像它从未存在一样。
使用 display:none 可以隐藏元素,同时不影响页面的其他部分。
5.flex
元素成为一个弹性容器(flex container)。
其子元素成为弹性项(flex items),可以在容器内以灵活的方式布局。
弹性布局(flexbox)是一种用于在页面上排列元素的一维布局方法。
(水平垂直居中:
父元素:display:flex; justify-content:center; align-items:center;)
6.grid
元素成为一个网格容器(grid container)。
其子元素成为网格项(grid items),可以在容器内以二维网格的方式布局。
网格布局(grid layout)是一种强大的二维布局系统,用于在页面上创建复杂的布局。
grid常用语法:
.container {
display: grid;
grid-template-rows: 100px 200px; /* 定义两行,第一行100px,第二行200px */
grid-template-columns: 1fr 2fr; /* 定义两列,第一列占1份,第二列占2份(基于可用空间) */
}
(水平垂直居中:
父元素:display:grid; justigy-content:center; align-items:center;
或者
父元素:display:grid; 子元素:margin:auto;
(限制是,它要求网格项目没有设置 height 或 max-height 属性(或者这些属性的值允许足够的空间来应用 margin:auto)。
)
7.table
元素显示为块级表格,类似于 <table> 标签。
其子元素可以使用 display:table-row, display:table-cell 等值来模拟表格的行和单元格。
8.table-row
元素显示为表格行,类似于 <tr> 标签。
该元素必须是 display:table 或 display:inline-table 元素的直接子元素。
9.table-cell
元素显示为表格单元格,类似于 <td> 或 <th> 标签。
该元素必须是 display:table-row, display:table-row-group, display:table-header-group 元素的直接子元素。
(水平垂直居中:
父元素:display:table-cell; text-align:center; vertival-align:middle;
(限制:它仅对块级元素以外的内容(如行内元素或行内块元素)有效。)
子元素可以:display:inline-block;
)
10.list-item
元素显示为列表项,类似于 <li> 标签。
该元素会产生一个额外的块级框(称为外部标记),通常包含项目符号或编号。
11.run-in
这是一个不太常用的值,它根据上下文将元素显示为块级或内联元素。
如果元素后面跟着一个块级元素,则它表现为内联元素;否则,表现为块级元素。
12.flow-root
元素会创建一个新的块格式化上下文(BFC),并且其内部的浮动元素会被包含。
这通常用于清除浮动或防止外边距合并等布局问题。
13.inline-flex 和 inline-grid
这两个值分别结合了 flex和 grid 布局与内联元素的特性。
元素像内联元素一样不独占一行,但内部使用弹性布局或网格布局。
14.contents
这是一个实验性值,它使元素本身不产生任何盒模型(即不显示也不占据空间)。
但是,其子元素仍然会被渲染和布局。
15.补充:水平垂直居中
1)使用flexbox
<div class="container">
<div class="centered-item">内容</div>
</div>
.container {
display: flex;
justify-content: center; /* 水平居中 */
align-items: center; /* 垂直居中 */
height: 100vh; /* 假设容器占满整个视口高度 */
}
2)使用grid
<div class="container">
<div class="centered-item">内容</div>
</div>
.container {
display: grid;
place-items: center; /* 同时水平和垂直居中 */
height: 100vh; /* 假设容器占满整个视口高度 */
}
3)使用绝对定位和变换
.container {
position: relative;
height: 100vh; /* 假设容器占满整个视口高度 */
}
.centered-item {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%); /* 偏移自身宽度和高度的50% */
}
4)使用table-cell
.container {
display: table;
width: 100%; /* 假设容器宽度为100% */
height: 100vh; /* 假设容器高度为视口高度 */
}
.centered-item {
display: table-cell;
text-align: center; /* 水平居中 */
vertical-align: middle; /* 垂直居中 */
}
四、BFC
1.BFC
Web页面的一种布局方式,是一个独立的渲染区域,只有属于这个渲染区域的元素才会受到BFC的影响。
BFC规定内部的块级元素如何布局,并且与这个区域外部的元素互不干扰。
2.触发条件(常见)
1.浮动元素(float不是none)
2.绝对定位或固定定位元素(position为absolute或fixed)
3.display为inline-block,flex,inline-flex,grid,inline-grid,flow-root
4.overflow不为visible
五、兄弟组件传递数据
1.通过父组件作为中介
父传子:props
子传父:emit
2.使用状态管理
vuex、pinia...
补充:Vuex和Pinia区别:
Vuex:
采用全局单例模式,通过一个store来管理所有的状态。
适用于中大型复杂应用,通过集中式状态管理来组织和管理应用状态。
提供了States、Mutations、Getters、Actions、modules五个模块。
支持模块化设计,但模块和状态的名字可能存在冲突,数据不够扁平化。
辅助函数:mapState、mapGetters、mapActions、mapMutations。
Pinia:
采用分离模式,每个组件都有自己的store实例。
设计力求简单和高效,提供更好的开发体验。
只有States、Getters、Actions三个模块设计。
没有modules配置,每一个独立的仓库都是通过defineStore生成的,使得代码更加扁平化和易于维护。
提供了更好的TypeScript支持,API设计简洁,避免了过多的概念和规则。
3.使用事件总线
(事件总线在vue.js用于组件间通信,可被视为一个中央调度器或事件中心,允许不同的组件发送和接收事件,实现数据的传递和组件间的交互)
(事件总线是一个独立的Vue实例,不具备DOM,只包含Vue实例方法,允许组件通过emit发送时间,通过on监听时间,通过$off移除事件监听)
4.使用本地存储
(localStorage或sessionStorage)
5.provide/inject
vue的provide和inject允许一个祖先组件向其子孙后代注入一个依赖,主要用于祖先和后代组件的通信,某些情况也可以用于兄弟组件之间的通信(非首选,会模糊组件间的界限)
但是provide和inject传递数据是非响应式的(父组件数据变化,子组件不会自动感知到并更新),在深层次嵌套的组件中非常有用,谨慎使用
provide,父组件定义的对象或函数,向子组件提供数据(数据可以被该组件的所有后代组件访问)
inject,子组件定义的对象,接收来自祖先组件提供的数据
父组件:
export default {
provide() {
return {
message: 'Hello from Parent Component',
// 可以提供函数、对象等其他类型的数据
};
},
// 其他组件选项...
};
子组件:
export default {
inject: ['message'],
// 其他组件选项...
};
六、watchEffect
1.watchEffect
- 自动追踪依赖:watchEffect会自动追踪回调函数中使用的所有响应式数据,无需手动指定要监听的具体属性。
- 立即执行:与watch不同,watchEffect在初始化时会立即执行一次回调函数。
- 性能优化:Vue 3会对watchEffect的执行进行优化,避免不必要的重复运算。但是,如果回调函数依赖的响应式数据很多,或者回调函数的执行开销很大,仍然可能会对性能产生影响。
2.与watch的区别
- 监听方式:watchEffect是自动追踪所有使用的响应式数据,而watch需要手动指定要监听的特定属性。
- 监听粒度:watchEffect监听的是响应式数据的整个变化,而watch可以监听指定的属性或表达式的变化。
- 使用场景:watchEffect适用于无需指定具体依赖的情况,只要回调函数内部使用的任何响应式数据发生变化,都会触发回调函数。而watch则适用于需要对数据进行深入定制的情况,可以监视多个数据源,并且可以通过options对象来自定义行为,如深度监听、立即执行等。
import { reactive, watchEffect } from 'vue';
const state = reactive({
count: 0,
name: 'John',
age: 25
});
watchEffect(() => {
console.log(`Count: ${state.count}, Name: ${state.name}, Age: ${state.age}`);
});
// 当 state 中的任何属性发生变化时,上面的回调函数都会再次执行
state.count++; // 输出: Count: 1, Name: John, Age: 25
state.name = 'Alice'; // 输出: Count: 1, Name: Alice, Age: 25
七、Vue3生命周期
1.setup
新增钩子函数,其他选项式API钩子之前调用,用于初始化响应式数据,props解析和事件监听器等,setup函数没有this上下文,不是组件实例的一部分
2.beforeCreate
实例初始化后,数据观测之前,data和methods未初始化,无法访问到props和data属性
3.created
实例创建完成后,data和methods已经初始化,模板渲染未开始,异步请求、事件监听器注册
4.beforeMount
挂载之前,模板编译已完成,DOM未生成
5.mounted
挂载完成,可以访问到组件的DOM元素
6.beforeUpdate
组件更新前调用,数据已被更改,页面未重新渲染
7.updated
组件更新完成后调用,DOM已经重新渲染完毕
8.beforeUnmount
卸载前使用,组件仍完全可用,进行清理操作,取消事件监听器和定时器
9.unmounted
卸载完成后,组件已经完全销毁,释放资源和清理操作
其余钩子函数:
8.onActivated
被<keep-alive>缓存的组件激活时调用
9.onDeactivated
被<keep-alive>缓存的组件停用时调用
10.onErrorCaptured
用于捕获并处理来自子孙组件的错误
八、arguments
1.定义
类数组对象,包含传递给函数的所有参数,对象不是Array的实例,但有类似数组的属性,如length,且可以用索引来访问各个参数(arguments[0])。
2.使用
arguments对象可以在函数体内部使用,允许访问传递给函数的参数。(不能用pop,push,slice等数组方法,但可以使用Array.prototype.slice.call(arguments)将arguments转换为真正的数组)
如果函数声明中定义了参数,这些参数在函数体内部也会作为局部变量存在。这些参数与arguments对象中的值是同步的,即修改其中一个也会影响到另一个。
3.箭头函数
箭头函数不绑定自己的arguments对象。箭头函数内部访问arguments时,实际引用的是包含箭头函数的那个函数的arguments对象,如果箭头函数是在非函数块中创建的(如直接在全局作用域),那它可能无法访问到有效的arguments对象。
4.rest参数
ES6及更高版本中,引入rest参数语法(...args),提供了更简洁的方式来处理不确定数量的参数,并将它们作为数组处理。rest参数是真正的数组实例,可以直接使用数组的所有方法。
九、数组方法
1.shift()
从数组的开头(索引0)移除第一个元素,并返回该元素的值(改变原数组)。
let arr = [1, 2, 3];
let firstElement = arr.shift(); // firstElement 的值为 1
console.log(arr); // 输出: [2, 3]
2.unshift()
在数组的开头(索引0)添加一个或多个元素,并返回新的数组长度(改变原数组)。
let arr = [1, 2, 3];
let newLength = arr.unshift(0); // newLength 的值为 4
console.log(arr); // 输出: [0, 1, 2, 3]
3.splice()
用于添加/删除/替换数组中的元素,(start,deleteCount,item1,item2,...)指定从start索引位置修改,deleteCount移除数组元素的个数,设为0不移除;
item1,item2,...,要添加进数组的元素,从start位置开始。
删除元素:
let arr = [1, 2, 3, 4, 5];
let removed = arr.splice(1, 2); // 从索引1开始删除2个元素
console.log(arr); // 输出: [1, 4, 5]
console.log(removed); // 输出: [2, 3]
添加元素:
let arr = [1, 2, 3, 4, 5];
arr.splice(2, 0, 'a', 'b'); // 从索引2开始插入'a'和'b'
console.log(arr); // 输出: [1, 2, 'a', 'b', 3, 4, 5]
替换元素:
let arr = [1, 2, 3, 4, 5];
arr.splice(1, 1, 'x', 'y'); // 从索引1开始替换1个元素为'x'和'y'
console.log(arr); // 输出: [1, 'x', 'y', 3, 4, 5]
4.slice()
从一个数组中提取一部分浅拷贝到新的数组对象中,(不改变原数组)
(begin,end),提取片段的开始位置和结束位置
let arr = [1, 2, 3, 4, 5];
let newArr = arr.slice(1, 3); // 提取索引1到2的元素(不包括索引3)
console.log(newArr); // 输出: [2, 3]
console.log(arr); // 输出: [1, 2, 3, 4, 5](原数组未改变)
复制整个数组:
let arr = [1, 2, 3, 4, 5];
let copiedArr = arr.slice(); // 不提供参数,复制整个数组
console.log(copiedArr); // 输出: [1, 2, 3, 4, 5]