内容输出
大胡子语句
通过 大胡子语句
我们可以很方便的中模板中输出数据,但是这种方式会有一个问题,当页面加载渲染比较慢的时候,页面中会出现 大胡子语句
,vue
提供了几个指令来解决这个问题
指令中的表达式不需要使用
大胡子语句
v-text
<p v-text="title"></p>
弊端:
v-text
会填充整个innerHTML
v-cloak
<p v-cloak>{{title}}</p>
需要配合 css 进行处理
<style>
[v-cloak] {
display: none;
}
</style>
v-html
为了防止 xss
攻击,默认情况下输出是不会作为 html
解析的,通过 v-html
可以让内容作为 html
进行解析
v-once
只渲染元素和组件一次,后期的更新不再渲染
v-pre
忽略这个元素和它子元素内容的编译
<!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>
逻辑处理
v-show与v-if
v-show
根据表达式的值(布尔值),切换元素的显示与隐藏(display 属性)
适用于状态切换比较频繁的情况
v-if
根据表达式的值(布尔值),创建或销毁元素
适用于状态切换不频繁的情况
当逻辑表达式为false
逻辑表达式为true时
v-else / v-else-if
与 v-else
配合
循环与列表
v-for
根据数据循环渲染 v-for
指令所在的元素及其子元素
可以循环的数据:Array | Object | number | string | Iterable (2.6 新增)
<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 中两者没有什么区别
let app = new Vue({
data: {
users: [
{ id: 1, username: 'zmouse' },
{ id: 2, username: 'mt' },
{ id: 3, username: 'hai' }
]
},
});
app.$mount('#app');
<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>
:key
默认情况下,在渲染 DOM
过程中使用 原地复用 ,这样一般情况下会比较高效,但是对于循环列表,特别是依赖某种状态的列表,会有一些问题,我们可以通过 :key
属性,来给每个循环节点添加一个标识。
虚拟dom为了节约性能,因此只会变化需要变化的地方。也就是说如果数据没有发生变化,那么DOM不会被重新渲染。
<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>
,这里的参数就是指定的属性名称
<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>
缩写
有的一些常用指令会有对应的缩写,v-bind
对应的缩写为::
<p :id='id'></p>
样式
针对样式属性,v-bind
值有一些特殊的写法
style
原生普通写法
<div style="width: 100px; height: 100px; background: red"></div>
v-bind 写法
<div :style="'width: 100px; height: 100px; background: red'"></div>
对象写法
<div :style="style1"></div>
...
<script>
new Vue({
el: '#app',
data: {
style1: {
width: '100px',
height: '100px',
background: 'green'
}
}
});
</script>
数组写法
<div :style="[style1, style2]"></div>
...
<script>
new Vue({
el: '#app',
data: {
style1: {
width: '100px',
height: '100px',
background: 'green'
}
},
style2: {
border: '1px solid black'
}
});
</script>
class
原生普通写法
<div class="box1 box2"></div>
v-bind 写法
<div :class="'box1 box2'"></div>
数组写法
<div :class="['box1', 'box2']"></div>
对象写法
<p :class="{'test1':flag}">xiaokang.me</p>
<script>
let app = new Vue({
data: {
flag: true,
}
});
app.$mount('#app');
</script>
使用对象写法,可以根据值(boolean)动态添加对应的 class,如上述代码,会根据变量flag
的值动态判断是否添加这个class。
双向数据流
<!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
更新时,也会更新试图。更新试图时,数据也会更新。
-
对于
text
和textarea
双向数据绑定默认绑定的是value属性
-
checkbox
和box
绑定的是checked属性
-
select
单选绑定到值,多选绑定到数组
指令修饰符
-
lazy
取代input监听,
change
事件。<input type="text" v-model.lazy='v1'>
-
number
将输入的字符串转为有效数字
-
trim
输入首尾空格过滤
自定义指令
自定义指令在VUE内部提供了两种注册方式:全局指令和局部指令。
全局指令即全局部分注册即可。
Vue.directive('指令名称', {指令配置});
局部指令注册即在某个对象内注册,指令的生效范围为当前对象的。例如在创建Vue对象内注册的指令,可以在这个对象的内使用。
new Vue({
el: '#app',
directives: {
'指令名称': {指令配置}
}
});
注册命令时不需要使用
v-
前缀,但使用时需要使用v-
前缀
指令生命周期(钩子函数)
指令的运行方式很简单,它提供了一组指令生命周期钩子函数,我们只需要在不同的生命周期钩子函数中进行逻辑处理就可以了
bind
: 只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置inserted
: 被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)update
: 所在组件更新的时候调用componentUpdated
: 所在组件更新完成后调用unbind
: 只调用一次,指令与元素解绑时调用
不同的生命周期钩子函数在调用的时候同时会接收到传入的一些不同的参数
el
: 指令所绑定的元素,可以用来直接操作 DOMbinding
: 一个对象,包含以下属性:name
: 指令名,不包括v-
前缀value
: 指令的绑定值(作为表达式解析后的结果)expression
: 指令绑定的表达式(字符串)arg
: 传给指令的参数,可选modifiers
: 传给指令的修饰符组成的对象,可选,每个修饰符对应一个布尔值oldValue
: 指令绑定的前一个值,仅在update
和componentUpdated
钩子中可用,无论值是否改变都可用
示例:点击按钮获取焦点
<!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
属性控制是否获取焦点。
案例:拖拽案例
<!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>