通过过滤器对 Vue 组件中的模型属性值进行格式化


过滤器简介

在 Vue 组件中,我们可以通过过滤器对模型属性值进行格式化,注意这里不要望文生义哈,过滤器不是传统的对列表数据按条件进行「过滤」,而是对某个具体属性值进行「过滤」。

我们以上篇教程编写的文章列表组件为例进行演示。

Vue 组件支持全局过滤器本地过滤器,我们先以文章发布日期格式化为例演示全局过滤器的定义和使用,再以文章状态格式化为例演示本地过滤器的定义和使用。

准备工作

文章列表接口数据调整

开始之前,我们先在后台返回的文章列表接口数据中新增 created_atstatus 两个字段,分别表示文章发布时间和状态:

Route::get('/get_posts', function () {
    return [
        [
            'id' => 1,
            'url' => url('/post/1'),
            'title' => '测试文章1',
            'created_at' => '2019-10-15 17:51:32',
            'status' => 0
        ],
        [
            'id' => 2,
            'url' => url('/post/2'),
            'title' => '测试文章2',
            'created_at' => '2020-01-21 15:53:06',
            'status' => 1
        ],
        [
            'id' => 3,
            'url' => url('/post/3'),
            'title' => '测试文章3',
            'created_at' => '2020-09-07 10:31:06',
            'status' => 0,
        ],
        [
            'id' => 4,
            'url' => url('/post/4'),
            'title' => '测试文章4',
            'created_at' => '2020-05-17 01:29:24',
            'status' => 1
        ],
        [
            'id' => 5,
            'url' => url('/post/5'),
            'title' => '测试文章5',
            'created_at' => '2020-10-20 15:26:43',
            'status' => 1
        ]
    ];
});

props 属性传递字段调整

我们将以卡片视图为例进行演示,因为这里可展示字段的空间更大一些。为了避免每次从 PostList 父级作用域传递多个 props 属性到 CardItem,我们将之前逐个传递字段属性的方式调整为传递整个 post 对象:

<CardItem v-for="post in posts" :key="post.id" :post="post">
    <template #title>{{ post.title }}</template>
    <template #action-label>点击查看</template>
</CardItem>

通过全局过滤器定义日期过滤器

接下来,我们先来演示全局日期过滤器的定义和使用。

显示文章发布日期字段

修改 CardItem 组件代码如下以便展示日期字段:

<template>
    <div class="col mb-4">
        <div class="card">
            <div class="card-body">
                <h5 class="card-title"><slot name="title"></slot></h5>
                <p class="card-text">
                    <small class="text-muted">
                        <svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-clock" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
                            <path fill-rule="evenodd" d="M8 15A7 7 0 1 0 8 1a7 7 0 0 0 0 14zm8-7A8 8 0 1 1 0 8a8 8 0 0 1 16 0z"/>
                            <path fill-rule="evenodd" d="M7.5 3a.5.5 0 0 1 .5.5v5.21l3.248 1.856a.5.5 0 0 1-.496.868l-3.5-2A.5.5 0 0 1 7 9V3.5a.5.5 0 0 1 .5-.5z"/>
                        </svg>
                        {{ post.created_at }}
                    </small>
                </p>
                <a :href="post.url" class="btn btn-primary"><slot name="action-label"></slot></a>
            </div>
        </div>
    </div>
</template>

<script>
export default {
    props: ['post']
}
</script>

在未使用日期过滤器之前,渲染效果如下:

-w1123

接下来,我们使用过滤器对日期格式进行格式化。

引入 Moment 包

在此之前,先通过 NPM 安装一个 JavaScript 中功能与 PHP Carbon 包类似的 Moment 包,我们可以使用它提供的 API 对指定日期时间进行格式化:

npm install moment --save

然后在 component-practice/resources/js/app.js 中引入它:

window.moment = require('moment');

日期过滤器的定义和使用

接下来,就可以基于 Moment 提供的 API 在 Vue 实例初始化之前定义一个全局的日期过滤器了,我们将这个过滤器的名称命名为 diff_for_human

Vue.filter('diff_for_human', datetime => {
    return moment(datetime).fromNow();
});

const app = new Vue({
    el: '#app'
});

非常简单。这样一来,就可以在 CardItem 组件中应用这个过滤器对日期属性进行格式化了。和 Linux 管道符一致,我们可以在模型属性值上通过 | 连接要使用的过滤器,然后模型属性值会作为第一个参数传递给后面的过滤器函数,最后将函数返回结果作为整个表达式的输出值:

{{ post.created_at | diff_for_human }}

刷新列表视图,切换到卡片视图,可以看到应用日期过滤器后的格式化效果:

-w1143

通过本地过滤器定义状态过滤器

除了全局定义过滤器外,还可以在组件内部定义本地过滤器。

我们以文章状态字段为例进行演示,首先在 CardItem 组件中渲染状态字段:

<style scoped>
.card-text {
    vertical-align: middle;
}
.card-text small {
    margin-right: 5px;
}
</style>

<template>
    <div class="col mb-4">
        <div class="card">
            <div class="card-body">
                <h5 class="card-title"><slot name="title"></slot></h5>
                <p class="card-text">
                    <small class="text-muted">
                        <svg v-show="post.status === 1" width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-file-check" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
                            <path fill-rule="evenodd" d="M4 0h8a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2zm0 1a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1H4z"/>
                            <path fill-rule="evenodd" d="M10.854 6.146a.5.5 0 0 1 0 .708l-3 3a.5.5 0 0 1-.708 0l-1.5-1.5a.5.5 0 1 1 .708-.708L7.5 8.793l2.646-2.647a.5.5 0 0 1 .708 0z"/>
                        </svg>
                        <svg v-show="post.status === 0" width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-file-x" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
                            <path fill-rule="evenodd" d="M4 0h8a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2zm0 1a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1H4z"/>
                            <path fill-rule="evenodd" d="M6.146 6.146a.5.5 0 0 1 .708 0L8 7.293l1.146-1.147a.5.5 0 1 1 .708.708L8.707 8l1.147 1.146a.5.5 0 0 1-.708.708L8 8.707 6.854 9.854a.5.5 0 0 1-.708-.708L7.293 8 6.146 6.854a.5.5 0 0 1 0-.708z"/>
                        </svg>
                        {{ post.status === 0 ? '草稿' : '已发布' }}
                    </small>
                    <small class="text-muted">
                        <svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-clock" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
                            <path fill-rule="evenodd" d="M8 15A7 7 0 1 0 8 1a7 7 0 0 0 0 14zm8-7A8 8 0 1 1 0 8a8 8 0 0 1 16 0z"/>
                            <path fill-rule="evenodd" d="M7.5 3a.5.5 0 0 1 .5.5v5.21l3.248 1.856a.5.5 0 0 1-.496.868l-3.5-2A.5.5 0 0 1 7 9V3.5a.5.5 0 0 1 .5-.5z"/>
                        </svg>
                        {{ post.created_at | diff_for_human }}
                    </small>
                </p>
                <a :href="post.url" class="btn btn-primary"><slot name="action-label"></slot></a>
            </div>
        </div>
    </div>
</template>

可以看到,在未使用过滤器之前,我们使用了三元运算符对状态显示文本进行优化,但是这只能应付比较简单的场景,如果文章状态值多于 3 个,代码的可读性就会越来越差,容易出错,如果涉及到一些更复杂的业务逻辑判断,则只能使用更好地方式来处理,比如过滤器。

我们在 CardItem 组件中定义一个本地过滤器 status_human_readable,用于根据不同的文章状态值显示对应的可读性更好的文本,然后将其应用到 post.status 属性值上面:

...
{{ post.status | status_human_readable }}
...
<script>
export default {
    props: ['post'],
    filters: {
        status_human_readable(status) {
            switch(status) {
                case 0:
                    return '草稿';
                case 1:
                    return '已发布';
                default:
                    return '未知状态';
            }
        }
    }
}
</script>

可以看到,本地过滤器和全局过滤器只是定义方式不同而已,使用方式完全一致。

刷新列表视图,切换到卡片视图,可以看到如下渲染效果:

-w1129

关于 Vue 过滤器学院君就简单介绍到这里,更多关于 Vue 过滤器的功能和使用(比如多个过滤器的串联、额外传递过滤器参数等),请参考官方文档


点赞 取消点赞 收藏 取消收藏

<< 上一篇: 基于子组件构建列表组件并实现视图模式切换功能

>> 下一篇: 基于 Laravel 8 模型工厂快速生成后端接口测试数据