1.vue.js的两个核心是什么?
vue.js的两个核心分别是数据驱动(MVVM)和组件化。
一、数据驱动
数据驱动也就是组件之间数据之间的传输。以前用jq或者原生js都是通过操作dom来改变数据。而vue是通过MVVM形式进行数据之间的传输。即以view——viewModel——model形式;
从vue的created状态到beforeMount状态这段过程是将数据以model的形式传递到viewModel,
然后从beforeMount到mounted这段过程是将数据从viewModel挂载到dom上,然后在view层进行显示;
当view视图上面的数据发生改变时,通过beforeUpdate钩子函数或者是watch属性监听到数据的变化并通过updated钩子函数将更改之后的数据挂载到dom上,从而使得其他与该数据相关的值也会发生改变。
二、组件化
组件系统是Vue的另一个重要概念,因为它是一种抽象,允许我们使用小型、独立和通常可复用的组件去构建大型应用。
2.vue的生命周期是什么?
在vue中主要由四部分组成的,分别是创建(create)、挂载(mount)、更新(update)、摧毁(destory)组成的,根据这四部分的前后关系,又分为8个钩子函数。分别是创建前(beforeCreate)、创建后(created)、挂载前(beforeMount)、挂载后(mounted)、更新前(beforeUpdate)、更新后(updated)、注销前(beforeDestory)、注销后(destoryed);
下面就要插入一段代码更清楚的了解vue从beforeCreate到mounted过程的生命周期
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>vue生命周期学习</title>
<script src="https://cdn.bootcss.com/vue/2.4.2/vue.js"></script>
</head>
<body>
<div id="app">
<input type="text" v-model="message" />
<h1>{{message}}</h1>
</div>
</body>
<script>
var vm = new Vue({
el: '#app',
data: {
message: 'Vue的生命周期'
},
beforeCreate: function() {
console.group('------beforeCreate创建前状态------');
console.log("%c%s", "color:red" , "el : " + this.$el); //undefined
console.log("%c%s", "color:red","data : " + this.$data); //undefined
console.log("%c%s", "color:red","message: " + this.message)
},
created: function() {
console.group('------created创建完毕状态------');
console.log("%c%s", "color:red","el : " + this.$el); //undefined
console.log("%c%s", "color:red","data : " + this.$data); //已被初始化
console.log("%c%s", "color:red","message: " + this.message); //已被初始化
},
beforeMount: function() {
console.group('------beforeMount挂载前状态------');
console.log("%c%s", "color:red","el : " + (this.$el)); //已被初始化
console.log(this.$el);
console.log("%c%s", "color:red","data : " + this.$data); //已被初始化
console.log("%c%s", "color:red","message: " + this.message); //已被初始化
},
mounted: function() {
console.group('------mounted 挂载结束状态------');
console.log("%c%s", "color:red","el : " + this.$el); //已被初始化
console.log(this.$el);
console.log("%c%s", "color:red","data : " + this.$data); //已被初始化
console.log("%c%s", "color:red","message: " + this.message); //已被初始化
},
beforeUpdate: function () {
console.group('beforeUpdate 更新前状态===============》');
console.log("%c%s", "color:red","el : " + this.$el);
console.log(this.$el);
console.log("%c%s", "color:red","data : " + this.$data);
console.log("%c%s", "color:red","message: " + this.message);
},
updated: function () {
console.group('updated 更新完成状态===============》');
console.log("%c%s", "color:red","el : " + this.$el);
console.log(this.$el);
console.log("%c%s", "color:red","data : " + this.$data);
console.log("%c%s", "color:red","message: " + this.message);
},
beforeDestroy: function () {
console.group('beforeDestroy 销毁前状态===============》');
console.log("%c%s", "color:red","el : " + this.$el);
console.log(this.$el);
console.log("%c%s", "color:red","data : " + this.$data);
console.log("%c%s", "color:red","message: " + this.message);
},
destroyed: function () {
console.group('destroyed 销毁完成状态===============》');
console.log("%c%s", "color:red","el : " + this.$el);
console.log(this.$el);
console.log("%c%s", "color:red","data : " + this.$data);
console.log("%c%s", "color:red","message: " + this.message)
}
})
</script>
打开页面,然后看控制台,会输出以上结果
然后看我画箭头的部分,相信大家应该明白什么意思了。
打开页面时,vue的生命周期会从vue实例创建之前到挂载完成,在控制台中科院看到
1.beforeCreate到created钩子函数之间的生命周期
这段时间主要是对vue实例进行初始化事件,并进行数据的观测(比如说data属性、computed属性、methods属性);
2.created到beforeMount钩子函数之间的生命周期
从图片上面的流程图可得知,created到beforeMount这个过程主要还是将查找el并将模板放到dom上,
首先是判断vue实例中是否有el选项,如果没有的话就会等到vm.$mount(el)挂载之后才能继续判断有无template参数选项;如果存在el选项的话就直接判断有无template参数选项,如果有的话则将其作为模板并编译成render函数
如果没有template选项,则将外部的HTML模板进行编译;
所以template中的模板优先级高于outerHTML的优先级。
3.beforeMount到mounted钩子函数之间的生命周期
由上面两张图片可以得知,从beforeMount到mounted钩子函数这个过程主要是给vue实例添加$el成员,并且替换掉挂载的DOM元素。
4.mounted钩子函数
此时数据已经挂载到dom上,并在view层显示。
5.beforeUpdate钩子函数到updated钩子函数之间的生命周期
[图片上传中...(image-14cea9-1543655591207-2)]
从上面两图片可得知,当监听到数据发生变化的时候,先触发beforeUpdate事件,这时候vm.message的值重新渲染到虚拟dom上;
6.beforeDestory到destoryed钩子函数之间的生命周期
beforeDestory钩子函数在实例销毁之前调用。在这一步,实例仍然完全可用。
destroyed钩子函数在Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。
3.请问v-if和v-show有什么区别?
v-if是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当的销毁和重建。
v-if也是惰性的:如果在处室渲染时条件块为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块;
相比之下,v-show就简单得多——不管初始条件是什么,元素总是被渲染,并能进行简单的css切换;
所以一般来说,v-if 有更高的切换开销,而v-show有更高的初始渲染开销。因此,如果是需要进行频繁的切换,则用v-show比较好;如果运行时条件很少改变,则用v-if比较好。
v-if还可以用来实现异步获取组件信息
4.vue常用的修饰符?
主要分为事件修饰符和按键修饰符;事件修饰符有以下几个:
.stop - 调用 event.stopPropagation()。
.prevent - 调用 event.preventDefault()。
.capture - 添加事件侦听器时使用 capture 模式。
.self - 只当事件是从侦听器绑定的元素本身触发时才触发回调。
.{keyCode | keyAlias} - 只当事件是从特定键触发时才触发回调。
.native - 监听组件根元素的原生事件。
.once - 只触发一次回调。
.left - (2.2.0) 只当点击鼠标左键时触发。
.right - (2.2.0) 只当点击鼠标右键时触发。
.middle - (2.2.0) 只当点击鼠标中键时触发。
.passive - (2.3.0) 以 { passive: true } 模式添加侦听器
5.v-on可以监听多个方法吗?
可以。监听一个方法里面运行若干个方法,就能监听若干个方法了;
6.vue中key值的作用
key的特殊属性主要用在Vue的虚拟dom算法,新旧nodes对比时辨识VNodes.如果不使用key,Vue会使用一种最大限度减少动态元素并且尽可能的尝试修复/再利用相同类型元素的算法。使用key,它会基于key的变化重新排列元素顺序,并且会移除key不存在的元素。
它可用于与v-for的结合,例如
<ul>
<li v-for="item in items" :key="item.id">...</li>
</ul>
它也可以用于强制替换元素/组件而不是重复使用它。当遇到如下场景时它可能会很有用:
完整地触发组件的生命周期钩子;
触发过渡
7.什么是双向绑定?在Vue中有几种方法实现双向绑定?
双向绑定是view层数据发生变化的时候会改变model层的数据变化;然后根据model层的数据变化进同步文本节点内容,使之也发生变化,即从model到view层的数据变化;
如果是输入框可以使用v-model实现双向绑定;因为v-model可以看做v-bind:value="aaa",v-on:input="aaa"组成的;
如果用原生表示的话,则可通过Object.defineProperty()中的get和set方法来完成数据双向绑定。即
var obj = {
foo: 'foo'
}
Object.defineProperty(obj, 'foo', {
get: function () {
console.log('将要读取obj.foo属性');
},
set: function (newVal) {
console.log('当前值为', newVal);
}
});
obj.foo; // 将要读取obj.foo属性
obj.foo = 'name'; // 当前值为 name
不过一般情况下出于后期项目维护考虑,在Vue中一般都是使用单向数据流的形式对数据进行传输,这样方便追踪数据来源,有利于项目维护。
8.Vue组件间的通信有几种场景?这些场景又有几种方法能够实现?
总共有以下几种场景:
1.父->子组件间的数据传递
父子组件的通信是开发是最常用的也是最重要的,一般情况下都是父元素设置属性然后子元素通过prop接受父元素传递的属性值。
注意项:
父组件传递数据时类似在标签中写了一个属性,如果是传递的数据是data中的自然是要在传递属性前加v-bind:,如果传递的是一个已知的固定值呢
字符串是静态的可直接传入无需在属性前加v-bind
数字,布尔,对象,数组,因为这些是js表达式而不是字符串,所以即使这些传递的是静态的也需要加v-bind,把数据放到data中引用,
如果prop传到子组件中的数据是一个对象的话,要注意传递的是一个对象引用,虽然父子组件看似是分离的但最后都是在同一对象下
如果prop传到子组件的值只是作为初始值使用,且在父组件中不会变化赋值到data中使用
如果传到子组件的prop的数据在父组件会被改变的,放到计算属性中监听变化使用。因为如果传递的是个对象的话,只改变下面的某个属性子组件中是不会响应式更新的,如果子组件需要在数据变化时响应式更新那只能放到computed中或者用watch深拷贝deep:true才能监听到变化
当然如果你又需要在子组件中通过prop传递数据的变化做些操作,那么写在computed中会报警告,因为计算属性中不推荐有任何数据的改变,最好只进行计算。如果你非要进行数据的操作那么可以把监听写在watch(注意deep深拷贝)或者使用computed的get和set如下图:
但问题又来了,如果你传进来的是个对象,同时你又需要在子组件中操作传进来的这个数据,那么在父组件中的这个数据也会改变,因为你传递的只是个引用, 即使你把prop的数据复制到data中也是一样的,无论如何赋值都是引用的赋值,你只能对对象做深拷贝创建一个副本才能继续操作,你可以用JSON的方法先转化字符串在转成对象更方便一点,
所以在父子传递数据时要先考虑好数据要如何使用,否则你会遇到很多问题或子组件中修改了父组件中的数据,这是很隐蔽并且很危险的
2.子->父组件间的数据传递
在vue中子向父传递数据一般用$emit自定义事件,在父组件中监听这个事件并在回调中写相关逻辑
// 父组件监听子组件定义的事件
<editor :inputIndex="index" @editorEmit='editorEmit'></editor>
// 子组件需要返回数据时执行,并可以传递数据
this.$emit('editorEmit', data)
那么问题来了,我是不是真的有必要去向父组件返回这个数据,用自定义事件可以在当子组件想传递数据或向子组件传递的数据有变化需要重新传递时执行,那么另外一种场景,父组件需要子组件的一个数据但子组件并不知道或者说没有能力在父组件想要的时候给父组件,那么这个时候就要用到组件的一个选项ref:
<editor ref="editor" @editorEmit='editorEmit'></editor>
父组件在标签中定义ref属性,在js中直接调用this.$refs.editor就是调用整个子组件,子组件的所有内容都能通过ref去调用,当然我们并不推荐因为这会使数据看起来非常混乱,
所以我们可以在子组件中定义一种专供父组件调用的函数,,比如我们在这个函数中返回子组件data中某个数据,当父组件想要获取这个数据就直接主动调用ref执行这个函数获取这个数据,这样能适应很大一部分场景,逻辑也更清晰一点
另外,父向子传递数据也可以用ref,有次需要在一个父组件中大量调用同一个子组件,而每次调用传递的prop数据都不同,并且传递数据会根据之后操作变化,这样我需要在data中定义大量相关数据并改变它,我可以直接用ref调用子组件函数直接把数据以参数的形式传给子组件,逻辑一下子清晰了
如果调用基础组件可以在父组件中调用ref执行基础组件中暴露的各种功能接口,比如显示,消失等
3.兄弟组件间的数据传递
vue中兄弟组件间的通信是很不方便的,或者说不支持的,那么父子组件中都有什么通信方式呢
- 路由URL参数
在传统开发时我们常常把需要跨页面传递的数据放到url后面,跳转到另外页面时直接获取url字符串获取想要的参数即可,在vue跨组件时一样可以这么做,
// router index.js 动态路由
{
path:'/params/:Id',
component:Params,
name:Params
}
// 跳转路由
<router-link :to="/params/12">跳转路由</router-link>
在跳转后的组件中用$route.params.id去获取到这个id参数为12,但这种只适合传递比较小的数据,数字之类的
Bus通信
在组件之外定义一个bus.js作为组件间通信的桥梁,适用于比较小型不需要vuex又需要兄弟组件通信的
bus.js中添加如下
import Vue from 'vue'
export default new Vue
组件中调用bus.js通过自定义事件传递数据
import Bus from './bus.js'
export default {
methods: {
bus () {
Bus.$emit('msg', '我要传给兄弟组件们')
}
}
}
兄弟组件中监听事件接受数据
import Bus from './bus.js'
export default {
mounted() {
Bus.$on('msg', (e) => {
console.log(e)
})
}
}
注:以上两种使用场景并不高所以只是简略提一下,这两点都是很久以前写过,以上例子网上直接搜集而来如有错误,指正
- Vuex集中状态管理
vuex是vue的集中状态管理工具,对于大型应用统一集中管理数据,很方便,在此对vuex的用法并不过多介绍只是提一下使用过程中遇到的问题
规范:对于多人开发的大型应用规范的制定是至关重要的,对于所有人都会接触到的vuex对其修改数据调用数据都应有一个明确严格的使用规范
vuex分模块:项目不同模块间维护各自的vuex数据
限制调用:只允许action操作数据,getters获取数据,使用mapGetters,mapActions辅助函数调用数据
- 对于vuex的使用场景也有一些争论,有人认为正常组件之间就是要用父子组件传值的方式,即使子组件需要使vuex中的数据也应该由父组件获取再传到子组件中,但有的时候组件间嵌套很深,只允许父组件获取数据并不是一个方便的方法,所以对于祖先元组件与子组件传值又有了新问题,vue官网也有一些方法解决,如下
4.组件深层嵌套,祖先组件与子组件之间的数据传递
provide/inject
除了正常的父子组件传值外,vue也提供了provide/inject
这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效
官网实例
// 父级组件提供 'foo'
var Provider = {
provide: {
foo: 'bar'
},
}
// 子组件注入 'foo'
var Child = {
inject: ['foo'],
created () {
console.log(this.foo) // => "bar"
}
}
- provide 选项应该是一个对象或返回一个对象的函数。该对象包含可注入其子孙的属性。
一个字符串数组,或
- 一个对象,对象的 key 是本地的绑定名,value 是:
在可用的注入内容中搜索用的 key (字符串或 Symbol),或
一个对象,该对象的:
from 属性是在可用的注入内容中搜索用的 key (字符串或 Symbol)
default 属性是降级情况下使用的 value
提示:provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的属性还是可响应的。
具体细节移步vue相关介绍****https://cn.vuejs.org/v2/api/#...
provide/inject还未在项目中应用过,后面会做尝试