一、介绍一下 vue 的生命周期
1. 有哪些生命周期
一共有八个生命周期
- beforeCreate
- created
- beforeMount
- mounted
- beforeUpdate
- updated
- beforeDestroy(beforeUnmount)
- destroyed(Unmount)
2. 一旦进入组件会执行哪些生命周期
- beforeCreate
- created
- beforeMount
- mounted
3. 在那个阶段有$el
$data
- beforeCreate undefined undefined
- created undefined
$data
- beforeMount undefined
$data
- mounted
$el
$data
4. 如果加入keep-alive多两个生命周期
- activated
- deactivated
5. 加入keep-alive,第一次进入组件会执行哪些生命周期
- beforeCreate
- created
- beforeMount
- mounted
- activated
6. 加入keep-alive,第二次进入组件会执行哪些生命周期
- activated
7. 为什么要 beforeDestroy(vue2) → beforeUnmount(vue3)
- 准确描述生命周期阶段:强调组件从 DOM 卸载,而非实例被销毁。
- 统一行业术语:与 React 等框架保持一致,降低学习成本。
二、什么是渐进式框架
- vue.js只是一个核心库专注于视图层渲染,而其他功能(router、vuex等)则通过官方或第三方插件扩展。
三、谈谈你对keep-alive的了解
- 是什么?
- vue 自带的一个组件,功能:是来缓存组件的 -> 提升性能
- 使用场景
- 就是来缓存组件,提升项目的性能。具体实现比如:首页进入详情页,如果用户在首页每次点击都是相同的,那么详情页就没必要请求n次,直接缓存起来就可以了,如果点击的不是同一个,那么就直接请求
四、v-if和v-show的区别
- 展示形式不同
- v-if 是创建一个dom节点
- v-show 不是
- 使用场景不同
- 初次加载v-if要比v-show好,页面不会做加载盒子
- 频繁切换v-show要比v-if好,创建和删除的开销太大了,显示和隐藏开销较小
五、v-if和v-for的优先级
- v-for的优先级要比v-if更高,源码中的genElement体现
- 如果同时出现,每次渲染都会执行循环再判断条件,无论如何循环都不可避免,浪费了性能
- 要避免出现这种情况,则在外层嵌套template,在这一层进行v-if判断,然后在内部进行v-for循环
六、ref是什么
- 来获取dom的
七、scoped原理
- 作用:让css样式在本组件中生效,不影响其他组件
- 原理:给节点新增自定义属性,然后css根据属性选择器添加样式
八、vue样式穿透
- scss:父元素 /deep/ 子元素
- stylus:父元素 >>> 子元素
九、vue组件通信
首先是父子组件通信,这是最基础的。父组件向子组件传递数据可以通过 props,在子组件中定义 props 选项,父组件在使用子组件时通过属性传递值;子组件向父组件传递信息则用 $emit 触发事件,父组件通过 v-on 监听。比如父组件给子组件传一个 title,子组件通过点击事件把输入的内容传给父组件。
然后是兄弟组件通信,这种情况可以借助一个共同的父组件作为中间层,子组件 A 通过 $emit 把数据传给父组件,父组件再通过 props 传给子组件 B;也可以用 Vuex 或 Pinia 这样的状态管理库,把共享数据放在 store 里,兄弟组件都去操作和读取这个 store 中的数据。
十、vue的双向数据绑定原理
通过数据劫持和发布订阅者模式来实现,同时利用Object.defineProperty()劫持各个属性的setter和getter或者利用proxy去代理整个对象,在数据发生变化的时候发布消息给订阅者,触发对应的监听回调渲染视图,也就是说数据和视图是同步的,数据发生变化,视图跟着发生变化,视图变化,数据也会发生改变。
- Vue 2 :基于 Object.defineProperty() 劫持对象属性的
getter/setter
,通过递归遍历实现深度响应式,但无法检测对象新增属性或数组索引变更。 - Vue 3 :基于 Proxy 代理整个对象,直接拦截对象的所有操作(包括属性新增 / 删除、数组变异方法),并通过 Reflect 反射机制保持原始行为,实现更高效、全面的响应式系统。
十一、axios的简单封装
javascript
import axios from 'axios';
// 创建 axios 实例
const service = axios.create({
baseURL: process.env.NODE_ENV === 'production' ? 'https://api.example.com' : '/api', // 生产环境和开发环境的基础 URL
timeout: 5000, // 请求超时时间
headers: {
'Content-Type': 'application/json;charset=utf-8'
}
});
// 请求拦截器
service.interceptors.request.use(
config => {
// 在发送请求前做些什么
// 例如:添加 token
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
error => {
// 对请求错误做些什么
console.error('请求错误:', error);
return Promise.reject(error);
}
);
// 响应拦截器
service.interceptors.response.use(
response => {
// 对响应数据做些什么
const res = response.data;
// 假设业务状态码 200 表示成功
if (res.code !== 200) {
console.error('业务错误:', res.message || '请求失败');
return Promise.reject(new Error(res.message || '请求失败'));
} else {
return res.data; // 直接返回业务数据
}
},
error => {
// 对响应错误做些什么
console.error('响应错误:', error.message);
// 可以根据错误类型进行不同处理
if (error.response) {
switch (error.response.status) {
case 401:
// 未登录处理
console.error('未登录或登录已过期');
break;
case 403:
// 权限不足处理
console.error('权限不足');
break;
case 500:
// 服务器错误处理
console.error('服务器内部错误');
break;
}
}
return Promise.reject(error);
}
);
// 封装常用请求方法
const request = {
get(url, params = {}) {
return service.get(url, { params });
},
post(url, data = {}) {
return service.post(url, data);
},
put(url, data = {}) {
return service.put(url, data);
},
delete(url, params = {}) {
return service.delete(url, { params });
}
};
export default request;
typescript
import request from './request';
// GET 请求
request.get('/users', { id: 123 })
.then(data => {
console.log('用户数据:', data);
})
.catch(error => {
console.error('获取用户失败:', error);
});
// POST 请求
request.post('/login', { username: 'test', password: '123456' })
.then(data => {
console.log('登录成功:', data);
})
.catch(error => {
console.error('登录失败:', error);
});
十二、vue路由的hash和history模式有什么区别
1. URL 表现形式
-
hash 模式
- URL 中包含
#
符号,例如:https://example.com/#/home
#
后的内容是路由路径,不会被发送到服务器。
- URL 中包含
-
history 模式
- URL 看起来像传统的 URL,没有
#
,例如:https://example.com/home
- 路径会被完整发送到服务器,需要服务器配合处理。
- URL 看起来像传统的 URL,没有
2. 实现原理
-
hash 模式
- 基于浏览器的
hashchange
事件,监听 URL 中#
后的变化。 - 无论
#
后的路径如何变化,浏览器都不会向服务器发送请求,因此不会刷新页面。
- 基于浏览器的
-
history 模式
- 基于 HTML5 的
History API
(pushState
和replaceState
)。 - 通过
pushState
和replaceState
修改浏览器历史记录,实现无刷新切换路由
- 基于 HTML5 的
3. 如何选择
- 如果项目对 URL 美观度、SEO 有要求(爬虫忽略#后的路径不处理),且服务器支持配置(如 Nginx、Node.js),推荐使用 history 模式。
- 需要兼容低版本浏览器(如 IE9 及以下),对 URL 格式没有严格要求。
十三、vue路由是如何传参的
- params传参
php
this.$router.push({name:'index',params:{id:item.id}})
this.$router.params.id
- 路由属性传参
css
this.$router.push({name:'/index/${item.id}'})
路由配置:{path:'/index/:id'}
- query传参(可解决页面刷新参数丢失的问题)
php
this,$router.push({
name:'index',
query:{id:item.id}
})
xml
<template>
<ul>
<li v-for="item in blogList">
<router-link :to="'/blog'+item.id">
{{item.title}}
</router-link>
</li>
</ul>
</template>
<script>
export default {
data(){
return {
blogList:[
{id:1,title:'1'},
{id:2,title:'2'},
{id:3,title:'3'}
]
}
}
}
</script>
xml
<template>
<h1>博客详情:{{$route.params.id}}</h1>
</template>
php
const router = createRouter({
history:createWebHashHistory(),
routes:[
{
path:"/",
component:List
},
{
path:"/blog/:id",
component:Blog
}
]
})
十四、路由拦截
scss
{
name:'index',
path:'/index',
component:Index,
meta:{
requireAuth:ture
}
}
router.beforeEach((to,from,next)=>{ //路由守卫
if(to.meta.requirAuth){
if(store.satte.token){
next()
}else{}
}
})
十五、动态路由
动态路由是指在路由配置中使用可变参数(如 :id
、:name
)来匹配一类相似页面的路由机制,其核心是用同一套路由规则处理不同数据的访问需求,而非为每个具体页面单独配置路由。这种设计减少了路由配置的冗余,实现了 "一套组件处理多组数据" 的复用效果。在技术实现上,动态路由通过路径参数(如 $route.params.id
)传递数据标识,并需注意组件复用场景下的路由变化监听(如使用 watch
或 beforeRouteUpdate
钩子)。典型应用场景包括用户个人主页(/user/:username
)、文章详情页(/article/:id
)等。
十六、vue组件是什么
Vue 组件是 Vue.js 框架中构建用户界面的基本单元,它将 HTML 模板、JavaScript 逻辑和 CSS 样式封装在一起,实现了代码复用、模块化开发和关注点分离,使大型应用的构建和维护更高效。
十七、v-for中的key有什么用
key属性是DOM元素的唯一标识
- key的作用主要是为了高效的更新虚拟dom,其原理是vue在patch过程中通过key可以精准判断两个节点是否是同一个,从而避免频繁更新不同的元素,使得整个patch过程更加高效,减少dom操作量,提高性能
- 当组件使用
v-for
渲染且需维护内部状态时,key
可确保状态与特定元素绑定。若没有key
,Vue 可能复用错误的组件实例,导致状态混乱。
十八、vue2 和 vue3 的区别
Vue 3 是一次全面升级,主要体现在 架构范式 、性能优化 和 开发体验 三个层面:
- 范式转变:
特性 | Vue2 | Vue3 |
---|---|---|
API 风格 | 选项式(data/methods) | 组合式(setup ()) |
响应式 | Object.defineProperty | Proxy |
多根节点 | 不支持 | 支持 |
TypeScript | 需额外配置 | 原生支持 |
性能 | 一般 | 更好(编译优化、体积小) |
逻辑复用 | mixins(有缺陷) | 组合式函数(更灵活) |
- 性能飞跃:
特性 | Vue2 (Object.defineProperty) | Vue3 (Proxy) |
---|---|---|
拦截方式 | 劫持单个属性 | 代理整个对象 |
新增 / 删除属性 | 需手动调用 Vue.set/Vue.delete | 自动响应 |
深层嵌套对象 | 初始化时递归处理 | 访问时懒代理 |
数组索引修改 | 不支持(需用 splice 等方法) | 支持 |
性能 | 初始化时递归遍历所有属性,开销大 | 按需代理,内存占用更少 |
缺陷 | 存在响应式死角,代码冗余 | 兼容性问题(IE 不支持 Proxy) |
-
开发体验升级:
- 原生支持 TypeScript,结合 Composition API 提供更完善的类型推导。
- 新增特性如 Teleport 和 Suspense 简化了复杂场景的实现(如模态框、异步加载)。
十九、ref和reactive的区别
- ref
- 接收一个基本数据类型作为参数,返回一个有.value属性的对象
- ref 解构后仍保持响应式
- reactive
- 接受一个对象或数组作为参数,返回一个处理过后的对象,直接通过属性访问
- reactive 解构后会失去响应式,需配合 toRefs 保持
二十、vue常见的内置令
v-if,v-else,v-show,v-bind,v-on,v-model
二十一、watch和computed的区别
computed
:像「公式」,根据已有数据自动计算新值。watch
:像「监控器」,当数据变化时执行特定操作。- 有「计算逻辑」→ 用
computed
- 有「副作用」(如发请求、改 DOM)→ 用
watch
二十二、首屏优化
- 缓存:将长时间不会改变的第三方类库或者静态资源设置为强缓存,将 max-age 设置为一个非常长的时间,再将访问路径加上哈希达到哈希值变了以后保证获取到最新资源,好的缓存策略有助于减轻服务器的压力。
- 懒加载:当 url 匹配到相应的路径时,通过 import 动态加载页面组件,这样首屏的代码量会大幅减少,webpack 会把动态加载的页面组件分离成单独的一个 chunk.js 文件
- 预渲染:由于浏览器在渲染出页面之前,需要先加载和解析相应的 html、css 和 js 文件,为此会有一段白屏的时间,可以添加loading,或者骨架屏幕尽可能的减少白屏对用户的影响体积优化
- http2:可以在多个 tcp 连接中并发多个请求没有限制
二十三、页面性能指标
-
LCP( Largest Contentful Paint,最大内容绘制)
- 定义:衡量页面加载的速度,即视口内最大的内容元素(如图片、文本块)完成渲染的时间。
- 目标:理想值≤2.5 秒,超过 4 秒则为差。
- 意义:反映用户 "页面是否加载完成" 的第一印象。
-
FID(First Input Delay,首次输入延迟)
- 定义:衡量页面的交互响应速度,即用户首次与页面交互(如点击按钮、输入文本)到浏览器开始处理该交互的时间。
- 目标:理想值≤100 毫秒,超过 300 毫秒则为差。
- 意义:反映页面 "是否卡顿" 的即时体验。
注:未来将被 INP(Interaction to Next Paint)替代,INP 更全面地衡量所有交互的响应延迟。
-
CLS(Cumulative Layout Shift,累积布局偏移)
- 定义:衡量页面的视觉稳定性,即页面元素在加载过程中意外偏移的累积分数。
- 目标:理想值≤0.1,超过 0.25 则为差。
- 意义:避免用户因元素突然移位(如图片加载后挤压文本)而误操作。
二十四、data为什么是函数而不是对象
vue组件可能存在多个实例,如果用对象形式定义data,则会导致它们共用一个data对象,那么状态变更将会影响所有组件实例,这是不合理的;采用函数式定义,有效规避多实例之间状态污染问题。而在vue根实例创建过程中则不存在该限制,也是因为根实例只能有一个,不需要担心这种情况。
二十五、spa 和 mpa 的区别
- 组成
- spa:一个主页以及多个页面片段
- mpa:多个主页面
- 刷新
- spa:局部刷新
- mpa:多个主页面
- url
- spa:hash/history
- mpa:history
- seo
- spa:比较难
- mpa:任意实现
二十六、双向绑定和响应式
特性 | 响应式 | 双向绑定 |
---|---|---|
数据流方向 | 单向(数据 → 视图) | 双向(数据 ⇄ 视图) |
触发机制 | 数据变化触发视图更新 | 数据变化或视图事件(如输入)触发更新 |
适用场景 | 展示数据(如列表、详情页) | 表单交互(如登录、注册) |
实现方式 | 数据劫持 / 发布 - 订阅模式 | 响应式 + 事件监听 |