Vue面试题一
Vue面试题一
转载链接:https://github.com/sudheerj/vuejs-interview-questions#what-is-vuejs
一、什么是Vue
答:Vue.js 是一个开源的、渐进式框架,用于构建增量型的用户界面。Vue.js 专注于视图图层 view layer ,并易于与现有的项目和其他框架集成。
英文原文:
Vue.js is an open-source, progressive Javascript framework for building user interfaces that aim to be incrementally adoptable. The core library of VueJS is focused on the view layer only, and is easy to pick up and integrate with other libraries or existing projects.
二、Vue的主要特点是什么?
以下是 Vue.js 的一些主要特点:
- Virtual DOM: 使用类似于 React.js、Ember 等框架的虚拟 DOM。Virtual DOM 是原始 HTML DOM 的轻量级内存树表示,并且在不影响原始 DOM 的情况下进行更新节点。
- 组件: 在 Vue.js 的应用程序中创建可重用的自定义元素。
- 模板: Vue.js 提供基于 HTML 的模板,将 DOM 与 Vue 实例数据绑定
- **路由:**页面之间的导航通过 vue-router 来实现的
- **轻量级:**与其他框架相比,Vue.js 是轻量级的库
英文原文:Below are the some of major features available with VueJS
- Virtual DOM: It uses virtual DOM similar to other existing frameworks such as ReactJS, Ember etc. Virtual DOM is a light-weight in-memory tree representation of the original HTML DOM and updated without affecting the original DOM.
- Components: Used to create reusable custom elements in VueJS applications.
- Templates: VueJS provides HTML based templates that bind the DOM with the Vue instance data
- Routing: Navigation between pages is achieved through vue-router
- Light weight: VueJS is light weight library compared to other frameworks.
三、Vue.js 的生命周期方法有哪些?
大致分为四个大部分:
- 创建部分: 在组件被添加到 DOM 之前。
beforeCreate:数据还不是响应式的created:数据变为响应式的,但模板和 DOM 没有挂载,无法通过访问this.$el来获取组件
- 挂载部分(插入 DOM): 可以在第一次挂载之前和之后访问组件
beforeMount:挂载之前触发,this.$el无法访问Mounted:挂载之后触发,可以访问任何东西
- 更新部分:触发于组件使用的响应式属性改变或者其他原因导致组件重新渲染
beforeUpdate:数据更新之后,重新渲染之前updated:渲染页面完成之后
- 销毁部分:
beforeDestroy:用于清理事件和响应式订阅destroyed:解除绑定指令,移除了事件监听
英文原文:https://github.com/sudheerj/vuejs-interview-questions#what-are-the-lifecycle-methods-of-vuejs
四、有哪些条件指令?
v-ifv-else-ifv-elsev-show
英文原文:
五、v-show和v-if区别
- 显示方式不同:v-if 切换是改变 DOM 渲染,v-show 切换是改变 CSS 的
display来切换显示 - 切换显示成本不同:v-if 花费成本更高,v-show 成本更低
- 配套指令不同:v-if 可以使用 v-else-if 和 v-else
- v-if 支持
<template>,而 v-show 不支持
英语原文:Below are some of the main differences between v-show and v-if directives,
- v-if only renders the element to the DOM if the expression passes whereas v-show renders all elements to the DOM and then uses the CSS display property to show/hide elements based on expression.
- v-if supports v-else and v-else-if directives whereas v-show doesn't support else directives.
- v-if has higher toggle costs while v-show has higher initial render costs. i.e, v-show has a performance advantage if the elements are switched on and off frequently, while the v-if has the advantage when it comes to initial render time.
- v-if supports ` tab but v-show doesn't support.
六、v-for 指令的作用是什么?
答:遍历数组和对象里的元素
- 数组用法:
( item, index ) in array - 对象用法:
( value, key, index) of object
英文原文:
七、什么是Vue实例
答: 每个 Vue 应用程序都通过使用 Vue 函数创建 Vue 的实例来工作。通常变量 vm(ViewModel 的缩写)用于引用 Vue 实例。您可以如下创建 vue 实例。
const vm = new Vue({
// option
})
八、如何实现有条件地显示一组元素
两个方法:
- 一个是
<template>与v-if一起使用
<template v-if="condition">
<h1>Name</h1>
<p>Address</p>
<p>Contact Details</p>
</template>
- 另一个是
<div>与v-show
英语原文:https://github.com/sudheerj/vuejs-interview-questions#how-do-you-achieve-conditional-group-of-elements
九、如何使用key属性
在下面的代码中,会发现文本框的值会复用,在许多场合不应该这样做。所以要在不需要复用的组件,添加 key 属性,这样 Vue 就会区别出这些文本框。
<template v-if="loginType === 'Admin'">
<label>Admin</label>
<input placeholder="Enter your ID" type="password">
</template>
<template v-else>
<label>Guest</label>
<input placeholder="Enter your name" disabled >
</template>
<script>
export default {
data() {
return {
message: 'Hello Vue!',
isShow: false,
}
},
methods: {
changeShow() {
this.isShow = !this.isShow
},
},
};
</script>
十、为什么不应该在同一个元素上同时使用 if 和 for 指令?
建议不要在与 v-for 相同的元素上使用 v-if。因为 v-for 指令的优先级高于 v-if 。所以 vue 在 v-for 渲染出所有列表选项后再执行 v-if 指令。
例如下面代码:在渲染出了 users 数组后,再判断每一项的 isActive 属性看是否显示出来。
<ul>
<li
v-for="user in users"
v-if="user.isActive"
:key="user.id"
>
{{ user.name }}
</li>
</ul>
正确的写法是使用计算属性把需要的数据筛选出来,再使用 v-for 渲染。这样写的好处:
- 使用
v-for="user in activeUsers"之后,我们在渲染的时候只遍历活跃用户,渲染更高效。 - 解耦渲染层的逻辑,可维护性 (对逻辑的更改和扩展) 更强。
<ul>
<li
v-for="user in activeUsers"
:key="user.id"
>
{{ user.name }}
</li>
</ul>
<script>
computed: {
activeUsers: function () {
return this.users.filter(function (user) {
return user.isActive
})
},
},
</script>
还有一个是使用另一个变量来控制 v-for 渲染,例如下面代码,使用 shouldShowUsers 变量来控制是否渲染 v-for 的结果。
<ul>
<li
v-for="user in users"
v-if="shouldShowUsers"
:key="user.id"
>
{{ user.name }}
</li>
</ul>
正确的方式是将 v-if 提取到父元素,这样就不会去渲染 v-for 的结果。
<ul v-if="shouldShowUsers">
<li
v-for="user in users"
:key="user.id"
>
{{ user.name }}
</li>
</ul>
十一、为什么需要在 for 指令上使用 key 属性?
为了跟踪每个节点的身份,从而重用和重新排序现有元素。
十二、突变方法有哪些?
- push() —— 在数组最后面添加元素
- pop() —— 在数组最后面删除元素
- shift() —— 删除数组最前面的元素
- unshift() —— 在数组最前面添加元素
- splice() —— 通过删除或替换现有元素或者原地添加新的元素来修改数组
- sort() —— 对数组的元素进行排序
- reverse() ——颠倒数组里的元素
正是因为这些方法原来是直接改变原数组,所以 vue.js 在原来的功能上加上了视图更新
十三、非突变方法有哪些?
- filter() —— 根据条件返回一个新数组
- concat() —— 连接多个数组后返回一个新数组
- slice() —— 截取部分数组返回一个新数组、
因为总是返回一个新数组,所以 vue.js 不会视图更新
十四、关于突变方法有哪些注意事项?
如果不用突变方法,下面两个修改数组的方式不会引起界面更新
- 使用下标改变对应的值
- 直接修改数组长度
对于第 1 种方式可以用下面方式代替:
// Vue.set() 方法
Vue.set(vm.todos, indexOfTodo, newTodoValue)
// Array.prototype.splice() 方法
// 从indexOfTodo开始删除1个元素,再添加newTodoValue
vm.todos.splice(indexOfTodo, 1, newTodoValue)
对于第 2 种方式:
vm.todos.splice(todosLength);
对于对象没有如果不用突变方法,添加或者删除对象中的属性都不会引起界面更新,例如下面代码。
var vm = new Vue({
data: {
user: {
name: 'John'
}
}
})
// 添加属性不会引起界面更新
vm.user.email = john@email.com
您可以使用 Vue.set(object, key, value) 方法或 Object.assign() 来克服这种情况
Vue.set(vm.user, 'email', 'john@email.com');
// Object.assign() 方法将其他参数赋值到第1个参数里
// 这里是将新属性和原来的属性添加到空对象里,再赋值
vm.user = Object.assign({}, vm.user, {
email: john@email.com
})
十五、如何使用事件处理?
您可以在 vue 中使用类似于纯 javascript 的事件处理。方法调用还支持特殊的 $event 变量,该变量有许多有个事件的属性。
<button v-on:click="show('Welcome to VueJS world', $event)">
Submit
</button>
methods: {
show: function (message, event) {
if (event) event.preventDefault()
}
}
十六、vue提供的事件修饰符有哪些?
通常,javascript 提供 event.preventDefault() / event.stopPropagation() 事件处理方法。
你可以在 vue 使用这些方法,但是这些方法是用于处理 DOM 事件,而响应方法里最主要的是业务逻辑,这样业务逻辑 和处理 DOM 事件会混在一起。
官网说明:https://cn.vuejs.org/v2/guide/events.html#%E4%BA%8B%E4%BB%B6%E4%BF%AE%E9%A5%B0%E7%AC%A6
Vue 为 v-on 提供了以下事件修饰符,这些修饰符是用点表示的指令后缀。
.stop—— 阻止事件冒泡传播.prevent—— 阻止事件的默认行为.capture—— 事件按照捕获模式传播.self——只有在本元素才能触发事件,(子元素不触发).once——该事件只会触发一次.passive——告诉浏览器一定会触发默认事件。一般和滚动事件一起使用,当发生滚动时,不会等待回调方法运行,直接滚动,因为在设置该属性之前,需要等待回调方法运行完成,看是否需要阻止默认行为——滚动。
二十一、如何定义自定义键修饰符
您可以通过全局定义自定义键修饰符别名 config.keyCodes。使用规则:
- 不能使用驼峰命名,可以使用短横命名
- 可以使用数组定义多个值
Vue.config.keyCodes = {
f1: 112,
// 短横命名
"media-play-pause": 179,
// 数组定义多个值
down: [40, 87]
}
二十五、v-model支持哪些修饰符?
三个修饰符:
- lazy:默认情况下,v-model 会在每次输入的时候修改 vue 里面的值。加上 lazy 修饰符的话,在失去焦点或者按下 enter 键后,才会更新。
- number:默认情况下,文本框的值都会被 vue 识别成字符串存入变量里,即使加上
type="number"。加上 number 修饰符,就不会被识别成字符串了。 - trim:自动去除字符串两边的空白
三十、如何使用v-model父子通信
v-model 实现父子通信,就是父组件使用 v-model,然后子组件在修改数据时触发 v-model 事件,让父组件更新数据。
为什么不直接修改 props 数据?因为 v-model 一般是传入基本类型,传递的是值,而不是地址,所以直接修改 props 数据,父组件不会产生变化。
具体的使用方式:
- 父组件使用 v-model,如第 1 行
- 子组件的 props 接收 value
- 然后使用
v-bind:value初始化<input/>的值 - 最后使用
v-on:input监听输入事件,当输入的时候触发父组件的input事件,即触发 v-model 同步数据
<custom-input v-model="xxxx"></custom-input>
<script>
Vue.component('custom-input', {
props: ['value'],
template: `
<input
v-bind:value="value"
v-on:input="$emit('input', $event.target.value)"
/>
`
})
</script>
但如果 props 是对象和数组,则不应该修改 props。因为在 vue.js 官网说过 【 这样会防止从子组件意外变更父级组件的状态,从而导致你的应用的数据流向难以理解 】
链接:https://cn.vuejs.org/v2/guide/components-props.html#%E5%8D%95%E5%90%91%E6%95%B0%E6%8D%AE%E6%B5%81
三十七、什么是非props属性?
看下面代码,父组件有 type 属性,子组件也有 type 属性,那么以哪个为准呢?
<!-- 父组件 -->
<bootstrap-date-input
:name="name"
type="text"
></bootstrap-date-input>
<!-- 子组件 -->
<input type="date" >
会以父组件的为准,因为子组件的 props 没有接收 type 属性,父组件就将其作为普通的属性处理了,而子组件就会自动继承父组件的属性。
如果不想子组件继承父组件的普通属性,则可以加上这个属性
Vue.component('my-component', {
inheritAttrs: false,
// ...
})
我们可以在子组件使用 $attr 属性来获取父组件的普通属性,即没有被 props 属性接收。我们可以这样在子组件这样写 v-bind="$attr" ,将组件不用的属性传递给子组件。然后就可以多代传递数据,而不会写得很冗余。例如下面代码:【参考链接:https://www.jianshu.com/p/ce8ca875c337】
- 父组件传入三个属性,name 、age 和 sex ,如第 3 行
- 然后子组件接收了 name 属性,如第 17 行
- 然后再将剩余属性传给孙子组件,如第 21 行
- 最后孙子组件的输出为
{ "age": "30", "sex": "男" }
<template>
<div>
<childcom :name="name" :age="age" :sex="sex"></childcom>
</div>
</template>
<script>
export default {
data(){
return {
'name':'张三',
'age':'30',
'sex':'男'
}
},
components:{
'childcom':{
props:['name'],
template:
`<div>
<div>{{name}}</div>
<grandcom v-bind="$attrs"></grandcom>
</div>`,
components: {
'grandcom':{
template:
`<div>{{$attrs}}</div>`,
}
}
}
}
}
</script>
三十九、如何定义自定义组件v-model
在 【三十、如何使用v-model父子通信】中,父组件使用 v-model 传入数据,为子组件 input 使用。
但如果是 单选框 radio 和多选框 checkbox 的话,传入子组件的属性和触发的事件不一样。
- input:传入的是 value,触发的事件是 input
- radio 和 checkbox:传入的是 checked,触发的事件是 change
所以需要自定义:
- model 里的 prop:将 v-model 绑定的属性改为 checked
- model 里的 prop:将 v-model 触发的事件改为 change
Vue.component('custom-checkbox', {
model: {
prop: 'checked',
event: 'change'
},
props: {
checked: Boolean
},
template: `
<input
type="checkbox"
v-bind:checked="checked"
v-on:change="$emit('change', $event.target.checked)"
>
`
})
四十四、如何使路由器参数更改为反应式?
当使用带参数的路由从一个 URL 导航到另一个 URL时,将重用相同的组件实例,不会调用组件的生命周期,这样组件的初始化方法就不会调用。
可以使用以下任一方法解决此问题
观察 $route 对象:
const User = { template: '<div>User {{ $route.params.name }} </div>', watch: { '$route' (to, from) { // ... } } }使用 beforeRouteUpdate 导航守卫:从 2.2 版本之后开始可用。
const User = { template: '<div>User {{ $route.params.name }} </div>', beforeRouteUpdate (to, from, next) { // .... } }
五十四、什么是插件
我们在初阶段学习的时候,使用到的 VueRouter 和 Vuex 都是插件。我们在 Vue-Cli 写项目的时候,会像下面注册插件:
const VueRouter = require('vue-router')
// 不要忘了调用此方法
Vue.use(VueRouter)
五十五、如何创建插件
参考链接:https://developer.aliyun.com/article/70997#slide-3
创建插件需要定义一个 install 方法,会有两个形参:Vue 和 options,插件里有四种添加方式。
MyPlugin.install = function (Vue, options) {
// 1. 添加实例方法
Vue.prototype.$myMethod = function (methodOptions) {
// 逻辑...
}
// 2. 添加全局方法或 property
Vue.myGlobalMethod = function () {
// 逻辑...
}
// 3. 添加全局资源
Vue.directive('my-directive', {
bind (el, binding, vnode, oldVnode) {
// 逻辑...
}
...
})
// 3. 注入组件选项
Vue.mixin({
created: function () {
// 逻辑...
}
...
})
}
- 首先是添加 实例方法 ,如上面代码的第 1 个:
- 核心思想:通过 prototype 来添加方法和属性。
例如下面代码添加实例方法:
//让输出的数字翻倍
Vue.prototype.doubleNumber = function (val) {
return val * 2;
}
然后可以直接使用 this.doubleNumber() 来调用这个方法。
如果添加实例属性,该属性不会被共享,都是独立存在的
- 然后是添加 全局方法或属性,如上面代码的第 2 个:
通过第 1 点添加,在组件里使用,通过 this.test() 来调用
通过第 2 点添加,通过 Vue 实例调用,如 Vue.test() 来调用
- 注入组件,也称混入,意思是将方法或者属性放入 Vue 对象里。这么说和上面的两种有点相似。不同的是,这种方式可以涉及到生命周期方法和自定义合并方法
如果放入的是自己的定义的生命周期方法,则 Vue 会先执行你的方法,然后再执行组件里的方法。
如果放入的是
methods、components和directives,则会进行合并,如果里面有对象键名冲突时,取组件对象的键值对。如果放入的是普通方法和属性,会以组件里的方法为主。
我们可以自定义合并策略:
Vue.config.optionMergeStrategies.myOption = function (toVal, fromVal) {
// 返回合并后的值
}
六十二、什么是自定义指令
自定义指令可以对 HTML 元素进行访问以及操作一些行为。
例如下面的代码:
// 全局注册v-focus指令
Vue.directive('focus', {
// 当元素被插入到DOM里触发
inserted: function (el) {
// 焦点放到注册的元素里
el.focus()
}
})
使用的时候像下面使用:
<input v-focus>
六十三、如何自定义化指令
- 我们可以使用 5 个指令生命周期函数
- bind: 当前指令绑定到元素里
- inserted: 当元素插入到 DOM 里
- update: 当元素更新了,但子元素还没更新
- componentUpdated: This hook is called once the component and the children have been updated.
- unbind: This hook is called only once when the directive is removed.