前端面试题之Vue
对SPA单页面的理解,它的优缺点分别是什么
参考答案
SPA在Web页面初始化时加载相应的HTML、CSS、JavaScript。一旦页面加载完成,SPA不会因为用户的操作而进行页面的重新加载或跳转。取而代之的是利用路由机制实现HTML内容的变换,UI与用户的交互,避免页面的重新加载。
优点:
- 用户体验好且快,内容的改变不需要重新加载整个页面,避免了不必要的跳转和重复的渲染;
- SPA单页面对服务器的压力小;
- 前后端职责分离,架构清晰,前端进行交互逻辑,后端负责数据处理。
缺点:
- 初次加载比较耗时:为实现单页Web应用功能及显示效果,需要在加载页面的时候将CSS、JavaScript这些都统一加载,部分页面按需加载;
- 前进后退路由管理:由于单页应用在一个页面中显示所有的内容,所以不能使用浏览器的前进后退功能,所有的页面切换需要自己建立堆栈来管理;
- SEO难度较大:由于所有的内容都在一个页面中动态替换显示,所以在SEO上比较弱。
Vue的生命周期有哪些
参考答案
- 创建:beforeCreate,created;
- 载入:beforeMount,mounted;
- 更新:beforeUpdate,updated;
- 销毁:beforeDestroy,destroyed。
生命周期示意图:
第一次页面加载会触发哪几个钩子
参考答案
beforeCreate、created、beforeMount、mounted
每一个周期具体适合哪些场景
参考答案
- beforeCreate:在 new 一个 Vue 实例后,只有一些默认的生命周期钩子和默认事件,其他的东西都还没创建。不能获取 DOM 节点。data 和 methods 中的数据都还没有初始化,不能在这个阶段使用 data 中的数据和 methods 中的方法。
- created:实例已经创建,仍不能获取 DOM 节点,data 和 methods 都已经被初始化好了,如果要调用 methods 中的方法,或者操作 data 中的数据,最早可以在这个阶段里操作。
- beforeMount:在内存中已经编译好模板了,但是还没有挂载到页面中,此时,页面还是旧的。
- mounted:Vue 实例已经初始化完成了,此时组件脱离了创建阶段,进入到运行阶段。如果我们想要通过插件操作页面上的 DOM 节点,最早可以在这个阶段中进行。
- beforeUpdate:当执行这个钩子时,页面中的显示的数据还是旧的,data 中的数据是更新后的,页面还没有和最新的数据保持同步。
- updated:页面显示的数据和 data 中的数据已经保持同步了,都是最新的。
- beforeDestroy:Vue 实例从运行阶段进入到了销毁阶段,这个时候所有的 data、methods、指令、过滤器等等都是处于可用状态,还没有真正被销毁。
- destroyed:组件已经被销毁,无法操作里面的任何东西了。
Vue获取数据一般在哪个周期函数
参考答案
在 created、beforeMount、mounted 中都可以。
不要在 updated 里更新数据。
父子组件在生命周期中调用的顺序是怎样的
参考答案
- 加载渲染过程:parent beforeCreate -> parent created -> parent beforeMount -> son beforeCreate -> son created -> son beforeMount -> son mounted -> parent mounted;
- 父组件更新过程:parent beforeUpdate -> parent updated;
- 子组件更新过程:parent beforeUpdate -> son beforeUpdate -> son updated -> parent updated;
- 销毁过程:parent beforeDestroy -> son beforeDestroy -> son destroyed -> parent destroyed。
怎样让CSS只在当前组件中生效
参考答案
在组件中的 style 标签上加上 scoped。
<style scoped>
</style>
template下为什么只能有一个div呢
参考资料
如何给vue自定义组件添加点击事件
如何获取DOM
参考答案
- 原生 JS 获取;
- ref 获取;
// 1. 原生 JS 获取
<div class="getDom"></div>
mounted() {
let getDom = document.querySelector(".getDom")
}
// 2. ref 获取
<div ref="getDom"></div>
mounted() {
let getDom = this.$refs.getDom
}
Vue常用的指令有哪些
参考答案
- v-html:会将html代码解析出来并进行渲染
- v-text:输出文本
- v-if:条件判断指令
- v-show:是否隐藏元素
- v-for:循环指令
- v-bind:属性绑定指令(数据的单向绑定)
- v-model:实现数据的双向绑定
- v-on:绑定事件指令
- v-once:表示元素和组件只会渲染一次
- v-pre、v-cloak
v-show与v-if的区别
参考答案
- v-show的本质是改变display的值。
- v-if是动态向DOM树内添加或者删除DOM元素。
v-show就是控制CSS的display,而v-if是不停的销毁和创建,因此如果需要频繁切换使用v-show性能会更好一点。
v-if和v-for的优先级是什么
参考答案
根据 官方文档 所描述的:不推荐同时使用 v-if 和 v-for。如果 v-if 与 v-for 一起使用时,v-for 具有比 v-if 更高的优先级。
这也就意味着 v-if 将分别重复运行于每个 v-for 循环中。因此是不推荐将这个两个指令同时使用。
vue-loader是什么及用途
参考答案
vue-loader 是解析 .vue
文件的一个加载器,它可以将 template / js / style 转换成 JS 模块。
⽤途:JS 可以写 ES6、style 样式可以 scss / less、template 可以加 jade 等等。
什么是MVVM
参考答案
MVVM是Model-View-ViewModel的缩写,它是一种设计思想。
- Model层代表数据模式,也可以在Model中定义数据修改和操作的业务逻辑
- View代表UI组件,它负责将数据模型转化为UI展现出来
- ViewModel是一个同步View和Model的对象
Vue组件之间如何通信
参考资料
Vue中key值的作用是什么
参考答案
使用 key 来给每个节点做一个唯一标识,Diff 算法就可以正确的识别此节点。
key 的作用主要是为了高效的更新虚拟 DOM。
v-model的原理
参考答案
v-model就是一个语法糖,它背后本质是包含了两个操作:
- v-bind绑定一个value属性
- v-on给当前元素绑定input事件
<input type="text" v-model="message">
<!-- 等价于 -->
<input type="text" :value="message" @input="message = $event.target.value">
Class与Style如何动态绑定
参考答案
Class
对象语法
绑定class对象语法:对象的键是类名,值是布尔值。
<div v-bind:class="{ active: isActive, size: isSize }">对象绑定Class</div>
data: {
isActive: true,
isSize: false
}
数组语法
绑定class数组语法:数组中的成员直接对应类名。
<div v-bind:class="[isActive,isSize]">数组绑定class</div>
data: {
isActive: 'active',
isSize: 'size'
}
Style
对象语法
<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }">对象绑定Style</div>
data: {
activeColor: 'red',
fontSize: 60
}
数组语法
<div v-bind:style="[styleColor, styleSize]">数组绑定Style</div>
data: {
styleColor: {
color: 'red'
},
styleSize: {
fontSize: '23px'
}
}
computed和watch的区别
参考答案
computed
computed 是计算属性,它会根据你所依赖的数据动态显示新的计算结果。计算结果会被缓存,computed 的值在 getter 执行后是会被缓存的,只有在它依赖的属性值改变之后,下一次获取 computed 的值时才会重新调用对应的 getter 来计算。
应用场景:购物车商品结算,当我们需要进行数值计算,并且依赖于其它数据时,应该使用 computed,可以利用 computed 的缓存特性,避免每次获取值时,都要重新计算。
watch
watch 是去监听一个值的变化,然后执行相对应的函数。它可以接受 2 个参数(newValue, oldValue),即变化的最新值和上一次变化的旧值。它是没有缓存的。
应用场景:搜索数据,如果你需要在某个数据变化时做一些事情,使用 watch 来观察这个数据变化。
Vue的修饰符有哪些
参考答案
表单修饰符
.lazy:让数据在失去焦点或者回车时才会更新同步;
.number:自动将用户的输入值转为数值类型;
.trim:自动过滤用户输入的首尾空白字符;
事件修饰符
.stop:阻止冒泡;
.prevent:阻止默认行为;
.self:只会触发自己范围内的事件,不包含子元素;
.once:事件将只会触发一次;
.capture:与事件冒泡的方向相反,事件捕获由外到内;
.passive:Vue还对应addEventListener中的passive选项提供了.passive修饰符,能够提升移动端的性能。
不要把.passive和.prevent一起使用,因为.prevent将会被忽略,同时浏览器可能会向你展示一个警告。请记住,.passive会告诉浏览器你不想阻止事件的默认行为。
按键修饰符
.enter:回车键;
.tab:制表键;
.delete:含删除和退格键;
.esc:返回键;
.space: 空格键;
.up:向上键;
.down:向下键;
.left:向左键;
.right:向右键;
系统修饰键
可以用如下修饰符来实现仅在按下相应按键时才触发鼠标或键盘事件的监听器。
.ctrl
.alt
.shift
.meta
鼠标按钮修饰符
.left:左键点击;
.right:右键点击;
.middle:中键点击;
.exact修饰符
.exact:修饰符允许你控制由精确的系统修饰符组合触发的事件。
<!-- 即使 Alt 或 Shift 被一同按下时也会触发 -->
<button v-on:click.ctrl="onClick">A</button>
<!-- 有且只有 Ctrl 被按下的时候才触发 -->
<button v-on:click.ctrl.exact="onCtrlClick">A</button>
<!-- 没有任何系统修饰符被按下的时候才触发 -->
<button v-on:click.exact="onClick">A</button>
Vue组件中data为什么是函数
什么是动态组件
参考答案
如果有多个组件需要通过同一个挂载点来进行组件的切换,此时就可以用到动态组件,使用 component
标签,并使用 :is,它的值是哪个组件的名称,那么就会显示哪个组件。
<component :is="otherComponent"></component>
什么是异步组件
参考答案
在大型应用中,我们可能需要将应用分割成小一些的代码块,并且只在需要的时候才从服务器加载一个模块。为了简化,Vue 允许你以一个工厂函数的方式定义你的组件,这个工厂函数会异步解析你的组件定义。Vue 只有在这个组件需要被渲染的时候才会触发该工厂函数,且会把结果缓存起来供未来重渲染。
components: {
asyncComponent: () => import('../components/asyncComponent')
}
keep-alive的作用是什么
Vue是如何实现数据的双向绑定的
参考答案
Vue 双向数据绑定是通过数据劫持结合发布订阅模式的方式来实现的,也就是说数据和视图同步,如果数据发生变化,那么视图也跟着变化,视图变化,数据也随之发生改变;其核心是 Object.defineProperty() 方法。
过滤器是什么,怎么使用呢
参考答案
Vue.js 允许你自定义过滤器,可被用于一些常见的文本格式化。过滤器可以用在两个地方:双花括号插值和 v-bind 表达式。过滤器应该被添加在 JavaScript 表达式的尾部,由“管道”符号指示:
<!-- 在双花括号中 -->
{{ message | capitalize }}
<!-- 在 `v-bind` 中 -->
<div v-bind:id="rawId | formatId"></div>
有两种注册方式:全局注册、局部注册。
// 全局注册
Vue.filter('capitalize', function (value) {
...
})
// 局部注册
filters: {
capitalize: function (value) {
...
}
}
注意:当全局过滤器和局部过滤器重名时,会采用局部过滤器。
PS:从 Vue 3.0 开始,过滤器已删除,不再支持。在 3.x 中,可以用方法调用或计算属性替换它们。
Vue中的$nextTick有什么作用
参考答案
- Vue.nextTick(callback):在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。
- vm.$nextTick(callback):将回调延迟到下次 DOM 更新循环之后执行。在修改数据之后立即使用它,然后等待 DOM 更新。
看如下代码:
<button @click="add">add</button>
<ul ref="ulRef">
<li v-for="item in items">{{item}}</li>
</ul>
let app = new Vue({
el: '#app',
data() {
return {
items: [1, 2, 3]
}
},
methods: {
add() {
this.items.push(Math.random())
let ele = this.$refs.ulRef
let length = ele.childNodes.length
console.log(length);
}
},
})
当我们点击按钮后,会发现控制台打印出 3,这不是我们想要的,我们认为此时应该打印出 4,这时候需要用到 $nextTick 来解决此问题。
add() {
this.items.push(Math.random())
this.$nextTick(() => {
let ele = this.$refs.ulRef
let length = ele.childNodes.length
console.log(length);
})
}
对mixin的理解,有什么应用场景
参考资料
Vue.observable()有什么用,怎么用
参考答案
它可以让一个对象可响应。Vue 内部会用它来处理 data 函数返回的对象。
返回的对象可以直接用于渲染函数和计算属性内,并且会在发生变更时触发相应的更新。也可以作为最小化的跨组件状态存储器。
在 Vue 2.x 中,被传入的对象会直接被 Vue.observable 变更,它和被返回的对象是同一个对象。而在 Vue 3.x 中,则会返回一个可响应的代理,而对源对象直接进行变更仍然是不可响应的。
在非父子组件通信时,可以使用 EventBus 或者 Vuex,但是如果实现的功能不是很复杂,就可以使用 observable。
创建 observable.js 文件:
import Vue from 'vue'
let state = Vue.observable({
name: 'LqZww',
age: 18
})
let mutations = {
changeName(name) {
state.name = name
},
changeAge(age) {
state.age = age
}
}
export { state, mutations }
然后随便在一个 vue 文件中使用:
<template>
<div>
{{name}} - {{age}}
<button @click="changeName('张三')">changeName</button>
<button @click="changeAge(99)">changeAge</button>
</div>
</template>
<script>
import { state, mutations } from "../observable";
export default {
computed: {
name() {
return state.name;
},
age() {
return state.age;
},
},
methods: {
changeName: mutations.changeName,
changeAge: mutations.changeAge,
},
};
</script>
自定义指令怎么写
参考答案
自定义指令分全局注册和局部注册。
全局注册:通过 Vue.directive 方法进行注册。
局部注册:通过在组件 options 选项中设置 directive 属性。
// 全局注册
Vue.directive('focus', {
inserted: function (el) {
el.focus()
}
})
// 局部注册
directives: {
focus: {
inserted: function (el) {
el.focus()
}
}
}
自定义指令有如下几个钩子函数:
- bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
- inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
- update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新。
- componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
- unbind:只调用一次,指令与元素解绑时调用。
指令钩子函数会被传入以下参数:
- el:指令所绑定的元素,可以用来直接操作 DOM。
- binding:一个对象,包含以下 property:
- name:指令名,不包括 v- 前缀。
- value:指令的绑定值,例如:
v-my-directive="1 + 1"
中,绑定值为 2。 - oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。
- expression:字符串形式的指令表达式。例如
v-my-directive="1 + 1"
中,表达式为"1 + 1"
。 - arg:传给指令的参数,可选。例如
v-my-directive:foo
中,参数为 "foo"。 - modifiers:一个包含修饰符的对象。例如:
v-my-directive.foo.bar
中,修饰符对象为{ foo: true, bar: true }
。
- vnode:Vue 编译生成的虚拟节点。
- oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。
注意:除了 el 之外,其它参数都应该是只读的,切勿进行修改。如果需要在钩子之间共享数据,建议通过元素的 dataset 来进行。
什么是虚拟DOM
vue-router路由模式有几种
参考答案
有两种模式,分别为hash模式和history模式
hash模式
地址栏URL中的#
符号。比如此URL:https://www.cnblogs.com/LqZww/p/13544020.html#4261640972
,hash的值为#/42616409711
。
- url路径会出现
#
字符; - hash值不包括在Http请求中,它是交由前端路由处理,所以改变hash值时不会刷新页面,也不会向服务器发送请求;
- hash值的改变会触发hashchange事件;
history模式
利用了HTML5 History Interface中新增的pushState()和replaceState()方法。这两个方法应用于浏览器的历史记录栈,在当前已有的back、forward、go的基础之上,它们提供了对历史记录进行修改的功能。只是当它们执行修改时,虽然改变了当前的 URL,但浏览器不会立即向后端发送请求。
- 整个地址重新加载,可以保存历史记录,方便前进后退;
- 依赖H5 API和后台配置,没有后台配置的话,页面刷新时会出现404;
vue-router有哪几种导航钩子
参考答案
vue-router提供的导航守卫主要用来通过跳转或取消的方式守卫导航。有多种机会植入路由导航过程中:全局的、单个路由独享的、组件级的。
参数或查询的改变并不会触发进入/离开的导航守卫。你可以通过观察$route
对象来应对这些变化,或使用beforeRouteUpdate
的组件内守卫。
- 全局守卫:router.beforeEach
- 全局解析守卫:router.beforeResolve
- 全局后置钩子:router.afterEach
- 路由独享的守卫:beforeEnter
- 组件内的守卫:beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave
vue-router导航解析的流程是什么
参考答案
- 导航被触发。
- 在失活的组件里调用 beforeRouteLeave 守卫。
- 调用全局的 beforeEach 守卫。
- 在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。
- 在路由配置里调用 beforeEnter。
- 解析异步路由组件。
- 在被激活的组件里调用 beforeRouteEnter。
- 调用全局的 beforeResolve 守卫 (2.5+)。
- 导航被确认。
- 调用全局的 afterEach 钩子。
- 触发 DOM 更新。
- 调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。
route和router的区别
参考答案
- $route 为当前 router 跳转对象,里面可以获取当前路由的 name、path、params、hash、query等等路由信息参数。
- $router是 VueRouter 的实例,是全局的路由对象,对象包括了路由的跳转方法,钩子函数等等。
vue-router实现路由懒加载
参考答案
主要有以下三种方式:
vue-router中params与query的区别
参考答案
query 传参与接收参数的方式:
this.$router.push({
path: "one",
query: {
num: 1,
},
})
this.$route.query.num
params 传参与接收参数的方式:
this.$router.push({
name: "two",
params: {
id: 1,
},
})
this.$route.params.id
区别如下:
- 用法:query 用 path 来引入,而 params 用 name 来引入;
- url:query 传递的参数要在浏览器地址栏上显示,而 params 不会显示;
- 刷新:query 刷新不会丢失 query 里面的数据,而 params 刷新会丢失 params 里面的数据。
Vuex是什么,怎么使用,哪种功能场景使用它
参考答案
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。它实现组件全局状态(数据)管理的一种机制,可以方便的实现组件之间数据的共享。
使用Vuex统一管理状态有如下好处:
- 能够在vuex中集中管理共享的数据,易于开发和后期维护;
- 能够高效地实现组件之间的数据共享,提高开发效率;
- 存储在vuex中的数据都是响应式的,能够实时保持数据与页面的同步;
在 main.js 引入 store,注入。新建了一个 store 目录,然后...export。
场景:单页应用中,组件之间的共享状态和方法。
Vuex有哪几种属性
参考答案
- State:用于数据的存储,提供唯一的公共数据源,所有共享的数据都要统一放到Store的State中进行存储;
- Getter:用于对Store中的数据进行加工处理形成新的数据,类似Vue中的计算属性,Store中数据发生变化,Getter的数据也会跟着变化;
- Mutation:用于变更Store中的数据,且不能用于处理异步操作;
- Action:用于处理异步任务,如果通过异步操作变更数据,必须通过Action,而不能使用Mutation,但是在Action中还是要通过触发Mutation的方式间接变更数据;
- Module:类似于命名空间,用于项目中将各个模块的状态分开定义和操作;
待完善!
如何理解Vue的单向数据流
vue-router是什么
vue-router如何响应路由参数的变化
vue-router的几种实例方法以及参数传递
vue-router的动态路由匹配以及使用
vue-router如何定义嵌套路由
更多面试题请移步至 我的新博客 - 持续更新地址