引言💭
记录我的第一场笔试。
专业相关基础题📑
1. Vue.js的生命周期函数及其执行顺序
Vue.js的生命周期函数可以分为以下几个阶段:
-
创建阶段:
beforeCreate
: 实例初始化之后,数据观测和事件配置之前。created
: 实例已经创建完成,数据观测和事件配置已经完成,但DOM尚未渲染。
-
挂载阶段:
beforeMount
: 在挂载开始之前,相关的render函数首次被调用。mounted
: 挂载完成后,DOM已插入页面中。
-
更新阶段:
beforeUpdate
: 数据变化导致视图更新之前。updated
: 数据变化导致视图更新之后。
-
销毁阶段:
beforeDestroy
: 实例销毁之前。destroyed
: 实例销毁之后。
顺序:beforeCreate
-> created
-> beforeMount
-> mounted
-> beforeUpdate
-> updated
-> beforeDestroy
-> destroyed
2. v-bind指令和v-model指令的区别
-
v-bind
:动态绑定一个属性到元素上。可以用于绑定任意一个属性值,常见用法有绑定href
、class
、style
等。ini<a v-bind:href="url">链接</a>
-
v-model
:用于实现双向绑定,通常用于表单元素如<input>
,<select>
,<textarea>
。它会根据控件类型自动绑定相应的事件和属性。ini<input v-model="message">
区别 :v-bind
主要用于单向绑定,v-model
实现双向绑定,并且主要用于表单控件。
3. Vue的组件通信方式
详细可看我的另一篇文章:组件通信的九种方式
4. Vue中的响应式原理
Vue.js通过 Object.defineProperty (Vue 2.x) 或 Proxy (Vue 3.x) 实现数据的响应式。当你访问一个数据属性时,Vue会触发 getter 方法;当你修改数据时,Vue会触发 setter 方法,进而通知依赖该数据的视图更新。
Vue 3 使用 Proxy,提供了更加高效和灵活的响应式系统,支持嵌套的对象和数组。
5. 如何在Vue中实现路由跳转
编程式导航:
- 选项式API
php
export default {
methods: {
navigate() {
// 字符串路径
this.$router.push('/about')
// 对象形式
this.$router.push({ path: '/about' })
// 命名路由
this.$router.push({ name: 'about' })
// 带参数
this.$router.push({ name: 'user', params: { id: '123' } })
// 带查询参数
this.$router.push({ path: '/search', query: { q: 'vue' } })
// 替换当前路由
this.$router.replace({ path: '/home' })
// 前进/后退
this.$router.go(1) // 前进一步
this.$router.go(-1) // 后退一步
}
}
}
- 组合式API
xml
<script setup>
import { useRouter } from 'vue-router'
const router = useRouter()
const navigate = () => {
// 基本跳转
router.push('/about')
// 命名路由带参数
router.push({
name: 'user',
params: { id: 123 }
})
// 替换当前路由
router.replace('/login')
// 前进/后退
router.go(1)
router.back() // 等同于 router.go(-1)
router.forward() // 等同于 router.go(1)
}
</script>
声明式导航:
- 使用<router-link>组件
xml
<template>
<!-- 基本用法 -->
<router-link to="/">首页</router-link>
<!-- 动态路径 -->
<router-link :to="'/user/' + userId">用户页面</router-link>
<!-- 命名路由 -->
<router-link :to="{ name: 'user', params: { id: 123 }}">用户详情</router-link>
<!-- 带查询参数 -->
<router-link :to="{ path: '/search', query: { q: 'vue' }}">搜索</router-link>
<!-- 替换当前路由(不记录历史) -->
<router-link :to="..." replace>替换</router-link>
<!-- 自定义激活类名 -->
<router-link :to="..." active-class="active-link">链接</router-link>
</template>
6. Vue.js中的computed和watch有什么区别
-
computed:计算属性,基于依赖的响应式数据缓存,只有当依赖数据发生变化时才会重新计算。
kotlincomputed: { fullName() { return this.firstName + ' ' + this.lastName; } }
-
watch:观察属性,监听响应式数据变化时,执行指定的函数。
javascriptwatch: { message(newVal, oldVal) { console.log('message changed from', oldVal, 'to', newVal); } }
区别 :computed
用于返回计算的结果,watch
用于处理副作用操作(如异步请求等)。
7. Vue.js中的v-for指令和v-if指令有什么区别
-
v-for:用于循环渲染一个数组或对象。
css<div v-for="item in items" :key="item.id">{{ item.name }}</div>
-
v-if:用于根据条件渲染 DOM 元素。
ini<div v-if="isVisible">This will be shown if isVisible is true.</div>
区别:
v-for
是用来渲染列表,v-if
是用来条件渲染元素。
- Vue 2.x:v-for 优先级高于 v-if
- Vue 3.x:v-if 优先级高于 v-for(在 Vue 3 中,这种用法会在控制台发出警告)
v-if
不建议与v-for
一起使用,因为它们会在不同的阶段运行,容易造成性能问题。
8. Vue.js中的mixins和extends的作用及其区别
-
mixins :用于封装可复用的代码块,可以将多个功能逻辑分离并共享到组件中。每个组件可以声明多个
mixins
。inimixins: [myMixin]
-
extends:用于将一个组件扩展为另一个组件,相当于继承。
makefileextends: ParentComponent
区别 :mixins
可以用于多个组件共享某些功能,而extends
更多用于继承父组件的行为。
9. Vue.js中的keep-alive组件有什么作用
keep-alive
是 Vue.js 的一个内置组件,主要用于缓存组件状态,让组件在切换时不会被销毁,从而保留数据和状态。
核心作用
- 保存组件状态:缓存不活动的组件,避免重复渲染
- 提升性能:减少组件销毁和重建的开销
- 保持数据:如表单输入内容、滚动位置等不会丢失
基本使用方式
html
<keep-alive>
<component :is="currentComponent"></component>
</keep-alive>
常用功能
-
指定缓存组件:
html<keep-alive include="Home,About"> <router-view></router-view> </keep-alive>
-
特殊生命周期:
activated()
- 组件被激活时调用deactivated()
- 组件被缓存时调用
-
限制缓存数量:
html<keep-alive :max="5"> <router-view></router-view> </keep-alive>
逻辑推理题🤔
1. 毒蘑菇
现在有1011
种蘑菇,其中1
种是毒蘑菇
。人一旦吃了微量的毒蘑菇,就会在72小时后发作身亡。现在用松鼠做试验,从开始喂松鼠计时,要在72小时后马上找出毒蘑菇,问最少需要多少只松鼠?(需要给出解题过程)
关键信息
- 蘑菇总数:1011种,其中1种是有毒的。
- 毒蘑菇的特性:即使微量,食用后也会在72小时后发作身亡。
- 实验对象:松鼠。可以给松鼠喂食蘑菇。
- 时间限制:必须在72小时后立即确定出毒蘑菇。这意味着我们只有一次观察松鼠生死的机会。
- 目标:找出最少需要多少只松鼠,才能在72小时后唯一确定毒蘑菇。
解题思路
这个问题类似于经典的"毒药与老鼠"问题,其中需要通过实验设计在有限的资源(老鼠或松鼠)下,通过它们的生死状态来唯一标识出有毒的物品(蘑菇)。
在这种类型的问题中,通常使用二进制编码的方法来解决。具体步骤如下:
- 编码:将每一种蘑菇分配一个独特的二进制编码。
- 分配:根据编码,决定哪些松鼠会吃哪些蘑菇。具体来说,如果某一位是1,对应的松鼠会吃这种蘑菇;如果是0,则不吃。
- 观察:72小时后,观察哪些松鼠死亡。死亡的松鼠对应的位为1,存活的为0。将这些信息组合起来,就能得到毒蘑菇的二进制编码。
- 解码:根据松鼠的生死模式,解码出毒蘑菇的编号。
计算所需松鼠的数量
关键在于确定需要多少位二进制数才能唯一表示1011种蘑菇。因为每一种蘑菇需要一个独特的编码,而n位二进制可以表示2^n个不同的数。
我们需要找到最小的n,使得:
2^n ≥ 1011
计算:
- 2^10 = 1024
- 2^9 = 512
1024 > 1011 > 512,因此需要10位二进制数。
总结
最少需要10只松鼠。
2. 帽子的颜色
在一个房间里, 有4
个戴了帽子的小朋友, 4顶帽子的颜色为2个黑色
和2个白色
,4个小朋友从左至右编号为1号、2号、3号、4号。如下图所示,其中1号和2号、3号、4号中间隔了一道墙,4个小朋友都只能向前看,看不到身后,彼此都无法看到墙对面的情况。
现在同时向四人提问:你头上戴的帽子是什么颜色的?
其他条件:
- 四人都知道帽子有2个黑色、2个白色,但看不到自己头上帽子的颜色;
- 站在台阶上的小朋友可以看到自己台阶下面所有小朋友的帽子颜色;
- 四人不能讨论,但可以听见其他人的回答;
- 四人都绝顶聪明;
- 知道自己头上帽子颜色的,必须第一时间喊出自己头上帽子的颜色;
- 如果不能确定自己头上帽子颜色,则不允许出声说话。
请问:是谁最先喊出自己帽子的颜色?

解题步骤
我们需要模拟每个小朋友的视角和推理过程。关键在于:
-
从能看到最多信息的小朋友开始推理(这里是4号,可以看到2号/3号)。
-
如果4号不立刻回答,说明ta无法直接确定,这会给其他人提供信息。
-
依次向下推理(3号、2号、1号)。
4号视角
4号可以看见2号和3号的帽子为1白1黑
,剩1白1黑
,不能确定自己头上帽子的颜色,不能出声。
3号视角
3号可以看见2号的帽子为1白
,根据4号没有出声可以推断出自己和2号帽子颜色不一致
,则可以推断出自己帽子颜色为黑色
。
则是3号最先喊出自己帽子的颜色。
2号视角
2号不能看见任何一个人的帽子颜色,ta只能知道自己和3号帽子颜色不一致。
1号视角
1号也不能看见任何一个人的帽子颜色,并且ta也不能收获任何信息。
总结
3号最先喊出自己帽子的颜色。
算法题✍🏻
数独
请你判断一个 9 x 9
的数独是否有效。只需要 根据以下规则 ,验证已经填入的数字是否有效即可。
- 数字
1-9
在每一行只能出现一次。 - 数字
1-9
在每一列只能出现一次。 - 数字
1-9
在每一个以粗实线分隔的3x3
宫内只能出现一次。(请参考示例图)
注意:
- 一个有效的数独(部分已被填充)不一定是可解的。
- 只需要根据以上规则,验证已经填入的数字是否有效即可。
- 空白格用
'.'
表示。

方法思路
通过遍历整个数独棋盘,使用三个数组(rows
、cols
、boxes
)分别记录每行、每列和每个 3x3 子数独区域中出现过的数字,并且在检查每个数字时确保该数字没有在相应的行、列和子数独区域中重复出现。如果发现重复,就立即返回 false
;如果遍历完没有问题,返回 true
。
代码
javascript
/**
* @param {character[][]} board
* @return {boolean}
*/
var isValidSudoku = function(board) {
// 初始化行、列和子数独的记录数组
const rows = Array.from({ length: 9 }, () => ({}));
const cols = Array.from({ length: 9 }, () => ({}));
const boxes = Array.from({ length: 9 }, () => ({}));
for (let i = 0; i < 9; i++) {
for (let j = 0; j < 9; j++) {
const cell = board[i][j];
if (cell === '.') continue; // 跳过空格
const num = parseInt(cell, 10);
const boxIndex = Math.floor(i / 3) * 3 + Math.floor(j / 3); // 计算子数独索引
// 检查当前数字是否已在行、列或子数独中出现
if (rows[i][num] || cols[j][num] || boxes[boxIndex][num]) {
return false;
}
// 标记当前数字在行、列和子数独中已出现
rows[i][num] = true;
cols[j][num] = true;
boxes[boxIndex][num] = true;
}
}
return true; // 所有检查通过,数独有效
};
代码解释
1. 初始化数据结构
ini
const rows = Array.from({ length: 9 }, () => ({}));
const cols = Array.from({ length: 9 }, () => ({}));
const boxes = Array.from({ length: 9 }, () => ({}));
rows
、cols
和boxes
都是长度为 9 的数组,其中每个元素是一个空对象。分别用来记录每行、每列和每个 3x3 子数独区域中出现过的数字。rows[i]
用来记录第i
行的数字,cols[j]
用来记录第j
列的数字,boxes[k]
用来记录第k
个 3x3 子数独的数字。
2. 遍历整个数独棋盘
ini
for (let i = 0; i < 9; i++) {
for (let j = 0; j < 9; j++) {
const cell = board[i][j];
if (cell === '.') continue; // 跳过空格
- 外层循环遍历每一行
i
,内层循环遍历每一列j
。 - 如果当前格子是空的(即
.
),则跳过,直接进行下一个格子的检查。
3. 处理非空格的数字
ini
const num = parseInt(cell, 10);
const boxIndex = Math.floor(i / 3) * 3 + Math.floor(j / 3); // 计算子数独索引
- 将当前格子的字符
cell
转换为整数num
。 boxIndex
计算出该数字所在的 3x3 子数独的索引。通过Math.floor(i / 3)
和Math.floor(j / 3)
来确定它位于哪一个子数独(3x3 方阵)。
4. 检查当前数字是否在行、列或子数独中重复
ini
if (rows[i][num] || cols[j][num] || boxes[boxIndex][num]) {
return false; // 如果在行、列或子数独中已经出现过该数字,返回 false
}
- 如果当前数字
num
在当前行、列或子数独中已经出现过,立即返回false
,表示数独无效。
5. 标记当前数字在行、列和子数独中已出现
ini
rows[i][num] = true;
cols[j][num] = true;
boxes[boxIndex][num] = true;
- 如果当前数字没有在行、列和子数独中出现过,则将其标记为已出现,以便以后检查其他格子时避免重复。
6. 返回结果
kotlin
return true; // 所有检查通过,数独有效
- 如果遍历完所有格子都没有发现重复的数字,说明数独有效,返回
true
。
结语📖
总的来说这套题并不像是前端的面试题,考察的一种思维吧。
