地址

Vue的简介和使用

Vue全套教程全开源

4个小时带你快速入门vue

MVVM

  • M : 模型 , 对应数据
  • V : 视图 , 对应html
  • VM : 视图模型 , 对应Vue

Hello world

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
<div id="box">
{{ 10+20 }}
<p>
{{ myname }}
{{ mysum }}
{{ 20>10?'aaa':'bbb' }}
</p>

<p>
{{ myhtml }}
</p>
<!-- 指令 -->
<p v-html="myhtml"></p>
</div>

<script>
var vm = new Vue({
el:"#box",
data:{
myname:"hyl",
mysum:10+20,
myhtml:"<h1>heyingliang<h1>",
}
})
</script>

data的数据类型

  • string
  • array
  • object
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<div id="box">
<p> {{ name }}</p>
<p> {{ msg.age }} </p>
<p>{{ li[0] }}</p>
</div>

<script>
var vm = new Vue({
el:'#box',
data:{
name:'hyl',
msg:{
'age':21,'sex':`nan`,
},
li:[21,23,45]
}
})
</script>

指令

其实可以把指令看成是标签的属性

v-text

设置标签的文本值(textContent)

1
2
3
4
5
6
7
8
9
10
11
12
<div id="box">
<p v-text="name"></p>
</div>

<script>
var vm = new Vue({
el:'#box',
data:{
name:'hyl',
}
})
</script>

支持字符串的拼接

1
2
3
4
<div id="box">
<p v-text="name +` is sb`"></p>
<p>{{ name + ' is sb' }}</p>
</div>

v-html

设置标签的innerHTML

双大括号会将数据解释为普通文本,而非 HTML 代码。为了输出真正的 HTML,你需要使用 v-html 指令

1
2
3
4
5
6
7
8
9
10
11
12
<div id="box">
<p v-html="myhtml"></p>
</div>

<script>
var vm = new Vue({
el:"#box",
data:{
myhtml:"<h1>heyingliang<h1>",
}
})
</script>

v-htmlv-text更高级 :

  • v-html内容中有html结构会被解析为标签
  • v-text指令无论内容是什么只会解析为文本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<div id="box">
<p v-html="name"></p>
<p v-html="name2"></p>
</div>

<script>
var vm = new Vue({
el:'#box',
data:{
name:'hyl',
name2:'<strong>123</strong>',
}
})
</script>

v-on

为元素事件绑定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<div id="box">
<button type="button" value="this is a button" v-on:click="myfun"></button>
<button type="button" value="this is a button" @click="myfun"></button>
</div>

<script>
var vm = new Vue({
el:'#box',
methods:{
myfun:function(){
alert("hello")
}
}
})
</script>

点击h2 : hyl改变成dsz

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<div id="box">
<h2 @click="myfun">{{ name }}</h2>
</div>

<script>
var vm = new Vue({
el:'#box',
data:{
name:'hyl',
},
methods:{
myfun:function(){
this.name = 'dsz'
}
}
})
</script>

在Vue中 , 不要考虑如何改变元素 , 而是元素绑定数据 , 然后直接修改数据

事件修饰符

事件后面跟上.修饰符可以对事件进行限制

比如.enter可以限制触发的按键为回车

  1. .stop : 停止事件冒泡
  2. .prevent : 停止默认行为
  3. .capture
  4. .self : 只有自己的行为才触发函数(事件冒泡上来的事件不会执行)
  5. .once : 只执行一次
  6. .passive
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!-- 阻止单击事件继续传播(停止冒泡) -->
<a v-on:click.stop="doThis"></a>

<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>

<!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThat"></a>

<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>

<!-- 添加事件监听器时使用事件捕获模式 -->
<!-- 即内部元素触发的事件先在此处理,然后才交由内部元素进行处理 -->
<div v-on:click.capture="doThis">...</div>

<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div v-on:click.self="doThat">...</div>

<!-- key的限定按键 -->
<input type="input" v-on:keyup.13="doThat">
<input type="input" v-on:keyup.enter="doThat">

事件冒泡现象依旧存在

事件冒泡 :

  • 当一个元素接收到事件的时候 会把他接收到的事件传给自己的父级,一直到window 。

  • 注意这里传递的仅仅是事件 并不传递所绑定的事件函数。

  • 所以如果父级没有绑定事件函数,就算传递了事件 也不会有什么表现 但事件确实传递了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<div id="box">
<ul @click="click_ul">
<li @click="click_li">a</li>
<li @click="click_li">b</li>
</ul>
</div>

<script>
var vm = new Vue({
el: "#box",
methods: {
click_li:function(){
console.log('click li')
},
click_ul:function(){
console.log('click ul')
}
}
})
</script>

点击了li , 会打印出click liclick ul

Vue的函数会自动传入event事件本身

1
2
3
4
click_li:function(event){
console.log('click li')
event.stopPropagation();
},

这时我们就可以使用Vue的语法糖.stop

1
<a v-on:click.stop="doThis"></a>

同理:
event.preventDefault() : 阻止元素发生默认的行为(例如,当点击提交按钮时阻止对表单的提交 , a标签被点击时的页面跳转)

1
<a href='http://www.baidu.com' @click.prevent='myfunc'>aaa</a>

v-show

动态显示和隐藏

1
2
3
4
5
6
7
8
9
10
11
12
13
<div id="box">
<p v-show="isshow">ppppp</p>
</div>

<script>
var vm = new Vue({
el:"#box",
data:{
isshow:true,
myhtml:"<h1>heyingliang<h1>",
}
})
</script>

age大于18才显示

1
2
3
4
5
6
7
8
9
10
11
12
<div id="box">
<p v-show="age>=18">isshow</p>
</div>

<script>
var vm = new Vue({
el:"#box",
data:{
age:19,
}
})
</script>

控制显示与隐藏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<div id="box">
<button class="btn btn-danger" @click="func">show or hide</button>
<p v-show="isshow">isshow</p>
</div>

<script>
var vm = new Vue({
el: "#box",
data: {
isshow: false,
},
methods: {
func: function() {
this.isshow = !this.isshow
}
}
})
</script>

v-if

动态创建和删除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<div id="box">
<p v-show="isshow">isshow</p>
<p v-if="iscreated">iscreated</p>
</div>

<script>
var vm = new Vue({
el:"#box",
data:{
isshow:true,
iscreated:true,
myhtml:"<h1>heyingliang<h1>",
}
})
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<div id="box">
<button class="btn btn-danger" @click="myfunc">show or hide</button>
<h1 v-if='show'>if-true</h1>
<h1 v-else>if-false</h1>
</div>

<script>
var vm = new Vue({
el: "#box",
data: {
show: true,
},
methods: {
myfunc() {
this.show = !this.show
}
}
})
</script>

还有v-else-if关键字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<div id="box">
<h1 v-if='num>0'>v-if --- num > 0</h1>
<h1 v-else-if='num==0'>v-else-if --- num = 0</h1>
<h1 v-else>v-else --- num < 0</h1>
</div>

<script>
var vm = new Vue({
el: "#box",
data: {
num: -1,
},
})
</script>

v-bind

用于绑定数据和元素属性 , 以此来修改元素属性(比如 : src , title , class)

通常我们可以将v-bind:简写成:

三目写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<div class="content">
<div id="box">
<span v-bind:class="isActive?'h1':'h6'">123</span>
</div>
</div>

<script>
var vm = new Vue({
el: '#box',
data: {
isActive: true,
},
})
</script>

注意 , "isActive?'h1':'h6'" , 三元表达式中要用两次引号

:class="{h1:isActive}" , class是不是h1取决于isActive

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<div class="content">
<div id="box">
<span :class="{h1:isActive}">123222222222222222</span>
</div>
</div>

<script>
var vm = new Vue({
el: '#box',
data: {
isActive: false,
},
})
</script>

对象写法

v-bind的绑定的对象是obj , 那么就会有这个obj对象的所有属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<div class="content">
<div id="box">
<span :class="myobj">123222222222222222</span>
</div>
</div>

<script>
var vm = new Vue({
el: '#box',
data: {
myobj:{
active:true,
h1:true,
flow:false,
}
},
})
</script>

这样 , span对象就会有activeh1两个class(flow因为为false,所以没有)

如果我们在console里查看vm.myobj对象, 发现myobj对象有六个方法 ,

  1. get active: ƒ reactiveGetter()
  2. set active: ƒ reactiveSetter(newVal)
  3. get h1: ƒ reactiveGetter()
  4. set h1: ƒ reactiveSetter(newVal)
  5. get flow: ƒ reactiveGetter()
  6. set flow: ƒ reactiveSetter(newVal)

说明myobj对象的属性的设置和获取就是由这六个方法拦截重载的

所以 , 如果我们新增一个actIve2属性也没有用 , 因为没有重载方法

数组写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<div class="content">
<div id="box">
<span :class="myarr">123222222222222222</span>
</div>
</div>

<script>
var vm = new Vue({
el: '#box',
data: {
myarr:['h1','active']
},
})
</script>

之后只要使用vm.myarr.push('flow')即可

三目的动态写法

1
2
3
<div :style="'background"'+(isActive?'red':'yellow')">
动态绑定style-三目写法.如果isActive为True,绑定red,否则绑定yellow
</div>

对象的动态写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<div class="content">
<div id="box">
<span :class="cls">123222222222222222</span>
<button class="btn btn-default" @click="toggle">change</button>
</div>
</div>

<script>
var vm = new Vue({
el: '#box',
data: {
cls: 'h1',
},
methods:{
toggle:function(){
this.cls = 'h6';
}
}
})
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<div id="box">
<button class="btn btn-default" @click="toggle">change</button>
<span class="btn" :class="{'btn-default':btn_style}">123</span>
</div>

<script>
var vm = new Vue({
el: "#box",
data: {
cls: 'btn-default',
btn_style:false,
},
methods: {
toggle: function() {
this.btn_style = true;
}
}
})
</script>

对象的动态写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<div class="content">
<div id="box">
<span :class="myobj">123222222222222222</span>
</div>
</div>

<script>
var vm = new Vue({
el: '#box',
data: {
myarr:['h1','active']
},
})
</script>
1
vm.myarr.push({fontSize:'30px'})

v-for

列表循环

1
2
3
4
5
6
7
8
9
10
11
12
<div id="box">
<h2 v-for='(num,idx) in myarr'>{{num}}---{{idx}}</h2>
</div>

<script>
var vm = new Vue({
el: "#box",
data: {
myarr:[11,22,33]
},
})
</script>

还可以遍历对象的属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<div id="box">
<h2 v-for='(value,key) in myobj'>{{value}}---{{key}}</h2>
</div>

<script>
var vm = new Vue({
el: "#box",
data: {
myobj:{
'name':'hyl',
'age':'22',
'sex':'male',
}
},
})
</script>

key的作用:

  • key是为了给Vue一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素
  • 需要为每项提供一个唯一的key属性。
  • 列表数据修改的时候,他会根据key值去判断某个值是否修改,如果修改,则重新渲染这一项,否则复用之前的元素;
  • 也就是说 :
    • 没有key属性时,状态默认绑定的是位置;
    • 有key属性时,状态根据key的属性值绑定到了相应的数组元素。

数据更新

原地修改方法:

  1. push()
  2. pop()
  3. shift()
  4. unshift()
  5. splice()
  6. sort()
  7. reverse()

非原地修改方法:

  1. filter()
  2. concat()
  3. slice()
  4. map()

注 : vue不能使用索引修改

  • eg: vm.obarr[1]='hyl'是不行的

  • 必须使用spilce()set()修改

    1
    2
    Vue.set(vm.obarr,1,'hyl')
    Vue.obarr.splice(0,1,'hyl')

同理 , 添加obj对象的属性 , 也可以使用set方法 . 他会自动定义obj的get重载方法和set重载方法

v-model

表单数据的双向绑定

vue中经常使用到<input><textarea>这类表单元素,vue对于这些元素的数据绑定和我们以前经常用的jQuery有些区别。vue使用v-model实现这些标签数据的双向绑定,它会根据控件类型自动选取正确的方法来更新元素。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<div id="box">
<input type="text" v-model="message" @keyup.enter=myfunc />
{{message}}
</div>

<script>
var vm = new Vue({
el: "#box",
data: {
message:"132",
},
methods:{
myfunc(){
alert(this.message);
}
}
})
</script>

v-model.lazy

区别 : 就是@input(时刻监听)@change(监听焦点) 的区别 . v-model 是 时刻监听 , v-model.lazy只会监听失去焦点的事件,节省资源

1
<input type='text' v-model.lazy='mytext'/>

v-model.trim

去除空格

1
<inputtype='text' v-model.trim="username">

计算属性

1
2
3
4
5
6
7
8
9
10
11
12
<div id="box">
<li>{{ mytext.substring(0,1).toUpperCase() + mytext.substring(1) }}</li>
</div>

<script>
var vm = new Vue({
el: "#box",
data: {
mytext:'heyingliang',
}
})
</script>

上面做法其实是不妥当的 , 把js的处理逻辑放到模板里 , 很混乱 , 这时就可以使用计算属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<div id="box">
<li>{{ UpperText }}</li>
</div>

<script>
var vm = new Vue({
el: "#box",
data: {
mytext: 'heyingliang',
},
computed: {
UpperText(){
return this.mytext.substring(0, 1).toUpperCase() + this.mytext.substring(1)
}
}
})
</script>

写成方法 , 但是最后是用属性来获取

计算属性和计算方法的区别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<div id="box">
<li>{{ UpperText }}</li>
<li>{{ getUpperText() }}</li>
</div>

<script>
var vm = new Vue({
el: "#box",
data: {
mytext: 'heyingliang',
},
methods: {
getUpperText: function() {
return this.mytext.substring(0, 1).toUpperCase() + this.mytext.substring(1)
}
},
computed: {
UpperText() {
return this.mytext.substring(0, 1).toUpperCase() + this.mytext.substring(1)
}
}
})
</script>
  • 计算属性 : 会将计算的结果作为属性存储下来 , 下次直接使用即可 . 当且仅当依赖的改变了,才会重新执行一次
  • 计算方法 : 每次都要重新计算

练习

分页计数器

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
<div class="content">
<div id="box">
<button class="btn btn-danger" value="-" @click="dec()">-</button>
<span>{{ num }}</span>
<button class="btn btn-danger" value="+" @click="inc()">+</button>
</div>
</div>

<script>
var vm = new Vue({
el: '#box',
data: {
num: 1,
},
methods: {
inc: function() {
if (this.num == 10) {
alert('到顶了');
} else {
this.num += 1;
}

},
dec: function() {
if (this.num == 1) {
alert('到底了');
} else {
this.num -= 1
}
}
}
})
</script>

模糊查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<div id="box">
<input v-model="mytext" type="text" @input="myfunc" />
<h4 v-for='value in myarr'>{{value}}</h4>
</div>

<script>
var vm = new Vue({
el: "#box",
data: {
mytext: '',
myarr: ['aa', 'bb', 'cc', 'dd'],
myarr2:['aa', 'bb', 'cc', 'dd'],
},
methods: {
myfunc: function() {
this.myarr = this.myarr2.filter(item => item.indexOf(this.mytext)>-1);
}
}
})
</script>

或者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<div id="box">
<input v-model="mytext" type="text" />
<h4 v-for='value in getdata'>{{value}}</h4>
</div>

<script>
var vm = new Vue({
el: "#box",
data: {
mytext: '',
myarr: ['aa', 'bb', 'cc', 'dd'],
},
computed:{
getdata(){
return this.myarr.filter(item => item.indexOf(this.mytext)>-1);
}
}
})
</script>

非自动的轮播图

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
<div class="content">
<div id="box">
<img class="img-responsive center-block" :src="img_arr[img_idx]" />
<button class="btn btn-default" v-show="img_idx!=0" @click="before">before</button>
<button class="btn btn-default" v-show="img_idx<img_arr.length-1" @click="next">next</button>
</div>
</div>

<script>
var vm = new Vue({
el: '#box',
data: {
img_arr: ['static/1.png', 'static/2.png', 'static/3.png', 'static/4.png', 'static/5.png'],
img_idx: 0,
},
methods: {
next: function() {
this.img_idx++;
},
before: function() {
this.img_idx--;
}
}
})
</script>

checkbox常用

多选

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<div id="box">
<label>Favorite sports</label>
<input type="checkbox" v-model="check_list" value='swimming'/>游泳
<input type="checkbox" v-model="check_list" value='run'/>长跑
<input type="checkbox" v-model="check_list" value='pingong'/>乒乓
{{ check_list }}
</div>

<script>
var vm = new Vue({
el: "#box",
data: {
check_list : []
},
})
</script>

单选

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<div id="box">
<label>Favorite lang</label>
<input type="radio" v-model="check" value='python'/>python
<input type="radio" v-model="check" value='js'/>js
<input type="radio" v-model="check" value='java'/>java
{{ check }}
</div>

<script>
var vm = new Vue({
el: "#box",
data: {
check:'',
},
})
</script>

简易购物车

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
<div id="box">
全选:<input type="checkbox" v-model="isAllCheck" @change="check_all">
<li v-for="data in datalist">
<input type="checkbox" v-model="check_list" :value='data' @change="check_all2">
{{ data }}
</li>
{{check_list}}

<h4>money total:</h4>
{{ getSum() }}
</div>

<script>
var vm = new Vue({
el: "#box",
data: {
isAllCheck: false,
check_list: [],
datalist: [{
'name': '商品1',
'price': 10,
'count': 1,
},
{
'name': '商品2',
'price': 20,
'count': 2,
},
{
'name': '商品3',
'price': 30,
'count': 3,
},
],
},
methods: {
getSum: function() {
var datas = this.check_list
var res = 0;
for (var i = 0; i < datas.length; i++) {
res += datas[i].count * datas[i].price;
}
return res
},
check_all: function() {
if (this.isAllCheck) {
this.check_list = this.datalist;
} else {
this.check_list = [];
}
},
check_all2: function() {
if (this.check_list.length === this.datalist.length) {
this.isAllCheck = true;
} else {
this.isAllCheck = false;
}
}
}
})
</script>

fetch

有些浏览器自带支持Ajax请求的库 , 这个库就是fetch

1
2
3
fetch(地址).then(res=>res.json()).then(res=>{
console.log(res)
})

axios

axios : 专门用于做AJax请求的库 .

axios 是一个基于Promise 用于浏览器和 node.js 的 HTTP 客户端,它本身具有以下特征:

  1. 从浏览器中创建 XMLHttpRequest
  2. 从 node.js 发出 http 请求
  3. 支持 Promise API
  4. 拦截请求和响应
  5. 转换请求和响应数据
  6. 取消请求
  7. 自动转换JSON数据
  8. 客户端支持防止 CSRF/XSRF

API

  1. axios.get(地址?key=value&key2=values).then(function(response), function(err){})

  2. axios,post(地址,{key:value,key 2:value2}).then(function(response)), function(err){})

  3. ```js
    axios({
    method: ‘get’,
    url: ‘http://bit.ly/2mTM3nY',
    headers: “{‘name’:’hyl’}”,
    responseType: ‘stream’
    })
    .then(function (response) {

    response.data.pipe(fs.createWriteStream('ada_lovelace.jpg'))
    

    });

    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

    4.



    ### 示例

    ```html
    <div id="box">
    <button class="btn btn-default" @click="getjoke">get joke</button>
    </div>

    <script>
    var vm = new Vue({
    el: "#box",
    methods: {
    getjoke: function() {
    axios.get(
    'https://autumnfish.cn/api/joke/list',{
    params:{
    num:2
    }
    }).then(function(response){
    console.log(response);
    },function(err){
    console.log(err);
    })
    }
    }
    })
    </script>

注意:

  • axIos回调函数中的this已经改变 , 无法访问到vue的data中的数据
  • 把this保存起来回调函数中直接使用保存的this即可

并发

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//获得用户信息的请求
function getUserAccount() {
return axios.get('/user/12345');
}

//获取用户许可证的请求
function getUserPermissions() {
return axios.get('/user/12345/permissions');
}

axios.all( [ getUserAccount(), getUserPermissions() ] )
.then(axios.spread(function (acct, perms) {
//两个请求现已完成
})
);

请求拦截器和响应拦截器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//请求拦截器
axios.interceptors.request.use(
function (config) {
// 在发送请求之前做些什么
return config;
},
function (error) {
// 对请求错误做些什么
return Promise.reject(error);
}
);

//响应拦截器
axios.interceptors.response.use(
function (config) {
// 对响应数据做点什么
return config;
},
function (error) {
// 对响应错误做点什么
return Promise.reject(error);
}
);

Vue component

组件 : 封装了样式 , 标签 , js的 代码片段

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
<div id="box">
<navbar></navbar>
</div>

<script type="application/javascript">
Vue.component('navbar',{
template:`
<div style='background:yellow'>
<button @click='show'>返回</button>
导航栏
<button>主页</button>
</div>`,
methods:{
show:function(){
console.log('show');
}
}
})
</script>

<script>
var vm = new Vue({
el: "#box",
})
</script>

全局组件和局部组件

  • 全局组件和局部组件 的关系 可以类比成python中全局变量和局部变量的关系
  • 全局组件 大家都可以访问 , 局部组件只有他的父亲才可以访问
  • Vue.component定义的是全局组件
  • 局部组件使用components关键字定义

全局组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<script type="application/javascript">
Vue.component('navbar',{
template:`
<div style='background:yellow'>
<button @click='show'>返回</button>
导航栏
<button>主页</button>
<navbarchild></navbarchild>
</div>`,
methods:{
show:function(){
console.log('show');
}
}
})
</script>

<script type="text/javascript">
Vue.component('navbarchild',{
template:`<h1>this is navbarchild</h1>`
})
</script>

局部组件:

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
<script type="application/javascript">
Vue.component('navbar',{
template:`
<div style='background:yellow'>
<button @click='show'>返回</button>
导航栏
<button>主页</button>
<navbarchild></navbarchild>
</div>`,
methods:{
show:function(){
console.log('show');
}
},
components:{
'navbarchild':{
template:`<h1>this is navbarchild</h1>`,
}
}
})
</script>

<script>
var vm = new Vue({
el: "#box",
})
</script>

组件编写方式 和 Vue实例的区别

  1. 自定义组件需要有一个root element
  2. 父子组件的data是无法共享的
  3. 组件可以有data , methods , computed, 但是 data必须是一个函数
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
<script type="application/javascript">
Vue.component('navbar',{
template:`
<div style='background:yellow'>
<button @click='show'>返回</button>
导航栏
<button>{{ navarname }}主页</button>
<navbarchild></navbarchild>
</div>`,
methods:{
show:function(){
console.log('show');
}
},
data(){
return {
navarname:'hyl',
}
},
components:{
'navbarchild':{
template:`<h1>this is navbarchild</h1>`,
}
}
})
</script>

父子通信

总结 :

  1. props down
  2. events up

父传子

props : 接收父组件传过来的属性

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
<div id="box">
<navbar username="hyl"></navbar>
<navbar username="czj"></navbar>
<navbar :username="parents"></navbar>
</div>

<script type="application/javascript">
Vue.component('navbar',{
template:`
<div>
<button>返回</button>
{{username}}导航栏
<button>{{ navarname }}主页</button>
</div>`,
props:['username']
})
</script>

<script>
var vm = new Vue({
el: "#box",
data:{
parents:'parents'
}
})
</script>

注意:

1
<navbar username="hyl"></navbar>

这样 , 传过去的hyl是string , 那么如果要传truefalse呢?

使用动态绑定

1
<navbar :isshow="true"></navbar>

属性类型验证

1
props:['username','isShow']

改成

1
2
3
4
props:{
'username':String,
'isShow':Boolean
}

子传父

子传父 , 需要使用事件:

儿子分发(触发)事件 , 父亲接收事件 , 然后执行函数

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
<div id="box">
<navbar @myevent="eventHandler"></navbar>
</div>

<script type="application/javascript">
Vue.component('navbar',{
template:`
<div>
<button @click="payMoney">click</button>
</div>`,
data(){
return {
childname:"子组件的状态"
}
},
methods:{
payMoney(){
this.$emit("myevent",this.childname)
}
}
})
</script>

<script>
var vm = new Vue({
el: "#box",
data:{
parents:'parents'
},
methods:{
eventHandler(ev){
console.log(ev)
}
}
})
</script>

子传父示例

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
<div id="box">
<navbar @myevent='eventHandler'></navbar>
<sidebar v-show='isShow'></sidebar>
</div>

<script type="text/javascript">
Vue.component('navbar',{
template:`
<div>
<button type="button" @click="alarm">show</button>
</div>`,
data(){
return {
isShow:true
}
},
methods:{
alarm(){
this.$emit("myevent")
}
}
})
</script>

<script type="text/javascript">
Vue.component('sidebar', {
template: `
<div style='background:yellow; width:200px'>
<ul>
<li></li>
<li></li>
<li></li>
</ul>
</div>`
})
</script>

<script>
var vm = new Vue({
el: "#box",
data: {
isShow: true
},
methods:{
eventHandler(){
this.isShow = !this.isShow
}
}
})
</script>

ref通信

  • ref放在标签上 , 拿到的是原生节点
  • ref放在组件上 , 拿到的是组件对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<div id="box">
<input type="text" ref='mytext' />
<button type="button" @click="handleAdd">add</button>
</div>

<script>
var vm = new Vue({
el: "#box",
methods: {
handleAdd(){
// this 就是这个Vue对象 , this.$refs.mytext就是input这个原生节点
console.log(1111,this.$refs.mytext.value)
}
},
})
</script>

既然能拿到组件对象 , 那么就能拿到组件的data数据 和 method方法

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
<div id="box">
<child ref='mychild'></child>
<button type="button" @click="handleAdd">add</button>
</div>

<script type="text/javascript">
Vue.component('child',{
template:`<div>
child
</div>`,
data(){
return {
msg:'this is component msg'
}
},
methods:{
alarm(){
console.log('this is component method')
}
}
})
</script>

<script>
var vm = new Vue({
el: "#box",
data: {
},
methods: {
handleAdd(){
console.log(1111,this.$refs.mychild.msg)
this.$refs.mychild.alarm()
}
},
})
</script>

可见 , 父组件具有对子组件的绝对控制权

非父子通信 – 事件总线

  • 兄弟之间的通讯其实可以通过父亲作为中介 : 儿子A -> 父亲 -> 儿子B
  • 同理孙子之间的通讯可以通过祖父作为中介 : 孙子A -> 父亲A -> 祖父 -> 父亲B -> 孙子B

这样 , 我们就能抽象出一个事件总线 :

  • 儿子A -> 事件总线 -> 儿子B
  • 孙子A -> 事件总线 -> 孙子B
  • 曾孙子A -> 事件总线 -> 曾孙子B

也就是说 , 事件总线相当于一个平台 , 用于沟通两个组件

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
<div id="box">
<server></server>
<client></client>
</div>

<script type="text/javascript">
// 空的Vue实例 , 就是中央时间总线
var bus = new Vue()

Vue.component('server', {
template: `
<div>
<input type='text' ref='mytext'>
<button @click='eventHandler'>server发布</button>
</div>`,
methods:{
eventHandler(){
bus.$emit('mymsg',this.$refs.mytext.value)
}
}
})

Vue.component('client', {
template: `
<div>client</div>`,
mounted() {
console.log('生命周期函数 , 当前组件的dom 创建渲染完成之后就会调用')
// 订阅bus
bus.$on('mymsg',(data)=>{console.log('收到推送了',data)})
}
})
</script>

<script>
var vm = new Vue({
el: "#box",
data: {
isShow: true
},
})
</script>

总结 :

1
2
3
4
5
6
7
8
9
10
// 相当于又new了一个vue实例,Event中含有vue的全部方法;
var Event = new Vue(); 

// 发送数据,第一个参数是发送数据的名称,接收时还用这个名字接收,第二个参数是这个数据现在的位置;
Event.$emit('msg',this.msg);

// 接收数据,第一个参数是数据的名字,与发送时的名字对应,第二个参数是一个方法,要对数据的操作
Event.$on('msg',function(msg){
//这里是对数据的操作
})

动态组件

  1. <component>标签 , 动态绑定组件到他的IS属性
  2. <keep-alive>标签 , 保留状态 , 避免重新渲染
1
2
 <!--等同于 <home></home>  -->
<component is='home'></component>

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<div id="box">
<li>
<a @click="who='home'">home</a>
<a @click="who='list'">list</a>
<a @click="who='cart'">cart</a>
</li>
<component :is="who"></component>
</div>


<script>
var vm = new Vue({
el: "#box",
data: {
who:'home'
},
components:{
'home':{template:`<div>home</div>`},
'list':{template:`<div>list</div>`},
'cart':{template:`<div>cart</div>`},
}
})
</script>

需要注意的是 , <component>在切换的时候 , 会干掉旧的标签 , 更换成新的标签

这时 , 我们就需要使用<keep-alive>标签

我们将需要保留的标签使用<keep-alive>标签包裹即可

1
2
3
<keep-alive>
<component :is="who"></component>
</keep-alive>

Vue-slot

slot插槽 (内容分发)

单个slot

  • 混合父组件的内容与子组件自己的模板 -> 内容分发
  • 父组件模板的内容在父组件作用域内编译;子组件模板的内容在子组件作用域内编译。

slot类似于jinja里的block , 用于插入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
  <div id="box">
<child>
<div>这是父组件插入子组件的div</div>
</chilld>
</div>

<script type="text/javascript">
Vue.component('child',{
template:`<div>
child
<slot></slot>
</div>`
})
</script>

<script>
var vm = new Vue({
el: "#box",
data: {
isShow: true
},
})
</script>

父组件模板的内容在父组件作用域内编译;子组件模板的内容在子组件作用域内编译。

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
<div id="box">
<navbar @myevent='eventHandler'></navbar>
<sidebar v-show='isShow'></sidebar>
</div>

<script type="text/javascript">
Vue.component('navbar',{
template:`
<div>
<button type="button" @click="alarm">show</button>
</div>`,
data(){
return {
isShow:true
}
},
methods:{
alarm(){
this.$emit("myevent")
}
}
})
</script>

<script type="text/javascript">
Vue.component('sidebar', {
template: `
<div style='background:yellow; width:200px'>
<ul>
<li></li>
<li></li>
<li></li>
</ul>
</div>`
})
</script>

<script>
var vm = new Vue({
el: "#box",
data: {
isShow: true
},
methods:{
eventHandler(){
this.isShow = !this.isShow
}
}
})
</script>

将上面子传父修改为

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
<div id="box">
<navbar>
<button type="button" @click="isShow=!isShow">show</button>
</navbar>
<sidebar v-show='isShow'></sidebar>
</div>

<script type="text/javascript">
Vue.component('navbar', {
template: `
<div>
<slot></slot>
</div>`
})
</script>
<script type="text/javascript">
Vue.component('sidebar', {
template: `
<div style='background:yellow; width:200px'>
<ul>
<li></li>
<li></li>
<li></li>
</ul>
</div>`
})
</script>
<script>
var vm = new Vue({
el: "#box",
data: {
isShow: true
},
})
</script>

具名slot

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<div id="box">
<child>
<div slot="header">这是父组件插入子组件的header</div>
<div slot="footer">这是父组件插入子组件的footer</div>
</child>
</div>

<script type="text/javascript">
Vue.component('child', {
template: `<div>
<slot name="header">这是子组件的header</slot>
<h1>child</h1>
<slot name="footer">这是子组件的footer</slot>
</div>`
})
</script>

<script>
var vm = new Vue({
el: "#box",
})
</script>

transition过渡

vue在插入、更新或者移除DOM时,自动添加/删除 CSS 类名,以此来提供多种不同方式的应用过渡效果

单元素/组件过渡

  • CSS过渡
  • CSS动画
  • 结合animate.css动画库
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
       <style type="text/css">
.myfade-enter-active,
.myfade-leave-active {
transition: all 1.5s;
}

.myfade-enter,
.myfade-leave-to {
opacity: 0;
transform: translateX(100px);
}

.mybound-enter-active {
animation: bounce-in .5s;
}

.mybound-leave-active {
animation: bounce-in .5s reverse;
}

@keyframes bounce-in {
from {
opacity: 0;
transform: translateX(100px);
}

to {
opacity: 1;
transform: translateX(0px);
}
}
</style>

<div id="box">
<button type="button" @click="isShow=!isShow">button</button>
<!-- 过渡风格动画 -->
<transition name="myfade">
<div v-show="isShow">
11111111111
</div>
</transition>

<!-- 关键帧风格动画 -->
<transition name="mybound">
<div v-show="isShow">
2222222
</div>
</transition>
</div>

<script>
var vm = new Vue({
el: "#box",
})
</script>

单组件过渡同理

多个元素/组件过渡(需要设置key)

<transition> can only be used on a single element. Use <transition-group> for lists.

  • 这里的多个元素 , 是指多个互斥元素 , 是if-elif-else , 只能有一个
  • 多个能通知存在的元素 , 称为列表元素
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<div id="box">
<button type="button" @click="isShow=!isShow">button</button>
<transition name="myfade">
<div v-if="isShow">11111111111</div>
<div v-else>222222</div>
</transition>
</div>

<script>
var vm = new Vue({
el: "#box",
data:{
isShow:true,
}
})
</script>
  • 上面的代码并不会触发动画 , 因为Vue的重用机制导致的 , 并不是真的删除<div v-if="isShow">11111111111</div>, 然后再创建 <div v-else>222222</div> , 而是直接修改innerHTML
  • 当有相同标签名的元素切换时,需要通过key特性设置唯一的值来标记以让Vue区分它们,否则Vue为了效率只会替换相同标签内部的内容。
  • 所以需要修改成
1
2
<div v-if="isShow" key='1'>11111111111</div>
<div v-else key='2'>222222</div>

过渡动画模式 (默认同时执行 in 和 out)

1
2
3
4
<!-- 先in后out -->
<transition name="myfade" mode="in-out">
<!-- 先out后in -->
<transition name="myfade" mode="out-in">

多个组件过渡同理

1
2
3
4
5
<keep-alive>
<transition name='bounce'>
<component :is="who"></component>
</transition>
</keep-alive>

列表过渡(需要设置key)

< transition-group>不同于 transition,它会以一个真实元素呈现:默认为一个<span>
你也可以通过tag特性更换为其他元素。

1
2
3
4
5
6
7
8
<ul>
<transition-group tag="ul" name="mybounce">
<li v-for='(data,idx) in datalist' :key='data'>
{{data}} -- {{idx}}
<button type="button" @click="del(idx)">del</button>
</li>
</transition-group>
</ul>

Vue 生命周期钩子

选项 / 生命周期钩子API

全部钩子

  • beforeCreate
  • created
  • beforeMount
  • mounted
  • beforeUpdate
  • updated
  • activated
  • deactivated
  • beforeDestroy
  • destroyed
  • errorCaptured
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
<script type="text/javascript">
var vm = new Vue({
el : "#app",
data : {
msg : "hi vue",
},
//在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。
beforeCreate:function(){
console.log('beforeCreate');
},
/* 在实例创建完成后被立即调用。
在这一步,实例已完成以下的配置:数据观测 (data observer),属性和方法的运算,watch/event 事件回调。
然而,挂载阶段还没开始,$el 属性目前不可见。 */
created :function(){
console.log('created');
},
//在挂载开始之前被调用:相关的渲染函数首次被调用
beforeMount : function(){
console.log('beforeMount');

},
//el 被新创建的 vm.$el 替换, 挂在成功
mounted : function(){
console.log('mounted');

},
//数据更新时调用
beforeUpdate : function(){
console.log('beforeUpdate');

},
//组件 DOM 已经更新, 组件更新完毕
updated : function(){
console.log('updated');

}
});
setTimeout(function(){
vm.msg = "change ......";
}, 3000);
</script>

过滤器

类似于jinja , 也是使用管道符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<div id="box">
<p :title="name | myupper">p标签</p>
</div>

<script>
Vue.filter('myupper',function(str){
return str + ' is sb';
})

var vm = new Vue({
el: "#box",
data: {
name: 'hyl'
},
})
</script>

自定义指令

为什么自定义指令 : 操作底层DOM

无参

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<div id="box">
<p v-hello>p标签</p>
</div>

<script>
Vue.directive('hello',{
// 当前的element
inserted(el){
console.log('当前节点插入到父节点了',el)
}
})
var vm = new Vue({
el: "#box",
})
</script>

带参

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<div id="box">
<p v-hello="'red'">p标签</p>
</div>

<script>
Vue.directive('hello',{
// 当前的element
inserted(el,bind){
console.log('当前节点插入到父节点了',el)
console.log('传入的参数值为',bind.value)
el.style.background=bind.value
},
update(el,bind){
el.style.background=bind.value
}
})
var vm = new Vue({
el: "#box",
data: {
name: 'hyl'
},
})
</script>

注意 : 指令只能接收一个参数

但是我们可以传入一个对象作为 , 变相传入多个参数

自定义指令的生命周期

  1. bind:只调用一次,指令第一次绑定到元素时调用,用这个钩子函数可以定义一个绑定时执行一次的初始化动作。
  2. inserted:被绑定元素插入父节点时调用(父节点存在即可调用,不必存在于document中)。
  3. update:被绑定于元素所在的模板更新时调用,而无论绑定值是否变化。通过比较更新前后的绑定值,可以忽略不必要的模板更新。
  4. componentUpdated:被绑定元素所在模板完成一次更新周期时调用。
  5. unbind:只调用一次,指令与元素解绑时调用。