内容输出

大胡子语句image-20200928145735963

通过 大胡子语句 我们可以很方便的中模板中输出数据,但是这种方式会有一个问题,当页面加载渲染比较慢的时候,页面中会出现 大胡子语句vue 提供了几个指令来解决这个问题

指令中的表达式不需要使用 大胡子语句

v-text

1
<p v-text="title"></p>

弊端:v-text 会填充整个 innerHTML

v-cloak

1
<p v-cloak>{{title}}</p>

需要配合 css 进行处理

1
2
3
4
5
<style>
[v-cloak] {
display: none;
}
</style>

v-html

为了防止 xss 攻击,默认情况下输出是不会作为 html 解析的,通过 v-html 可以让内容作为 html 进行解析

v-once

只渲染元素和组件一次,后期的更新不再渲染

v-pre

忽略这个元素和它子元素内容的编译

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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
[v-cloak] {
display: none;
}
</style>
</head>

<body>

<div id="app">
<!-- v-text指令会将内部全部inner HTML替换 -->
<p v-text='title'>你好</p>

<!-- 会输出文本和变量的值 -->
<p v-cloak>你好,{{title}}</p>

<!-- 会将content变量作为html输出 -->
<p v-html='content'></p>

<!-- 忽略语法,直接输出{{title}} -->
<p v-pre>{{title}}</p>
</div>

<script src="./js/vue.js"></script>
<script>

/**
* 指令
* - 值是表达式模式
* - 不支持 大胡子语句 语法
*
* v-if 与 v-show 的区别
* v-if:值为true的时候渲染标签,false删除(不渲染)标签
* v-show:渲染结构,但是根据值来隐藏和显示标签
* 使用v-if还是v-show:该结构是否经常变化
*
*/
let app = new Vue({
el: '#app',
data: {
title: '小康',
content: '<h1>小康</h1>'
}
});
</script>
</body>

</html>

image-20200927194247024

逻辑处理

v-show与v-if

v-show根据表达式的值(布尔值),切换元素的显示与隐藏(display 属性)

适用于状态切换比较频繁的情况

v-if根据表达式的值(布尔值),创建或销毁元素

适用于状态切换不频繁的情况

当逻辑表达式为false

image-20200927195010721

逻辑表达式为true时

image-20200927195652604

v-else / v-else-if

v-else 配合

循环与列表

v-for

根据数据循环渲染 v-for 指令所在的元素及其子元素

可以循环的数据:Array | Object | number | string | Iterable (2.6 新增)

1
2
3
<div v-for="(item, index) in items"></div>
<div v-for="(val, key) in object"></div>
<div v-for="(val, key, index) in object"></div>

v-for 中也可以使用 of 语法,在 vue 中两者没有什么区别

1
2
3
4
5
6
7
8
9
10
let app = new Vue({
data: {
users: [
{ id: 1, username: 'zmouse' },
{ id: 2, username: 'mt' },
{ id: 3, username: 'hai' }
]
},
});
app.$mount('#app');
1
2
3
4
5
6
7
8
9
10
11
12
<ul>
<!-- for in 与 for of没有区别 -->
<li v-for="(user,index) of users">
<input type="checkbox"> {{index}} - {{user.username}}
</li>
</ul>

<ul>
<li v-for="i of 10">
{{i}}
</li>
</ul>

image-20200927201821367

:key

默认情况下,在渲染 DOM 过程中使用 原地复用 ,这样一般情况下会比较高效,但是对于循环列表,特别是依赖某种状态的列表,会有一些问题,我们可以通过 :key 属性,来给每个循环节点添加一个标识。

虚拟dom为了节约性能,因此只会变化需要变化的地方。也就是说如果数据没有发生变化,那么DOM不会被重新渲染。

1
2
3
4
5
6
<ul>
<!-- for in 与 for of没有区别 -->
<li v-for="(user,index) of users" :key="user.id">
<input type="checkbox"> {{index}} - {{user.username}}
</li>
</ul>

属性绑定

v-bind

绑定数据(表达式)到指定的属性上,<div v-bind:参数="值/表达式"></div>,这里的参数就是指定的属性名称

1
2
3
4
5
6
7
8
9
10
11
12
13
<div id="app">
<p v-bind:id='id'></p>
<p v-bind:id='"id"'></p>
</div>

<script>
new Vue({
el: '#app',
data: {
id: 'test'
}
})
</script>

image-20200927210741884

缩写

有的一些常用指令会有对应的缩写,v-bind 对应的缩写为::

1
<p :id='id'></p>

样式

针对样式属性,v-bind 值有一些特殊的写法

style

原生普通写法

1
<div style="width: 100px; height: 100px; background: red"></div>

v-bind 写法

1
<div :style="'width: 100px; height: 100px; background: red'"></div>

对象写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<div :style="style1"></div>

...
<script>
new Vue({
el: '#app',
data: {
style1: {
width: '100px',
height: '100px',
background: 'green'
}
}
});
</script>

数组写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<div :style="[style1, style2]"></div>

...
<script>
new Vue({
el: '#app',
data: {
style1: {
width: '100px',
height: '100px',
background: 'green'
}
},
style2: {
border: '1px solid black'
}
});
</script>

image-20200927210917692

class

原生普通写法

1
<div class="box1 box2"></div>

v-bind 写法

1
<div :class="'box1 box2'"></div>

数组写法

1
<div :class="['box1', 'box2']"></div>

对象写法

1
2
3
4
5
6
7
8
9
10
11
<p :class="{'test1':flag}">xiaokang.me</p>

<script>
let app = new Vue({
data: {
flag: true,
}
});
app.$mount('#app');

</script>

image-20200927211218924

使用对象写法,可以根据值(boolean)动态添加对应的 class,如上述代码,会根据变量flag的值动态判断是否添加这个class。

双向数据流

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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<title>Title</title>
</head>

<body>

<div id="app">
<input type="text" v-model='text'>
</div>

<script src="./js/vue.js"></script>
<script>

// 模板 => vdom => dom

let app = new Vue({
data: {
text: '文本'
}
});
app.$mount('#app');

</script>
</body>

</html>

当数据text更新时,也会更新试图。更新试图时,数据也会更新。

0229caa6-1200-4bda-adcf-077d365295a8

  • 对于texttextarea

    双向数据绑定默认绑定的是value属性

  • checkboxbox

    绑定的是checked属性

  • select

    单选绑定到值,多选绑定到数组

指令修饰符

  • lazy

    取代input监听,change事件。

    1
    <input type="text" v-model.lazy='v1'>

    8ef5108c-402e-499e-baf4-7811499fa017

  • number

    将输入的字符串转为有效数字

  • trim

    输入首尾空格过滤

自定义指令

自定义指令在VUE内部提供了两种注册方式:全局指令局部指令

全局指令即全局部分注册即可。

1
Vue.directive('指令名称', {指令配置});

局部指令注册即在某个对象内注册,指令的生效范围为当前对象的。例如在创建Vue对象内注册的指令,可以在这个对象的内使用。

1
2
3
4
5
6
new Vue({
el: '#app',
directives: {
'指令名称': {指令配置}
}
});

注册命令时不需要使用v-前缀,但使用时需要使用v-前缀

指令生命周期(钩子函数)

指令的运行方式很简单,它提供了一组指令生命周期钩子函数,我们只需要在不同的生命周期钩子函数中进行逻辑处理就可以了

  • bind : 只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置
  • inserted : 被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)
  • update : 所在组件更新的时候调用
  • componentUpdated : 所在组件更新完成后调用
  • unbind : 只调用一次,指令与元素解绑时调用

不同的生命周期钩子函数在调用的时候同时会接收到传入的一些不同的参数

  • el : 指令所绑定的元素,可以用来直接操作 DOM
  • binding : 一个对象,包含以下属性:
    • name : 指令名,不包括 v- 前缀
    • value : 指令的绑定值(作为表达式解析后的结果)
    • expression : 指令绑定的表达式(字符串)
    • arg : 传给指令的参数,可选
    • modifiers : 传给指令的修饰符组成的对象,可选,每个修饰符对应一个布尔值
    • oldValue : 指令绑定的前一个值,仅在 updatecomponentUpdated 钩子中可用,无论值是否改变都可用

示例:点击按钮获取焦点

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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<title>Title</title>
</head>

<body>

<div id="app">
<h1>{{title}}</h1>
<hr>

<input type="text" v-focus="isFocus" />

<button v-on:click="isFocus = !isFocus">按钮</button>
</div>

<script src="./js/vue.js"></script>

<script>

Vue.directive('focus', {
bind(el) {
// 该生命周期执行完成的时候,虽然可以得到所在的dom节点对象,但是并不代表该节点已经渲染到页面(dom树)中
// el.focus();
},
inserted(el, binding) {
// 当元素插入到页面时执行
console.log(binding);
binding.value && el.focus();
},
update(el, binding, vNode, oldVNode) {
// 页面更新时会执行
console.log('binding', binding, vNode, oldVNode);
binding.value && el.focus();

}
});

let app = new Vue({
el: '#app',
data: {
title: '指令',
isFocus: false
}
});

</script>

</body>

</html>

通过isFocus属性控制是否获取焦点。

案例:拖拽案例

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
79
80
81
82
83
84
85
86
87
88
89
90
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.box {
position: absolute;
left: 100px;
top: 100px;
width: 100px;
height: 100px;
background: red;
}
</style>
</head>

<body>

<div id="app">
<button v-on:click="isCanDrag = !isCanDrag">拖拽 - {{isCanDrag}}</button>
<div class="box" v-drag.limit="isCanDrag"></div>
</div>

<script src="./js/vue.js"></script>

<script>

// 注册全局指令 drag
Vue.directive('drag', {
// 将值结构
bind(el, { value: isCanDrag, modifiers: { limit } }) {
// 为元素绑定事件
let x = 0;
let y = 0;
let isDown = false;
el._isCanDrag = isCanDrag;

el.addEventListener('mousedown', function (e) {

if (!this._isCanDrag) return;

x = e.clientX - this.offsetLeft;
y = e.clientY - this.offsetTop;
isDown = true;

e.preventDefault();
});

el.addEventListener('mousemove', function (e) {
if (isDown) {
let L = e.clientX - x;
let T = e.clientY - y;

if (limit) {
if (L < 0) {
L = 0;
}
}

this.style.left = L + 'px';
this.style.top = T + 'px';
}
});

el.addEventListener('mouseup', function (e) {
isDown = false;
});

},
// 更新值
update(el, { value: isCanDrag }) {
el._isCanDrag = isCanDrag;
}
});

let app = new Vue({
el: '#app',
data: {
title: '指令',
isCanDrag: false
}
});

</script>

</body>

</html>