Vue.js
文章目录
指令
表达式的值除了可以出现内容中,也可以使用在其它位置,比如:属性。但是不能使用 {{}}
语法,而是需要 指令
在 vue
中,指令是一个带有 v-
前缀的属性,与普通属性不一样的地方在于,指令的值是引号括起来的 表达式
,不同的指令有不同的作用,vue
内置了一些常用的指令,后期我们还可以自定义属于自己的指令
- 内容输出
- 循环
- 逻辑
- 属性绑定
- 事件
- 其它
内容输出
通过 {{}}
我们可以很方便的中模板中输出数据,但是这种方式会有一个问题,当页面加载渲染比较慢的时候,页面中会出现 {{}}
,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
忽略这个元素和它子元素内容的编译
逻辑处理
v-show
根据表达式的值(布尔值),切换元素的显示与隐藏(display 属性)
适用于状态切换比较频繁的情况
v-if
根据表达式的值(布尔值),创建或销毁元素
适用于状态切换不频繁的情况
v-else / v-else-if
与 v-else
配合
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
[v-cloak] {
display: none;
}
</style>
</head>
<body>
<div id="app">
<h1>这是vue</h1>
<hr />
<div>n: {{n}}</div>
<hr>
<div v-text="'n: ' + n"></div>
<hr>
<p v-cloak>n: {{n}}</p>
<hr>
<p v-show="isShow">内容</p>
<hr>
<p v-if="isShow">内容</p>
</div>
<script src="js/vue.js"></script>
<script>
let app = new Vue({
data: {
n: 1,
isShow: false
}
});
app.$mount('#app');
</script>
</body>
</html>
循环与列表
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 中两者没有什么区别
:key
默认情况下,在渲染 DOM
过程中使用 原地复用 ,这样一般情况下会比较高效,但是对于循环列表,特别是依赖某种状态的列表,会有一些问题,我们可以通过 :key
属性,来给每个循环节点添加一个标识
[注意:key是作为绑定数据的唯一标识,用数组下标的话会有问题,因为数组中的某一条数据经过排序更换位置后,下标也会随着改变]
[对于以下代码(没有:key时),原地复用表现在,排序之后,只会在{{user.name}}部分更改值,而li和input并没有发生变化,更直观地说,checkbox勾选的内容也不会随着排序而变化,而是留在原地,不符合常规需求]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
[v-cloak] {
display: none;
}
</style>
</head>
<body>
<div id="app">
<h1>这是vue</h1>
<hr />
<ul>
<li v-for="user of users" :key="user.name">
<input type="checkbox" />
{{user.name}}
</li>
</ul>
</div>
<button>按钮</button>
<script src="js/vue.js"></script>
<script>
let app = new Vue({
data: {
users: [
{id: 1, name: 'mt'},
{id: 2, name: 'haizi'},
{id: 3, name: 'zmouse'}
]
}
});
app.$mount('#app');
let button = document.querySelector('button');
button.onclick = function() {
app.users.sort( _ => {
return Math.random() - .5;
} );
}
</script>
</body>
</html>
属性绑定
v-bind
绑定数据(表达式)到指定的属性上,<div v-bind:参数="值/表达式"></div>
,这里的参数就是指定的属性名称
<div id="app">
<div v-bind:id="'box1'"></div>
<div v-bind:id="myId"></div>
</div>
<script>
new Vue({
el: '#app',
data: {
myId: 'kaikeba'
}
})
</script>
缩写
有的一些常用指令会有对应的缩写,v-bind
对应的缩写为::
<div :id="myId"></div>
样式
针对样式属性,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>
对象写法
[值:键,这里’box1’是值,要加单引号;isActive是键,对应true或者false的值]
<div :class="{'box1': isActive, 'box2': isChecked}"></div>
使用对象写法,可以根据值(boolean)动态添加对应的 class
单向数据流
通过上面的知识点和案例,我们可以看到,当数据更新的时候,页面视图就会更新,但是页面视图中绑定的元素更新的时候,对应的数据是不会更新的
<input type="text" :value="title" />
我们称为:单向数据流 数据 -> 视图
在 vue 中,还有一种双向数据流绑定的方式
<body>
<div id="app">
<h1>{{n1}}</h1>
<input type="text" v-bind:value="n1" />
</div>
<script src="js/vue.js"></script>
<script>
/**
* 通过 {{}}、v-bind等 语法,可以把实例中的数据显示在视图中
* 当实例中的数据发生变化的时候,还会自动去更新视图(数据流,数据->视图)
* 视图发生了变化,数据不会变
* {{}}、v-bind等都是单向绑定
*/
let app = new Vue({
el: '#app',
data: {
n1: 1
}
});
//视图重新渲染的原生方法
// let input = document.querySelector('input');
// input.oninput = function() {
// app.n1 = this.value;
// }
</script>
</body>
v-model
<input type="text" v-model="title" />
数据 title
更新,视图中 input
的 value
就会更新。同时,当 input 中的 value
更新的时候,数据 title
也会更新,这就是我们说的 数据双向绑定 [与 React 中的受控组件类似]
<body>
<div id="app">
<h1>{{n1}}</h1>
<input type="text" v-model="n1" />
</div>
<script src="js/vue.js"></script>
<script>
/**
* v-model
* 实现数据视图的双向绑定
* 不是所有元素都能够使用v-model,表单元素+自定义组件
*/
let app = new Vue({
el: '#app',
data: {
n1: 1
}
});
</script>
</body>
表单
针对一般元素,比如 div、span、p、img 等,采用的是单向绑定:v-bind,只需要把数据绑定到视图中就可以,但是对于表单这种交互性比较强的元素或组件,我们一般可能需求双向绑定,即:用户对视图元素的操作同时更新数据
v-model 在内部为不同的输入元素使用不同的属性和事件来处理数据
text
和textarea
checkbox
和radio
select
text
和 textarea
text
和 textarea
元素使用 value
属性和 input
事件
[绑定value,监听input]
<div id="app">
<input type="text" v-model="v1" />
<textarea v-model="v2" cols="30" rows="10"></textarea>
</div>
let app = new Vue({
el: '#app',
data: {
v1: 'aaa',
v2: 'bbb'
}
});
checkbox
和 radio
checkbox
和 radio
使用 checked
属性和 change
事件
单选框绑定一个值
<div id="app">
<input type="radio" v-model="v3" value="男" /> 男
<input type="radio" v-model="v3" value="女" /> 女
</div>
let app = new Vue({
el: '#app',
data: {
v3: '女',
}
});
多选框绑定到一个布尔值或数组
<div id="app">
<input type="checkbox" v-model="v4" /> 同意
<hr/>
<input type="checkbox" v-model="v5" value="足球" /> 足球
<input type="checkbox" v-model="v5" value="音乐" /> 音乐
</div>
let app = new Vue({
el: '#app',
data: {
v4: true,
v5: ['足球', '音乐']
}
});
select
select
字段将 value
作为 prop
并将 change
作为事件
单选绑定到值,多选绑定到数组
<div id="app">
<select v-model="v3">
<option value="男">男</option>
<option value="女">女</option>
</select>
<select v-model="v5" multiple>
<option value="足球">足球</option>
<option value="音乐">音乐</option>
</select>
</div>
指令修饰符
一个指令可以包含的内容包括:
- 指令名称
- 指令值
- 指令参数
- 指令修饰符
<组件 指令:参数.修饰符1.修饰符2="值" />
<body>
<div id="app">
<h1>{{n1 + 10}}</h1>
<input type="text" v-model.lazy.number="n1" />
</div>
<script src="js/vue.js"></script>
<script>
/**
* 指令名称
* 指令值
* 指令修饰符
* 指令参数
*
* v-bind:id="myId"
*
* 不同的指令有不同的参数,修饰符
*/
let app = new Vue({
el: '#app',
data: {
n1: 1
}
});
</script>
</body>
以下是v-model的修饰符
.lazy
取代 input
监听 change
事件
.number
输入字符串转为有效的数字
.trim
输入首尾空格过滤
自定义指令
我们还可以通过 Vue
提供的方法来自定义指令
[用于autofocus这种无法通过数据改变来改变的属性,只限于有无,而不是数据]
注册指令
vue
提供了两种指令注册方式
- 全局指令
- 局部指令
全局指令
Vue.directive('指令名称', {指令配置});
[使用的时候用用v-指令名称,声明的时候不用写v-]
局部指令
new Vue({
el: '#app',
directives: {
'指令名称': {指令配置}
}
});
在使用指令的时候,需要使用
v-指令名称
的方式来调用
指令生命周期(钩子函数)
指令的运行方式很简单,它提供了一组指令生命周期钩子函数,我们只需要在不同的生命周期钩子函数中进行逻辑处理就可以了
-
bind : 只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置
[不一定在DOM中,因为第一次解析文本时解析到v-指令的时候就调用bind()。用于初始化设置,特别是与DOM无关的初始化设置(例如获取宽高,获取id )] -
inserted : 被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)
[类似bind,但是可以操作DOM] -
update : 所在组件更新的时候调用
-
componentUpdated : 所在组件更新完成后调用
-
unbind : 只调用一次,指令与元素解绑时调用
不同的生命周期钩子函数在调用的时候同时会接收到传入的一些不同的参数
-
el : 指令所绑定的元素,可以用来直接操作 DOM
-
binding : 一个对象,包含以下属性:
- name : 指令名,不包括
v-
前缀 - value : 指令的绑定值(作为表达式解析后的结果)
- expression : 指令绑定的表达式(字符串)
- arg : 传给指令的参数,可选
- modifiers : 传给指令的修饰符组成的对象,可选,每个修饰符对应一个布尔值
- oldValue : 指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用,无论值是否改变都可用
- name : 指令名,不包括
案例
官网的例子
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
<input type="text" v-focus>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
Vue.directive('focus', {
inserted(el) {
el.focus();
}
});
let app = new Vue({
el: '#app'
});
</script>
</body>
</html>
[详细解释]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<input type="text" v-focus />
</div>
<script src="js/vue.js"></script>
<script>
Vue.directive('focus', {
/* {}里的配置类似回调函数:
success(){
},
fail(){
},beforeSend(){
},
...
多行注释:Alt+Shift+A
*/
bind(el, binding) {
console.log('bind');
// el.focus();不可以是因为这个时候DOM还没出来 而focus需要用到DOM
},
inserted(el, binding) {
el.focus();//因此只能在inserted里面用而不是bind
}
});
let app = new Vue({
el: '#app'
});
// let input = document.querySelector('input');
//
// setTimeout(() => {
// input.focus();
// }, 2000);
</script>
</body>
</html>
扩展:自定义拖拽指令
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
.box {
position: absolute;
left: 100px;
top: 100px;
width: 100px;
height: 100px;
background: red;
}
</style>
</head>
<body>
<div id="app">
<button @click="canDrag = !canDrag">Drag : {{canDrag}}</button>
<div class="box" v-drag.limit="canDrag"></div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
Vue.directive('drag', {
bind(el, {modifiers,value}) {
let isDragStart = false;
//dis计算鼠标到元素的差值
let disX = 0;
let disY = 0;
el.canDrag = value;
el.addEventListener('mousedown', e => {
if (!el.canDrag) return;
disX = e.clientX - el.offsetLeft;
disY = e.clientY - el.offsetTop;
isDragStart = true;
e.preventDefault();
});
document.addEventListener('mousemove', e => {
if (isDragStart) {
let x = e.clientX - disX;
let y = e.clientY - disY;
if (modifiers.limit) {
if (x < 0) {
x = 0;
}
if (y < 0) {
y = 0;
}
}
el.style.left = x + 'px';
el.style.top = y + 'px';
}
});
document.addEventListener('mouseup', e => {
isDragStart = false;
});
},
componentUpdated(el, {value}) {
console.log('componentUpdated', value);
el.canDrag = value;
}
});
let app = new Vue({
el: '#app',
data: {
canDrag: false
}
});
</script>
</body>
</html>