最常见前端面试vue篇(持续更新完善~)

Vue.js 是什么,它有什么有点和缺点

Vue.js 是一个轻巧、高性能、可组件化的 MVVM 库,拥有非常容易上手的 API,是一个构建数据驱动的 Web 界面的库。

优点

  1. 渐进式:通俗点讲就是,你想用啥你就用啥,咱也不强求你。你想用component就用,不用也行,你想用vuex就用,不用也可以。
  2. 组件化
  3. 轻量级
  4. 虚拟dom
  5. 响应式设计
  6. 单页面路由
  7. 数据与视图分开
  8. 双向数据绑定减少了 DOM 操作,将更多精力放在数据和业务逻辑上

缺点

  1. 单页面不利于seo
  2. 不支持IE8以下
  3. 首屏加载时间长
vue响应式原理

1.响应式原理
在生成vue实例时,为对传入的data进行遍历,使用Object.defineProperty把这些属性转为getter/setter。
Object.defineProperty 不支持 IE8 以及更低版本浏览器的原因。
每个vue实例都有一个watcher实例,它会在实例渲染时记录这些属性,并在setter触发时重新渲染。
在这里插入图片描述
Vue 无法检测到对象属性的添加或删除
Vue 不允许动态添加根级别的响应式属性。但是,可以使用 Vue.set(object, propertyName, value)方法向嵌套对象添加响应式属性。

2.声明响应式属性
由于 Vue 不允许动态添加根级响应式属性,所以你必须在初始化实例前声明所有根级响应式属性,哪怕只是一个空值。
如果你未在 data 选项中声明 message,Vue 将警告你渲染函数正在试图访问不存在的属性。
3.异步更新队列
数据变化、更新是在主线程中同步执行的;在侦听到数据变化时,watcher将数据变更存储到异步队列中,当本次数据变化,即主线成任务执行完毕,异步队列中的任务才会被执行(已去重)。
如果你在js中更新数据后立即去操作DOM,这时候DOM还未更新;vue提供了nextTick接口来处理这样的情况,它的参数是一个回调函数,会在本次DOM更新完成后被调用。
使用方法:

  • 1.在组件内使用 vm.$nextTick() 实例方法特别方便,因为它不需要全局 Vue,并且回调函数中的 this 将自动绑定到当前的 Vue 实例上:
Vue.component('example', {
  template: '<span>{{ message }}</span>',
  data: function () {
    return {
      message: '未更新'
    }
  },
  methods: {
    updateMessage: function () {
      this.message = '已更新'
      console.log(this.$el.textContent) // => '未更新'
      this.$nextTick(function () {
        console.log(this.$el.textContent) // => '已更新'
      })
    }
  }
})
  • 2.因为 $nextTick() 返回一个 Promise 对象,所以你可以使用新的 ES2016 async/await 语法完成相同的事情:
methods: {
  updateMessage: async function () {
    this.message = '已更新'
    console.log(this.$el.textContent) // => '未更新'
    await this.$nextTick()
    console.log(this.$el.textContent) // => '已更新'
  }
}
谈谈你对MVVM的理解?

参考:MVVM是什么

谈谈你对Vue中响应式数据的理解?

vue实现数据双向绑定主要是:采用数据劫持结合发布者-订阅者模式的方式。
通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应监听回调。
当把一个普通 Javascript 对象传给 Vue 实例来作为它的 data 选项时。
Vue 将遍历它的属性,用 Object.defineProperty 将它们转为 getter/setter。
用户看不到 getter/setter,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化。
vue的数据双向绑定 将MVVM作为数据绑定的入口,整合Observer,Compile和Watcher三者。
通过Observer来监听自己的model的数据变化,通过Compile来解析编译模板指令(vue中是用来解析 {{}})。
最终利用watcher搭起observer和Compile之间的通信桥梁,达到数据变化 —>视图更新;
视图交互变化(input)—>数据model变更双向绑定效果。

为什么说Vue是一个渐进式框架?

渐进式:通俗点讲就是,你想用啥你就用啥,咱也不强求你。你想用component就用,不用也行,你想用vuex就用,不用也可以

Vue跟React的异同点?

相同点
1.都使用了虚拟dom
2.组件化开发
3.都是单向数据流(父子组件之间,不建议子修改父传下来的数据)
4.都支持服务端渲染
不同点
1.React的JSX,Vue的template
2.数据变化,React手动(setState),Vue自动(初始化已响应式处理,Object.defineProperty)
3.React单向绑定,Vue双向绑定
4.React的Redux,Vue的Vuex

Vue和JQuery的区别在哪?为什么放弃JQuery用Vue?

.jQuery是直接操作DOM,Vue不直接操作DOM,Vue的数据与视图是分开的,Vue只需要操作数据即可
2.在操作DOM频繁的场景里,jQuery的操作DOM行为是频繁的,而Vue利用虚拟DOM的技术,大大提高了更新DOM时的性能
3.Vue中不倡导直接操作DOM,开发者只需要把大部分精力放在数据层面上
4.Vue集成的一些库,大大提高开发效率,比如Vuex,Router等

为什么data是个函数并且返回一个对象呢?

data之所以只一个函数,是因为一个组件可能会多处调用,而每一次调用就会执行data函数并返回新的数据对象,这样,可以避免多处调用之间的数据污染。
通俗来说就是:如果你不用函数,只用对象保存数据时,所有组件将引用一个公共的obj。一个属性改动,其他组件上全部都更改了。用函数来返回就完美解决这个问题。

使用过哪些Vue的修饰符呢?
  • .lazy:改变输入框的值时value不会改变,当光标离开输入框时,v-model绑定的值value才会改变
  • .trim 类似于js的tram()方法,作用事把v-model值得空格去掉
  • .number 将值转化为数字,但是先输入字符串和先输入数字,是两种情况,先输入数字的话只取前面的部分,先输入字母的话,number修饰符无效
  • .stop 阻止猫跑
  • .self 只有点击事件绑定的本身才会触发
  • .once 事件只执行一次
  • .prevent 阻止默认事件(例如a标签跳转)
  • .native 加在自定义组件的事件上,保证事件能执行
  • .left .rihgt .middle 这三个修饰符是鼠标的左中右案件触发的事件
  • .sync 父子组件船只,子组件想更新这个值,使用此修饰符可简写。
vue的虚拟dom和真实dom的区别?

1、虚拟DOM不会进行排版与重绘操作
2、虚拟DOM进行频繁修改,然后一次性比较并修改真实DOM中需要改的部分(注意!),最后并在真实DOM中进行排版与重绘,减少过多DOM节点排版与重绘损耗
3、真实DOM频繁排版与重绘的效率是相当低的
4、虚拟DOM有效降低大面积(真实DOM节点)的重绘与排版,因为最终与真实DOM比较差异,可以只渲染局部
总之,一切为了减弱频繁的大面积重绘引发的性能问题,不同框架不一定需要虚拟DOM,关键看框架是否频繁会引发大面积的DOM操作

使用过哪些Vue的内部指令呢?
  • v-text更新元素的textContent
  • v-html更新元素的innerHtml
  • v-show更改显示隐藏,当条件变化时会触发过度效果
  • v-if v-else v-else-if
  • v-for列表循环渲染
  • v-on 缩写是on绑定事件
  • v-bind 缩写是:动态绑定数据
  • v-molde 双向数据绑定
  • v-slot缩写是#插槽名
  • v-once元素组件只渲染一次
  • v-pre跳过这个元素和子元素编译过程,可以用来显示原始mustache标签。跳过大量没有指令的节点加快编译
路由有哪些模式呢?又有什么不同呢?

hash模式
1、url路径会出现 # 字符
2、hash值不包括在 HTTP 请求中,它是交由前端路由处理,所以改变hash值时不会刷新页面,也不会向服务器发送请求
3、hash值的改变会触发hashchange事件
history模式
1、整个地址重新加载,可以保存历史记录,方便前进后退
2、使用 HTML5 API(旧浏览器不支持)和 HTTP服务端配置,没有后台配置的话,页面刷新时会出现404

如何设置动态class,动态style?
  • 动态class对象:
  • 动态class数组:
  • 动态style对象:
  • 动态style数组:
v-if和v-show有何区别?
  • 1.v-if是通过控制dom元素的删除和生成来实现显隐,每一次显隐都会使组件重新跑一遍生命周期,因为显隐决定了组件的生成和销毁
  • 2.v-show是通过控制dom元素的css样式来实现显隐,不会销毁
  • 3.频繁或者大数量显隐使用v-show,否则使用v-if
computed和watch有何区别?

参考:超详细的computed和watch

vue生命周期函数

beforeCreate( 创建前 ) 实例了vue但还没进行数据的初始化与响应式处理
created(创建后)数据已被初始化和响应式处理,在这里可以访问数据,也可以使用数据
beforeMount(挂在前)render函数在这里被调用,生成虚拟DOM但是还没转成真是DOM并替换到el
mounted(挂载后)真实DOM挂载完毕
beforeUpdate(数据更新前调用)数据更新后新的DOM生成但还没跟旧虚拟DOM对比打补丁
updated(更新后)新旧DOM打补丁后进行真实DOM更新
beforeDestroy(实例销毁前)实例销毁前调用在这一步依然可以访问数据
destroyed(实例销毁后) 实例销毁后该钩子被调用,对应的vue事件被解绑所有的监听事件被移除所有的子实例被销毁
activated(被 keep-alive 缓存的组件激活时调用)
decactived(被 keep-alive 缓存的组件停用时调用)

vue父子组件的生命周期

1.加载渲染过程
父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted
2.子组件更新过程
父beforeUpdate->子beforeUpdate->子updated->父updated
3.父组件更新过程
4.销毁过程
父beforeDestroy->子beforeDestroy->子destroyed->父destroyed

为什么v-if和v-for不建议用在同一标签?

在Vue2中,v-for优先级是高于v-if的,咱们来看例子

<div v-for="item in [1, 2, 3, 4, 5, 6, 7]" v-if="item !== 3">
    {{item}}
</div>

上面的写法是v-for和v-if同时存在,会先把7个元素都遍历出来,然后再一个个判断是否为3,并把3给隐藏掉,这样的坏处就是,渲染了无用的3节点,增加无用的dom操作,建议使用computed来解决这个问题:

<div v-for="item in list">
    {{item}}
</div>

computed() {
    list() {
        return [1, 2, 3, 4, 5, 6, 7].filter(item => item !== 3)
    }
  }

你对组件的理解

可组合,可复用,可维护,可测试

vuex的有哪些属性?用处是什么?

在这里插入图片描述

  • State:定义了应用状态的数据结构,可以在这里设置默认的初始状态。
  • Getter:允许组件从 Store 中获取数据,mapGetters 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性。
  • Mutation:是唯一更改 store 中状态的方法,且必须是同步函数。
  • Action:用于提交 mutation,而不是直接变更状态,可以包含任意异步操作。
  • Module:允许将单一的 Store 拆分为多个 store 且同时保存在单一的状态树中。
不需要响应式的数据应该怎么处理?

在我们的Vue开发中,会有一些数据,从始至终都未曾改变过,这种死数据,既然不改变,那也就不需要对他做响应式处理了,不然只会做一些无用功消耗性能,比如一些写死的下拉框,写死的表格数据,这些数据量大的死数据,如果都进行响应式处理,那会消耗大量性能。

//方法一:将数据定义在data之外
data () {
    let abc = 123
    return {}
 }
// 方法二:Object.freeze()
data(){
	return{
		abc:Object.freeze({})
	}
}
watch有哪些属性,分别有什么用?
watch: {
    value () {
        // do something
    }
}
watch: {
    obj: {
       handler () { // 执行回调
           // do something
       },
       deep: true, // 是否进行深度监听
       immediate: true // immediate用于首次监听,默认监听只会在属性发生改变时才会触发,当immediate属性为true时,数据第一次渲染也会被触发监听属性。
    }
}

参考:Vue.js中 watch 用法详解(立即执行,深度监听)

-----------------------------------------------------------------------------------------------

对象新属性无法更新视图,删除属性无法更新视图,为什么?怎么办?
因:Object.defineProperty没有对对象的新属性进行属性劫持
对象新属性无法更新视图:使用Vue.$set(obj, key, value),组件中this.$set(obj, key, value)
删除属性无法更新视图:使用Vue.$delete(obj, key),组件中this.$delete(obj, key)
直接arr[index] = xxx无法更新视图怎么办?为什么?怎么办?

原因:Vue没有对数组进行Object.defineProperty的属性劫持,所以直接arr[index] = xxx是无法更新视图的
使用数组的splice方法,arr.splice(index, 1, item)
使用Vue.$set(arr, index, value)

说说vue的自定义指令

稍后更新

插槽的使用以及原理?

稍后更新

说说nextTick的用处?

稍后更新
-----------------------------------------------------------------------------------------------

vue中使用v-for时为什么要用到key?为什么不能用index作为key?

参考:vue中使用v-for时为什么要用到key?为什么不能用index作为key?

Vue的SSR是什么?有什么好处?
  1. SSR就是服务端渲染
  2. 基于nodejs serve服务环境开发,所有html代码在服务端渲染
  3. 数据返回给前端,然后前端进行“激活”,即可成为浏览器识别的html代码
  4. SSR首次加载更快,有更好的用户体验,有更好的seo优化,因为爬虫能看到整个页面的内容,如果是vue项目,由于数据还要经过解析,这就造成爬虫并不会等待你的数据加载完成,所以其实Vue项目的seo体验并不是很好
Vue的el属性和$mount优先级?

下面这种情况会先渲染哪个

new Vue({
  router,
  store,
  el: '#app',
  render: h => h(App)
}).$mount('#ggg')

这是官方的一张图,可以看出el和$mount同时存在时,el优先级 > $mount
在这里插入图片描述

4.vue的组件通信

1.方法一:props和$emit
父组件通过props向下传递数据给子组件,子组件通过event给父组件发送消息,实际上就是子组件把自己的数据发送给父组件。

方法二: a t t r s 和 attrs和 attrslisteners
第一种方式处理父子组件之间的数据传输有一个问题:如果父组件A下面有子组件B,组件B下面有组件C,这时如果组件A想传递数据给组件C怎么办呢? 如果采用第一种方法,我们必须让组件A通过prop传递消息给组件B,组件B在通过prop传递消息给组件C;要是组件A和组件C之间有更多的组件,那采用这种方式就很复杂了。Vue 2.4开始提供了 a t t r s 和 attrs和 attrslisteners来解决这个问题,能够让组件A之间传递消息给组件C。

<!--组件C-->
<template>
	<div>
 		<input type="text" v-model="$attrs.messagec" @input="passCData($attrs.messagec)">
  </div>
</template>
 
<script>
	export default {
    name: 'C',
    data () {
      return {}
    },
    methods:{
      passCData(val){
        //触发父组件A中的事件,把从A得到的数据hello C还给A组件
        this.$emit('getCData',val)
      }
   	}
  }
</script>
<!--组件B-->
<template>
	<div>
    <input type="text" v-model="mymessage" @input="passData(mymessage)">
    <!-- C组件中能直接触发getCData的原因在于 B组件调用C组件时 使用 v-on 绑定了$listeners 属性 -->
    <!-- 通过v-bind 绑定$attrs属性,C组件可以直接获取到A组件中传递下来的props(除了B组件中props声明的) -->
    <C v-bind="$attrs" v-on="$listeners"></C>
  </div>
</template>
 
<script>
	export default {
    name: 'B',
    props:[
      message:{
      	type: String,
      	default: null
      }
    ],//得到父组件A传递过来的数据,hello B
    methods:{
      passData(val){
        //触发父组件中的事件,把message给组件A
        this.$emit('getChildData',val)
      }
    }
  }
</script>
<!--组件A-->
<template>
	<div>
    <p>this is parent compoent!</p>
    <B 
     :messagec="messagec" 
     :message="message" 
     :getCData="getCData"
     :getChildData="getChildData"></B>
  </div>
</template>
 
<script>
	export default {
    name: 'A',
    data(){
      return {
        message:'hello B',
        messagec:'hello C' //传递给c组件的数据
      }
    },
    methods:{
      getChildData(val){
        console.log('这是来自B组件的数据',val) //打印hello B
      },
      //执行C子组件触发的事件
      getCData(val){
        console.log("这是来自C组件的数据:", val) //打印hello C
      }
    }
  }
</script>

3.方法三:provide和 inject
在 Vue.js 的 2.2.0+ 版本中添加加了 provide 和 inject 选项。他们成对出现,用于父级组件向下传递数据。

父组件中通过provide来提供变量,然后在子组件中通过inject来注入变量。不论子组件有多深,只要调用了inject那么就可以注入provide中的数据。而不是局限于只能从当前父组件的prop属性来获取数据,只要在父组件的生命周期内,子组件都可以调用。

<!--父组件-->
<template>
  <div>
    <p>this is parent compoent!</p>
    <child></child>
  </div>
</template>
 
<script>
	export default {
    name: 'parent',
    provide: {
      forChidrenData:'这是要个子组件的数据'
    },
    data(){
      return {
        message:'hello'
      }
    }
  }
</script>
 
<!--子组件-->
<template>
  <div>
    <input type="text" v-model="mymessage">
  </div>
</template>
 
<script>
	export default {
    name: 'child',
    inject:['forChidrenData'], //得到父组件传递过来的数据
    data(){
      return {
        mymessage:this.forChidrenData
      }
    }
  }
</script>

4.方法四:vuex处理组件之间的数据交互
如果业务逻辑复杂,很多组件之间需要同时处理一些公共的数据,这个时候才有上面这一些方法可能不利于项目的维护,vuex的做法就是将这一些公共的数据抽离出来,然后其他组件就可以对这个公共数据进行读写操作,这样达到了解耦的目的。
5.方法五:中央事件总线
如果两个组件不是父子关系呢?这种情况下可以使用中央事件总线的方式。新建一个Vue事件bus对象,然后通过bus. e m i t 触 发 事 件 , b u s . emit触发事件,bus. emitbus.on监听触发的事件。

公共事件总线eventBus的实质就是创建一个vue实例,通过一个空的vue实例作为桥梁实现vue组件间的通信。它是实现非父子组件通信的一种解决方案。

<!--兄弟组件A-->
<template>
  <div>
    <p>this is brother1 compoent!</p>
    <input type="text" v-model="mymessage" @input="passData(mymessage)">
  </div>
</template>
 
<script>
	export default {
    name: 'brother1',
    data(){
      return {
       	mymessage:'hello brother1'
      }
    },
    methods:{
      passData(val){
        //触发全局事件globalEvent
        bus.$emit('globalEvent', val)
      }
    }
  }
</script>
 
 
<!--兄弟组件B-->
<template>
  <div>
    <p>this is brother2 compoent!</p>
    <p>brother1传递过来的数据:{{brothermessage}}</p>
  </div>
</template>
 
<script>
	export default {
    name: 'brother2',
    data(){
      return {
        brothermessage:''
      }
    },
    mounted(){
      //绑定全局事件globalEvent
      bus.$on('globalEvent',(val)=>{
        this.brothermessage = val;
      })
    }
  }
</script>
//中央事件总线
var bus=new Vue();
var app=new Vue({
  el:'#app',
  template:`
    <div>
      <brother1></brother1>
      <brother2></brother2>
    </div>
  `,
   beforeDestroy(){
sd     bus.$off('globalEvent')
  }
})

6.方法六: p a r e n t 和 parent和 parentchildren

Vue.component('child',{
  props:{
    value:String, //v-model会自动传递一个字段为value的prop属性
  },
  data(){
    return {
      mymessage:this.value
    }
  },
  methods:{
    changeValue(){
      this.$parent.message = this.mymessage;//通过如此调用可以改变父组件的值
    }
  },
  template:`
    <div>
      <input type="text" v-model="mymessage" @change="changeValue">
    </div>`
})
  
Vue.component('parent',{
  template:`
    <div>
      <p>this is parent compoent!</p>
      <button @click="changeChildValue">test</button >
      <child></child>
    </div>
  `,
  methods:{
    changeChildValue(){
      this.$children[0].mymessage = 'hello';
    }
  },
  data(){
    return {
      message:'hello'
    }
  }
})
 
var app=new Vue({
  el:'#app',
  template:`
    <div>
      <parent></parent>
    </div>
  `
})
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值