Vue初级前端面试整理
目录:
- v-show与v-if的区别
- v-model是什么,原理
- 对Vue虚拟DOM的理解
- 方法调用,computed(计算),watch(监听)的区别
- 什么是生命周期钩子函数、vue的生命周期钩子函数有哪些、你在工作中什么地方用过
- keep-alive组件有什么作用
- 组件中的data为什么不用对象
- 为什么脚手架中style标签有scoped属性
- vue如何创建组件,组件之间如何通信
- vue特点,好处,与其他框架有什么区别
- vue代理如何配置
- vue模板渲染的原理是什么
- vue路由的原理是什么,如何实现
- 什么是MVC,MVP,MVVM
1.v-show与v-if的区别
实现本质:
- v-show:通过display:none和display:block之间切换
- v-if:通过DOM节点的插入,删除来实现切换
性能对比:
v-if:
- 切换时需要删除、插入节点开销大
- 但是在初始化的时候,如果条件是false是不会插入节点渲染的,会节约性能
- 总结:如果不是频繁切换只需要渲染时,使用v-if
v-show:
- 有更高的初始渲染开销。就算是false也会渲染
- 但是在切换的时候只是改变样式,消耗少
- 总结:在频繁切换的时候用v-show
2.v-model是什么,原理
vue中利用v-model来进行表单数据的双向绑定
原理:v-bind绑定了一个value的属性,利用v-on把当前元素绑定到了一个事件上
<div id="app">
<input v-model="inputValue"/>
<p>{{inputValue}}</p>
<!------------->
<input v-bind:value="inputValue2" v-on:input="inputValue2 = $event.target.value" />
<p>{{inputValue2}}</p>
</div>
data:{
inputValue: '',
inputValue2: ''
}
3.对Vue虚拟DOM的理解
- 从本质上来说,是一个js对象,通过对象的方式来表示dom的结构,用来模拟真实的dom
- 作用是高效的渲染页面,减少不必要的dom操作,提高渲染效率
- 最初的目的是为了更好的跨平台,比如 说node.js就没有dom,如果想实现服务端的渲染,就可以借助虚拟dom。
- 说白了就是以js对象的形式去添加dom元素,本质上是优化了diff算法,采用了新旧dom的对比,获取你的差异的dom,一次性更新到你真实的dom上
Dom节点在HTML文档中的表现通常时这样的
<ul id="list">
<li>Item 1</li>
<li>Item 2</li>
</ul>
DOM节点也可以表示为一个js对象,就像这样
//用javascript代码表示DOM节点的伪代码
let domNode = {
tag: 'ul',
attributes: {id : 'list'},
children: [
//这里是li
]
}
当我们更新虚拟DOM节点时
domNode.children.push('<li>Item 3</li>')
- diff算法:通过 diff 算法 比对 多次生成的 VDOM,
将不同的内容比对出来,然后在进行真实DOM渲染,一样的内容是不会进行渲染的,这就是VDOM 的 ‘就地复用’ | ‘惰性原则’ - 列表渲染为什么要加key:为了区分在虚拟dom中相同的元素,避免影响渲染
4.方法调用,computed(计算),watch(监听)的区别
- 方法:页面数据每次重新渲染时都会重新执行。性能消耗大。除非不希望有缓存的时候用。
<body>
<p>合计:{{res()}}<p/>
<p>合计:{{sum}}<p/>
<body/>
data:{
arr:[
{title:'手机',num:1,price:'1000'},
{title:'电脑',num:2,price:'2000'},
{title:'电视',num:3,price:'3000'},
]
}
methods:{
res:function(){
var res = 0;
for(var i=0;i<this.arr.length;i++){
res+=this.arr[i].num*this.arr[i].price;
}
return res
}
}
- computed:是计算属性,依赖其他属性计算值,并且computed的值有缓存,只有当计算值变化时才会返回内容
computed: {
sum:function(){
var res = 0;
for(var i=0;i<this.arr.length;i++){
res+=this.arr[i].num*this.arr[i].price;
}
return res
}
}
- watch:监听到的值变化就会执行回调,在回调中可以进行一些逻辑操作
watch:{
arr:{
handler:function(v){
var res = 0;
for(var i=0;i<this.arr.length;i++){
res+=this.arr[i].num*this.arr[i].price;
}
this.sum = res
}
}
}
- 总结:
除非不希望缓存,一般都不用方法
一般来说需要依赖别的属性来动态获得值得时候可以使用computed
对于监听到值的变化需要做异步操作或开销较大的操作时用watch
5.什么是生命周期钩子函数、vue的生命周期钩子函数有哪些、你在工作中什么地方用过
什么是生命周期:
生命周期就是物体从诞生到死亡的过程,vue生命周期就是vue从初始化到销毁的过程
什么是钩子函数:
在生命周期的过程中我们有很多特殊的时间段,我希望在这些特殊的时间段对vue做一些事情,所以出现了钩子函数,钩子函数就是作者在设计vue的时候,在vue从初始化到销毁这段时间内的特殊时间段给我们一些定义函数的权利,如果咱们定义了,就会执行,不定义就不会执行
vue都有哪些生命周期:
beforeCreate 创建前:刚执行new的操作,其他什么都没有做
created 创建后:属性和方法挂载到了实例上面,初始化事件,$el属性还没有显示
beforeMount 挂载前:找到了el或者mount对应的节点范围,但数据还没有替换
mounted 挂载后:vue范围内的变量都会被替换成data里面的数据
beforeUpdate 数据更新前:根据更改之前的数据
updated 数据更新后:数据更改之后的数据
beforeDestroy 销毁前:在实例销毁之前调用,实例仍然完全可用。
destroyed 销毁后:这个vue就不可以再用了,所有的事件监听器会被移除,所有的子实例也会被销毁
我们在项目中都有什么时候用到过:
页面初始化时候需要获取数据,这个时候就可以在生命周期里面调用
- created:进行ajax请求异步数据的获取、初始化数据
- mounted:挂载元素dom节点的获取
- nextTick:针对单一事件更新数据后立即操作dom
- updated:任何数据的更新,如果要做统一的业务逻辑处理
- watch:监听数据变化,并做相应的处理
6.keep-alive组件有什么作用
动态组件如果不加keep-alive相当于每次都会销毁,诞生
<body>
<div id="app">
<button @click="show(i)" :class="{active:currentIndex==i}" v-for="(v,i) in arr">{{v.btnName}}</button>
<keep-alive>
<component :is="arr[currentIndex].componentName"></conponent>
</keep-alive>
</div>
</body>
<script>
Vue.component('aaa',{
template:'<div>我是内容1</div>',
activated(){
console.log("进来")
},
deactivated(){
console.log("离开")
}
});
Vue.component('bbb',{
data:function(){
return:{
msg:''
}
}
template:'<div>我是内容2---> <span>{{msg}}</span>
<input type="text" v-model="msg" />
</div>',
});
Vue.component('ccc',{
data:function(){
return:{
msg:''
}
}
template:'<div>我是内容3---> <span>{{msg}}</span>
<input type="text" v-model="msg" />
</div>',
});
new Vue({
el:"app",
data:{
arr: [
{btnName:'按钮1',componentName:'aaa'},
{btnName:'按钮2',componentName:'bbb'},
{btnName:'按钮3',componentName:'ccc'},
],
currentIndex:0
},
methods:{
show:function(i){
this.currentIndex=i;
}
}
})
</script>
- 如果你需要在组件切换的时候,保存一些组件的状态防止多次渲染,就可以使用keep-alive组件保存的组件
- 对与keep-alive组件来说,它拥有两个独有的生命周期钩子函数,分别为activated(进来)和deactivated(离开)
- 用keep-alive包裹的组件在切换时不会销毁,而是缓存到内存中并执行deactivated钩子函数
keep-alive属性:
<keep-alive include="aaa">
<component :is="arr[currentIndex].componentName"></conponent>
</keep-alive>
- include - 字符串或正则表达式,只有名称匹配的组件会被缓存
- exclude - 字符串或正则表达式,任何名称匹配的组件都不会被缓存
- max - 数字,最多可以缓存多少组件实例
7.组件中的data为什么不用对象
- 组件复用时所有组件实例都会共享data,如果data是对象的话,就会造成一个组件修改data以后会影响到其他所有组件,所以需要将data写成函数,每次用到就调用一次函数获得新的数据。
- 当我们使用new Vue() 的方式的时候,无论我们将data设置为对象还是函数都是可以的,因为new Vue() 的方式是生成一个根组件,该组件不会复用,也就不存在共享data的情况了。
let com = { //写成如下格式就不会冲突
template:'
<div>
<input type="text" v-model="msg" />
<span>{{msg}}</span>
</div>
',
data:function(){
return{
msg:''
}
}
}
8.为什么脚手架中style标签有scoped属性
- 如果我们用脚手架开发vue,或者利用webpack配置开发vue项目的时候,肯定会接触组件化开发
- 组件的定义有三种形式、全局组件、局部组件、文件组件
- 所谓的文件组件就是.vue的组件形式,一个文件一个组件
- 但是会出现一个问题就是.vue的文件无法被浏览器所解析。
- Vue Loader就是把.vue文件解析成浏览器能看懂的html,css,js文件。
- 当然webpack中还有其他很多loader作用都是一样的
- 当
<style>
/*全局样式*/
</style>
<style scoped>
/*本地样式*/
</style>
两者是可以混用的。
- 使用scoped后,父组件的样式将不会渗透到子组件中
9.vue如何创建组件,组件之间如何通信
什么是组件化:
任何一个页面我们都可以抽象成由一堆组件构成的一个大的组件树。大到一个页面,小到一个按钮都可以是一个组件,一个页面就是由很多的组件嵌套拼接组成。这就是组件化
组件化的好处:
复用性强,分公开发,代码好管理,耦合度低
vue如何创建组件:
- 全局组件
vue.component('组件名称',{})
- 局部组件
new Vue({
//...
components:{
'组件名称':{}
}
})
- 单文件组件
.vue文件
<template></template>
<style></style>
<script></script>
组件之间的通讯方式:
props
自定义事件$emit
data{
return{
msg:'',
msg2:''
}
}
//父组件
<child :tit="msg" @onFn="fn" /> //父传子1:通过:tit 自定义属性来进行父传子
fn(v){
this.msg2=v //子传父2:通过自定义函数接收数据
}
//子组件
props:['tit'] //父传子2:通过props获取父组件的属性tit的值
send(){
this.$emit("onFn",this.msg2) //子传父1:通过$emit触发自定义函数方法
}
$parent
//子组件获取父组件
get(){
this.$parent.msg
}
$children 数组 uid
//父组件获取子组件,数组需写下标
get(){
this.$children[0].msg
}
ref=“xxx” 类似id感觉,找范围内
//子组件定义ref
<child ref="c1"><child/>
//父组件使用
this.$refs.c1.msg
let Event = new Vue() 公共数据,中央主线
数据的发送
Event.$emit('接口',数据)
数据的接收
Event.$on('接口',function(val){ //val 发过来的数据
alert(val)
})
let Event = new Vue() //使用该方法需先声明vue
//父组件中$eimt声明aaa就是“暗号”
methods:{
send:function(){
Event.$emit('aaa',this.msg)
}
}
//子组件中$on接收
mounted:function{
Event.$on('aaa',function(val){
alert(val)
})
}
- 插槽
let com2={
data:function(){
return {msg:'我是子级的数据'},
},
template:'
<div>
<slot name="s1" :msg="msg" :tit="tit"></slot> //name获取插槽,:msg属性传参
<p>我是组件2</p>
<slot name="s2"></slot>
</div>
',
};
let com1={
template:'
<div>
<p>{{msg}}</p>
<com2>
<template #s1="res"> //#s1起名
<p>我要插入的标签数据---{{res.msg}}</p> //自定义res获取里面的msg
</template>
<template #s2>
<p>我要插入的标签数据2</p>
</template>
</com2>
</div>
',
data:function () {
return {
msg:'我是父级的数据'
}
}
};
- vuex
//脚手架中,src>sotre>index.js
export default new Vuex.store({
state:{
msg: 0 //设置值,在路由页面可以使用,数据共享,改变时都改变
}
})
//home路由页面
<span>{{this.$store.state.msg}}</span> //通过这种方法获取值,改变时所有该数据都改变
- attrs,listeners
$attrs
包含了父作用域中不作为 prop被识别(且获取)的attribute 绑定(class和 style除外)。
当一个组件没有声明任何 prop时,这里会包含所有父作用域的绑定(class和 style除外),
并且可以通过 v-bind="$attrs”传入内部组件—在创建高级别的组件时非常有用。
$listeners
包含了父作用域中的(不含.native 修饰器的) v-on事件监听器。
它可以通过v-on="$listeners”传入内部组件—在创建更高层次的组件时非常有用。
<com3 v-bind="$attrs" v-on="$listeners"></com3>
mounted:function(){
this.$emit("onFn",123)
}
<span>{{this.$attrs.tit}}</span>
<com2 :tit="msg" @onFn="fn"></com2>
- provide,inject
provide和 inject主要在开发高阶插件/组件库时使用。并不推荐用于普通应用程序代码中。
父组件中通过provider来提供变量,然后在子组件中通过inject来注入变量。
不论子组件有多深,只要调用了inject那么就可以注入provider中的数据。
而不是局限于只能从当前父组件的prop属性来获取数据,只要在父组件的生命周期内,子组件都可以调用
//父组件定义变量
provide:{
aaa:'msg'
}
//子组件直接获取
inject:['aaa'],
data(){
return{
msg:this.aaa
}
}
- v-model
父组件通过v-model传递值给子组件时,会自动传递一个value的prop属性
在子组件中通过this.$emit( ‘input’,val)自动修改v-model绑定的值
//父组件中绑定子组件的v-model
<child v-model="msg"></child>
//子组件中直接props获取
props:['value'],
data(){
return{
msg:this.value
}
}
10.vue特点,好处,与其他框架有什么区别
官网介绍:渐进式的javascript框架
既可以把vue当作第三方库来使用,也可以利用vue构建复杂的单页面项目
特点
入门容易:只需要会html,css,js就可以,如果是react还要先学习ES6,jsx,函数式编程
灵活:可以当作第三方库来使用,也可以用来构架项目
高效:虚拟DOM
使用方便:指令、模板、数据双向绑定和react相比,数据双向绑定可以自动修改,react需要手动执行setState
组件化:和react项目,vue的组件化更容易被新手接受html css js,而react都是通过js实现
11.vue代理如何配置
脚手架中,新建vue.config.js文件
module.exports = {
devServer: {
proxy: {
'/api': {
target: '<url>',
changeOrigin: true
},
'/foo': {
target: '<other-url>'
}
}
}
}
- 使用: /api/接口,localhost/api/接口,会把/api之前的包括/api替换写成的地址
12.vue模板渲染的原理是什么
<span>{{msg}}</span>
<span>你好</span>
data:{
msg:'你好'
}
- 正则表达式,匹配{{msg}}格式,取到xxx数据
13.vue路由的原理是什么,如何实现
什么是路由:就是通过点击不同的按钮展示不同页面或者组件的功能
原理:根据不同的路径地址,展示不同的页面、组件。
实现:
hash #a #b
history /c /d
如何监视这两者发生变化
hash:hashchange
history:popstate
window.addEventListener('hashchange',function(){
console.log(1)
},false)
14.什么是MVC,MVP,MVVM
MVC
- Model代表数据模型,主要任务就是操作数据
- View代表UI视图,主要任务将数据模型转化成UI视图展现出来。
- Controller 控制器,主要任务负责处理业务逻辑
- View传送指令到 controller,Controller 完成业务逻辑后,要求 Model改变状态,Model将新的数据发送到View,用户得到反馈
MVP
- 各部分之间的通信,都是双向的。
- View 与Model不发生联系,都通过Presenter传递。
- View非常薄,不部署任何业务逻辑,称为"“被动视图”(Passive View),即没有任何主动性,而Presenter非常厚,所有逻辑都部署在那里。
MVVM
- Model代表数据模型,主要任务就是操作数据
- View 代表UI视图,主要任务将数据模型转化成UI视图展现出来。
- ViewModel监听模型数据的改变和控制视图行为、处理用户交互,简单理解就是一个同步View和Model的对象,连接Model和View。
- 在MMA架构下,View和Model之间并没有直接的联系,而是通过ViewModel进行交互,Model和ViewModel之间的交互是双向的,因此view数据的变化会同步到Model中,而Model数据的变化也会立即反应到view上。
- ViewModel通过双向数据绑定把View层和Model层连接了起来,而View和Model之间的同步工作完全是自动的,无需人为干涉,因此开发者只需关注业务逻辑,不需要手动操作DOM,不需要关注数据状态的同步问题,复杂的数据状态维护完全由MM来统一管理。