VUE基础
- Vue基础
-
- 数据绑定
- 过滤器
- 指令
- 计算属性
-
- 计算属性的getter、setter方法
- 计算属性扩展
- 计算属性缓存
- v-bind
-
- 绑定class
-
- 使用对象语法绑定class:
- 使用数组语法绑定class
- 绑定内联样式
-
- 使用数组语法绑定多个样式
- 内置指令
-
- v-cloak
- v-pre
- v-once
- v-if、v-else-if、v-else
- v-for
-
- v-for迭代整数
- 更新数组触发数据改变的方法
- v-on
- v-model
- 组件
-
- 挂载组件
- 通过props传递数据:
- 子组件向父组件传值
- 使用v-model和$emit传值
- 中央事件总线
-
- 父(\$parent)链传递数据
- 子组件索引
- slot插槽
- 具名slot
- 使用component标签挂载模板
- $nextTick
- 自定义指令
- 虚拟dom
- vue-router
-
- 安装vue-router
- 配置vue-router
- 配置跳转
-
- 方式一,router-link
- 方式二,api跳转
- router钩子函数
-
- beforeEach
- afterEach
- vuex
-
- 安装vuex
- 配置vuex
- 使用vuex,创建数据
-
- 获取vuex中的数据
- 使用mutations修改states的数据
- commit接收对象
- getters
- 异步操作数据使用actions
- modules状态区分
- 中央事件总线vue-bus
Vue基础
数据绑定
通过{{}}可以与vue中data中的数据绑定。如果不想使用{{}}可以使用
v-pre
取消{{}}数据绑定。
<!DOCTYPE html>
<html lang="en">
<head>
<title>Document</title>
</head>
<body>
<id id="app">
<span >{{ a }}</span>
<span v-pre>{{a}}</span>
</id>
<script src="./js/vue.min.js"></script>
<script>
let vue = new Vue({
el: "#app",
data: {
a: '<a href="www.baidu.com" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" >百度</a>'
},
created() {
console.log(this);
console.log(this.a);
console.log(this.$el);
// vue数据实例创建完成,数据生成
},
mounted() {
console.log(this);
console.log(this.$el);
// 挂载el,与dom关联
}
})
</script>
</body>
</html>
使用v-html输出html内容。
<id id="app">
<span >{{ a }}</span>
<span v-pre>{{a}}</span>
<span v-html='a'></span>
</id>
<script src="./js/vue.min.js"></script>
<script>
let vue = new Vue({
el: "#app",
data: {
a: '<a href="www.baidu.com" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" >百度</a>'
}
})
</script>
过滤器
filters属性加上|操作符实现过滤:
<id id="app">
<!-- name 即为要过滤的数据 | 表示要使用过滤 formartName表示使用的哪个过滤器 -->
{{ name | formartName}}
</id>
<script src="./js/vue.min.js"></script>
<script>
let app = new Vue({
el: "#app",
data: {
name: 'zs'
},
// 在filters中使用过滤器方法
filters: {
formartName(value) { // value就是需要过滤的数据
return "@_" + value + "[email protected]";
}
}
})
</script>
可以指定使用多个过滤器,也可以给过滤器传递参数:
<id id="app">
可以使用多个过滤器,并且还能给过滤器传递参数
{{ name | formartName | formartName1('*')}}
</id>
<script src="./js/vue.min.js"></script>
<script>
let app = new Vue({
el: "#app",
data: {
name: 'zs'
},
filters: {
formartName(value) { // value就是需要过滤的数据
return "@_" + value + "[email protected]";
},
formartName1(value, param) {
return value + '->' + param;
}
}
})
</script>
指令
使用
v-show
,如果为false时,只是设置了对应的元素的
display
属性为none:
<id id="app">
<p v-show="isShow">v-show的使用</p>
</id>
<script src="./js/vue.min.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
isShow: true
}
})
</script>
v-bind
作数据绑定,可以为元素动态绑定一个属性。
<id id="app">
为p标签绑定了一个name属性,name属性的值就是data数据中text对应的值
<p v-bind:name='text' v-show="isShow">v-show的使用</p>
</id>
<script src="./js/vue.min.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
isShow: true,
text: '_text'
}
})
</script>
v-on
指令,可以用来监听事件,其后接一个methods中定义的方法或者一个表达式。
<id id="app">
<p v-bind:name='text' v-show="isShow" v-on:click="isShow = false">v-show的使用</p>
</id>
<script src="./js/vue.min.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
isShow: true,
text: '_text'
}
})
</script>
计算属性
computed
时计算属性,计算属性也是一个属性,类似于data中的数据,计算属性是一个函数,计算属性的值是函数的返回值。
<id id="app">
{{ fullName }}
</id>
<script src="./js/vue.min.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
firstName: 'Jhon',
lastName: 'Cena'
},
computed: {
fullName() {
return this.firstName + " " + this.lastName;
}
}
})
</script>
计算属性的getter、setter方法
计算属性有getter和setter方法,分别用于读取和设置计算属性的值。
<id id="app">
{{ fullName }}
</id>
<script src="./js/vue.min.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
firstName: 'Jhon',
lastName: 'Cena'
},
computed: {
fullName: {
get() {
return this.firstName + " " + this.lastName;
},
set(value) {
let vs = value.split(' ');
this.firstName = "*" + vs[0];
this.lastName = vs[1];
}
}
}
})
app.fullName = 'zhang san';
</script>
计算属性是依赖其他属性的,只要其他属性发生改变,计算属性就会跟着发生变化。setter属性可以接收一个参数,这个参数接收了并不是一定能改变计算属性的值,而是根据参数,去改变对应的计算属性依赖的值,依赖的值改变,getter方法会返回对应格式的数据。
计算属性扩展
计算属性可以依赖其他计算属性,计算属性也可以依赖其他实例数据,这里演示计算属性依赖其他实例数据。
<div id="app1">
{{ msg }}
</div>
<div id="app2">
{{message}}
</div>
<script src="./js/vue.min.js"></script>
<script>
let app1 = new Vue({
el: '#app1',
data: {
msg: 'message'
}
});
let app2 = new Vue({
el: '#app2',
computed: {
message() {
return app1.msg.split('').reverse().join('');
}
}
})
app1.msg = 'hello world!';
</script>
计算属性缓存
计算属性(computed)类似于方法(methods),但是方法每次调用都会被执行,而计算属性则不同,只有在计算属性所依赖的数据发生改变时才会执行。
<div id="app1">
计算属性是一个属性,可以直接使用,methods是方法,需要调用
{{ cTime }},<br/> {{ mTime() }}
</div>
<script src="./js/vue.min.js"></script>
<script>
let app1 = new Vue({
el: '#app1',
data: {
data: new Date()
},
computed: {
cTime() {
return this.data + " --- ";
}
},
methods: {
mTime() {
return new Date();
}
}
});
v-bind
绑定class
使用对象语法绑定class:
<div id="app">
<span :class="{ 'red': isRed, 'error': isError}">这段话的颜色</span>
</div>
<script src="./js/vue.min.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
isRed: true,
isError: true,
active: 'active',
primary: 'primary'
}
})
</script>
<span :class="{ ‘red’: isRed, ‘error’: isError}">这段话的颜色</span>
,这里为class绑定一个对象,对象的属性名将作为class属性的值,当且仅当对象属性的值为true时,才会被应用。
使用数组语法绑定class
使用数组语法,可以嵌套对象语法。
<div id="app">
<span :class="[{ 'red': isRed, 'error': isError}, active, primary]">这段话的颜色</span>
</div>
<script src="./js/vue.min.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
isRed: true,
isError: true,
active: 'active',
primary: 'primary'
}
})
</script>
要绑定多个class属性需要使用对象语法或者数组语法,如果都不使用,而是直接使用数据,最多只能有一个数据。
错误写法:
正确写法(只能有一个):
绑定内联样式
对象语法,与绑定class不同的是,对象属性作为内联样式属性,数据的值作为样式的值,而不是根据true和false来确定应用谁
<div id="app">
<span :style="{'color': red, 'fontSize': fontSize + 'px'}">这段话的颜色</span>
</div>
<script src="./js/vue.min.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
red: 'red',
fontSize: 20
}
})
</script>
使用数组语法绑定多个样式
<div id="app">
<span :style="[color, font]">这段话的颜色</span>
</div>
<script src="./js/vue.min.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
color: {
color: 'red',
background: 'green'
},
font: {
fontSize: 20 + 'px'
}
}
})
</script>
内置指令
v-cloak
v-cloak
的作用:如果网速较慢等情况下,数据加载较慢,{{data}}可能会直接显示在页面上,而v-cloak的作用就是阻止这种情况的发生。使用v-cloak时需要配合下面css样式:
[v-cloak] {
display: none;
}
<div id="app">
<span v-cloak>{{ msg }}</span>
</div>
<script src="./js/vue.min.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
msg: 'message'
}
})
</script>
v-pre
v-pre
的作用:{{data}}是取数据,而有时候我们需要原样输出,即{{}}不具备任何功能,这是就可以使用v-pre
<div id="app">
<span v-pre>{{ msg }}</span>
</div>
<script src="./js/vue.min.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
msg: 'message'
}
})
</script>
v-once
使用v-once,数据只被渲染一次,之后数据发生变化使用了v-once的数据也不会被渲染
<div id="app" @click="changeMsg">
<span>{{ msg }}</span><br/>
<span v-once>{{ msg }}</span>
</div>
<script src="./js/vue.min.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
msg: 'message'
},
methods: {
changeMsg() {
this.msg = this.msg.split('').reverse().join('');
}
}
})
v-if、v-else-if、v-else
v-if和
v-show
的区别是,v-if不会加载html标签,v-show时设置css样式dispaly为none,
v-show
多用于频繁操作的场景。
<div id="app">
<span v-if='status == 1'>1</span>
<span v-else-if='status == 2'>2</span>
<span v-else-if='status == 3'>3</span>
<span v-else>4</span>
</div>
<script src="./js/vue.min.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
status: 3
}
})
</script>
可以使用
<template>
标签作为v-if的载体,
<template>
最终不会被加载到html中。
注:使用v-if时vue会尽可能的复用组件,比如下面代码,点击切换的时候,vue做的其实就是把相同标签的不同的地方更换了,即把手机号换成了邮箱,name属性的phone换成了email:
<div id="app">
<template v-if='status === 1'>
<label>手机号:</label>
<input type="text" name="phone">
</template>
<template v-if='status === 2'>
<label>邮箱:</label>
<input type="text" name="emial">
</template>
<button @click='change'>切换</button>
</div>
<script src="./js/vue.min.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
status: 1
},
methods: {
change() {
this.status = this.status === 1 ? 2 : 1;
}
}
})
</script>
如果不需要组件被复用,可以组件添加一个key属性,属性的值必须是唯一的。
v-for
v-for用来遍历数组或者对象属性,也可以结合template标签使用。
遍历数组时,有两个参数可选,第一个是每一项的数据,第二个是索引。遍历对象时,第一个参数是对象的属性名,第二个参数是对象属性值,第三个参数是索引:
<div id="app">
<ul>
<li v-for="(a, index) of arr"> {{ a }} -- {{ index }}</li>
</ul>
<template v-for="(key, value, index) of person">
{{ index }} : {{ key }} -> {{ value }}<br>
</template>
</div>
<script src="./js/vue.min.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
arr: [1, 3, 5, 7],
person: {
name: 'zs',
age: 18,
address: 'China'
}
}
})
v-for迭代整数
<div id="app">
<span v-for="(item, index) of integer">{{index}} : {{item}} /</span>
</div>
<script src="./js/vue.min.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
integer: 10
}
})
</script>
更新数组触发数据改变的方法
-
push():向原数组末尾增加一个元素
示例:
<div id="app" >
{{arr}}
<button @click="change">change</button>
</div>
<script src="./js/vue.min.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
arr: [1,2,3,4,5]
},
methods: {
change() {
this.arr.push(6);
}
}
})
-
pop():从数组末尾删除一个元素
示例:
<div id="app" >
{{arr}}
<button @click="change">change</button>
</div>
<script src="./js/vue.min.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
arr: [1,2,3,4,5]
},
methods: {
change() {
this.arr.pop();
}
}
})
</script>
- shift():从数组开头删除一个元素
<div id="app" >
{{arr}}
<button @click="change">change</button>
</div>
<script src="./js/vue.min.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
arr: [1,2,3,4,5]
},
methods: {
change() {
this.arr.shift();
}
}
})
</script>
- unshift():向数组开始位置添加一个元素
<div id="app" >
{{arr}}
<button @click="change">change</button>
</div>
<script src="./js/vue.min.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
arr: [1,2,3,4,5]
},
methods: {
change() {
this.arr.unshift(1);
}
}
})
</script>
-
splice():改变数组长度
示例:
<div id="app" >
{{arr}}
<button @click="change">change</button>
</div>
<script src="./js/vue.min.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
arr: [1,2,3,4,5]
},
methods: {
change() {
this.arr.splice(2);
}
}
})
</script>
- sort():数组排序
<div id="app" >
{{arr}}
<button @click="change">change</button>
</div>
<script src="./js/vue.min.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
arr: [7,2,1,4,5]
},
methods: {
change() {
this.arr.sort((a, b) => {
// a-b就相当于a要大于b,升序排序
return a - b;
})
}
}
})
</script>
-
reverse() :反转数组
示例:
<div id="app" >
{{arr}}
<button @click="change">change</button>
</div>
<script src="./js/vue.min.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
arr: [7,2,1,4,5]
},
methods: {
change() {
this.arr.reverse();
}
}
})
</script>
-
filter():数组过滤,不改变原数组,返回一个新的数组
示例:
<div id="app" >
{{arr}}
<button @click="change">change</button>
</div>
<script src="./js/vue.min.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
arr: [7,2,1,4,5]
},
methods: {
change() {
console.log(this.arr);
// 不改变原数组,产生一个新的数组
this.arr = this.arr.filter(function(data) {
return data > 2;
});
}
}
})
</script>
-
concat():连接数组,把一个数组追加大当前数组后,不改变原有数组,产生新的数组
示例:
<div id="app" >
{{arr}}
<button @click="change">change</button>
</div>
<script src="./js/vue.min.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
arr: [7,2,1,4,5]
},
methods: {
change() {
let arr2 = ['a', 'b', 'c'];
this.arr = this.arr.concat(arr2);
}
}
})
</script>
-
slice():根据下脚标查找元素,不改变原有数组,产生一个新数组并返回
示例:
<div id="app" >
{{arr}}
<button @click="change">change</button>
</div>
<script src="./js/vue.min.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
arr: [7,2,1,4,5]
},
methods: {
change() {
let arr2 = [2,4];
// 查找数组中索引大于等于2的所有元素,组成一个新数组并返回
// this.arr = this.arr.slice(2);
// 查找下脚标大于等于0小于1的元素,组成一个新数组并返回
this.arr = this.arr.slice(0,1);
}
}
})
</script>
v-on
监听事件:
<div id="app" >
{{count}}
<button @click="count++">change</button>
</div>
<script src="./js/vue.min.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
count: 0
}
})
</script>
绑定使用方法:
<div id="app" >
{{count}}
可以传参,也可以传入当前事件$event
<button @click="change('abc',$event)">change</button>
</div>
<script src="./js/vue.min.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
count: 0
},
methods: {
change(param) {
// 可以通过event去访问事件
console.log(param, event);
}
}
})
</script>
事件修饰符.stop,.prevent,.capture,.self,.once
-
.stop 阻止事件冒泡
示例:点击button按钮,如果事件冒泡的话,也会触发div的单击事件,但是在button上使用.stop就会阻止事件冒泡
<div id="app" >
{{count}}
<div @click="count++">
<button @click.stop=''>change</button>
</div>
</div>
-
.prevent 阻止事件的默认行为
示例:
<div id="app" >
{{count}}
<div @click="count++">
<a href="www.baidu.com" @click.prevent=''>change</a>
</div>
</div>
-
.capture 事件捕获模式
下面代码先执行1后执行2,如果不适应.capture的话是先执行2后执行1
<div id="app" >
<div @click.capture="change(1)">
<button @click.capture='change(2)'>change</button>
</div>
</div>
- .self 元素本身触发事件时才执行
<div id="app" >
{{count}}
<div @click.self="count++">
<!--点击button不会触发div的单击事件-->
<button @click.prevent=''>change</button>
</div>
</div>
v-model
使用v-model实现表单数据绑定,v-model可以用@input来实现。
<div id="app" >
{{msg}}
<input v-model="msg" placeholder="请输入...">
<input @input="input" placeholder="请输入..." :value="msg">
</div>
<script src="./js/vue.min.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
msg: ''
},
methods: {
input(e) {
this.msg = e.target.value;
}
}
})
</script>
组件
组件注册
<div id="app" >
<!-- 使用全局注册的组件 -->
<my-component></my-component>
<!-- 使用局部注册的组件 -->
<child></child>
</div>
<script src="./js/vue.min.js"></script>
<script>
// 注册全局组件
Vue.component('my-component', {
template: '<div>xxx</div>'
});
let app = new Vue({
el: '#app',
// 注册局部组件
components: {
'child': {
template: '<div>child</div>'
}
}
})
</script>
挂载组件
在类似于table这样的标签中,只能使用td,tr等,不能使用自定义组件的时候,需要把组件挂载到某个节点上。
<div id="app" >
<table>
<!-- 通过is属性挂载组件 -->
<tbody is='child'></tbody>
</table>
</div>
<script src="./js/vue.min.js"></script>
<script>
let app = new Vue({
el: '#app',
// 注册局部组件
components: {
'child': {
template: '<th>child</th>'
}
}
})
</script>
通过props传递数据:
<div id="app" >
<table>
<!-- 通过is属性挂载组件 -->
<tbody is='child' message='传递数据的方式'></tbody>
</table>
</div>
<script src="./js/vue.min.js"></script>
<script>
let app = new Vue({
el: '#app',
// 注册局部组件
components: {
'child': {
props: ['message'],
template: '<th>{{message}}</th>'
}
}
})
</script>
子组件向父组件传值
<div id="app" >
{{ msg }}
<!-- 通过子组件定义的事件来接收子组件传递的值 -->
<child @data='changeData' message='h'></child>
</div>
<script src="./js/vue.min.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
msg: 'o'
},
methods: {
changeData(data) {
// data就是 子组件 传递过来的数据
this.msg = data;
}
},
// 注册局部组件
components: {
'child': {
props: ['message'],
template: '<div>{{message}}' +
'<button @click="change">传递数据</button></div>',
methods: {
change() {
// 子组件通过$emit传值,第一个参数是自定义事件名称,第二个参数是传的值
this.$emit('data','abc')
}
}
}
}
})
</script>
使用v-model和$emit传值
<div id="app" >
{{msg}}
<child v-model='msg'></child>
</div>
<script src="./js/vue.min.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
msg: '0'
},
methods: {
changeMsg(data) {
this.msg = data;
}
},
components: {
child: {
template: '<button @click="sendMsg">传递数据</button>',
methods: {
sendMsg() {
this.$emit('input', 'abc');
}
}
}
}
})
</script>
上面这个对应的自定义事件写法:
<div id="app" >
{{msg}}
<child @input='changeMsg'></child>
</div>
<script src="./js/vue.min.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
msg: '0'
},
methods: {
changeMsg(data) {
this.msg = data;
}
},
components: {
child: {
template: '<button @click="sendMsg">传递数据</button>',
methods: {
sendMsg() {
this.$emit('input', 'abc');
}
}
}
}
})
</script>
中央事件总线
<div id="app" >
{{msg}}
<child></child>
</div>
<script src="./js/vue.min.js"></script>
<script>
// 创建一个空实例vue,只用于传递数据
var bus = new Vue();
let app = new Vue({
el: '#app',
data: {
msg: '0'
},
mounted() {
let _this = this;
// 通过这个空实例vue监听这个事件,并接收数据
bus.$on('input', (data) => {
_this.msg = data;
})
},
components: {
child: {
template: '<button @click="sendMsg">传递数据</button>',
methods: {
sendMsg() {
// 通过空实例vue注册一个事件,并传递数据
bus.$emit('input', 'abc');
}
}
}
}
})
</script>
父($parent)链传递数据
多个父组件只需要多次.$parent就可以了。但是这种子组件直接修改父组件的方式不推荐使用。(紧耦合)
<div id="app" >
{{msg}}
<child></child>
</div>
<script src="./js/vue.min.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
msg: '0'
},
components: {
child: {
template: '<button @click="sendMsg">传递数据</button>',
methods: {
sendMsg() {
// 直接修改父组件的数据
this.$parent.msg = 'hahaha';
}
}
}
}
})
</script>
子组件索引
<div id="app" >
{{msg}}
<!-- ref 为组件取一个别名 -->
<child ref='childA'></child>
</div>
<script src="./js/vue.min.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
msg: '0'
},
mounted() {
// 通过$refs.别名 去访问子组件的数据
this.msg = this.$refs.childA.msg;
},
components: {
child: {
template: '<button @click="sendMsg">传递数据</button>',
data() {
return {
msg: '12345'
}
},
methods: {
sendMsg() {
// 直接修改父组件的数据
this.$parent.msg = 'hahaha';
}
}
}
}
})
</script>
slot插槽
<div id="app" >
<child>
<!-- 向插槽内添加数据 -->
<p>{{msg}}</p>
</child>
</div>
<script src="./js/vue.min.js"></script>
<script>
let vue = new Vue({
el: '#app',
data: {
msg: '12345'
},
components: {
child: {
// slot作为插槽,在使用当前组件时,如果在标签内写入了内容,就替换插槽内的内容
template: "<div>子组件的内容:<slot>插槽</slot></div>"
}
}
});
</script>
具名slot
用于向指定的插槽内添加数据:
<div id="app" >
<child>
<!-- 向插槽内添加数据 -->
<p>
<!-- 向name为n2的插槽内添加数据 -->
<div slot='n2'>{{msg}}</div>
</p>
</child>
</div>
<script src="./js/vue.min.js"></script>
<script>
let vue = new Vue({
el: '#app',
data: {
msg: '12345'
},
components: {
child: {
// slot作为插槽,在使用当前组件时,如果在标签内写入了内容,就替换插槽内的内容
template: "<div>子组件的内容:<slot name='n1'>插槽</slot><slot name='n2'>2</slot></div>"
}
}
});
</script>
使用component标签挂载模板
is指定要挂载的是哪个模板,可以动态指定
:is
<div id="app" >
<component is='child'></component>
</div>
<script src="./js/vue.min.js"></script>
<script>
let vue = new Vue({
el: '#app',
data: {
msg: '12345',
},
components: {
child: {
name: 'child',
template: "<div>123</div>"
}
}
});
</script>
$nextTick
下面代码执行会报错:
<div id="app" >
<div id="content" v-if='isShow'>hello world!</div>
<button @click='isShow = false'>隐藏</button>
<button @click='getText'>显示并获取内容</button>
</div>
<script src="./js/vue.min.js"></script>
<script>
let vue = new Vue({
el: '#app',
data: {
isShow: true
},
methods: {
getText() {
this.isShow = true;
// this.isShow = true 此时并没有去更新dom,而是开启一个队列,在队列中处理数据(去重,得到最终改变),
// 不是数据改变一次,页面就渲染一次,只有所有数据最终固定后渲染数据,改变dom,所以此时去获取一个不存在的元素报错
// 在下一次事件循环tick完成更新
let msg = document.getElementById("content").innerText;
console.log(msg);
}
}
});
</script>
修改后,使用$netTick
<div id="app" >
<div id="content" v-if='isShow'>hello world!</div>
<button @click='isShow = false'>隐藏</button>
<button @click='getText'>显示并获取内容</button>
</div>
<script src="./js/vue.min.js"></script>
<script>
let vue = new Vue({
el: '#app',
data: {
isShow: true
},
methods: {
getText() {
this.isShow = true;
// this.isShow = true 此时并没有去更新dom,而是开启一个队列,在队列中处理数据(去重,得到最终改变),
// 不是数据改变一次,页面就渲染一次,只有所有数据最终固定后渲染数据,改变dom,所以此时去获取一个不存在的元素报错
// 在下一次事件循环tick完成更新
// $nextTick可以知道什么时候dom更新完成
this.$nextTick(() => {
let msg = document.getElementById("content").innerText;
console.log(msg);
});
}
}
});
</script>
自定义指令
注册指令的两种方式
一、全局注册
Vue.directive('指令名', {
// 指令选项
})
二、局部注册
new Vue({
el: '#app',
directivies: {
'指令名': {
// 指令选项
}
}
})
使用 v-指令名 即可。
全局方式:
<div id="app" >
<input v-focus>
</div>
<script src="./js/vue.min.js"></script>
<script>
Vue.directive ('focus', {
inserted(el) {
el.focus();
}
})
</script>
局部方式:
<div id="app" >
<input v-focus>
</div>
<script src="./js/vue.min.js"></script>
<script>
let app = new Vue({
el: '#app',
directives: {
focus: {
inserted: function(el) {
el.focus();
}
}
}
})
</script>
虚拟dom
<div id="app" >
<anchor :level='2' title="特性">特性</anchor>
</div>
<script src="./js/vue.min.js"></script>
<script>
Vue.component('anchor', {
props: {
level: {
type: Number,
required: true
},
title: String,
default: ''
},
render: function(createElement) {
return createElement(
'h' + this.level,
[
createElement(
'a',
{
domProps: {
href: '#' + this.title
}
},
this.$slots.default
)
]
)
}
})
let app = new Vue({
el: '#app'
})
</script>
vue-router
安装vue-router
npm install vue-router --save
配置vue-router
import Vue from 'vue'
import App from './App.vue'
// 导入vue-router
import VueRouter from 'vue-router'
// 导入vue组件
import Index from './views/Index'
import About from './views/About'
// 配置路由和组件映射
const Routers = [
{
path: '/index',
component: Index
},
{
path: '/about',
component: About
},
{
path: '*',
redirect: '/index'
}
]
// 配置路由
const RouterConfig = {
mode: 'history',
routes: Routers
}
// 创建vue-router实例
const router = new VueRouter(RouterConfig)
Vue.use(VueRouter)
Vue.config.productionTip = false
new Vue({
el: '#app',
router: router,
render: h => h(App)
}).$mount('#app')
配置跳转
方式一,router-link
<router-view></router-view>
标签内显示组件内容,
router-link
标签类似a标签,tag标签使用与指定router-link使用哪个标签实现。
<template>
<div id="app">
hello
<router-view></router-view>
<router-link to='/index'>首页</router-link>
<router-link to='/about' tag='span'>关于</router-link>
</div>
</template>
<script>
export default {
}
</script>
<style scoped>
#app {
color: red;
}
</style>
router-link
的其他属性:
replace :不会留下历史记录,回退不能回到上个页面
<router-link replace to='/about' >关于</router-link>
active-class:当前router-link被选中时的类名,比如下面这个关于被选中时的class属性为about
<router-link to='/about' tag='span' replace active-class='about'>关于</router-link>
方式二,api跳转
this.$router.push(’/about’)
<template>
<div id="app">
hello
<router-view></router-view>
<router-link to='/index'>首页</router-link>
<router-link to='/about' tag='span' replace active-class='about'>关于</router-link>
<button @click="handleRouter">API跳转</button>
</div>
</template>
<script>
export default {
methods: {
handleRouter () {
// 通过api的方式跳转
this.$router.push('/about')
}
}
}
</script>
<style scoped>
#app {
color: red;
}
</style>
$router的其他方法:
replace: 方法类似于router-link中的replace属性,this.$router.replace('/index')
go:类似于window.history.go(),表示向前或向后后退多少步。例如this.$router.go(-1)后退1页,this.$router.go(2)前进2页
router钩子函数
beforeEach
进入路由前beforeEach函数:
// 配置路由和组件映射
const Routers = [
{
path: '/index',
meta: {
title: '首页'
},
component: Index
},
{
path: '/about',
meta: {
title: '关于'
},
component: About
},
{
path: '*',
redirect: '/index'
}
]
// 配置路由
const RouterConfig = {
mode: 'history',
routes: Routers
}
// 创建vue-router实例
const router = new VueRouter(RouterConfig)
// 进入路由前
router.beforeEach((to, from, next) => {
window.document.title = to.meta.title
})
meta字段可以自定义信息。
三个参数:
to:要跳转到的路由
from: 从哪个路由跳转的
next:函数,调用这个函数才进入下一个钩子,否则路由不跳转
afterEach
每一个路由中的设置
比如进入每个页面后,滚动条都在起始位置:
router.afterEach((to, from, next) => {
window.scrollTo(0,0)
})
或者判断是否登录:
router.afterEach((to, from, next) => {
if ('登录') {
next(); // 允许路由跳转
} else {
next('login'); // 否则跳转到登录页面
}
})
next()
函数,不接受参数,路由跳转,设置为false,路由取消跳转,参数为具体路由,跳转到指定路由。
vuex
安装vuex
npm install vuex --save
配置vuex
在main.js中配置
import Vuex from 'vuex'
Vue.use(Vuex)
// 配置vuex
const store = new Vuex.Store({
})
new Vue({
el: '#app',
router: router,
store: store, // 配置vuex
render: h => h(App)
}).$mount('#app')
使用vuex,创建数据
数据保存在state字段:
const store = new Vuex.Store({
state: {
count: 0
}
})
获取vuex中的数据
通过$store.state.xxx访问数据
<template>
<div>
首页 {{ $store.state.count }}
<button @click="handleCount">加一</button>
</div>
</template>
<script>
export default {
methods: {
handleCount () {
this.$store.state.count++
}
}
}
</script>
这样访问每次都会调用
$store.state.xxx
,可以把这个作为计算属性
<template>
<div>
首页 {{ count }}
<button @click="handleCount">加一</button>
</div>
</template>
<script>
export default {
computed: {
count () {
return this.$store.state.count
}
},
methods: {
handleCount () {
this.$store.state.count++
}
}
}
</script>
使用mutations修改states的数据
state中的数据不建议直接修改,而是通过mutations属性定义方法来修改
// 配置vuex
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
},
decrease (state, n = 1) {
state.count -= n
}
}
})
在组件中掉哟mutations中的方法来改变数据,调用mutations中的数据要使用commit来调用:
<template>
<div>
首页 {{ count }}
<button @click="handleCountA">加</button>
<button @click="handleCountD">减</button>
</div>
</template>
<script>
export default {
computed: {
count () {
return this.$store.state.count
}
},
methods: {
handleCountA () {
this.$store.commit('increment')
},
handleCountD () {
this.$store.commit('decrease', 2)
}
}
}
</script>
commit接收对象
main.js中定义方法:
mutations: {
increment (state) {
state.count++
},
decrease (state, n = 1) {
state.count -= n
},
// param作为一个对象
change (state, param) {
state.count += (param.count1 - param.count2)
}
}
在组件中调用,type就是要调用的方法名,其他的就是具体的参数:
changeCount () {
this.$store.commit({
type: 'change',
count1: 1,
count2: 5
})
}
在mutations中不要做异步操作,commit后数据不能及时跟新。
getters
getters类似于计算属性
需求:对一个数组进行过滤,去大于10的数
state: {
count: 11,
list: [1, 3, 5, 7, 10, 11, 13, 15]
},
getters: {
listFilter: state => state.list.filter(data => data > 10),
// getters中的数据也可以访问其他getters中的数据
equalseCount: (state, getters) => getters.listFilter.filter(data => data === state.count)
},
访问getter数据:
getter中的数据 {{ $store.getters.listFilter }} -> {{ $store.getters.equalseCount}}
异步操作数据使用actions
定义actions
mutations: {
increment (state) {
state.count++
},
decrease (state, n = 1) {
state.count -= n
},
change (state, param) {
state.count += (param.count1 - param.count2)
}
},
actions: {
increment (context, param) {
context.commit('change', param)
}
}
调用actions要使用dispatch方法
actions () {
this.$store.dispatch({ // 使用dispath方法调用action中的方法
type: 'increment',
count1: 1,
count2: 5
})
}
异步案例:
actions:
actions: {
asyncIncrement (context) {
return new Promise(resolve => {
setTimeout(() => {
context.commit('increment')
resolve('success')
}, 2000)
})
}
}
在组件中调用:
async () {
this.$store.dispatch('asyncIncrement').then(data => console.log(data))
}
数据改变用mutations,存在业务逻辑用actions
modules状态区分
如果所有的状态写在一起不容易区分,这时可以用modules来做区分:
const moduleA = {
state: {
count: 10
}
}
const moduleB = {
state: {
count: 20
},
getters: {
sumCount (state, getters, rootState) {
return state.count + rootState.b.count + rootState.count
}
}
}
// 配置vuex
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
},
state: {
count: 11,
list: [1, 3, 5, 7, 10, 11, 13, 15]
},
}
获取,要注意的是 模块内部的 action、mutation、和 getter 注册在全局命名空间,不能使用模块名.getters等方式去调用
<template>
<div>介绍 {{ $store.state.count }}
moduleB中的Count {{ $store.state.b.count }}
moduleB中的sumCount {{ $store.getters.sumCount }}
</div>
</template>
<script>
export default {
}
</script>
中央事件总线vue-bus
新建一个vue-bus.js文件:
const install = function (Vue) {
const Bus = new Vue({
methods: {
emit(event, ...args) {
this.$emit(event, args)
},
on(event, callback) {
this.$on(event, callback);
},
off(event, callback) {
this.off(event, callback)
}
}
})
Vue.prototype.$bus = Bus
}
export default install
在main.js中引入并使用:
import VueBus from './vue-bus'
Vue.use(VueBus)
在组件中使用-发送数据:
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png">
<button @click="handleNum">click</button>
<HelloWorld/>
</div>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue'
export default {
name: 'app',
components: {
HelloWorld
},
methods: {
handleNum() {
this.$bus.emit('add', 10);
}
}
}
</script>
<style>
</style>
获取数据:
<template>
<div class="hello">
<h1>{{ number }} </h1>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
data() {
return {
number: 0
}
},
methods: {
handle(num) {
let data = parseInt(num)
this.number += data
}
},
created() {
this.$bus.on('add', this.handle)
},
beforeDestroy() {
this.$bus.off('add', data => console.log(data))
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>