vue面试题

复盘vue常见面试题。

1、vue和react、angular的相同点和不同点

React 和 Vue 相同点:
1、使用 Virtual DOM;
2、提供了响应式 (Reactive) 和组件化 (Composable) 的视图组件;
3、将注意力集中保持在核心库,而将其他功能如路由和全局状态管理交给相关的库;

React 和 Vue 不同点:
1、性能
vue和React的性能都非常高。优化:
在 React 应用中,当某个组件的状态发生变化时,它会以该组件为根,重新渲染整个组件子树。如要避免不必要的子组件的重渲染,你需要手动实现;在 Vue 应用中,组件的依赖是在渲染过程中自动追踪的,所以系统能精确知晓哪个组件确实需要被重渲染,开发者不需要考虑组件是否需要重新渲染之类的优化。
2、HTML & CSS
React使用的JSX语法,将HTML、CSS和JS混写;而Vue使用的是templates模板方式,完全融合于经典的Web技术。
3、组件作用域内的 CSS
CSS 作用域在 React 中是通过 CSS-in-JS 的方案实现的 (比如 styled-components、glamorous 和 emotion);在Vue中是通过给style标签加scoped标记实现的。
4、规模
Vue 的路由库和状态管理库都是由官方维护支持且与核心库同步更新的。React 则是选择把这些问题交给社区维护,因此创建了一个更分散的生态系统。但相对的,React 的生态系统相比 Vue 更加繁荣。
5、原生渲染
React Native 能使你用相同的组件模型编写有本地渲染能力的 APP (iOS 和 Android)。能同时跨多平台开发,对开发者是非常棒的。

Angular与Vue的相同点:
Vue 的一些语法和 AngularJS 的很相似 (例如 v-if vs ng-if)。因为 AngularJS 是 Vue 早期开发的灵感来源。

AngularJS和Vue不同点:
1、复杂性:在 API 与设计两方面上 Vue.js 都比 AngularJS 简单得多。
2、灵活性:Vue.js 是一个更加灵活开放的解决方案。
3、数据绑定:AngularJS 使用双向绑定,Vue 在不同组件间强制使用单向数据流。这使应用中的数据流更加清晰易懂。
4、指令和组件:在 Vue 中指令和组件分得更清晰。指令只封装 DOM 操作,而组件代表一个自给自足的独立单元——有自己的视图和数据逻辑。在 AngularJS 中,每件事都由指令来做,而组件只是一种特殊的指令。
5、运行时性能:Vue 有更好的性能,并且非常非常容易优化,因为它不使用脏检查。在 AngularJS 中,当 watcher 越来越多时会变得越来越慢,因为作用域内的每一次变化,所有 watcher 都要重新计算。并且,如果一些 watcher 触发另一个更新,脏检查循环 (digest cycle) 可能要运行多次。

Angular和Vue的不同点
1、Angular事实上必须用TypeScript来开发,而且Angular对于TS的支持非常全面,而Vue暂时对于TS的支持还在改进阶段。
2、Vue的体积更小,一个包含了 Vuex + Vue Router 的 Vue 项目 (gzip 之后 30kB) 相比使用了这些优化的 angular-cli 生成的默认项目尺寸 (~65KB) 还是要小得多。
3、灵活性:vue更灵活。
4、学习曲线:angular学习曲线很陡峭,它的设计目标就是只针对大型的复杂应用。

2、对于MVVM的理解

MVVM是Model-View-Viewmodel的缩写。其中:
Model代表数据模型,数据和业务逻辑都在Model层定义。
View代表UI视图,负责数据的展示。
ViewModel负责监听Model层中数据的变化,并且控制视图的更新,处理用户交互操作。
在MVVM框架中,Model和View并无直接的联系,而是通过ViewModel进行关联的,Model和ViewModel具有双向数据绑定的联系,因此Model中数据发生变化会触发View层的刷新,View中由于用户交互操作改变的数据也会同步到Model中。
这种模式实现了Model和View的数据自动同步,开发者只需要关注业务逻辑,不需要手动操作DOM。

3、v-if和v-show指令有什么区别

v-if控制DOM节点存在与否,即元素的重建和销毁。v-show是通过CSS的display:none/block来控制元素显示与否。当我们需要频繁切换元素的显示和隐藏时,使用v-show可以节省性能上的开销。当只需要一次显示和隐藏,则使用v-if更合理。

4、vue常用的事件修饰符

阻止单击事件冒泡:<a v-on:click.stop="doThis"></a>
阻止默认事件:<form v-on:submit.prevent="onSubmit"></form>
当event.target是当前元素自身时触发,即事件不是从内部元素触发:<div v-on:click.self="doThat">...</div>
添加事件监听器时使用事件捕获模式,即元素自身触发的事件先在此处理,然后才交由内部元素进行处理:

<div v-on:click.capture="doThis">...</div>

点击事件将只会触发一次:<a v-on:click.once="doThis"></a>
滚动事件的默认行为 (即滚动行为) 将会立即触发,而不会等待 onScroll 完成,.passive 会告诉浏览器你不想阻止事件的默认行为,不要把 .passive 和 .prevent 一起使用,因为 .prevent 将会被忽略:<div v-on:scroll.passive="onScroll">...</div>

5、vue组件间的参数传递/通信

父组件传给子组件:子组件通过props方法接受数据;
子组件传给父组件:$emit方法传递参数
例子:
作者:李小白1
来源:优快云
原文:https://blog.youkuaiyun.com/qq449736038/article/details/80898496
5.1、父组件与子组件间的传值

父组件:

 <template>
      <div class="hello">
        <h1 @click="changeMsg">click send message to child by props</h1>    //点击事件触发修改msg值,即可修改子组件的值
        <h2>props demo</h2>
        <children @receiveMsg="receive" :childMsg="msg"></children>    //使用:childMsg="msg"将子组件的变量与父组件的msg绑定起来,@receiveMsg作为子组件给父组件传值emit时的关键字
      </div>
    </template>
    <script type="es6">
    import Children from '@/components/Children'    //引入子组件
    export default {
      name: 'Parent',
      data () {
        return {
          msg: 'hello boy'
        }
      },
      components: {children: Children},    //注册子组件
      methods: {
        changeMsg () {
          this.msg = 'hello girl'
        },
        receive (data) {    //通过触发@receiveMsg事件而调用的函数
          console.log(data)
        }
      }
    }
    </script>

子组件:

<template>
  <div class="hello" style="border:1px solid #666">
    <h1>children component</h1>
    <h1>there is children component,receive message :{{childMsg}}</h1>    //此处使用props变量
    <h2 @click="send">click here send msg to parent</h2>
  </div>
</template>
<script>
export default {
  name: 'children',
  props: ['childMsg'],    //这里定义好变量
  data () {
    return {
      msg: 'child'
    }
  },
  methods: {
    send () {
      this.$emit('receiveMsg', 'from child')    //给父组件发送事件
    }
  }
}
</script>

5.2、非父子组件传值
简单的应用场景下,可以使用一个空的Vue实例作为中央事件总线,即创建一个事件中心,相当于中转站,可以用它来传递事件和接收事件。
main.js

import Vue from 'vue'
import App from './App'
import router from './router'

Vue.config.productionTip = false
Vue.prototype.bus = new Vue()    //此处全局注册一个Vue作为事件中心
/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  components: { App },
  template: '<App/>'
})

Brother1.vue

<template>
  <div class="hello" style="border:1px solid #666">
    <h1>Brother1 component</h1>
    <h2 @click="send">click send onb2</h2>
  </div>
</template>
<script>
export default {
  name: 'Brother1',
  data () {
    return {
      msg: 'Brother1'
    }
  },
  methods: {
    send () {
      this.bus.$emit('onb2', 'from brother1') // 触发Brother2.vue监听的onb2事件
    }
  },
  created () {
    this.bus.$on('onb1', (param) => { // 创建时,进行事件onb1监听
      console.log('receive onb1', param)
    })
  },
  beforeDestroy () {
    this.bus.$off('onb1') // 销毁时,事件onb1监听取消
    console.log('onb1 listener was closed')
  }
}
</script>

Brother2.vue

<template>
  <div class="hello" style="border:1px solid #666">
    <h1>Brother2 component</h1>
    <h2 @click="send">click send onb1</h2>
  </div>
</template>
<script>
export default {
  name: 'Brother2',
  data () {
    return {
      msg: 'Brother2'
    }
  },
  methods: {
    send () {
      this.bus.$emit('onb1', 'from brother2') // 触发Brother1.vue监听的onb1事件
    }
  },
  created () {
    this.bus.$on('onb2', (param) => { // 创建时,进行事件onb1监听
      console.log('receive onb2', param)
    })
  },
  beforeDestroy () {
    this.bus.$off('onb2') // 销毁时,事件onb2监听取消
    console.log('onb2 listener was closed')
  }
}
</script>

作者: 踩坑小王子
来源:优快云
原文: https://blog.youkuaiyun.com/Clark_Fitz817/article/details/79193771
官方推荐的eventbus 解决方案的缺陷在于, 在数据传递过程中,两个组件必须都已经被渲染过。
在复杂的情况下,可以考虑使用Vue 官方提供的状态管理模式——Vuex来进行管理。
store.js

//位于store.js 中
import Vue from 'vue'
import Vuex from 'vuex'
 
Vue.use(Vuex);
 
export default new Vuex.Store({
    /*
    * state指的就是储存的数据,
    * 下面的数据是我在项目中需要用的数据字段
    * */
  state: {
    has_login: false,
    id: 1,
    mobile_num: '',
    name: ''
  },
  /*
    * mutations里面规定的就是想要改变state(数据)的动作函数,
    * 下面的user_message 就是我将传入的message赋值给仓库中的
    * state数据字段,达到更新数据的目的
    * */
  mutations: {
    user_message (state, message) {
      state.has_login = true;
      state.id = message.data.id;
      state.mobile_num = message.data.mobile_num;
      state.name = message.data.name;
    }
  }
})

然后在login.vue组件中,提交收到的用户信息
//位于login.vue

import userMessage from '../store';
export default new Vuex.Store({
   methods: {
      loginSubmit () {
        this.$axios({
          method: 'post',
          url: '/student/login/',
          data: {
            username: this.username,
            password: this.password
          }
        })
          .then(function (response) {
              //这里调用 store.js中 mutations里面的user_message函数,从而改变仓库中的state数据
            userMessage.commit('user_message', response);
            this.$router.replace({path: 'user'});
          }.bind(this))
          .catch(function (error) {
            console.log(error);
            alert('用户名或密码错误');
          });
      }
})

最后在user.vue组件中接收vuex仓库中存储的信息,即先引入仓库

//位于user.vue 中
import userMessage from '../store';

然后可以直接将 userMessage.state 赋值给user.vue作用域中的数据字段,同时,vuex 的state有热更新的属性,对于数据的同步很有帮助,优点良多。
所以,中大型的项目还是在一开始就直接使用vuex是明智的决定,对于开发有很大的便利。

6、动态组件

通过使用保留的 元素,动态地绑定到它的 is 特性,我们让多个组件可以使用同一个挂载点,并可以动态地切换。

<component v-bind:is="currentTabComponent"></component>

除此之外,Vue还提供了 。当在这些组件之间切换的时候,你有时会想保持这些组件的状态,以避免反复重渲染导致的性能问题。为了解决这个问题,我们可以用一个 元素将其动态组件包裹起来。
注意这个 要求被切换到的组件都有自己的名字,不论是通过组件的 name 选项还是局部/全局注册。
keep-alive:https://cn.vuejs.org/v2/api/#keep-alive

<!-- 失活的组件将会被缓存!-->
<keep-alive>
  <component v-bind:is="currentTabComponent"></component>
</keep-alive>

7、为什么组件中的data属性的值必须是一个函数

组件是可复用的vue实例,如果data是对象的话,object是引用类型,当同一个组件被多次复用时,data拷贝的就会是引用的地址即指针,当在其中某个组件修改data的值,其他组件中的值也会变化。用function return 其实就相当于申明了新的变量,相互独立,自然就不会有这样的问题。

data() {
  return {
    count: 0
  }
}

8、vue有哪些指令

v-html、v-show、v-if、v-else、v-else-if、v-for、v-on、v-bind等等

9、vue的响应式(双向数据绑定)原理

当一个Vue实例创建时,vue会遍历data选项的所有属性,并用 Object.defineProperty 将它们转为 getter/setter,并且在内部追踪相关依赖,在属性被访问和修改时通知变化。
每个组件实例都有相应的 watcher 实例,它会在组件渲染的过程中把“接触”过的数据属性记录为依赖。之后当依赖项的 setter 触发时,会通知 watcher,从而使它关联的组件重新渲染。
在这里插入图片描述
js实现简单的双向绑定:

<body>
	<div id="app">
		<input type="text" id="txt"/>
		<p id="show"></p>
	</div>
</body>
<script type="text/javascript">
	var obj={};
	Object.defineProperty(obj,'txt',{
		get:function(){
			return obj;
		},
		set:function(newVal){
			document.getElementById("txt").value = newVal;
			document.getElementById("show").innerHTML = newVal;
		}
	});
	document.getElementById('txt').addEventListener('keyup',function(e){
		obj.txt = e.target.value;
	});
</script>

10、vue如何监控某个属性值的变化

比如现在需要监控data中,obj.a 的变化。Vue中监控对象属性的变化你可以使用:
侦听属性watch

计算属性computed

watch:{
	obj:{
		handler:function(newVal,oldVal){
			console.log("obj is changed");
		},
		deep:true
	}
}

deep属性表示深层遍历,但是这么写会监控obj的所有属性变化,并不是我们想要的效果,所以做点修改:

watch:{
	'obj.a':{
		handler:function(newVal,oldVal){
			console.log("obj.a is changed");
		}
	}
}

还有一种方法,可以通过computed 来实现,只需要:

computed:{
	a1:function(){
		return this.obj.a;
	}
}

利用计算属性的特性来实现,当依赖改变时,便会重新计算一个新值。

## 官方例子:

<div id="demo">{{ fullName }}</div>
  var vm = new Vue({
  el: '#demo',
  data: {
    firstName: 'Foo',
    lastName: 'Bar',
    fullName: 'Foo Bar'
  },
  watch: {
    firstName: function (val) {
      this.fullName = val + ' ' + this.lastName
    },
    lastName: function (val) {
      this.fullName = this.firstName + ' ' + val
    }
  }
})

上面代码是命令式且重复的。将它与计算属性的版本进行比较:

var vm = new Vue({
  el: '#demo',
  data: {
    firstName: 'Foo',
    lastName: 'Bar'
  },
  computed: {
    fullName: function () {
      return this.firstName + ' ' + this.lastName
    }
  }
})

11、Vue中给data中的对象属性添加一个新的属性时会发生什么,如何解决

示例:

<template>
  <div>
    <ul>
      <li v-for="value in obj" :key="value">
        {{value}}
      </li>
    </ul>
    <button @click="addObjB">添加obj.b</button>
  </div>
</template>
<script>
export default {
  data () {
    return {
      obj: {
        a: 'obj.a'
      }
    }
  },
  methods: {
    addObjB () {
      this.obj.b = 'obj.b'
      console.log(this.obj)
    }
  }
}
</script>

当点击button按钮后,obj.b已经添加成功,但是视图并未更新,因为vue实例创建时,obj.b未声明,因此未被vue转化为响应式的属性,自然就不会触发视图的更新。也就是以上方法新增属性并不是响应式的。为了解决这个问题可以使用:

Vue.set( target, propertyName/index, value )

参数:

{Object | Array} target
{string | number} propertyName/index
{any} value

返回值:设置的值。

用法:
向响应式对象中添加一个属性,并确保这个新属性同样是响应式的,且触发视图更新。它必须用于向响应式对象上添加新属性,因为 Vue 无法探测普通的新增属性 (比如 this.myObject.newProperty = 'hi')

注意对象不能是 Vue 实例,或者 Vue 实例的根数据对象。

addObjB () {
      // this.obj.b = 'obj.b'
      this.$set(this.obj, 'b', 'obj.b')
      console.log(this.obj)
    }

12、delete、splice、Vue.delete删除数组元素的区别

delete:将被删除元素值变成了empty/undefined,数组的长度不变,其他元素的键值也不改变。
splice、Vue.delete:删除了数组元素,使得其他元素的键值发生变化,数组长度也变化。

 <script type="text/javascript">
    	let arr1=[1,2,3,4];
    	let arr2=[1,2,3,4];
    	let arr3=[1,2,3,4];
    	delete arr1[1];
    	console.log(arr1);
    	arr2.splice(1,1);
    	console.log(arr2);
        this.$delete(arr3,1);
        console.log(arr3);
  </script>

结果:
在这里插入图片描述
在这里插入图片描述

13、优化SPA应用的首屏加载速度慢的问题

1、路由懒加载:
使用require.ensure,router懒加载就是按需加载组件,只有当路由被访问时才会加载对应的组件,而不是在加载首页的时候就加载,项目越大,对首屏加载的速度提升得越明显。
2、使用CDN资源
在Vue项目中,引入到工程中的所有js、css文件,编译时都会被打包进vendor.js,浏览器在加载该文件之后才能开始显示首屏。若是引入的库众多,那么vendor.js文件体积将会相当的大,影响首开的体验。
解决方法是,将引用的外部js、css文件剥离开来,不编译到vendor.js中,而是用资源的形式引用,这样浏览器可以使用多个线程异步将vendor.js、外部的js等加载下来,达到加速首开的目的。
外部的库文件,可以使用CDN资源,或者别的服务器资源等。
3、gzip压缩
4、异步加载组件
5、服务器端渲染

14、前端如何优化网站性能

1、减少HTTP请求数量:
在浏览器与服务器进行通信时,主要是通过 HTTP 进行通信。浏览器与服务器需要经过三次握手,每次握手需要花费大量时间。而且不同浏览器对资源文件并发请求数量有限(不同浏览器允许并发数),一旦 HTTP 请求数量达到一定数量,资源请求就存在等待状态,这是很致命的,因此减少 HTTP 的请求数量可以很大程度上对网站性能进行优化。

合并CSS文件
合并JS文件
使用精灵图CSS Sprites

采用懒加载lazyload

2、控制资源文件加载优先级:
浏览器在加载 HTML 内容时,是将 HTML 内容从上至下依次解析,解析到 link 或者 script 标签就会加载 href 或者 src 对应链接内容,为了第一时间展示页面给用户,就需要将 CSS 提前加载,不要受 JS 加载影响。
一般情况下都是 CSS 在头部,JS 在底部。

3、利用浏览器缓存
浏览器缓存是将网络资源存储在本地,等待下次请求该资源时,如果资源已经存在就不需要到服务器重新请求该资源,直接在本地读取该资源。

4、减少重排(Reflow)
基本原理:重排是DOM的变化影响到了元素的几何属性(宽和高),浏览器会重新计算元素的几何属性,会使渲染树中受到影响的部分失效,浏览器会验证 DOM 树上的所有其它结点的visibility属性,这也是Reflow低效的原因。如果Reflow的过于频繁,CPU使用率就会急剧上升。
减少Reflow,如果需要在DOM操作时添加样式,尽量使用 增加class属性,而不是通过style操作样式。

5、减少 DOM 操作
6、图标使用 IconFont 替换

15、网页到输入网址到渲染完成经历了哪些过程

1、输入网址
2、发送到DNS服务器,并获取域名对应的web服务器的对应的IP地址
3、与web服务器建议TCP连接
4、浏览器向web服务器发生http请求
5、web服务器响应请求并返回指定URL的数据、或错误信息、或重定向的新的URL地址
6、浏览器下载web服务器返回的数据及解析HTML源文件
7、生成DOM树,解析css、js,渲染页面,直至显示完成

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值