-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
2889d40
commit aff77ae
Showing
3 changed files
with
364 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
# 过滤器 | ||
|
||
在 vue2.0 之前,是有内置过滤器的,在2.0中已经没有内置的过滤器了,但我们可以自定义过滤器 | ||
|
||
Vue.js 允许你自定义过滤器,可被用于一些常见的文本格式化。过滤器可以用在两个地方:**双花括号插值和 `v-bind` 表达式** (后者从 2.1.0+ 开始支持)。过滤器应该被添加在 JavaScript 表达式的尾部,由“管道”符号指示: | ||
|
||
即过滤器是用来格式化数据的一个函数。**过滤器不会修改原始数据**,它的作用是过滤数据,就是**对数据进行加工处理并返回处理后的数据**,比如做一些数据格式上的修改,状态转换等。 | ||
|
||
`这里的this指向的是window,过滤器无法修改vue实例的数据` | ||
|
||
```javascript | ||
<!-- 在双花括号中 --> | ||
{{ message | 过滤器名称 }} | ||
| ||
<!-- 在 `v-bind` 中 --> | ||
<div v-bind:id="id | 过滤器名称"></div> | ||
|
||
<!-- 过滤器的使用 --> | ||
数据 | 过滤器的名字 | ||
数据 | 过滤器的名字(参数1,参数2,...) | ||
|
||
<img height="100" :src="item.logoData | base64" alt=""> | ||
|
||
``` | ||
|
||
## 局部过滤器 | ||
|
||
你可以在一个组件的选项中定义本地的过滤器: | ||
|
||
组件内 `filters:{ 过滤器名: fn }` fn 内通过 return 返回最终的数据 | ||
|
||
使用 `{{ 数据 | 过滤器名 }}` | ||
|
||
```javascript | ||
filters: { | ||
capitalize: function (value) { | ||
console.log(this) // 这里的this指向的是window,过滤器无法操作vue实例的数据 | ||
if (!value) return '' | ||
value = value.toString() | ||
return value.charAt(0).toUpperCase() + value.slice(1) | ||
} | ||
} | ||
|
||
在Vue配置对象中增加属性filters,值是一个对象; | ||
filters对象的属性名即是过滤器的名字,属性值是函数; | ||
过滤器函数返回的值即是过滤后要渲染的内容; | ||
过滤器函数接收的第一个参数即是要被过滤的数据 | ||
过滤器函数接收的第二个参数开始才是使用过滤器传递的参数 | ||
这里的this指向的是window,过滤器无法操作vue实例的数据 | ||
|
||
data () { | ||
return { | ||
msg: 'hello world' | ||
} | ||
}, | ||
//定义私用局部过滤器。只能在当前 vue 对象中使用 | ||
filters: { | ||
dataFormat: (msg, a) => { // msg表示要过滤的数据,a表示传入的参数 | ||
return msg + a; | ||
} | ||
} | ||
<p>{{ msg | dataFormat('!')}}</p> // 结果: hello world! | ||
``` | ||
|
||
## 全局过滤器 | ||
|
||
或者在创建 Vue 实例之前全局定义过滤器:所有组件共享 | ||
|
||
全局 `Vue.filter('过滤器名',fn)` fn 内通过 return 返回最终的数据 | ||
|
||
使用 `{{ 数据 | 过滤器名 }}` | ||
|
||
```javascript | ||
Vue.filter('capitalize', function (value) { | ||
if (!value) return '' | ||
value = value.toString() | ||
return value.charAt(0).toUpperCase() + value.slice(1) | ||
}) | ||
|
||
new Vue({ | ||
// ... | ||
}) | ||
|
||
自定义全局过滤器 | ||
Vue.filter('过滤器名称', function(val) { // val表示要被处理的数据 | ||
// 过滤器业务逻辑,要有返回值 | ||
}) | ||
<div>{{ msg | 过滤器名称 }}</div> | ||
<div v-bind="msg | 过滤器名称"></div> | ||
``` | ||
|
||
> 当全局过滤器和局部过滤器重名时,会采用局部过滤器。 | ||
## 过滤器参数 | ||
|
||
过滤器本质上是 JavaScript 函数,因此可以接收参数: | ||
|
||
过滤器函数总接收表达式的值 (之前的操作链的结果) 作为第一个参数 | ||
|
||
```javascript | ||
{{ message | capitalize }} | ||
capitalize 过滤器函数将会收到 message 的值作为第一个参数 | ||
{{ message | filterA('arg1', arg2) }} | ||
这里,filterA 被定义为接收三个参数的过滤器函数。其中 message 的值作为第一个参数,普通字符串 'arg1' 作为第二个参数,表达式 arg2 的值作为第三个参数。 | ||
|
||
过滤器函数接收的第一个参数即是要被过滤的数据 | ||
过滤器函数接收的第二个参数开始才是使用过滤器传递的参数 | ||
``` | ||
|
||
## 过滤器串联 | ||
|
||
过滤器可以串联 | ||
|
||
```javascript | ||
{{ message | filterA | filterB }} | ||
|
||
在这个例子中,filterA 被定义为接收单个参数的过滤器函数,表达式 message 的值将作为参数传入到函数中。然后继续调用同样被定义为接收单个参数的过滤器函数 filterB,将 filterA 的结果传递到 filterB 中。 | ||
``` | ||
|
||
## 过滤器顺序 | ||
|
||
> 当全局过滤器和局部过滤器名字重复的时候,会以就近原则进行调用,即:局部过滤器优先于全局过滤器被调用 | ||
> | ||
> 一个表达式可以使用多个过滤器,其执行顺序从左往右,前一个过滤器的结果作为后一个过滤器的被处理数据,所以要注意使用顺序 | ||
> | ||
> 全局注册时是 filter 没有 s , 而组件过滤器是 filters,是有 s 的,虽然写的时候没有 s 也不报错,但是过滤器是没有效果的。 | ||
## 使用建议 | ||
|
||
熟悉 vue 的童鞋会知道,过滤器有时候同methods、computed、watch一样可以达到处理数据的目的,但又不能替代它们,因为它不能改变原始值。如果一个过滤器的内部特别复杂,可以考虑把它写成一个计算属性,因为计算属性本身带有缓存,可复用性强,而过滤器一般用来做一些简单的操作。 | ||
|
||
在实际开发中,全局的过滤器要比局部过滤器使用的更广泛一些,说白了我们为什么要使用过滤器,其实就跟使用函数是一样,把一些方法封装,供其它组件使用,这样调用起来更方便也更快捷。 | ||
|
||
大家知道全局过滤器是在 main.js 中定义的,但万一项目过大,有多个过滤器,那 main.js 就一堆代码,为了项目模块化,最好是有专门的目录来统一存放这些过滤器,然后把处理函数给抽离出去,放在一个.js文件中,下边通过实例代码展示。 | ||
|
||
## 抽离全局过滤器 | ||
|
||
```javascript | ||
// lib/vue-filter.js 文件 | ||
|
||
export default { | ||
date(t){ | ||
const timer = new Date(t); | ||
return timer.getFullYear() + "-" + | ||
(timer.getMonth() + 1).toString().padStart(2, 0) | ||
}, | ||
currency(v,n=2,type="$"){ | ||
return type+v.toFixed(n); | ||
} | ||
} | ||
``` | ||
|
||
```javascript | ||
//vue.html 文件 | ||
import filters from "./lib/vue-filters.js"; | ||
for(let key in filters){ | ||
Vue.filter(key,filters[key]) | ||
} | ||
``` | ||
|
||
|
||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
# 计算属性 | ||
|
||
模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护。例如: | ||
|
||
```javascript | ||
<div id="example"> | ||
{{ message.split('').reverse().join('') }} | ||
</div> | ||
``` | ||
|
||
在这个地方,模板不再是简单的声明式逻辑。你必须看一段时间才能意识到,这里是想要显示变量 message 的翻转字符串。当你想要在模板中的多处包含此翻转字符串时,就会更加难以处理。所以,对于任何复杂逻辑,你都应当使用计算属性。 | ||
|
||
```javascript | ||
* 相当于React 当中的 useMemo 方法。 | ||
* 计算属性的本质是一个函数,但是在模板中要作为(函数的结果)属性来使用。 | ||
* 可以使用实例中的属性进行复杂的逻辑运算,并将结果进行缓存。 | ||
* 如果影响结果的实例属性发生改变,那么计算属性函数会重新计算结果,如果未改变会从缓存中提取结果。 | ||
* 在什么时候用:当逻辑复杂,结果受多个数据状态影响且需要缓存时使用。 | ||
* 计算属性的值会被缓存,只有实例中相关依赖值改变时,才重新计算,性能好但不适合做异步请求。 | ||
* 计算属性是声明式的描述一个值依赖了其他值,依赖的值改变后重新计算结果更新DOM。属性监听的是定义的变量,当定义的值发生变化时,执行相对应的函数。 | ||
``` | ||
|
||
## 基本使用 | ||
|
||
使用插值表达式 | ||
|
||
```javascript | ||
<div id="example"> | ||
{{ message.split('').reverse().join('') }} | ||
</div> | ||
``` | ||
|
||
使用计算属性 | ||
|
||
```javascript | ||
<div id="example"> | ||
<p>Original message: "{{ message }}"</p> | ||
<p>Computed reversed message: "{{ reversedMessage }}"</p> | ||
</div> | ||
``` | ||
|
||
```javascript | ||
var vm = new Vue({ | ||
el: '#example', | ||
data: { | ||
message: 'Hello' | ||
}, | ||
computed: { | ||
// 计算属性的 getter | ||
reversedMessage: function () { | ||
// `this` 指向 vm 实例 | ||
return this.message.split('').reverse().join('') | ||
} | ||
} | ||
}) | ||
|
||
这里我们声明了一个计算属性 reversedMessage。我们提供的函数将用作 property vm.reversedMessage 的 getter 函数: | ||
console.log(vm.reversedMessage) // => 'olleH' | ||
vm.message = 'Goodbye' | ||
console.log(vm.reversedMessage) // => 'eybdooG' | ||
``` | ||
|
||
你可以打开浏览器的控制台,自行修改例子中的 vm。`vm.reversedMessage` 的值始终取决于 `vm.message` 的值。 | ||
|
||
你可以像绑定普通 property 一样在模板中绑定计算属性。Vue 知道 `vm.reversedMessage` 依赖于 `vm.message`,因此当 `vm.message` 发生改变时,所有依赖 `vm.reversedMessage` 的绑定也会更新。而且最妙的是我们已经以声明的方式创建了这种依赖关系:计算属性的 getter 函数是没有副作用 (side effect) 的,这使它更易于测试和理解。 | ||
|
||
## 计算属性缓存 vs 方法 | ||
|
||
你可能已经注意到我们可以通过在表达式中调用方法来达到同样的效果 | ||
|
||
```javascript | ||
<p>Reversed message: "{{ reversedMessage() }}"</p> | ||
|
||
// 在组件中 | ||
methods: { | ||
reversedMessage: function () { | ||
// 如果函数设置为箭头函数,那么this指向的是window,这里指向的是vm实例 | ||
return this.message.split('').reverse().join('') | ||
} | ||
} | ||
|
||
``` | ||
|
||
我们可以将同一函数定义为一个方法而不是一个计算属性。两种方式的最终结果确实是完全相同的。然而,不同的是**计算属性是基于它们的响应式依赖进行缓存的**。只在相关响应式依赖发生改变时它们才会重新求值。这就意味着只要 `message` 还没有发生改变,多次访问 `reversedMessage` 计算属性会立即返回之前的计算结果,而不必再次执行函数。 | ||
|
||
这也同样意味着下面的计算属性将不再更新,因为 Date.now() 不是响应式依赖: | ||
|
||
```javascript | ||
computed: { | ||
now: function () { | ||
return Date.now() | ||
} | ||
} | ||
``` | ||
|
||
相比之下,每当触发重新渲染时,调用方法将**总会**再次执行函数。 | ||
|
||
我们为什么需要缓存?假设我们有一个性能开销比较大的计算属性 **A**,它需要遍历一个巨大的数组并做大量的计算。然后我们可能有其他的计算属性依赖于 **A**。如果没有缓存,我们将不可避免的多次执行 **A** 的 getter!如果你不希望有缓存,请用方法来替代。 | ||
|
||
## 计算属性 vs 侦听属性 | ||
|
||
Vue 提供了一种更通用的方式来观察和响应 Vue 实例上的数据变动:**侦听属性**。当你有一些数据需要随着其它数据变动而变动时,你很容易滥用 `watch`——特别是如果你之前使用过 AngularJS。然而,通常更好的做法是使用计算属性而不是命令式的 `watch` 回调。细想一下这个例子: | ||
|
||
```javascript | ||
<div id="demo">{{ fullName }}</div> | ||
|
||
var vm = new Vue({ | ||
el: '#demo', | ||
data: { | ||
firstName: 'Foo', | ||
lastName: 'Bar', | ||
fullName: 'Foo Bar' | ||
}, | ||
watch: { | ||
firstName: function (val) { | ||
this.fullName = val + ' ' + this.lastName | ||
}, | ||
lastName: function (val) { | ||
this.fullName = this.firstName + ' ' + val | ||
} | ||
} | ||
}) | ||
|
||
``` | ||
|
||
上面代码是命令式且重复的。将它与计算属性的版本进行比较: | ||
|
||
```javascript | ||
var vm = new Vue({ | ||
el: '#demo', | ||
data: { | ||
firstName: 'Foo', | ||
lastName: 'Bar' | ||
}, | ||
computed: { | ||
fullName: function () { | ||
return this.firstName + ' ' + this.lastName | ||
} | ||
} | ||
}) | ||
``` | ||
|
||
|
||
|
||
## 计算属性的 setter | ||
|
||
计算属性默认只有 getter(读取计算属性),不过在需要时(修改计算属性)你也可以提供一个 setter: | ||
|
||
```javascript | ||
// ... | ||
computed: { | ||
fullName: { | ||
// getter | ||
get: function () { | ||
return this.firstName + ' ' + this.lastName | ||
}, | ||
// setter | ||
set: function (newValue) { | ||
var names = newValue.split(' ') | ||
this.firstName = names[0] | ||
this.lastName = names[names.length - 1] | ||
} | ||
} | ||
} | ||
// ... | ||
``` | ||
|
||
现在再运行 `vm.fullName = 'John Doe'` 时,setter 会被调用,`vm.firstName` 和 `vm.lastName` 也会相应地被更新。 |