前言

本文是对vue2.0的重新学习后的补充知识点


v-for使用js实现

vue是声明式编码(声明就行),下方的为命令式编码(一步一步走缺一不可)

1
2
3
4
5
6
7
8
9
<ul></ul>
// js部分
var list = [{str: '第一个'}, {str: '第二个'}]
var ul = document.querySelector('ul')
var li = ''
list.forEach(item => {
li += `<li>${item.str}</li>` // 这里使用 += 可以拼接作用
})
ul.innerHTML = li

vue初识

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
初识Vue
1.想让Vue工作,就必须创建一个Vue实例,且要传入一个配置对象;
2.Vue实例和容器是一一对应的;// 不能一个实例对应多个vue模板
<div id="root"> // vue 模板
<h1>Hello,{{name}}</h1>
</div>
Vue.config.productionTip = false // 阻止vue在启动时生成生产提示。生产中不需要提示警告等。
new Vue({
el:'#root', // 还可以写成document.querySelect('#root')
data:{
name:'moxie',
}
})

// --------

3.{{ xxx }}{{1+1}}中要写js表达式,或者到data中的属性;当然只要是vm中的属性都可以写。
注意区分:js表达式 和 js代码(语句)
1.表达式:一个表达式会产生一个值,可以放在任何一个需要值的地方:
(1). a
(2). a+b
(3). demo(1)
(4). x === y ? 'a' : 'b'
2.js代码(语句)
(1). if(){}
(2). for(){}

vue的基础补充

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 单向绑定v-bind:x(简写:x),双向绑定v-module:value只能用于表单元素上(简写v-module)
// 如下代码是错误的,因为v-model只能应用在表单类元素(输入类元素)上
<h2 v-model:x="name">你好啊</h2>

// el的第二种写法
const vm = new Vue({
data:{
name:'默谐'
}
})
v.$mount('#root')
// 在实例化配置好Vue,再进行手动绑定DOM

// ----------
// data的函数式写法
// 由Vue管理的函数,一定不要写箭头函数,一旦写了箭头函数,this就不再是Vue实例了。
// ES6:对象里面的方法可以简写method(){},不需要function

MVVM模型

1
2
3
4
5
6
7
MVVM模型
1. M:模型(Model) :data中的数据
2. V:视图(View) :模板代码
3. VM:视图模型(ViewModel):Vue实例
观察发现:
1.data中所有的属性,最后都出现在了vm身上。
2.vm身上所有的属性 及 Vue原型上所有属性,在Vue模板中都可以直接使用。

数据代理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// Object.defineProperty()方法
var person = {}
Object.defineProperty(person,'age',{
// value:18,
// enumerable:true, // 控制属性是否可以枚举,默认值是false
// writable:true, // 控制属性是否可以被修改,默认值是false
// configurable:true // 控制属性是否可以被删除,默认值是false
})

// 高级用法get、set
Object.defineProperty(person,'age',{
//当有人读取person的age属性时,get函数(getter)就会被调用,且返回值就是age的值
get(){
console.log('有人读取age属性了')
return number
},

//当有人修改person的age属性时,set函数(setter)就会被调用,且会收到修改的具体值
set(value){
console.log('有人修改了age属性,且值是',value)
number = value // 重新赋值number、进而影响value
}
})

// ----------------------

// vue中的使用
// 数据代理:通过一个对象代理对另一个对象中属性的操作(读/写)
//也就是一个对象的值与另外的值完全绑定在一起,值改变对象的值也会跟着改变
let obj = {x:100}
let obj2 = {y:200}

Object.defineProperty(obj2,'x',{
get(){
return obj.x
},
set(value){
obj.x = value
}
})

//
1.Vue中的数据代理:通过vm对象来代理data对象中属性的操作(读/写)
2.Vue中数据代理的好处:更加方便的操作data中的数据
3.基本原理:
通过Object.defineProperty()把data对象中所有属性添加到vm上。
为每一个添加到vm上的属性,都指定一个getter/setter。
在getter/setter内部去操作(读/写)data中对应的属性。
// data中的数据,vue处理后放在vm_data中,再进行数据代理到vm下的每个属性,模板中就可以直接使用

事件处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
// 事件的基本使用
<div id="root">
<h2>{{name}}</h2>
<button @click="showInfo1">点我提示信息1(不传参)</button>
<button @click="showInfo2($event,'参数')">点我提示信息2(传参)</button>
</div>
new Vue({
el:'#root',
data:{
name:'moxie',
},
methods:{
showInfo1(event){
// 上面的@click绑定事件不带(),其第一个参数就是事件对象
console.log(event) // 事件对象
},
showInfo2(event,val){
// 带上()则需要手动带上$event参数(当然其绑定时候位置可以更换)
console.log(event,val) // 事件对象,'参数'
}
}
})

// 事件修饰符
/*
Vue中的事件修饰符:
1.prevent:阻止默认事件(常用);
2.stop:阻止事件冒泡(常用);
3.once:事件只触发一次(常用);
4.capture:使用事件的捕获模式;
5.self:只有event.target是当前操作的元素时才触发事件;
6.passive:事件的默认行为立即执行,无需等待事件回调执行完毕;
*/

// 键盘修饰符
/*
1.Vue中常用的按键别名:
回车 => enter
删除 => delete (捕获“删除”和“退格”键)
退出 => esc
空格 => space
换行 => tab (特殊,必须配合keydown去使用)
上 => up
下 => down
左 => left
右 => right
2.Vue未提供别名的按键,可以使用按键原始的key值去绑定,但注意要转为kebab-case(短横线命名)
3.系统修饰键(用法特殊):ctrl、alt、shift、meta
(1).配合keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发。
(2).配合keydown使用:正常触发事件。
4.也可以使用keyCode去指定具体的按键(不推荐)
5.Vue.config.keyCodes.自定义键名 = 键码,可以去定制按键别名
*/

计算属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
/* 计算属性:
1.定义:要用的属性不存在,要通过已有属性计算得来。
2.原理:底层借助了Objcet.defineproperty方法提供的getter和setter。
3.get函数什么时候执行?
(1).初次读取时会执行一次。
(2).当依赖的数据发生改变时会被再次调用。
4.优势:与methods实现相比,内部有缓存机制(复用),效率更高,调试方便。
5.备注:
1.计算属性最终会出现在vm上,直接读取使用即可。
2.如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变。
*/

<div id="root">
姓:<input type="text" v-model="firstName">
名:<input type="text" v-model="lastName">
全名:<span>{{fullName}}</span>
</div>

const vm = new Vue({
el:'#root',
data:{
firstName:'张',
lastName:'三',
},
computed:{
fullName:{
// get有什么作用?当有人读取fullName时,get就会被调用,且返回值就作为fullName的值
// get什么时候调用?1.初次读取fullName时。2.所依赖的数据发生变化时。
get(){
return this.firstName + '-' + this.lastName
},
// set什么时候调用? 当fullName被修改时。
set(value){
// 加入修改fullName = '李-四'
const arr = value.split('-')
this.firstName = arr[0]
this.lastName = arr[1]
}
}
}
})

// 计算属性的简写方式
<div id="app">{{show(message)}}</div>
computed:{
fullName(){
return this.firstName + '-' + this.lastName
}
}

// 计算属性的接收参数(与filters的作用一致)
new Vue({
el: '#app',
data: {
message: '消息'
},
computed: {
show() {
// 返回一个函数来接收参数
return function (value) {
return '处理后的' + value
}
}
// 不简写的方式
/*show: {
// 返回一个函数来接收参数
get() {
return (value) => {
return '处理后的' + value
}
}

}*/
}
})

// 注意computed写上异步操作的如setTimeOut(function(){})时候,computed没有返回值,
// 而是setTimeOut里面的函数返回,而这个函数不归vue管理,而是浏览器js调用

侦听属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// watch侦听的写法
watch:{
isHot:{
// 初始化时让handler调用一下
immediate:true,
// 开启深度侦听,watch默认不监测对象内部值的改变,配置deep:true可以
// Vue自身可以监测对象内部值的改变,但Vue提供的watch默认不可以
deep:true,
// handler什么时候调用?当isHot发生改变时。
handler(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue)
}
}
//简写
/* isHot(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue,this)
} */
}

// watch的另外的写法
vm.$watch('isHot',{}) // 不是在实例创建时候就侦听,手动侦听
vm.$watch('isHot',()=>{}) // 简写

computed和watch之间的区别:

  1. computed能完成的功能,watch都可以完成。

  2. watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作。

两个重要的小原则:

  1. 所被Vue管理的函数,最好写成普通函数,这样this的指向才是vm 或 组件实例对象。

  2. 所有不被Vue所管理的函数(定时器的回调函数、ajax的回调函数等、Promise的回调函数),最好写成箭头函数,这样this的指向才是vm 或 组件实例对象。


绑定样式

1
2
3
4
5
6
7
8
1. class样式
写法:class="xxx" xxx可以是字符串、对象、数组。
字符串写法适用于:类名不确定,要动态获取。
对象写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用。
数组写法适用于:要绑定多个样式,个数不确定,名字也不确定。
2. style样式
:style="{fontSize: xxx}"其中xxx是动态值。
:style="[a,b]"其中a、b是样式对象。

条件渲染

1
2
3
4
5
6
7
8
9
10
11
12
13
14
1.v-if
适用于:切换频率较低的场景。
特点:不展示的DOM元素直接被移除。
注意:v-if可以和:v-else-if、v-else一起使用,但要求结构不能被“打断”。

2.v-show
写法:v-show="表达式"
适用于:切换频率较高的场景。
特点:不展示的DOM元素未被移除,仅仅是使用样式隐藏掉

3.备注:使用v-if的时,元素可能无法获取到,而使用v-show一定可以获取到。

4.注意:
v-if只能与template的配合使用,而v-show是并不可以的

列表渲染

1
2
3
4
5
1.可遍历:数组、对象、字符串(用的很少)、指定次数(用的很少)
2.v-for有两种写法(推荐第一种)
v-for="(item, index) in xxx" :key="yyy"
v-for="(item, index) of xxx" :key="yyy"
3.注意key值

key的原理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
1. 虚拟DOM中key的作用:
key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】,
随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下:

2.对比规则:
(1).旧虚拟DOM中找到了与新虚拟DOM相同的key:
①.若虚拟DOM中内容没变, 直接使用之前的真实DOM!
②.若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM。

(2).旧虚拟DOM中未找到与新虚拟DOM相同的key
创建新的真实DOM,随后渲染到到页面。

3. 用index作为key可能会引发的问题:
1. 若对数据进行:逆序添加、逆序删除等破坏顺序操作:
会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。

2. 如果结构中还包含输入类的DOM:
会产生错误DOM更新 ==> 界面有问题。

4. 开发中如何选择key?:
1.最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。
2.如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,
使用index作为key是没有问题的。

举例:使用index作为key的部分缺陷

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 用户已经在input上输入了内容,当在改变渲染的数组前面追加一个元素时,
// 因为input虚拟DOM无改变复用到第一个上。
<div id="root">
<!-- 遍历数组 -->
<h2>人员列表(遍历数组)</h2>
<button @click.once="add">添加一个老刘</button>
<ul>
<li v-for="(p,index) of persons" :key="index">
{{p.name}}-{{p.age}}
<input type="text">
</li>
</ul>
</div>

<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el:'#root',
data:{
persons:[
{id:'001',name:'张三',age:18},
{id:'002',name:'李四',age:19},
{id:'003',name:'王五',age:20}
]
},
methods: {
add(){
const p = {id:'004',name:'老刘',age:40}
this.persons.unshift(p)
}
},
})
</script>

vue的监视数据的原理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Vue监视数据的原理:
1. vue会监视data中所有层次的数据。
2. 如何监测对象中的数据?
通过setter实现监视,且要在new Vue时就传入要监测的数据。
(1).对象中后追加的属性,Vue默认不做响应式处理
(2).如需给后添加的属性做响应式,请使用如下API:
Vue.set(target,propertyName/index,value) 或
vm.$set(target,propertyName/index,value)
3. 如何监测数组中的数据?
通过包裹数组更新元素的方法实现,本质就是做了两件事:
(1).调用原生对应的方法对数组进行更新。
(2).重新解析模板,进而更新页面。
4.在Vue修改数组中的某个元素一定要用如下方法:
1.使用这些API:push()、pop()、shift()、unshift()、splice()、sort()、reverse()
2.Vue.set() 或 vm.$set()
特别注意:Vue.set() 和 vm.$set() 不能给vm 或 vm的根数据对象添加属性!!!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 一个模拟vue的数据监听

let data = {
name:'默谐',
}

//创建一个监视的实例对象,用于监视data中属性的变化
const obs = new Observer(data)
console.log(obs)

//准备一个vm实例对象
let vm = {}
vm._data = data = obs

function Observer(obj){
//汇总对象中所有的属性形成一个数组
const keys = Object.keys(obj)
//遍历
keys.forEach((k)=>{
Object.defineProperty(this,k,{
get(){
return obj[k]
},
set(val){
console.log(`${k}被改了,我要去解析模板,生成虚拟DOM.....我要开始忙了`)
obj[k] = val
}
})
})
}

v-model表单使用

1
2
3
4
5
6
7
8
9
10
// <input type="radio"/>,多个input添加name属性,并且添加value值
// <input type="checkbox"/>
1.没有配置input的value属性,那么收集的就是checked(勾选 or 未勾选,是布尔值)
2.配置input的value属性:
(1)v-model的初始值是非数组,那么收集的就是checked(勾选 or 未勾选,是布尔值)
(2)v-model的初始值是数组,那么收集的的就是value组成的数组
// 备注:v-model的三个修饰符:
lazy:失去焦点再收集数据
number:输入字符串转为有效的数字
trim:输入首尾空格过滤

过滤器补充

1
2
3
4
5
6
7
8
语法:
1.注册过滤器:Vue.filter(name,callback) 或 new Vue{filters:{}}
2.使用过滤器:{{ xxx | 过滤器名}} 或 v-bind:属性 = "xxx | 过滤器名"
备注:
1.过滤器也可以接收额外参数、多个过滤器也可以串联
2.并没有改变原本的数据, 是产生新的对应的数据

注意:过滤器和methods一样没有缓存,用了几次就会调用几次

内置指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
v-cloak指令(没有值):
1.本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性。
2.使用css配合v-cloak可以解决网速慢时页面展示出{{xxx}}的问题。

// css
<style>
[v-cloak]{
display:none;
}
</style>
// html
<div v-cloak>{{name}}</div>

// js
// new Vue({}) 实例创建完毕并接管容器后移除v-cloak

// ---------------------
v-once指令:
1.v-once所在节点在初次动态渲染后,就视为静态内容了。
2.以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能。

// ---------------------
v-pre指令:
1.跳过其所在节点的编译过程。{{}}等v-bind都不会被解析
2.可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译。

自定义指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// 一个directives的配置对象
directives:{
// 指令何时会被调用?
// 1.指令与元素成功绑定时(一上来)。
// 2.指令所在的模板被重新解析时。
// 接收两个参数1绑定的元素、2是绑定的属性(value值等)
big(element,binding,vNoed){
console.log('big',this) //注意此处的this是window
element.innerText = binding.value * 10
vNoed.context // 获取vue实例
vNode.context.$nextTick() // 使用nextTick
},
// 配置对象的写法(三个常用的钩子函数)
fbind:{
// 指令与元素成功绑定时(一上来)
bind(element,binding){
element.value = binding.value
},
// 指令所在元素被插入页面时
inserted(element,binding){
// 注意有些要找到真实DOM才能操作(如下)
element.focus()
},
// 指令所在的模板被重新解析时
update(element,binding){
element.value = binding.value
}
}
}

// -------------------------

// 全局指令
// Vue.directive(指令名,配置对象) 或 Vue.directive(指令名,回调函数)
// 注意:
// 1.指令定义时不加v-,但使用时要加v-;
// 2.指令名如果是多个单词,要使用kebab-case命名方式,不要用camelCase命名。(下面的key用引号包起来)

声命周期

1
2
3
4
5
6
7
常用的生命周期钩子:
1.mounted: 发送ajax请求、启动定时器、绑定自定义事件、订阅消息等【初始化操作】。
2.beforeDestroy: 清除定时器、解绑自定义事件、取消订阅消息等【收尾工作】。
关于销毁Vue实例:
1.销毁后借助Vue开发者工具看不到任何信息。
2.销毁后自定义事件会失效,但原生DOM事件依然有效。(重要)
3.一般不会在beforeDestroy操作数据,因为即便操作数据,也不会再触发更新流程了。

组件

非单文件组件的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
Vue中使用组件的三大步骤:
①定义组件(创建组件)
②注册组件
③使用组件(写组件标签)
一、如何定义一个组件?
使用Vue.extend(options)创建,options和new Vue传入的那个几乎一样,但也有如下区别:
1.el不能写,所有组件都经过vm管理,由vm中的el决定服务哪个容器。使用template配置组件结构。
2.data必须写成函数,避免组件被复用时,数据存在引用关系。
二、如何注册组件?
1.局部注册:靠new Vue的时候传入components选项
2.全局注册:靠Vue.component('组件名',组件)
三、编写组件标签:
<school></school>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//第一步组件
const my-info = Vue.extend({
template:`<div class="demo">{{name}}</div>`,
data(){
return {
name:'moxie',
}
}
})

// 注册组件
Vue.component('my-info',my-info) // 全局注册
// 局部注册
//创建vm
new Vue({
el:'#root',
components:{'my-info'}
})

/* 注意:
1. el不能写结构,要在template里写,并且只能写一个标签;
2. 关于组件名:
一个单词组成:
第一种写法(首字母小写):school
第二种写法(首字母大写):School
多个单词组成:
第一种写法(kebab-case命名):my-school
第二种写法(CamelCase命名):MySchool (需要Vue脚手架支持)
// 组件添加name属性后,在开发者工具中呈现为配置的名字。
3.关于组件标签:
第一种写法:<school></school>
第二种写法:<school/>,不用使用脚手架时,会导致后续组件不能渲染。
4.组件的嵌套,就是在组件里面配置components属性,并在template里面引用
5.不使用Vue.extend(options),直接 const school = options 一个配置对象,template里面引用就会自动调用
new VueComponent(options),在使用脚手架时候就是 export default 暴露一个配置对象{}
*/

关于VueComponent

  1. 每调用Vue.extend,返回的都是一个全新的VueComponent(组件实例对象)

  2. this指向:组件中this是VueComponent实例对象,vue的实例对象是Vue实例对象。

  3. 组件实例对象(简称vc);vue的实例对象(简称vm)。

  4. 一个重要的内置关系:VueComponent.prototype.proto === Vue.prototype

    作用:让组件实例对象(vc)可以访问到 Vue原型上的属性、方法。 默认情况:VueComponent.prototype.__proto__指向的是Object,Vue进行处理了。


非单文件组件与单文件组件

  1. 非单文件组件:简单来说就是没有使用脚手架搭建项目,直接引入vue.js在HTML中使用;

  2. 单文件组件:使用脚手架搭建项目,一个组件就是一个vue文件,包含了HTML、CSS、js。


Vue脚手架

Vue脚手架index.html的相关注释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<!-- 针对IE浏览器的一个特殊配置,含义是让IE浏览器以最高的渲染级别渲染页面 -->
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- 开启移动端的理想视口 -->
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<!-- 配置页签图标 <%= BASE_URL %>为配置好的路径就是public -->
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<!-- 引入第三方样式 -->
<link rel="stylesheet" href="<%= BASE_URL %>css/bootstrap.css">
<!-- 配置网页标题 -->
<title>硅谷系统</title>
</head>
<body>
<!-- 当浏览器不支持js时noscript中的元素就会被渲染 -->
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %>
doesn't work properly without JavaScript enabled. Please enable it to continue.
</strong>
</noscript>
<!-- 容器 -->
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

其他补充

1
2
3
4
5
6
7
8
9
10
11
12
13
// 安装 npm install -g @vue/cli
// 显示vue默认的webpack配置 vue inspect > output.js
/*
main.js引入Vue版本是vue.runtime.esm.js(在node_modules>vue文件里package.json里有个module配置)
1. vue.js与vue.runtime.xxx.js的区别:
vue.js是完整版的Vue,包含:核心功能 + 模板解析器。
vue.runtime.xxx.js是运行版的Vue,只包含:核心功能;没有模板解析器。
2. 没有模板解析器不能使用template这个配置项,需要使用render函数,
在vue文件可以写template原因是引入了vue-template-compiler包。
*/

// vue.config.js中有些配置不能为空,避免使用Crtl /屏蔽;
// 如:pages: {}推荐不写这个(其他配置好像暂无影响)

ref属性

1
2
3
4
5
6
/*
1. 被用来给元素或子组件注册引用信息(id的替代者)
2. 应用在html标签上获取的是真实DOM元素,应用在组件标签上是组件实例对象(vc)
3. 使用方式:
1. 打标识:```<h1 ref="xxx">.....</h1>``` 或 ```<School ref="xxx"></School>```
2. 获取:```this.$refs.xxx``` */

props

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// props三种写法

// 1. 第一种方式(只接收)
props:['name']
// 2.第二种方式(限制类型):
props:{name:String}
// 3. 第三种方式(限制类型、限制必要性、指定默认值):
props:{
name:{
type:String, //类型
required:true, //必要性
default:'老王' //默认值
}
}
// 注意:required一般不和default在一起,其他Object的默认值是一个函数里面return

mixin混入

1
2
3
4
5
6
7
8
9
10
11
/*
1. 功能:可以把多个组件共用的配置提取成一个混入对象
2. 使用方式:
mixin.js文件中
export default {
data(){...}, // 在data与methods的以组件为主
mounted(){...} // 生命周期中是先调用混入的再调用组件的
}
使用混入:
全局混入:Vue.mixin(xxx)
局部混入:mixins:['xxx'] */

插件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/*
插件本质:包含install方法的一个对象,
install的第一个参数是Vue,第二个以后的参数是插件使用者传递的数据。

定义插件:
export default {
install(Vue, options) {
// 1. 添加全局过滤器
Vue.filter(....)
// 2. 添加全局指令
Vue.directive(....)
// 3. 配置全局混入(合)
Vue.mixin(....)
// 4. 添加实例方法
Vue.prototype.$myMethod = function () {...}
Vue.prototype.$myProperty = xxxx
}
}
使用插件:Vue.use() */

scoped样式

1
2
3
4
5
/*
1. 作用:让样式在局部生效,防止冲突。
2. 写法:<style scoped> */

// 查看版本命令 npm view webpack versions

webStorage

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/*
1. 存储内容大小一般支持5MB左右(不同浏览器可能还不一样)
2. 浏览器端通过 Window.sessionStorage 和 Window.localStorage 属性来实现本地存储机制。
3. 相关API:
1. xxxxxStorage.setItem('key', 'value');
该方法接受一个键和值作为参数,会把键值对添加到存储中,如果键名存在,则更新其对应的值。

2. xxxxxStorage.getItem('person');
该方法接受一个键名作为参数,返回键名对应的值。

3.xxxxxStorage.removeItem('key');
该方法接受一个键名作为参数,并把该键名从存储中删除。

4.xxxxxStorage.clear()
该方法会清空存储中的所有数据。

4. 备注:
1. SessionStorage存储的内容会随着浏览器窗口关闭而消失。
2. LocalStorage存储的内容,需要手动清除才会消失。
3. xxxxxStorage.getItem(xxx)如果xxx对应的value获取不到,那么getItem的返回值是null。
4. JSON.parse(null)的结果依然是null。*/

自定义事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// 1.使用props传递事件(基本不用)
// 子组件
// props接收事件
props:['addTodo'],
methods: {
add(){
this.addTodo(todoObj) // 触发事件(并传递参数)
}
},
// 父组件中props
<Child :addTodo="addTodo"></Child>
methods:{
addTodo(value){
consolelog(value)
}

// -------------------------------

// 2.绑定自定义事件:
/*
1. 第一种方式,在父组件中:<Demo @atguigu="test"/>或<Demo v-on:atguigu="test"/>

2. 第二种方式,在父组件中:
<Demo ref="demo"/>
......
mounted(){
this.$refs.xxx.$on('atguigu',this.test) // 可以传递多个参数
}

3. 若想让自定义事件只能触发一次,可以使用 once 修饰符,或 $once 方法。

4. 触发自定义事件:this.$emit('atguigu',数据)

5. 解绑自定义事件this.$off('atguigu'),解绑多个this.$off(['','']),全部解绑this.$off()

6. 组件上也可以绑定原生DOM事件,需要使用 native 修饰符。

7. 注意:通过 this.$refs.xxx.$on('atguigu',回调)
绑定自定义事件时,回调要么配置在methods中,
this.$refs.student('atguigu',this.getStudentName)
要么用箭头函数,否则this指向会出问题!
this.$refs.student('atguigu',()=>{console.log(this)}) // 不是有箭头函数this为子组件vc

全局事件总线

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 安装全局事件总线:
new Vue({
......
beforeCreate() {
Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vm
},
......
})

// 使用事件总线:
// 1. 接收数据:组件中给$bus绑定自定义事件,事件的回调留在组件自身
methods(){
demo(data){......}
}
......
mounted() {
this.$bus.$on('xxxx',this.demo)
}

// 2. 提供数据:this.$bus.$emit('xxxx',数据)

// 3. 最好在beforeDestroy钩子中,用$off去解绑当前组件所用到的事件。
// 不能直接使用this.off()这样所有的自定义事件全部销毁了

// 使用全局事件总线时注意要确保$on事件挂载后,才能触发$emit,
// 也就是绑定$on组件加载完成(如:组件是隐藏)

消息订阅与发布(pubsub)

所有的框架都可以使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/*
1. npm i pubsub-js // 安装pubsub

2. import pubsub from 'pubsub-js' // 引入

3. A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身。
mounted() {
this.pid = pubsub.subscribe('subscribeName',(name,data)=>{})
// 每次订阅会生成一个id,使用id作为取消订阅的参数
// subscribe接收两个参数,第一个为订阅名字,第二个是函数(接收两个参数订阅名和数据)
}

4. 提供数据:pubsub.publish('subscribeName',数据)

5. 最好在beforeDestroy钩子中,用PubSub.unsubscribe(pid)去取消订阅。*/

nextTick

1
2
3
1. 语法:this.$nextTick(回调函数)
2. 作用:在下一次 DOM 更新结束后执行其指定的回调。
3. 什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行。

Vue封装的过度与动画

基本使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// 使用transition标签包裹
<transition name="hello" appear>
// name属性控制下方的class的名字(默认是v-)
// appear属性控制是否一进来就加载动画
<h1 v-show="isShow">你好啊!</h1>
</transition>

// 使用关键帧动画
// 进入过程中
.hello-enter-active{
animation: atguigu 0.5s linear;
}
// 离开过程中
.hello-leave-active{
animation: atguigu 0.5s linear reverse;
}
@keyframes atguigu {
from{
transform: translateX(-100%);
}
to{
transform: translateX(0px);
}
}

// ------------------

// 使用transition过度动画
/* 进入的起点、离开的终点 */
.hello-enter,.hello-leave-to{
transform: translateX(-100%);
}
.hello-enter-active,.hello-leave-active{
transition: 0.5s linear;
// 也可以不写在这里(在需要使用动画的标签处写,推荐写在这里可以不动其他代码)
}
/* 进入的终点、离开的起点 */
.hello-enter-to,.hello-leave{
transform: translateX(0);
}

// -------------------
// transition-group的使用
<transition-group appear name="hello" >
<h1 v-show="!isShow" key="1">你好啊!</h1>
<h1 v-show="isShow" key="2">尚硅谷!</h1>
</transition-group>
// 注意:transition里面只能有一个标签,而transition-group可以多个但是每个需要添加key

// mode属性:out-in(先离开后进入)、in-out(先进入后离开)
<transition mode="out-in"></transition>

使用第三方animate.css

1
2
3
4
5
6
7
8
9
10
11
12
// 安装 npm install animate.css --save
// 使用 import 'animate.css'
<transition-group
appear
name="animate__animated animate__bounce"
enter-active-class="animate__swing"
leave-active-class="animate__backOutUp"
>
<h1 v-show="!isShow" key="1">你好啊!</h1>
<h1 v-show="isShow" key="2">尚硅谷!</h1>
</transition-group>
// enter-active-class为进入的class;leave-active-class为离开class

代理配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/*
1.方法一
在vue.config.js中添加如下配置:直接配置
devServer:{
proxy:"http://localhost:5000"
}
1. 优点:配置简单,请求资源时直接发给前端(8080)即可。
2. 缺点:不能配置多个代理,不能灵活的控制请求是否走代理。
3. 工作方式:若按照上述配置代理,当请求了前端不存在的资源时,
那么该请求会转发给服务器 (优先匹配前端资源)

// --------------

2.方法二
就是正常配置:
1. 优点:可以配置多个代理,且可以灵活的控制请求是否走代理。
2. 缺点:配置略微繁琐,请求资源时必须加前缀。*/

vue-resource的使用

1
2
3
4
5
6
// 安装 npm install vue-resource
// main.js使用
import vueResource from 'vue-resource'
Vue.use(vueResource)
// 之后在vue实例下会有一个$http的方法(axios替换成$http即可)
this.$http.get(url).then()

插槽补充

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 具名插槽的写法
<template slot="center">
<div>html结构1</div>
</template>
<template v-slot:footer>
<div>html结构2</div>
</template>
// 注意 <div v-slot:center></div>的写法报错(v-slot只能用在template)
<div slot="center"></div> // 普通标签的写法

// 作用域插槽
// 不推荐:
// <template scope="scopeData"></template>
// <template slot-scope="scopeData"></template>
<template v-slot:child="{ msg }"></template>

Vuex的补充

Vuex的环境

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 安装 npm i vuex@3  
//注意不能直接 npm i vuex 这样的话就是vuex4,使用的vue2版本不支持,只能是vue3使用

// store/index.js
import Vue from 'vue'
import Vuex from 'vuex' //引入Vuex
Vue.use(Vuex) //应用Vuex插件
// 三个配置对象
const actions = {}
//准备mutations——用于操作数据(state)
const mutations = {}
//准备state——用于存储数据
const state = {}
//准备getters——用于将state中的数据进行加工
const getters = {}
//创建并暴露store
export default new Vuex.Store({
actions,
mutations,
state,
getters
})

// main.js
import store from './store' // 引入store,要先use
new Vue({
store, // 添加一个配置项(上方不引入Vuex的话,会丢失该配置项,相当于无这个key)
// 上方不引入store,vue不知道找到那个具体文件
})
// 注意:store必须在Vue.use(Vuex)之后,如果都在main.js里面引入,
// js文件会将所有的import提升在前面执行,故Vue.use(Vuex)在后会报错

vuex的简写map…函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// 为了template中的简写,在computed里面可以手动配置每个state或者getters里面的数据
// vuex提供了的函数,可以方面生成计算属性
// 并且在vue的开发者工具中显示的是vuex bind(更加方便区分)
import {mapState,mapGetters} from 'vuex'
computed:{
// 借助mapState生成计算属性,从state中读取数据。(对象写法)
// ...mapState({he:'sum',xuexiao:'school',xueke:'subject'}),

//借助mapState生成计算属性,从state中读取数据。(数组写法)
...mapState(['sum','school','subject']),

//借助mapGetters生成计算属性,从getters中读取数据。(对象写法)
// ...mapGetters({bigSum:'bigSum'})

//借助mapGetters生成计算属性,从getters中读取数据。(数组写法)
...mapGetters(['bigSum'])
},
// 注意:使用mapState()方法生成的是一个对象,所以在computed里面需要...
// mapState({sum})相当于mapState({sum:sum}),sum不是一个字符串故不能

// ------------------------
// mapActions与mapMutations使用时,
// 若需要传递参数需要:在模板中绑定事件时传递好参数,否则参数是事件对象。
import {mapMutations,mapActions} from 'vuex'
methods: {
//借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(对象写法)
...mapMutations({increment:'JIA',decrement:'JIAN'}),

//借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(数组写法)
// ...mapMutations(['JIA','JIAN']),

incrementWait(){
this.$store.dispatch('jiaWait',this.n)
}, */

//借助mapActions生成对应的方法,方法中会调用dispatch去联系actions(对象写法)
...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'})

//借助mapActions生成对应的方法,方法中会调用dispatch去联系actions(数组写法)
// ...mapActions(['jiaOdd','jiaWait'])
},

vuex模块化+命名空间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// index.js
import countOptions from './count'
import personOptions from './person'
export default new Vuex.Store({
modules:{
countAbout:countOptions,
personAbout:personOptions
}
})
// count.js文件
export default = {
namespaced:true, // 开启命名空间

}
// person.js文件
export default = {
...
}

// 使用数据(可以写多个),下方的写法需要开启命名空间
computed:{
//借助mapState生成计算属性,从state中读取数据。(数组写法)
...mapState('countAbout',['sum','school','subject']),
...mapState('personAbout',['personList']),
//借助mapGetters生成计算属性,从getters中读取数据。(数组写法)
...mapGetters('countAbout',['bigSum'])
},
methods: {
//借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(对象写法)
...mapMutations('countAbout',{increment:'JIA',decrement:'JIAN'}),
//借助mapActions生成对应的方法,方法中会调用dispatch去联系actions(对象写法)
...mapActions('countAbout',{incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
},
// 直接使用
this.$store.state.personAbout.list
this.$store.getters['personAbout/firstPersonName']
this.$store.dispatch('personAbout/addPersonWang',person)
this.$store.commit('personAbout/ADD_PERSON',person)

vue路由补充

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// params路由
// 跳转并携带params参数,to的字符串写法
<router-link :to="`/home/message/detail/${m.id}/${m.title}`">跳转</router-link>

// 跳转并携带params参数,to的对象写法
<router-link
:to="{
name:'xiangqing',
params:{
id:666,
title:'你好'
}
}"
>跳转</router-link>
// 特别注意:路由携带params参数时,若使用to的对象写法,则不能使用path配置项,必须使用name配置!
// params携带多个参数时,路由处要配置,如: path:'detail/:id/:title' (携带多个参数)
// 正常使用方式 router.push(`/user/${id}`)

路由props属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
name:'xiangqing',
path:'detail/:id',
component:Detail,

//第一种写法:props值为对象,该对象中所有的key-value的组合最终都会通过props传给Detail组件
// props:{a:900}

//第二种写法:props值为布尔值,布尔值为true,则把路由收到的所有params参数通过props传给Detail组件
// props:true

//第三种写法:props值为函数,该函数返回的对象中每一组key-value都会通过props传给Detail组件
props($route){
return {
id:$route.query.id,
title:$route.query.title
}
}
}
// 使用props接收
props:['id','title']

编程式导航

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//$router的两个API
this.$router.push({
name:'xiangqing',
params:{
id:xxx,
title:xxx
}
})

this.$router.replace({
name:'xiangqing',
params:{
id:xxx,
title:xxx
}
})
this.$router.forward() //前进
this.$router.back() //后退
this.$router.go() //可前进也可后退

缓存路由组件

1
2
3
4
5
6
7
8
<keep-alive include="News"> 
<router-view></router-view>
</keep-alive>
// include里的名字是组件的名字(即组件的name的值)
// 缓存多个
<keep-alive :include="[a,b]">
<router-view></router-view>
</keep-alive>

路由组件独有两个新的生命周期

1
2
3
4
5
6
7
8
9
activated() {
console.log('News组件被激活了')
},
deactivated() {
console.log('News组件失活了')
clearInterval(this.timer)
},
// 当keep-alive缓存组件后,不会触发beforeDestroy
// 使用deactivated清除原生的定时器等事件

路由独享守卫

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 只有前置守卫没有后置守卫
beforeEnter(to,from,next){
console.log('beforeEnter',to,from)
if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制
if(localStorage.getItem('school') === 'atguigu'){
next()
}else{
alert('暂无权限查看')
// next({name:'guanyu'})
}
}else{
next()
}
}

组件内守卫

1
2
3
4
5
6
7
//进入守卫:通过路由规则,进入该组件时被调用
beforeRouteEnter (to, from, next) {
},
//离开守卫:通过路由规则,离开该组件时被调用
beforeRouteLeave (to, from, next) {
}
// 注意:不是后置守卫,是组件离开时候调用