web前端常见面试题-Vue
1.什么是vue的生命周期?
Vue实例从创建到销毁的过程,就是生命周期。
也就是从开始创建、初始化数据、编译模板、挂载DOM->渲染、更新->渲染、卸载等一系列过程,我们称这是Vue的生命周期。
2.Vue生命周期总共有几个阶段?分别对应哪些钩子函数
它可以总共分为8个阶段:创建前/后,载入前/后,更新前/后,销毁前/销毁后
生命周期中有多个事件钩子,让我们在控制整个vue实例的过程时更容易形成好的逻辑。
beforecreated:
在实例初始化之后,el 和 data 并未初始化(这个时期,this变量还不能使用,在data下的数据,和methods下的方法,watcher中的事件都不能获得到;)
created:完成了 data 数据的初始化,el没有(这个时候可以操作vue实例中的数据和各种方法,但是还不能对"dom"节点进行操作;)
beforeMount:完成了 el 和 data 初始化 //这里的el是虚拟的dom;
mounted :完成挂载,在这发起后端请求,拿回数据,配合路由钩子做一些事情(挂载完毕,这时dom节点被渲染到文档内,一些需要dom的操作在此时才能正常进行)
beforeUpdate:是指view层数据变化前,不是data中的数据改变前触发;
update:是指view层的数据变化之后,
beforeDestory: 你确认删除XX吗?
destoryed :当前组件已被删除,清空相关内容
3.第一次页面加载会触发哪几个钩子?DOM 渲染在 哪个周期中就已经完成?
第一次页面加载时会触发 beforeCreate, created, beforeMount, mounted 这几个钩子
DOM 渲染在 mounted 中就已经完成了。
4.简单描述每个周期具体适合哪些场景
beforecreate : 可以在这加个loading事件,在加载实例时触发
created : 初始化完成时的事件写在这里,如在这结束loading事件,异步请求也适宜在这里调用
mounted : 挂载元素,获取到DOM节点
updated : 如果对数据统一处理,在这里写上相应函数 beforeDestroy : 可以做一个确认停止事件的确认框 nextTick : 更新数据后立即操作dom;
5.开发中常用的vue指令有哪些?
v-model:一般用在表达输入,很轻松的实现表单控件和数据的双向绑定
v-html:更新元素的innerHTML
v-text:文本插入和更新类似innerText
v-show与v-if:条件渲染,注意二者区别
v-on:click:可以简写为@click,@绑定一个事件。如果事件触发了,就可以指定事件的处理函数
v-for:基于源数据多次渲染元素或模板
v-bind:当表达式的值改变时,将其产生的连带影响,响应式地作用于DOM语法,v-bind:title=”msg”简写:title=“msg”
v-once页面的vue数据只加载一次
6.Vue绑定class的数组用法
1.对象方法v-bind:class="{'orange':isRipe, 'green':isNotRipe}”
2.数组方法v-bind:class="[class1,class2]"
3.行内v-bind:style="{color:color,fontSize:fontSize+'px'}”
7.v-show与v-if的区别
v-show是css切换,v-if是完整的销毁和重新创建
使用频繁切换时用v-show,运行时较少改变时用v-if
V-if=’false’v-if是条件渲染,当false的时候
不会渲染
使用v-if的时候,如果值为false,那么页面将不会有这个html标签生成
v-show则是不管值是为true还是false,html元素都会存在,只是css中的display显示或隐藏
v-show 仅仅控制元素的显示方式,将 display 属性在 block 和 none 来回切换;而v-if会控制这个 DOM 节点的存在与否。当我们需要经常切换某个元素的显示/隐藏时,使用v-show会更加节省性能上的开销;当只需要一次显示或隐藏时,使用v-if更加合理。
8.Vue双向数据绑定的原理
Object.defineProperty 通过 getter 和 setter 劫持了对象赋值的过程,在这个过程中可以进行更新 dom 操作
首先我们为每个vue属性用Object.defineProperty()实现数据劫持,为每个属性分配一个订阅者集合的管理数组dep;
然后在编译的时候在该属性的数组dep中添加订阅者,v-model会添加一个订阅者,{{}}也会,v-bind也会,只要用到该属性的指令理论上都会;
接着为input会添加监听事件,修改值就等于为该属性赋值,则会触发该属性的set方法,在set方法内通知订阅者数组dep,订阅者数组循环调用各订阅者的update方法更新视图。
Vue数据绑定这样做的好处:组件数据改变时,不会修改存储prop的子组件数据,只是以子组件数据为媒介,完成对prop的双向修改。
9.简述一下,你理解的MVVM模式
M-model,model代表数据模型,也可以在model中定义数据修改和操作的业务逻辑
V-view,view代表UI组件,它负责将数据模型转化为UI展现出来
VM-viewmodel,viewmodel监听模型数据的改变和控制视图行为、处理用户交互,简单理解就是一个同步view和model的对象,连接model和view
10.Vue中computed和watch有什么区别
computed
computed是计算属性,也就是计算值,它更多用于计算值的场景
computed具有缓存性,computed的值在getter执行后是会缓存的,只有在它依赖的属性值改变之后,下一次获取computed的值时重新调用对应的getter来计算
computed适用于计算比较消耗性能的计算场景
watch
watch更多的是[观察]的作用,类似于某些数据的监听回调,用于观察props $emit或者本组件的值,当数据变化时来执行回调进行后续操作
无缓存性,页面重新渲染时值不变化也会执行
总结:当我们要进行数值计算,而且依赖于其他数据,那么把这个数据设计为computed
如果你需要在某个数据变化时做一些事情,使用watch来观察这个数据变化。
11.vue组件的scoped属性的作用
在style标签上添加scoped属性,以表示它的样式作用于当下的模块,很好的实现了样式私有化的目的;
但是也得慎用:样式不易(可)修改,而很多时候,我们是需要对公共组件的样式做微调的;
解决办法:
①:使用混合型的css样式:(混合使用全局跟本地的样式)
<style> /* 全局样式 */ </style><style scoped> /* 本地样式 */ </style>
②:深度作用选择器(>>>)如果你希望 scoped 样式中的一个选择器能够作用得“更深”,例如影响子组件,你可以使用 >>> 操作符:<style scoped> .a >>> .b { /* ... */ } </style>
12.vue是渐进式的框架的理解
Vue的核心的功能,是一个视图模板引擎
我们可以通过添加组件系统、客户端路由、大规模状态管理来构建一个完整的框架。
更重要的是,这些功能相互独立,你可以在核心功能的基础上任意选用其他的部件,不一定要全部整合在一起。
可以看到,所说的“渐进式”,其实就是Vue的使用方式,同时也体现了Vue的设计的理念
渐进式代表的含义是:主张最少。视图模板引擎
每个框架都不可避免会有自己的一些特点,从而会对使用者有一定的要求,这些要求就是主张,主张有强有弱,它的强势程度会影响在业务开发中的使用方式。
比如说,Angular,它两个版本都是强主张的,如果你用它,必须接受以下东西:
必须使用它的模块机制- 必须使用它的依赖注入- 必须使用它的特殊形式定义组件(这一点每个视图框架都有,难以避免)
所以Angular是带有比较强的排它性的,如果你的应用不是从头开始,而是要不断考虑是否跟其他东西集成,这些主张会带来一些困扰。
Vue可能有些方面是不如React,不如Angular,但它是渐进的,没有强主张,你可以在原有大系统的上面,把一两个组件改用它实现,当jQuery用;也可以整个用它全家桶开发,当Angular用;还可以用它的视图,搭配你自己设计的整个下层用。也可以函数式,都可以,它只是个轻量视图而已,只做了自己该做的事,没有做不该做的事,仅此而已。
13.vue.js的两个核心是什么?
数据驱动和组件系统
数据驱动:Object.defineProperty和存储器属性: getter和setter(所以只兼容IE9及以上版本),可称为基于依赖收集的观测机制,核心是VM,即ViewModel,保证数据和视图的一致性。
组件系统:
1)能够把页面抽象成多个相对独立的模块
2)实现代码重用,提高开发效率和代码质量,使得代码易于维护
3) 组件可以扩展HTML元素,封装可重用代码
● 在较高层面上,组件是自定义元素,Vue.js的编译器为他添加特殊功能
● 有些情况下,组件也可以表现用js特性进行了扩展的原生的HTML元素
14.v-on可以监听多个方法吗?
可以,一个元素绑定多个事件的两种写法,一个事件绑定多个函数的两种写法,修饰符的使用。
<a style="cursor:default" v-on='{click:DoSomething,mouseleave:MouseLeave}'>doSomething</a>
<button @click="a(),b()">点我ab</button>
15.Vue 组件中 data 为什么必须是函数
vue组件中data值不能为对象,因为对象是引用类型,组件可能会被多个实例同时引用。
如果data值为对象,将导致多个实例共享一个对象,其中一个组件改变data属性值,其它实例也会受到影响
16.vue等单页面应用及其优缺点
缺点:
不支持低版本的浏览器,最低只支持到IE9;
不利于SEO的优化(如果要支持SEO,建议通过服务端来进行渲染组件);
第一次加载首页耗时相对长一些;
不可以使用浏览器的导航按钮需要自行实现前进、后退。
优点:
无刷新体验,提升了用户体验;
前端开发不再以页面为单位,更多地采用组件化的思想,代码结构和组织方式更加规范化,便于修改和调整;
API 共享,同一套后端程序代码不用修改就可以用于Web界面、手机、平板等多种客户端
用户体验好、快,内容的改变不需要重新加载整个页面。
17.vuex是什么?怎么使用?哪种功能场景使用它?
Vuex是Vue框架中状态管理,
有五种,分别是 State、 Getter、Mutation 、Action、 Module
使用场景:单页应用中,组件之间的状态。音乐播放、登录状态、加入购物车
vuex的State特性
A、Vuex就是一个仓库,仓库里面放了很多对象。其中state就是数据源存放地,对应于一般Vue对象里面的data
B、state里面存放的数据是响应式的,Vue组件从store中读取数据,若是store中的数据发生改变,依赖这个数据的组件也会发生更新
C、它通过mapState把全局的 state 和 getters 映射到当前组件的 computed 计算属性中
vuex的Getter特性
A、getters 可以对State进行计算操作,它就是Store的计算属性
B、 虽然在组件内也可以做计算属性,但是getters 可以在多组件之间复用
C、 如果一个状态只在一个组件内使用,是可以不用getters
vuex的Mutation特性
改变store中state状态的唯一方法就是提交mutation,就很类似事件。
每个mutation都有一个字符串类型的事件类型和一个回调函数,我们需要改变state的值就要在回调函数中改变。
我们要执行这个回调函数,那么我们需要执行一个相应的调用方法:store.commit。
Action 类似于 mutation,不同在于:Action 提交的是 mutation,而不是直接变更状态;
Action 可以包含任意异步操作,Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,
因此你可以调用 context.commit 提交一个 mutation,
或者通过 context.state 和 context.getters 来获取 state 和 getters。
Action 通过 store.dispatch 方法触发:eg。
store.dispatch('increment')
vuex的module特性
Module其实只是解决了当state中很复杂臃肿的时候,module可以将store分割成模块,
每个模块中拥有自己的state、mutation、action和getter
18.Vue深入组件传值
vue组件传值在日常开发中比较常见,
一般有三种传值方式:1.父传子、2.子传父、3.兄弟组件之间通信
父子组件其实就是组件包含关系
A组件中包含B组件,A是父组件,B是子组件
一般会在子组件里面定义props来做接收,这是比较常见的情况
1.父传子
这是父组件
<template>
<div>
<div>我是父组件</div>
<div>我发送给第一个组件的信息是:{{msg}}</div>
<div>
<div id="child1">
<ChildOne :msg="msg" />
</div>
</div>
</div>
</template>
<script>
import ChildOne from "../components/children1";
import ChildTwo from "../components/children2";
export default {
components: {
ChildOne,
ChildTwo
},
data() {
return {
msg: "我是父组件,我给你发消息",
};
}
};
</script>
可以看到父组件上面传入了一个msg,
那么在子组件上就需要定义一个props用来接收传进来的参数msg
子组件
<template>
<div>
<div id="title">我是第一个子组件</div>
<div>我接受到的父组件的消息是:{{msg}}</div>
</div>
</template>
<script>
export default {
props: {
msg: {
type: String
}
}
可以简写为 props:['msg']
};
</script>
2.子传父
需要利用vue中的$emit将想要传递的值通过函数的形式传出,在父组件接收
this.$emit(arg1,arg2)
arg1:方法名字,arg2:要传出的值
这是子组件
<template>
<div>
<div id="title">我是子组件</div>
<div>我要发送给父组件的值:{{msg}}</div>
<button @click="toParent">向父组件发送信息</button>
</div>
</template>
<script>
export default {
data() {
return {
msg: "我是第二组件,我要给父组件传值",
};
},
methods: {
toParent() {
this.$emit("toParent", this.msg);
}
}
};
</script>
在button上绑定了一个点击事件,
函数里面传出了一个方法名为toParent的方法,
这时候我们就要去父组件接收这个函数,
它会带一个返回值,这个返回值就是我们需要从子组件传的值。
这是父组件
<template>
<div>
<div>我是父组件</div>
<div>我即将接收第二组件传值是:{{child2Msg}}</div>
<div>
<div id="child2">
<ChildTwo @toParent="getMag" />
</div>
</div>
</div>
</template>
<script>
import ChildOne from "../components/children1";
import ChildTwo from "../components/children2";
export default {
components: {
ChildOne,
ChildTwo
},
data() {
return {
child2Msg: ""
};
},
methods: {
getMag(msg) {
this.child2Msg = msg;
}
}
};
</script>
在父组件里面定义了一个
@toParent方法这个名称要和子组件里面this.$emit(arg1)的命名一样,用来接收。
在getMag里面接收一个参数就是当前传回的值。
3.兄弟组件传值
组件互相不包含,其实这种兄弟传值完全可以使用vuex来做,下边会说到
在vue文件main.js中,我们利用
`Vue.prototype.bus=new Vue()` 来定义,
此时我们就有了一个中间量。在组件中需要使用this.bus来调用
import Vue from 'vue'
export default new Vue
export default是不能省略的
定义第一个组件传递值
<template>
<div>
<div id="title">我是第一个子组件</div>
我要给第二个兄弟发信息,内容是:
<input type="text" v-model="to" />
</div>
<button @click="toBrother">点我给兄弟传值</button>
</div>
</template>
<script>
export default {
data() {
return {
to: "哈喽兄弟"
};
},
methods: {
toBrother() {
this.bus.$emit("toBrother", this.to);
}
}
};
</script>
在button上绑定了一个方法,在方法内部使用中间变量bus中的$emit来传递值,参数同子传父的参数一致。
定义第二个组件用于接收值
<template>
<div>
<div id="title">我是第二个子组件</div>
<div>我得到的兄弟组件信息是:{{get}}</div>
</div>
</template>
<script>
export default {
data() {
return {
get: ""
};
},
使用钩子函数去接收 ,在创建vue之前就接收数据,后边必须使用箭头函数,普通函数是无效的
beforeCreate() {
this.bus.$on("toBrother", msg => {
this.get = msg;
});
}
};
</script>
在第二个子组件里面通过beforeCreate生命周期来获得传过来的值,
这时候需要用this.bus.$on
来接收,
第一个参数是this.bus.$emit
定义的第一个方法名,
第二个参数是一个方法,此方法带一个返回参数。此时的方法需要使用箭头函数。
19. Vuex制作组件传值
首先引入vuex
npm install vuex --save
或者
npm install --save vuex
或者
npm i vuex -S
然后创建store/store.js用于存储配置参数
最后需要在main.js中导入store/store.js
import store from '@src/store/store'
或者
import store from '../store/store.js' //这个可根据自己路径修改
new Vue({
el: '#app',
store,//像router一样挂载进vue实例中
template: '<App/>',
components: { App }
})
store/store.js配置内容
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
// 实际上,可以直接写入Store实例中,但是这种方式更加简洁易读
const state = {
// input框中的数据
inputVal : '张三',
// 数组数据
list:['lucy','lily','polly'],
}
// 定义同步方法修改state状态
const mutations = {
addTr(state,inputVal){
// 数组的添加
state.list.push(inputVal);
state.inputVal = ''
},
delTr(state,index){
// 数组的删除
state.list.splice(index,1)
}
}
//定义异步方法修改state状态
// 异步方法 参数一般都是 context 与 {commit}
const actions = {
addTrAction({commit},inputVal){
return commit('addTr',inputVal)
},
delTrAction({commit},index){
return commit('delTr',index)
},
}
// 定义Vuex的Store模式对象
export default new Vuex.Store({
state,
mutations,
actions,
// 除state,mutations和actions之外,vuex还有getterrs和modules对象属性,可以参见我另外一篇博客,路径不给你了,自己找吧(*^_^*)
})
写好vuex配置后,
看下组件使用state状态数据
组件一(可以是父子、兄弟的组件关系)
<template>
<div id="list">
<h1>这是list组件,数据展示如下:</h1>
<!-- 注意。如果需要vuex中使用双向数据绑定,那么只能使用第一种方式 -->
<input type="text" v-model="$store.state.inputVal">
<!-- <button @click="addTr(inputVal)">点击添加列表项</button> -->
<button @click="addTrAction(inputVal)">点击添加列表项</button>
<!-- 直接获取store.js的list数据 -->
<p>{{list}}</p>
<br><br>
<table border="1" align="center" width="500px">
<tr>
<th>序号</th>
<th>内容</th>
<th>操作</th>
</tr>
<tr v-for="(item,index) in list">
<td v-text="index+1"></td>
<td v-text="item"></td>
<td v-text>
<!-- <button @click="delTr(index)">删除</button> -->
<button @click="delTrAction(index)">删除</button>
</td>
</tr>
</table>
<div style="height: 12.5rem;"></div>
</div>
</template>
<script>
import {mapState,mapMutations,mapActions} from 'vuex'
export default{
name:'list',
computed:{
...mapState(['inputVal','list'])
},
methods:{
// ...mapMutations(['addTr','delTr']),
...mapActions(['delTrAction','addTrAction'])
}
}
</script>
<style>
/* odd 奇数 可以使用2n+1 */
/* tr:nth-child(odd){
background-color: #AAD5E4;
}
tr:nth-child(even){
background-color: lightpink;
} */
</style>
20.vue中router以及route,routes的使用
route,它是一条路由。
{ path: '/home', component: Home }
routes,是一组路由。
const routes = [
{ path: '/home', component: Home },
{ path: '/about', component: About }
]
router可以理解为一个容器,或者说一种机制,它管理了一组route。简单来说,route只是进行了URL和函数的映射,而在当接收到一个URL之后,去路由映射表中查找相应的函数,这个过程是由router来处理的。
const router = new VueRouter({
routes // routes: routes 的简写
})
$route为当前router跳转对象里面可以获取name、path、query、params等。
r
o
u
t
e
r
为
V
u
e
R
o
u
t
e
r
实
例
,
想
要
导
航
到
不
同
U
R
L
,
则
使
用
router为VueRouter实例,想要导航到不同URL,则使用
router为VueRouter实例,想要导航到不同URL,则使用router.push方法,返回上一个history使用
r
o
u
t
e
r
.
g
o
方
法
。
跟
上
面
说
的
一
样
,
这
里
的
router.go方法。跟上面说的一样,这里的
router.go方法。跟上面说的一样,这里的router就管理路由的跳转,英文里er结尾的都表示一种人,这里你可以把这个想象中一个管理者,他来控制路由去哪里(push、go),这样就比较容易记。