Skip to Content
Nextra 4.0 is released 🎉
企业真题xxx科技-前端工程师

xxx科技-前端工程师

1.Vue的路由实现, 如果要配置history模式要怎么处理

答案:需要服务器配置支持

原理对比:

模式URL 格式原理需要服务器配置
hash//#/home使用 URL 的 hash 部分❌ 否
history/home使用 History API✅ 是

前端配置:

// router/index.js import { createRouter, createWebHistory } from 'vue-router'; const router = createRouter({ history: createWebHistory(), // 使用 history 模式而不是 createWebHashHistory routes: [ { path: '/', component: Home }, { path: '/about', component: About } ] }); export default router;

服务器配置(必需):

// Node.js/Express app.use(express.static('dist')); // 对所有非文件请求,返回 index.html app.get('*', (req, res) => { res.sendFile(path.join(__dirname, 'dist', 'index.html')); });
# Nginx location / { try_files $uri $uri/ /index.html; }
// Python/Flask @app.route('/') @app.route('/<path:path>') def catch_all(path=None): return render_template('index.html')

2.Vue2 和 Vue3 双向数据绑定有什么区别

特性Vue 2Vue 3
实现方式Object.defineProperty()Proxy
支持情况已有对象属性变化对象新增属性也支持
性能对象属性逐个劫持一次代理整个对象
Array 监听需要特殊处理原生支持
兼容性支持 IE9+不支持 IE

Vue 2 限制示例:

data() { return { user: { name: 'John' } } }, methods: { updateUser() { // ❌ 新增属性不会被追踪 this.user.age = 30; // 视图不会更新 // ✅ 需要使用 $set this.$set(this.user, 'age', 30); } }

Vue 3 的优势:

setup() { const user = reactive({ name: 'John' }); // ✅ 直接添加新属性即可被追踪 user.age = 30; // 视图会自动更新 }

3.Vue3 的新增特性

主要新增特性:

  1. Composition API

    • 按逻辑组织代码
    • 更好的代码复用
  2. 更快的性能

    • 基于 Proxy 的响应式系统
    • 更小的包体积
  3. 更好的 TypeScript 支持

    • 原生支持 TS 的泛型
    • 更好的类型推导
  4. Fragment、Teleport、Suspense

    <!-- Fragment:不需要根元素 --> <template> <div>1</div> <div>2</div> </template> <!-- Teleport:传送门 --> <Teleport to="body"> <div>Modal</div> </Teleport> <!-- Suspense:异步组件 --> <Suspense> <AsyncComponent /> </Suspense>
  5. 新的 <script setup> 语法糖

  6. 响应式 API 改进

    • refreactivecomputedwatch

4.Vue 中 v-if 和 v-show 的区别和使用场景

特性v-ifv-show
实现销毁/创建 DOM 元素切换 CSS display 属性
初始渲染快(条件为 false)慢(总是编译)
切换开销高(创建/销毁)低(仅改变属性)
支持 v-else✅ 支持❌ 不支持

使用场景:

<template> <!-- v-if:不需要频繁切换时 --> <div v-if="isLoggedIn"> <Profile /> </div> <!-- v-show:经常切换的场景 --> <div v-show="isVisible"> 可见内容 </div> <!-- v-if 支持 v-else --> <div v-if="type === 'A'">A</div> <div v-else-if="type === 'B'">B</div> <div v-else>C</div> </template> <script setup> const isLoggedIn = ref(false); const isVisible = ref(false); </script>

5.路由传参数组, 对象, 或者长字符, 防止被截取, 怎么解决

问题: URL 过长或含有特殊字符时,参数可能被截取

解决方案:

// 方案1:使用 query 参数(推荐小数据) router.push({ name: 'details', query: { id: 123, name: 'John', data: JSON.stringify({ x: 1, y: 2 }) } }); // 访问:/details?id=123&name=John&data=%7B%22x%22:1%7D // 方案2:使用 params(URL 中不显示某些参数) router.push({ name: 'details', params: { id: 123 } }); // 方案3:使用 sessionStorage/localStorage(最安全) // 传递时只传递 key,组件中获取真实数据 const passData = (data) => { const key = 'temp_' + Date.now(); sessionStorage.setItem(key, JSON.stringify(data)); router.push({ name: 'details', query: { key } }); }; // 接收组件 const key = route.query.key; const data = JSON.parse(sessionStorage.getItem(key)); sessionStorage.removeItem(key); // 清理

最佳实践:

// 传递复杂数据时 const complexData = { array: [1, 2, 3], object: { name: 'John', age: 30 }, nested: { level: 2, data: 'value' } }; // 使用 sessionStorage + key 方案 const key = 'data_' + Date.now(); sessionStorage.setItem(key, JSON.stringify(complexData)); router.push({ name: 'target', query: { key } }); // 目标页面接收 const key = route.query.key; const data = JSON.parse(sessionStorage.getItem(key));

6.computed 和 watch 的区别

特性computedwatch
作用计算属性,可缓存侦听属性变化
缓存✅ 有缓存❌ 无缓存
返回值必须可选
依赖追踪自动手动指定
异步❌ 不支持✅ 支持

computed 示例:

setup() { const firstName = ref('John'); const lastName = ref('Doe'); // 只有当 firstName 或 lastName 变化时才重新计算 const fullName = computed(() => { return `${firstName.value} ${lastName.value}`; }); return { fullName }; }

watch 示例:

setup() { const user = ref({ name: 'John', age: 30 }); // 监听单个属性 watch(() => user.value.name, (newVal, oldVal) => { console.log(`名字从 ${oldVal} 变为 ${newVal}`); }); // 监听对象(深度监听) watch( () => user.value, (newVal) => { console.log('用户信息更新:', newVal); }, { deep: true } ); // 异步操作 watch(user, async (newVal) => { const response = await fetch(`/api/user/${newVal.id}`); const data = await response.json(); console.log(data); }); return { user }; }

7.Vue3 的 setup 入口函数有什么作用

setup 函数: Vue 3 Composition API 的核心

export default { setup(props, context) { // props:接收父组件的 props // context.emit:派发事件 // context.slots:插槽 // context.attrs:属性 // context.expose:暴露属性 // 在这里定义响应式数据和方法 const count = ref(0); const increment = () => { count.value++; }; // 必须返回对象,其中的属性和方法才能在模板中使用 return { count, increment }; } }

setup 执行时机:

  • beforeCreatecreated 之间执行
  • 此时组件实例还未创建
  • 无法访问 this

新语法糖 <script setup>

<script setup> import { ref } from 'vue'; // 不需要手动 return,自动暴露 const count = ref(0); const increment = () => count.value++; // defineProps、defineEmits 等宏可直接使用 const props = defineProps({ msg: String }); const emit = defineEmits(['update']); </script>

8.vite 对比 vue-cli, 有什么优势和不足

特性ViteVue-CLI
开发服务器启动极快(秒级)较慢(分钟)
热更新极快需等待重新编译
打包工具esbuild/Rollupwebpack
配置学习曲线较平缓陡峭
生态插件快速增长成熟丰富
兼容性现代浏览器广泛支持
调试SourceMap 支持好需配置

优势:

// Vite:快速的开发体验 vite dev // 秒级启动

不足:

  • 较新,插件生态还在完善
  • 某些老版本浏览器不支持

9.vuex 实现几种不同的映射

import { mapState, mapGetters, mapMutations, mapActions } from 'vuex'; export default { // 1. mapState:映射 state computed: { ...mapState(['count', 'user']), // 或自定义名称 ...mapState({ counter: 'count', userData: 'user' }) }, // 2. mapGetters:映射 getters computed: { ...mapGetters(['doubleCount', 'userName']) }, // 3. mapMutations:映射 mutations methods: { ...mapMutations(['increment', 'decrement']), // 或自定义名称 ...mapMutations({ add: 'increment' }) }, // 4. mapActions:映射 actions methods: { ...mapActions(['fetchUser', 'updateUser']) } }

10.v-for 获取回数据未能立刻刷新视图

原因: Vue 不能检测到数组和对象变化的某些方法

问题示例:

// ❌ 不会触发视图更新 this.items[0] = newItem; this.obj.name = 'newName'; // ✅ 使用 $set 保证更新 this.$set(this.items, 0, newItem); this.$set(this.obj, 'name', 'newName'); // ✅ Vue 3 的 reactive API 自动处理 const state = reactive({ items: [], obj: {} }); state.items[0] = newItem; // 自动更新

11.列出几种父子组件通信的方法

  1. props + $emit

    <!-- 父 --> <Child :msg="message" @change="handleChange" /> <!-- 子 --> <script> props: ['msg'], emits: ['change'], methods: { update() { this.$emit('change', newValue); } } </script>
  2. v-model

    <Child v-model="count" />
  3. parent/parent / children(不推荐)

  4. ref

    <Child ref="child" /> <!-- 父组件直接访问子组件 --> this.$refs.child.method();
  5. Provide/Inject(跨层级)


12.Object.defineProperties() 和 Object.defineProperty() 的区别

特性definePropertydefineProperties
定义属性个数单个多个
语法(obj, prop, descriptor)(obj, props)

示例:

const obj = {}; // defineProperty:单个属性 Object.defineProperty(obj, 'name', { value: 'John', writable: true }); // defineProperties:多个属性 Object.defineProperties(obj, { age: { value: 30, writable: true }, email: { value: 'john@example.com', enumerable: true } });

13.base64 保证 image 标签正常使用, 应该怎么处理

// 方案1:直接在 src 中使用 const img = document.createElement('img'); img.src = 'data:image/png;base64,iVBORw0KGg...'; // 方案2:Canvas 转 base64 const canvas = document.createElement('canvas'); canvas.width = 200; canvas.height = 200; const ctx = canvas.getContext('2d'); ctx.fillStyle = 'blue'; ctx.fillRect(0, 0, 200, 200); const base64 = canvas.toDataURL('image/png'); img.src = base64; // 方案3:File/Blob 转 base64 const fileInput = document.querySelector('input[type="file"]'); const file = fileInput.files[0]; const reader = new FileReader(); reader.onload = (e) => { img.src = e.target.result; // 自动是 base64 格式 }; reader.readAsDataURL(file); // 方案4:确保 MIME 类型正确 const mimeType = 'image/png'; // 或 jpeg、webp 等 const base64String = '...'; // base64 字符串 img.src = `data:${mimeType};base64,${base64String}`;

14.区分 onLoad 和 onShow, onHide 和 onUnload

函数触发时机说明
onLoad页面加载仅一次,接收路由参数
onShow页面显示多次,每次切换到此页面
onHide页面隐藏页面失焦或被覆盖时
onUnload页面卸载页面被销毁时

15.some 和 every 的区别以及使用场景

方法返回条件使用场景
some至少一个元素满足条件检查是否存在符合条件的元素
every全部元素都满足条件检查是否全部符合条件
const nums = [1, 2, 3, 4, 5]; // some:是否存在大于 3 的数 nums.some(n => n > 3); // true // every:是否全部大于 0 nums.every(n => n > 0); // true // 实际应用 const users = [ { name: 'John', verified: true }, { name: 'Jane', verified: false } ]; // 检查是否有未验证的用户 if (users.some(u => !u.verified)) { console.log('存在未验证用户'); } // 检查是否全部验证通过 if (users.every(u => u.verified)) { console.log('全部用户已验证'); }

16.列出几种删除对象指定属性的方法

const obj = { a: 1, b: 2, c: 3 }; // 1. delete 操作符 delete obj.a; // { b: 2, c: 3 } // 2. 解构赋值(获取需要的属性) const { b, c } = obj; const newObj = { b, c }; // { b: 2, c: 3 } // 3. Object.keys() + reduce const newObj = Object.keys(obj) .filter(key => key !== 'a') .reduce((acc, key) => { acc[key] = obj[key]; return acc; }, {}); // { b: 2, c: 3 } // 4. Object.assign() + delete const newObj = { ...obj }; delete newObj.a; // { b: 2, c: 3 } // 5. Lodash omit const _ = require('lodash'); const newObj = _.omit(obj, ['a']); // { b: 2, c: 3 }

17.TypeScript 的 as 语法是什么

as 语法: 类型断言,告诉编译器把值当作特定类型

// 基础用法 const num = '123' as number; // ❌ 运行时不安全 const num = parseInt('123'); // ✅ 正确做法 // 实际应用 const elem = document.getElementById('app') as HTMLDivElement; // any 类型到具体类型 const data: any = { name: 'John' }; const user = data as { name: string; age?: number }; // 类型缩小 interface A { x: number } interface B { y: string } const val: A | B = { x: 1 } as A;

18.什么是 TypeScript 中的类型断言

类型断言: 告诉编译器值的具体类型,绕过类型检查

// 方式1:as 语法 const value = '123' as string; // 方式2:<type> 语法(JSX 中会冲突) const value = <string>'123'; // 使用场景 function getValue(value: string | number): string { if (typeof value === 'string') { return value.toUpperCase(); // 编译器能推断 } return (value as number).toFixed(2); }

19.解释 TypeScript 中的泛型

泛型: 编写可复用的、灵活的代码,同时保持类型安全

// 基础泛型 function identity<T>(value: T): T { return value; } identity<string>('hello'); // T = string identity<number>(123); // T = number // 泛型接口 interface Box<T> { content: T; } const stringBox: Box<string> = { content: 'hello' }; const numberBox: Box<number> = { content: 123 }; // 泛型约束 function printName<T extends { name: string }>(obj: T) { console.log(obj.name); } printName({ name: 'John', age: 30 }); // ✅ // printName({ age: 30 }); // ❌ Error // 默认泛型 type Response<T = any> = { status: number; data: T; }; const res: Response = { status: 200, data: 'any' }; const typedRes: Response<string> = { status: 200, data: 'hello' };
Last updated on