前言
写这篇笔记文章其实主要是不是面向找工作,算是一种对vue和前端的学习,因为面试题里面好些内容都是开发时没怎么注意到的,部分偏向理论和源码和....
文章主要包含了Vue2/3
、HTML、CSS
、浏览器...
的一些面试问题和知识,可能不是特别全,同时一个问题分为两个部分,我自己的理解
和来自百度、官方文档等
;有不对的地方希望各位大佬们不吝赐教,多多指正和交流
关于自己:一个特普通的学生,目前高三,省级、(国家\国际级)奖项拿了一些😊,喜欢摸鱼,但对于追女生这件事真的真的是汗颜🤔,目前主要学习和使用的框架/组件库:Vue2/3
、ts(基础)
、element-ui/plus
、spring boot(会用)
Vue2 🎉
Vue.js 2
是一个开源的前端 JavaScript 框架,由 Evan You 创建。以其易用性、灵活性和高效性而著称。 Vue2 的主要特性包括:
- 数据绑定:Vue2 实现了自动化的数据绑定,这意味着当你改变组件的状态(数据)时,相关的 UI 元素会自动更新,反之亦然。
- 组件化:Vue2 鼓励将用户界面拆分为可重用的组件,每个组件都有自己的独立逻辑和样式。
- 响应式系统:Vue2 使用 Observer 模式监视数据对象的变化,并在数据变化时触发相应的视图更新。
- 虚拟 DOM:Vue2 使用虚拟 DOM 算法来高效地比较和更新实际的 DOM,从而提高页面渲染的性能。
- 指令和插槽 :Vue2 提供了一套简洁的模板语法,包括指令(如
v-bind
、v-if
等)和内容分发插槽,用于在组件中声明动态部分。 - 路由和状态管理:虽然 Vue2 本身不包含这些功能,但可以与第三方库(如 Vue Router 和 Vuex)无缝集成,以实现复杂的单页应用(SPA)的路由管理和全局状态管理。
- 渐进式框架:Vue2 设计为渐进式框架,使用者可以根据需要逐步采用其功能,可以很容易地与其他现有项目或库集成。
简述题
-
请解释Vue2中的组件生命周期及其主要阶段的作用
-
my:主要常用的就是组件创建时
created
此时可以访问到data等,但是访问不到el节点可以在这时请求数据;挂载时mounted
此时可以访问到dom元素,可以做一些操作dom的东西;更新时updated
;销毁前beforeDestroy
是在组件销毁前的钩子,像一些离开一面给个未保存提示就可以在这里操作; -
Vue2中的组件生命周期主要包括以下主要阶段:
- 初始化阶段 :
beforeCreate
:在这个阶段,Vue实例刚刚被创建,数据观测和事件配置都还未完成,因此无法访问到数据和方法。created
:在这个阶段,Vue实例已经创建完成,数据观测、属性和方法的绑定都已经生效,但DOM挂载尚未进行。这个阶段通常用于执行一些初始化的ajax请求或其他设置。
- 挂载阶段 :
beforeMount
:在挂载开始之前被调用,此时模板编译已完成,但是还没有挂载到DOM中。mounted
:在Vue实例挂载完成后被调用,此时组件已经被渲染到页面中,可以进行DOM相关的操作。
- 更新阶段 :
beforeUpdate
:当数据发生变化触发组件更新时,在重新渲染视图前调用。在此阶段,可以获取到新的数据,但旧的DOM还未更新。updated
:数据更新后,组件DOM也已更新,此时可以访问到更新后的DOM。
- 销毁阶段 :
beforeDestroy
:在Vue实例销毁之前调用,此时实例仍然完全可用。destroyed
:Vue实例被销毁后调用,所有绑定和引用都被解绑,所有的子实例也会被销毁。
- 初始化阶段 :
-
-
在Vue2中,如何实现组件间的通信?列举并解释至少两种方法
-
my:中央事件总线(bus)、vuex监听、父子组件props/$emit事件监听传值
-
在Vue2中,实现组件间通信主要有以下几种常见方法:
-
props / $emit:
- 父子组件间的通信通常通过
props
向下传递数据,父组件将需要的数据作为属性传给子组件,子组件通过props选项接收并使用这些数据。 - 反向通信(子组件向父组件传递信息)则可以通过自定义事件
$emit
实现。子组件触发一个事件,并携带要传递的参数,父组件监听这个事件并在回调函数中处理传递过来的信息。
示例:
javascript// 子组件 Vue.component('child', { props: ['parentMessage'], methods: { sendToParent() { this.$emit('childEvent', 'Message from child'); } } }); // 父组件 <div id="app"> <child :parent-message="parentMsg" @childEvent="handleChildEvent"></child> </div> <srcipt> data() { return { parentMsg: 'Hello from Parent', }; }, methods: { handleChildEvent(childMessage) { console.log(childMessage); // 输出:Message from child } } </script>
- 父子组件间的通信通常通过
-
全局事件总线(Event Bus) :
- 全局事件总线是一种让任意两个没有直接父子关系的组件之间通信的方式。创建一个新的Vue实例作为中央事件总线,然后利用它的
$on
、$emit
和$off
方法来发布和订阅事件。
示例:
javascript// 创建一个全局事件总线 const eventBus = new Vue(); // 在组件A中发送事件 eventBus.$emit('customEvent', someData); // 在组件B中接收事件 created() { eventBus.$on('customEvent', (data) => { console.log(data); }); // 组件销毁时移除事件监听 beforeDestroy() { eventBus.$off('customEvent'); } }
- 全局事件总线是一种让任意两个没有直接父子关系的组件之间通信的方式。创建一个新的Vue实例作为中央事件总线,然后利用它的
-
Vuex:
- Vuex是Vue.js的状态管理模式,它采用集中式存储管理应用的所有组件的状态,能够更方便地进行状态管理和组件间的通信。组件可以直接从store中获取状态或触发actions修改状态,从而实现跨组件的数据共享。
-
provide/inject:
- Vue2提供了
provide
和inject
选项,允许祖先组件向所有后代组件提供数据,无需明确指定哪一个后代组件会使用这些数据。这为跨越多层嵌套的组件间通信提供了一种简洁的方式。
- Vue2提供了
-
-
-
请描述Vue2中的vue-router路由守卫有哪些,以及它们在何时被调用
-
my:
- 全局路由守卫:
beforeEach
在路由跳转前拦截,可以重定向或阻止路由跳转,比如验证登录等;afterEach
拦截发生在路由跳转后,无法干预路由,可以获取到从哪里和到哪去,此时可以获取到document
,比如可以在此时修改页面title - 组件内路由守卫:
beforeRouteEnter
组件渲染前触发,但是此时还不能访问到组件的this;beforeRouteUpdate
在路由发生跳转但组件被复用时触发,比如动态路由/user/:id
;beforeRouteLeave
,离开组件时触发,可以访问this
- 全局路由守卫:
-
Vue Router在Vue2中提供了多种路由守卫,它们可以用于在路由切换的过程中执行自定义的逻辑,比如权限验证、数据预取等。以下是Vue Router 2.x中的主要路由守卫:
-
全局前置守卫 (
router.beforeEach
和router.beforeResolve
)beforeEach
:每次路由导航发生时都会调用。它接受三个参数:to
(即将要进入的目标路由对象)、from
(当前导航正要离开的路由)、和next
(一个必须调用的函数,决定是否继续导航)。beforeResolve
:与beforeEach
类似,但在所有组件内守卫和异步路由组件被解析之后,但在导航被确认之前调用。
-
全局后置钩子 (
router.afterEach
)- 在每次路由跳转完成后调用,不具有控制路由跳转的能力,主要用于页面浏览历史记录的跟踪或其他需要在路由跳转完成后的操作。
-
路由独享守卫 (
beforeEnter
)- 可以直接在路由配置对象上定义此方法,仅对特定路由生效。当试图进入这个路由时会调用,接收同样的
to
和from
参数以及next
函数。
- 可以直接在路由配置对象上定义此方法,仅对特定路由生效。当试图进入这个路由时会调用,接收同样的
-
组件内的守卫:这些守卫是定义在组件内部的,与组件实例生命周期紧密相关:
beforeRouteEnter
:在渲染该组件的对应路由被确认前调用,在这里无法访问到this
,因为守卫在路由守卫调用前就已经被调用了。不过可以通过传给next
函数的回调来访问组件实例。beforeRouteUpdate
:在当前路由改变,但是通过同一个组件渲染新路由的情况下调用,此时可以访问到this
,并可用来更新组件状态。beforeRouteLeave
:在导航离开路由时调用,无论目标路由是什么,都可以访问到this
,常用于清除或保存信息等操作。
-
-
-
Vue2中如何创建并使用自定义指令(custom directive)?
-
my:?
-
首先,在项目的
src
目录下或你希望存放指令的地方创建一个文件,比如directives.js
。javascript// directives.js export default { // 定义名为 'my-directive' 的指令 myDirective: { // 绑定元素到DOM时调用,仅执行一次 bind(el, binding, vnode) { console.log('bind:', el); // 可以在这里设置事件监听器等初始化操作 }, // 被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被渲染) inserted(el, binding, vnode) { console.log('inserted:', el); }, // 组件更新时调用,此时可以访问到新旧两个虚拟节点 update(el, binding, vnode, oldVnode) { console.log('update:', el); }, // 组件 DOM 更新完成时调用 componentUpdated(el, binding, vnode, oldVnode) { console.log('componentUpdated:', el); }, // 指令与元素解绑时调用 unbind(el, binding, vnode) { console.log('unbind:', el); // 在这里进行清理工作,如删除事件监听器等 } } }
然后,在
main.js
文件中引入并注册这个自定义指令:javascriptimport Vue from 'vue'; import App from './App.vue'; import customDirectives from './directives'; // 引入自定义指令模块 // 全局注册自定义指令 Object.keys(customDirectives).forEach(key => { Vue.directive(key, customDirectives[key]); }); new Vue({ render: h => h(App), }).$mount('#app');
最后,在Vue组件的模板中,你可以像这样使用刚刚创建的自定义指令:
html<template> <div v-my-directive="someValue"></div> </template>
这里的
v-my-directive
就是你在directives.js
中定义的指令名称。如果需要传递参数或者修饰符,可以按照Vue的指令语法来书写,例如:v-my-directive:argument.modifier="expression"
。
-
-
在Vue2中,什么是 Vuex 状态管理库?请描述其核心概念及作用。
-
my:vuex是vue的集中式数据管理存储库,任何组件都可以访问到里面的值,为多组件间互通带来便利,也可以让数据不混乱;vuex中主要有5个东西:
state
用于存放数据;getters
类似于计算属性,常用于处理和格式化某些数据,比如手机号加 * 号;mutations
用于改变state
中的值,且只有他能;actions
是异步修改state的方式,但是他依旧不是直接修改state,而是通过调用mutations
来修改;modules
用于将vuex模块化,每一个模块都可以拥有自己的getters
、mutations
等方法,可以让业务逻辑更加清晰不混乱 -
Vuex 是 Vue.js 的官方状态管理库,它为大型单页应用(SPA)提供了一种集中式存储和管理组件状态的方式。在Vue2中,Vuex通过以下几个核心概念实现了对复杂应用程序状态的高效管理和控制:
- State: Vuex的核心是store中的state对象,它是单一源数据存放地,所有组件共用一个状态树。所有可共享的状态数据都集中在这里进行维护。
- Getters: 类似于计算属性,getters用于从store的state派生出新的状态。它们允许我们定义一些基于store状态的函数,并且这些函数的结果会根据store状态的变化自动更新。
- Mutations: 变更state的唯一方法是提交mutation。每个mutation都有一个字符串类型标识符和一个处理函数,这个函数接收state作为第一个参数,其他需要的参数可以通过payload传递。Mutation必须是同步操作。
- Actions: 当需要异步操作来改变状态时,可以使用actions。Actions可以包含异步逻辑并提交mutations,从而间接改变状态。Actions同样接受context对象作为参数,该对象包含commit、dispatch和 getters 方法,以便于触发mutation或调用其他actions。
- Modules: 随着应用规模的扩大,state结构可能变得非常庞大和复杂。Vuex支持模块化(state modules),允许我们将store分割成多个模块,每个模块拥有自己的state、mutations、actions和getters,这样可以更好地组织代码和重用状态管理逻辑。
综上所述,Vuex的主要作用是:
- 提供了一个中心化的存储系统,便于跨组件共享和管理状态。
- 通过严格的规则确保状态以可预测的方式进行修改,增强了应用的状态一致性。
- 提供了工具帮助开发者更好地组织复杂的业务逻辑和异步操作。
-
-
请简述Vue2中的
v-for
循环指令的用法及其可选参数-
my:v-for是vue提供的模板循环指令,有两个可选参数为(item,index),可以用于遍历数组;(key,value)可以用于遍历一个对象,还可以直接遍历一个数字或字符串:
o in 10
、o in 'abc'
,一般推荐在使用v-for的时候给节点加上key属性html<div v-for="(item,index) in list" :key="index">{{ item }}</div>
-
Vue2中的
v-for
循环指令用于对数组或对象进行迭代渲染,将数据映射为DOM结构。它的基本用法如下:- 循环数组:
html<div v-for="item in items"> {{ item }} </div>
或者可以带索引:
html<div v-for="(item, index) in items"> {{ index }}: {{ item }} </div>
在这里,
items
是需要遍历的数组,item
是当前项的别名,index
是可选的当前项索引。
2. 循环对象(键值对):html<div v-for="(value, key, index) in object"> Key: {{ key }} - Value: {{ value }} </div>
在这个例子中,
object
是要遍历的对象,value
和key
分别代表当前键值对的值和键,index
是当前项在对象所有属性中的索引。可选参数:
item
:必填项,表示数组元素或者对象属性的别名,在循环体内部可以直接使用。index
(数组):可选参数,表示当前元素的索引位置,从0开始计数。key
(对象):可选参数,表示对象当前属性的键名。(value, key) in object.entries()
(ES6对象):对于支持ES6 Map和Set的数据结构,或者通过Object.entries()转换后的原生对象,可以同时访问键和值。
此外,
v-for
还可以配合v-bind:key
一起使用,为每一个循环生成的节点指定一个唯一的键值,提高虚拟DOM更新的效率:html<div v-for="(item, index) in items" :key="item.id"> <!-- ... --> </div>
在这个例子中,
:key
属性通常应绑定到唯一标识每个列表项目的值,以便Vue能够更高效地处理列表更新时的DOM操作。
-
-
如何在Vue2中实现组件间的深度选择器(deep select)以及它的应用场景是什么?
-
my:一看到懵了一下,事实上指的的是style中的样式穿透,因为一般组件里面的
<style>
我们会加上一个作用域修饰符scoped
,但是有时候我们要控制组件外的样式的时候就需要用到样式穿透,比如我的组件里面引入了一个组件库的组件,但是我要调整一下这个组件的样式,但是我只希望改变不是全局的,就可以用样式穿透;vue2中的语法.child-component /deep/ .el__table {}
-
在Vue2中,如果你想要修改子组件内部的样式而不影响到其他地方该组件的样式,可以使用深度选择器(有时也称为作用域穿透)。Vue.js自身并不直接支持CSS中的深度选择器,但通过特殊的CSS组合器或伪类选择器可以在单文件组件(SFC)的
<style>
标签内实现类似的功能。在Vue2中,深度选择器的常见实现方式是使用
/deep/
或者>>>
(在旧版本的CSS预处理器如Sass中可能需要使用这个符号,但在CSS本身以及现代预处理器中已弃用):html<style scoped> .child-component /deep/ .target-class { /* ... */ } </style>
或者
html<style scoped> .child-component >>> .target-class { /* ... */ } </style>
在Vue3及更高版本中,由于
/deep/
和>>>
已被弃用,你可以使用新的:deep()
伪类选择器:html<style scoped> .child-component :deep(.target-class) { /* ... */ } </style>
应用场景:
- 当你希望在一个父级组件中为第三方UI库的子组件定制样式时,而又不希望这些样式影响到全局或其他使用同样UI组件的地方。
- 当你需要修改一个嵌套层级较深的元素样式,而这个元素不在当前组件的作用域内时。
-
-
请解释Vue2中的依赖收集机制,并说明是如何触发视图更新的
-
my:?
-
Vue2中的依赖收集机制是其响应式系统的核心部分,它基于
Object.defineProperty()
方法对数据对象的属性进行getter和setter拦截。当一个组件渲染时,Vue会遍历模板中的表达式,并在访问这些表达式对应的属性时调用它们的getter。getter中内嵌了依赖收集逻辑,会将当前正在运行的Watcher(观察者)实例添加到被访问属性的依赖收集列表(Dep)中。这个Watcher通常与组件实例绑定,代表了该组件视图的更新任务。
例如,在模板中有一个绑定到data对象上某个属性的插值表达式
{{ message }}
。当Vue初始化或重新渲染组件时,会触发message
属性的getter,进而将当前组件的Watcher添加到message
属性对应的Dep中。当组件实例的数据发生变化时,如通过
this.message = 'new value'
赋新值,会触发message
属性的setter。setter内部会通知所有订阅了该属性变化的Watcher,即从Dep中取出所有的Watcher并执行它们的update方法,从而触发视图的重新渲染。总结来说,Vue2依赖收集机制的工作流程如下:
- 初始化阶段,Vue使用
Object.defineProperty()
为数据对象的所有属性创建getter和setter。 - 渲染过程中,访问数据属性时自动触发getter,收集当前组件的Watcher。
- 当数据发生变化时,调用setter,setter通知所有订阅者(Watcher)。
- Watcher收到通知后,执行视图更新函数,对比前后虚拟DOM树差异,然后根据差异同步更新真实DOM。
最终,Vue正是通过这种依赖收集和派发更新的方式实现了数据驱动视图的变更。
- 初始化阶段,Vue使用
-
-
在Vue2项目中,如何处理路由懒加载和按需加载,以优化页面加载性能?
-
my:Vue中默认加载的时候会直接导入所有路由中所导入组件,不管有没有访问这个路由,可能会导致一些无效的代码体积,所以可以使用动态导入,当要访问这个路由的时候才加载对应的组件;
() => import('@/views/error/404.vue')
-
在Vue2项目中,使用Vue Router进行路由懒加载和按需加载以优化页面性能的方法是通过动态导入(
import()
)实现的。以下是如何配置路由懒加载的方式:假设有一个名为
Home.vue
、About.vue
和Contact.vue
的组件,想在需要时才加载它们:javascript// 在 Vue Router 的配置文件 router.js 中 // 引入 Vue Router import VueRouter from 'vue-router'; // 创建一个路由实例 const router = new VueRouter({ routes: [ // 使用函数形式定义路由,并返回一个 Promise,该 Promise 解析为异步导入的组件 { path: '/', component: () => import('./views/Home.vue') }, { path: '/about', component: () => import('./views/About.vue') }, { path: '/contact', component: () => import('./views/Contact.vue') } ] }); export default router;
这里每个路由对应的
component
属性值不再是一个直接引用到组件的路径,而是一个返回Promise的函数,这个Promise最终会解析成组件模块。当用户导航至相应的路由时,Vue Router会自动处理异步加载对应组件的逻辑,从而实现按需加载,减少初始页面加载时间,提高应用性能。此外,在Webpack等构建工具的支持下,这些动态导入会被转换为可执行的代码片段,确保只加载所需路由所关联的代码块。
-
-
Vue2中如何利用
v-model
实现组件的双向数据绑定,并解释其实现原理?-
my:?
-
在Vue2中,要实现自定义组件的
v-model
双向数据绑定,你需要遵循以下步骤:- 定义自定义组件 : 创建一个具有内部状态(value属性)并能响应外部变化的自定义组件。例如创建一个名为
CustomInput
的输入框组件。
html<!-- CustomInput.vue --> <template> <input :value="internalValue" @input="onInput"> </template> <script> export default { props: { // 定义用于双向绑定的prop value: { type: String, required: true } }, data() { return { // 内部状态,初始值与props.value同步 internalValue: this.value }; }, methods: { onInput(event) { // 当input元素的值发生变化时,触发更新 this.$emit('input', event.target.value); } }, watch: { // 监听外部传入的value prop的变化,并同步到内部状态 value(newVal) { this.internalValue = newVal; } } }; </script>
- 使用v-model进行绑定 : 在父组件中通过
v-model
指令将自定义组件与父组件的数据属性关联起来。
html<!-- ParentComponent.vue --> <template> <div> <custom-input v-model="parentData"></custom-input> </div> </template> <script> import CustomInput from './CustomInput.vue'; export default { components: { CustomInput }, data() { return { parentData: '' }; } }; </script>
- 原理解释:
- Vue中的
v-model
实际上是语法糖,它默认情况下等同于:value
绑定和@input
事件监听器的组合。 - 在自定义组件内部,我们声明了一个
value
prop来接收父组件通过v-model
传递过来的值,并将其赋给内部状态internalValue
。 - 当内部的输入元素发生
input
事件时,调用onInput
方法,该方法触发一个名为input
的自定义事件,并携带新的值作为参数。这个自定义事件名称与原生input
元素的input
事件相呼应,这是Vue期望自定义组件支持v-model
时需要发出的事件名称。 - 父组件接收到这个
input
事件后,会自动更新parentData
的值,从而实现了从子组件到父组件的数据流动。 - 另一方面,我们也设置了对
value
prop的watcher,当父组件更改parentData
进而导致value
prop变更时,会同步更新自定义组件内部的internalValue
,这样就完成了从父组件到子组件的数据流动,形成了双向绑定的效果。
- 定义自定义组件 : 创建一个具有内部状态(value属性)并能响应外部变化的自定义组件。例如创建一个名为
-
Vue3 🚀
Vue3,由尤雨溪(Evan You)主导开发,并于2020年正式发布。Vue3 在保持易用性和灵活性的同时,对框架的核心架构进行了重构和优化,以提升性能、可维护性以及与现代前端开发实践的契合度。
相较于 Vue2,Vue3 的主要提升和改进包括:
- 响应式系统升级 :Vue3 使用 ES6 的 Proxy 对象替代了 Vue2 中基于
Object.defineProperty
的实现,新系统的响应式原理能够更好地处理对象属性的动态添加和删除,且性能更优。 - 编译器优化 :
- 静态提升(hoistStatic):Vue3 编译器可以识别并提前编译静态节点,减少运行时的计算负担。
- 模板缓存和增量编译:通过优化模板编译过程,提高大型应用的构建速度。
- 组件初始化速度提升:通过静态分析,Vue3 能够在编译阶段预知某些部分的状态,从而减少运行时的工作量。
- Virtual DOM 算法优化:Vue3 对 Virtual DOM 的 diff 算法进行了优化,减少了不必要的渲染开销。
- 更好的 TypeScript 支持:Vue3 的设计过程中就考虑到了 TypeScript 的集成,提供了更好的类型提示和静态类型检查能力。
- Composition API:引入了 Composition API,它提供了一种更加灵活、易于逻辑复用和测试的编程模型,相比 Vue2 的 Options API 更具组织性和扩展性。
- 按需编译和打包体积减小:Vue3 使用 Tree-shaking 技术使得最终构建产物中只包含实际使用的代码,进一步缩小了 JavaScript 打包体积。
- API 设计简化与废弃 :移除了一些不常用或者容易引起混淆的 API,如过滤器
$on
、$off
、$once
等实例方法,同时提供了新的事件处理机制。 - 自定义渲染API增强:Vue3 提供了更强大的自定义渲染API,使得开发Weex、SSR等场景下的应用更为便捷。
简述题
在写Vue2的时候发现由于平时是间断的自学,导致感觉还是有好些东西学的不好,只停留在会用阶段,对于一些偏向
思想
还有基础
、底层
、更好的写法
都还是很欠缺,所以Vue3部分的就先打算暂缓,接下来一段时间将针对性的学一下Vue3更深的一些知识,ES近几个版本,像ES2020之类的,写代码更好的写法,就像是体育生沉淀
后端部分学的也比较汗颜,在沉淀完上一条计划后应该是就打算好好的学一学spring
和后端技术体系,比如分布式、比如周边生态(Linux
、*SQL
)
文章有写的不对或不好的地方希望大佬多多指正,一起交流,多带带我
,谢谢!