跳到主要内容

过滤器,按键修饰符,自定义指令

一、品牌管理案例

如下图,

1、实现输入id和name后,点击add按钮,添加到table中;

2、点击数据的del,可以删除这条数据。

代码:

<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="./lib/vue-2.4.0.js"></script>
<style>
* {
padding: 0;
margin: 0;
position: relative;
}

/* 实现任意无宽高盒子居中显示 */
#app {
position: absolute;
left: 50%;
top: 100px;
transform: translateX(-50%);
}

.box {
width: 500px;
height: 40px;
background-color: #ccc;
display: inline-block;
text-align: center;
line-height: 40px;
border: 1px solid #aaa;
box-sizing: border-box;
border-bottom: none;
}

.tb {
width: 500px;
text-align: center;
border-collapse: collapse;
border-color: #ccc;
}
</style>
</head>

<body>
<div id="app">
<div class="box">
<label for="id">
ID:
<input type="text" id="id" v-model="id">
</label>
<label for="name">
name:
<input type="text" id="name" v-model="name">
</label>
<input type="button" value="add" @click="addClick">
</div>

<table border="1" cellspacing="0" class="tb">
<tr v-for="item in list">
<td>{{item.id}}</td>
<td>{{item.name}}</td>
<td>{{item.ctime}}</td>
<td>
<!-- 绑定的事件是可以传参数的,这里传入需要删除的对象id -->
<a href="javascript:;" @click.prevent="delClick(item.id)">del</a>
</td>
</tr>
</table>
</div>


<script>
var vm = new Vue({
el: "#app",
data: {
id: "",
name: "",
list: [{
id: 1,
name: 'tony',
ctime: new Date()
},
{
id: 2,
name: 'stark',
ctime: new Date()
}
]
},
methods: {
addClick() {
// 1.获取表单数据
// 2.组织出一个对象
// 3.将对象添加到data中(不需要重新渲染页面)
let item = {
id: this.id,
name: this.name,
ctime: new Date()
};
if ((this.id != "") && (this.name != "")) {
this.list.push(item);
}
// 4.最后将表单清空
this.id = this.name = "";
},
delClick(id) {
// 1.根据id找到索引(循环查找)
// 2.调用数组的 splice方法删除
this.list.filter((item, index) => {
if (item.id == id) {
this.list.splice(index, 1);
return true;
}
});

}
}

});
</script>
</body>
</html>

注意:

1、事件名后面可以加括号(@click="addClick" 等价于 @click="addClick() ,这样写的话,就可以传参。)

1、增加搜索需求

在Query中输入字符串,如果name项中包括Query中的字符串,则显示。

分析:

如果要满足条件才显示相关行,那么 <tr v-for="item in list"> 中的list就是一个可变的。这里我们使用一个函数,函数里面进行判断是否包含Query中的字符串,然后将包含的拷贝到新数组中,填充到list的位置:

<tr v-for="item in search(keywords)">

methods: {
addClick() {
//...
},
delClick(id) {
//...
},
// 添加的用于判断的函数
search(keywords) {
// 定义新数组保存符合条件的项
let newList = [];
this.list.forEach((item, index) => {
// 包含则返回true
if (item.name.includes(keywords)) {
newList.push(item);
}
});
return newList;
}
}

二、过滤器

vue 允许自定义过滤器,可被用作一些常见的文本格式化。

过滤器只能用在两个地方:插值表达式v-bind表达式

1、全局过滤器

基本用法:(必须在new Vue之前定义)

<body>
<div id="box">
<p>{{msg | msgFormat}}</p>
</div>

<script>
// 使用Vue.filter来定义一个过滤器:
// 第一个参数:过滤器函数名称
// 第二个参数:过滤器函数体
// 过滤器的参数就是管道符的前面待处理的字符串。
Vue.filter('msgFormat', (data) => {
return data.replace("vue", "Daotin");
});

var vm = new Vue({
el: " #box ",
data: {
msg: "hello vue"
}
});
</script>
</body>

1、使用 Vue.filter 来定义一个过滤器。

2、必须在new Vue之前定义。

2、第一个参数:过滤器函数名称;第二个参数:过滤器函数体

3、过滤器的第一个参数就是管道符的前面待处理的字符串

插值表达式里的过滤器函数可以带参数:

相应的,在 Vue.filter('msgFormat', (data, arg1,arg2,...) 中 msgFormat 的参数从第二位开始放置。

可以带多个参数。

<body>
<div id="box">
<!--hello Daotin is a boy and good-->
<p>{{msg | msgFormat('is a boy', 'and good')}}</p>
</div>

<script>
// 使用Vue.filter来定义一个过滤器:
// 第一个参数:过滤器函数名称
// 第二个参数:过滤器函数体
// 过滤器的参数就是管道符的前面待处理的字符串。
Vue.filter('msgFormat', (data, arg1, arg2='and bad') => {
return data.replace("vue", "Daotin " + arg1 + arg2);
});

var vm = new Vue({
el: " #box ",
data: {
msg: "hello vue"
},
methods: {}
});
</script>
</body>

使用全局过滤器格式化品牌管理案例的 ctime:

<td>{{item.ctime | ctimeFormat}}</td>
...

// ctime 过滤器
Vue.filter('ctimeFormat', (data) => {
let time = new Date(data);

let year = time.getFullYear();
let month = time.getMonth() + 1;
let day = time.getDate();
let hour = time.getHours();
let minute = time.getMinutes();

return `${year}-${month}-${day} ${hour}:${minute}`;
});

2、局部过滤器

局部过滤器只能在当前组件使用。

注意:过滤器的调用原则是就近原则,即先调用局部过滤器再调用全局过滤器。

padStart和padEnd

// ctime 过滤器
Vue.filter('ctimeFormat', (data) => {
let time = new Date(data);

let year = time.getFullYear();
let month = (time.getMonth() + 1).toString().padStart(2, '0');
let day = (time.getDate()).toString().padStart(2, '0');
let hour = (time.getHours()).toString().padStart(2, '0');
let minute = (time.getMinutes()).toString().padStart(2, '0');
let second = (time.getSeconds()).toString().padStart(2, '0');

return `${year}-${month}-${day} ${hour}:${minute}:${second}`;
});

使用ES6中的字符串新方法 String.prototype.padStart(maxLength, fillString='')String.prototype.padEnd(maxLength, fillString='')来填充字符串;

padStart:从开头填充

padEnd:从结尾填充

maxLength:填充后最大的长度

fillString:需要填充的字符串(fillString='',不填则以空字符填充)

三、按键修饰符

我们现在有个需求就是输入ID和name后不点击add按钮,而是按下回车键也需要添加到列表中。

我们可以在name输入框中加入按键抬起事件,并且指定是enter键抬起时才触发。

<label for="name">
name:
<input type="text" id="name" v-model="name" @keyup.enter="addClick">

.enter : 就是按键修饰符。

系统提供的按键修饰符有:

  • .enter
  • .tab
  • .delete (捕获“删除”和“退格”键)
  • .esc
  • .space
  • .up
  • .down
  • .left
  • .right

如果我们想自定义其他的按键怎么办呢?

通过Vue.config.keyCodes.f2 = 113; ;可以将f2作为按键修饰符添加到系统按键修饰符中。

具体每个按键的值是多少?下面是常见的按键的码。

keyCode 8 = BackSpace BackSpace
keyCode 9 = Tab Tab
keyCode 12 = Clear
keyCode 13 = Enter
keyCode 16 = Shift_L
keyCode 17 = Control_L
keyCode 18 = Alt_L
keyCode 19 = Pause
keyCode 20 = Caps_Lock
keyCode 27 = Escape Escape
keyCode 32 = space
keyCode 33 = Prior
keyCode 34 = Next
keyCode 35 = End
keyCode 36 = Home
keyCode 37 = Left
keyCode 38 = Up
keyCode 39 = Right
keyCode 40 = Down
keyCode 41 = Select
keyCode 42 = Print
keyCode 43 = Execute
keyCode 45 = Insert
keyCode 46 = Delete
keyCode 47 = Help
keyCode 48 = 0 equal braceright
keyCode 49 = 1 exclam onesuperior
keyCode 50 = 2 quotedbl twosuperior
keyCode 51 = 3 section threesuperior
keyCode 52 = 4 dollar
keyCode 53 = 5 percent
keyCode 54 = 6 ampersand
keyCode 55 = 7 slash braceleft
keyCode 56 = 8 parenleft bracketleft
keyCode 57 = 9 parenright bracketright
keyCode 65 = a A
keyCode 66 = b B
keyCode 67 = c C
keyCode 68 = d D
keyCode 69 = e E EuroSign
keyCode 70 = f F
keyCode 71 = g G
keyCode 72 = h H
keyCode 73 = i I
keyCode 74 = j J
keyCode 75 = k K
keyCode 76 = l L
keyCode 77 = m M mu
keyCode 78 = n N
keyCode 79 = o O
keyCode 80 = p P
keyCode 81 = q Q at
keyCode 82 = r R
keyCode 83 = s S
keyCode 84 = t T
keyCode 85 = u U
keyCode 86 = v V
keyCode 87 = w W
keyCode 88 = x X
keyCode 89 = y Y
keyCode 90 = z Z
keyCode 96 = KP_0 KP_0
keyCode 97 = KP_1 KP_1
keyCode 98 = KP_2 KP_2
keyCode 99 = KP_3 KP_3
keyCode 100 = KP_4 KP_4
keyCode 101 = KP_5 KP_5
keyCode 102 = KP_6 KP_6
keyCode 103 = KP_7 KP_7
keyCode 104 = KP_8 KP_8
keyCode 105 = KP_9 KP_9
keyCode 106 = KP_Multiply KP_Multiply
keyCode 107 = KP_Add KP_Add
keyCode 108 = KP_Separator KP_Separator
keyCode 109 = KP_Subtract KP_Subtract
keyCode 110 = KP_Decimal KP_Decimal
keyCode 111 = KP_Divide KP_Divide
keyCode 112 = F1
keyCode 113 = F2
keyCode 114 = F3
keyCode 115 = F4
keyCode 116 = F5
keyCode 117 = F6
keyCode 118 = F7
keyCode 119 = F8
keyCode 120 = F9
keyCode 121 = F10
keyCode 122 = F11
keyCode 123 = F12
keyCode 124 = F13
keyCode 125 = F14
keyCode 126 = F15
keyCode 127 = F16
keyCode 128 = F17
keyCode 129 = F18
keyCode 130 = F19
keyCode 131 = F20
keyCode 132 = F21
keyCode 133 = F22
keyCode 134 = F23
keyCode 135 = F24

示例:

<input type="text" id="name" v-model="name" @keyup.f2="addClick">

//...

<script>
Vue.config.keyCodes.f2 = 113;
</script>

四、自定义指令

除了核心功能默认内置的指令 (v-model 等),Vue 也允许注册自定义指令。自定义指令是以 v-开头的指令。

比如我们想让品牌管理案例中,在刚进入页面的时候,就获取 Query输入框的焦点,但是vue并没有提供这样的指令。

之前我们可以通过获取DOM元素,然后使用 DOM元素.focus(); 方法来获取焦点。但是在vue里面不推荐获取DOM元素的方式。这时我们可以使用自定义指令的方式来达到获取焦点。

比如:我们想定义一个v-focus 的指令来获取焦点。

<label for="sel">
Query:
<input type="text" id="sel" v-model="keywords" v-focus>
</label>

<script>
// 自定义全局指令 v-focus,为绑定的元素自动获取焦点:
Vue.directive('focus', {
inserted: function (el) { // inserted 表示被绑定元素插入父节点时调用
el.focus();
}
});
</script>

1、定义自定义指令的时候,不需要加v- ,使用的时候需要加。

2、注意: 在每个 函数中,第一个参数,永远是 el ,表示 被绑定了指令的那个元素,这个 el 参数,是一个原生的JS对象。

3、和JS行为有关的操作,最好在 inserted 中去执行

如果想注册局部指令,组件中也接受一个 directives 的选项:

directives: {
focus: {
// 指令的定义
inserted: function (el) {
el.focus()
}
}
}

1、钩子函数

inserted 是钩子函数,类似的钩子函数还有几个,但是应用场景不同:

  • inserted :被绑定元素插入DOM元素时调用一次(从内存渲染到页面时如果绑定了 inserted 就执行)。
  • bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置(在代码加载到内存中的时候,如果绑定了bind就执行,比如样式style的设定,使用bind绑定)。
  • update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。
  • componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
  • unbind:只调用一次,指令与元素解绑时调用。

(added in 20210105)

bind和inserted区别:

共同点: dom插入都会调用

不同点:bind在inserted之前调用,bind 时元素的父节点为 null,inserted 时元素的父节点存在。

bind: function (el) {
console.log(el.parentNode) // null
},

inserted: function (el) {
console.log(el.parentNode) // <div id="app">...</div>
}

update和componentUpdated区别:

共同点: 组件更新都会调用

不同点:update在组件更新前调用,componentUpdated在组件更新后调用。

update: function (el) {
console.log(el.innerHTML) // Hello, before updated
},
componentUpdated: function (el) {
console.log(el.innerHTML) // Hi, after updated
}

2、钩子函数参数

指令钩子函数会被传入以下参数:

  • el:指令所绑定的元素,可以用来直接操作 DOM 。
  • binding:一个对象,包含以下属性:
    • name:指令名,不包括 v- 前缀。
    • value:指令的绑定值,例如:v-my-directive="10" 中,绑定值为 10
    • expression:字符串形式的指令表达式。例如 v-my-directive="1 + 1" 中,表达式为 "1 + 1"
    • arg:传给指令的参数,可选。例如 v-my-directive:foo 中,参数为 "foo"
    • modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }
  • vnode:Vue 编译生成的虚拟节点。
  • oldVnode:上一个虚拟节点,仅在 updatecomponentUpdated 钩子中可用。

注意:除了 el 之外,其它参数都应该是只读的,切勿进行修改。

我们可以为自定义指令赋值,比如我们想给Query文本框的字体颜色赋值:

<label for="sel">
Query:
<input type="text" id="sel" v-model="keywords" v-focus v-color="'red'">
</label>

之所以使用v-color="'red'",而不是v-color="red",是因为如果是red字符串的话,就当做变量在vue的data中查找,而这里要表达的是字符串red。

自定义v-color指令:

Vue.directive("color", {
bind(el, binding) {
el.style.color = binding.value;
}
});

(added in 20210105)

案例补充: 点击元素之外的地方执行相应的方法

一般我们在开发项目的时候都会遇到这样的场景,当点击一个按钮的时候,弹出下拉菜单,再次点击的时候,下拉菜单收起。但是我们有时候不想再次点击这个按钮,而是点击屏幕其他地方,也需要这个下拉菜单收起。

常用的做法就是给整个页面绑定一个click事件,当点击页面的时候收起下拉菜单。但是如果有很多页面都需要这样做的话,就比较麻烦,今天我们做一个vue指令,绑定到该下拉菜单上,实现点击屏幕其他地方,收起下拉菜单。

<div v-if="showproducts" v-clickoutside="closeProducts"></div>

// 执行方法
closeProducts() {
this.showproducts = false;
}

假设有这样一个div,点击屏幕其他位置的时候,showproducts=false,关闭弹框。Vue指令如何编写呢?

在点击的时候,给document绑定一个click事件,事件的处理函数可以获取鼠标点击的元素e.target,

判断鼠标点击的位置是否在绑定的元素中,是的话retuen false;否则的话执行传入的函数。

这里的:

  • binding.expression:表示v-clickoutside 后面是否传入了表达式,我们传入了closeProducts字符串。

  • binding.value:表示v-clickoutside 绑定的具体的值。 binding.value(); 就相当于执行closeProducts()

由于给document绑定了click事件,在showproducts==false,也就是指令解绑的时候,解绑click事件,el._vueClickOutside_ 用来传递同一个click事件对应的函数。

// 点击绑定了元素之外的地方,执行相应的方法
Vue.directive('clickoutside', {
bind: function (el, binding) {
function domHandler(e) {
if(el.contains(e.target)) {
return false;
}
if (binding.expression) {
binding.value(); // 执行函数
}
}
el._vueClickOutside_ = domHandler;
document.addEventListener('click', domHandler);
},
unbind: function (el) {
document.removeEventListener('click', el._vueClickOutside_);
delete el._vueClickOutside_;
}
});

3、动态指令参数

指令的参数可以是动态的。例如,在 v-mydirective:[argument]="value" 中,argument 参数可以根据组件实例数据进行更新!这使得自定义指令可以在应用中被灵活使用。

例如你想要创建一个自定义指令,用来通过固定布局将元素固定在页面上。我们可以像这样创建一个通过指令值来更新竖直位置像素值的自定义指令:

<div id="baseexample">
<p>Scroll down the page</p>
<p v-pin="200">Stick me 200px from the top of the page</p>
</div>

<script>
Vue.directive('pin', {
bind: function (el, binding, vnode) {
el.style.position = 'fixed'
el.style.top = binding.value + 'px'
}
})

new Vue({
el: '#baseexample'
});
</script>

这会把该元素固定在距离页面顶部 200 像素的位置。但如果场景是我们需要把元素固定在左侧而不是顶部又该怎么办呢?这时使用动态参数就可以非常方便地根据每个组件实例来进行更新。

<div id="dynamicexample">
<h3>Scroll down inside this section ↓</h3>
<p v-pin:[direction]="200">I am pinned onto the page at 200px to the left.</p>
</div>

<script>
Vue.directive('pin', {
bind: function (el, binding, vnode) {
el.style.position = 'fixed'
var s = (binding.arg == 'left' ? 'left' : 'top')
el.style[s] = binding.value + 'px'
}
})

new Vue({
el: '#dynamicexample',
data: function () {
return {
direction: 'left'
}
}
})</script>

4、函数简写

我们之前都是在自定义指令名称的后面跟上一个对象,里面写上 bind,inserted,update的钩子函数列表。

现在简写的话,就不需要跟上一个对象了:

directives: {
"color": (el, binding) => {
el.style.color = binding.value;
}
}

自定义名称后可以直接跟上一个匿名函数,这个匿名函数就相当于bind和update的钩子函数集合。所以,如果你只需要在bind或者update中进行元素的设置的话,就可以用这种简写的方式。

当然如果是inserted的话,还是要写回对象的方式。

5、对象字面量

如果指令需要多个值,可以传入一个 JavaScript 对象字面量。记住,指令函数能够接受所有合法的 JavaScript 表达式。

<div v-demo="{ color: 'white', text: 'hello!' }"></div>

<script>
Vue.directive('demo', function (el, binding) {
console.log(binding.value.color) // => "white"
console.log(binding.value.text) // => "hello!"
})
</script>