复盘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,渲染页面,直至显示完成