Vue源码系列讲解——过滤器篇【二】(工作原理)

news2024/10/7 3:25:51

目录

1. 前言

2. resolveFilter函数分析

3. 串联过滤器原理

4. 过滤器接收参数

5. 小结


1. 前言

通过上一篇用法回顾我们知道,过滤器有两种使用方式,分别是在双花括号插值中和在 v-bind 表达式中。但是无论是哪一种使用方式,过滤器都是写在模板里面的。既然是写在模板里面,那么它就会被编译,会被编译成渲染函数字符串,然后在挂载的时候会执行渲染函数,从而就会使过滤器生效。举个例子:

假如有如下过滤器:

{{ message | capitalize }}

filters: {
    capitalize: function (value) {
        if (!value) return ''
        value = value.toString()
        return value.charAt(0).toUpperCase() + value.slice(1)
    }
}

那么它被编译成渲染函数字符串后,会变成这个样子:

_f("capitalize")(message)

如果你是初次看到这个_f这样的函数,请不要惊慌。这跟我们在介绍模板编译篇中代码生成阶段时所看到的_c_e函数一样,它都对应着一个函数,_f对应的是resolveFilter函数,通过模板编译会生成一个_f函数调用字符串,当执行渲染函数的时候,就会执行_f函数,从而让过滤器生效。

也就是说,真正让过滤器生效的是_f函数,即resolveFilter函数,所以接下来我们就分析一下resolveFilter函数的内部原理。

2. resolveFilter函数分析

resolveFilter函数的定义位于源码的src/core/instance/render-helpers.js中,如下:

import { identity, resolveAsset } from 'core/util/index'

export function resolveFilter (id) {
  return resolveAsset(this.$options, 'filters', id, true) || identity
}

可以看到,resolveFilter函数内部只有一行代码,就是调用resolveAsset函数并获取其返回值,如果返回值不存在,则返回identity,而identity是一个返回同参数一样的值,如下:

/**
 * Return same value
 */
export const identity = _ => _

显然,更令我们关心的是resolveAsset函数,该函数的定义位于源码的src/core/util/options.js中,如下:

export function resolveAsset (options,type,id,warnMissing) {
  if (typeof id !== 'string') {
    return
  }
  const assets = options[type]
  // 先从本地注册中查找
  if (hasOwn(assets, id)) return assets[id]
  const camelizedId = camelize(id)
  if (hasOwn(assets, camelizedId)) return assets[camelizedId]
  const PascalCaseId = capitalize(camelizedId)
  if (hasOwn(assets, PascalCaseId)) return assets[PascalCaseId]
  // 再从原型链中查找
  const res = assets[id] || assets[camelizedId] || assets[PascalCaseId]
  if (process.env.NODE_ENV !== 'production' && warnMissing && !res) {
    warn(
      'Failed to resolve ' + type.slice(0, -1) + ': ' + id,
      options
    )
  }
  return res
}

调用该函数时传入了4个参数,分别是当前实例的$options属性,typefiltersid为当前过滤器的id

在该函数内部,首先判断传入的参数id(即当前过滤器的名称id)是否为字符串类型,如果不是,则直接退出程序。如下:

if (typeof id !== 'string') {
    return
}

接着,获取到当前实例的$options属性中所有的过滤器,赋给变量assets,上篇文章中说过,定义过滤器有两种方式,一种是定义在组件的选项中,一种是使用Vue.filter定义。在之前的文章中我们说过,组件中的所有选项都会被合并到当前实例的$options属性中,并且使用Vue.filter定义的过滤器也会被添加到$options中的filters属性中,所以不管是以何种方式定义的过滤器,我们都可以从$options中的filters属性中获取到。如下:

const assets = options[type]

获取到所有的过滤器后,接下来我们只需根据过滤器id取出对应的过滤器函数即可,如下:

// 先从本地注册中查找
if (hasOwn(assets, id)) return assets[id]
const camelizedId = camelize(id)
if (hasOwn(assets, camelizedId)) return assets[camelizedId]
const PascalCaseId = capitalize(camelizedId)
if (hasOwn(assets, PascalCaseId)) return assets[PascalCaseId]
// 再从原型链中查找
const res = assets[id] || assets[camelizedId] || assets[PascalCaseId]
if (process.env.NODE_ENV !== 'production' && warnMissing && !res) {
    warn(
        'Failed to resolve ' + type.slice(0, -1) + ': ' + id,
        options
    )
}
return res

上述代码中,根据过滤器id查找过滤器首先先从本地注册中查找,先通过hasOwn函数检查assets自身中是否存在,如果存在则直接返回;如果不存在,则将过滤器id转化成驼峰式后再次查找,如果存在则直接返回;如果也不存在,则将过滤器id转化成首字母大写后再次查找,如果存在则直接返回;如果还不存在,则再从原型链中查找,如果存在则直接返回;如果还不存在,则在非生产环境下抛出警告。

以上,就是resolveFilter函数的所有逻辑。可以看到,resolveFilter函数其实就是在根据过滤器id获取到用户定义的对应的过滤器函数并返回,拿到用户定义的过滤器函数之后,就可以调用该函数并传入参数使其生效了。如下图所示: 

3. 串联过滤器原理

上文分析了单个过滤器的工作原理,对于多个过滤器串联一起使用其原理也是相同的,还是先根据过滤器id获取到对应的过滤器函数,然后传入参数调用即可,唯一有所区别的是:对于多个串联过滤器,在调用过滤器函数传递参数时,后一个过滤器的输入参数是前一个过滤器的输出结果。举个例子:

假如有如下过滤器:

{{ message | filterA | filterB }}

filters: {
    filterA: function (value) {
        // ...
    },
    filterB: function (value) {
        // ...
    },
}

那么它被编译成渲染函数字符串后,会变成这个样子:

可以看到,过滤器filterA的执行结果作为参数传给了过滤器filterB

4. 过滤器接收参数

上一篇文章中说了,过滤器本质上就是一个JS函数,既然是函数,那它肯定就可以接收参数,唯一一点需要注意的就是:过滤器的第一个参数永远是表达式的值,或者是前一个过滤器处理后的结果,后续其余的参数可以被用于过滤器内部的过滤规则中。举个例子:

假如有如下过滤器:

{{ message | filterA | filterB(arg) }}

filters: {
    filterA: function (value) {
        // ...
    },
    filterB: function (value,arg) {
        return value + arg
    },
}

那么它被编译成渲染函数字符串后,会变成这个样子:

可以看到,当过滤器接收其余参数时,它的参数都是从第二个参数开始往后传入的。

5. 小结

本篇文章介绍了过滤器的内部工作原理,就是将用户写在模板中的过滤器通过模板编译,编译成_f函数的调用字符串,之后在执行渲染函数的时候会执行_f函数,从而使过滤器生效。

所谓_f函数其实就是resolveFilter函数的别名,在resolveFilter函数内部是根据过滤器id从当前实例的$options中的filters属性中获取到对应的过滤器函数,在之后执行渲染函数的时候就会执行获取到的过滤器函数。

现在我们已经了解了过滤器的工作原理,那么Vue在模板编译的时候是如何识别出用户所写的过滤器并且解析出过滤器中的内容呢?下篇文章我们来介绍Vue如何解析过滤器。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1494566.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python 系统学习总结(基础语法+函数+数据容器+文件+异常+包+面向对象)

🔥博客主页: A_SHOWY🎥系列专栏:力扣刷题总结录 数据结构 云计算 数字图像处理 力扣每日一题_ 六天时间系统学习Python基础总结,目前不包括可视化部分,其他部分基本齐全,总结记录&#xff0…

快速上手:剧本杀dm预约平台小程序的制作流程

在当今的娱乐市场中,剧本杀已经成为一种备受欢迎的娱乐方式。为了给玩家提供更好的服务和体验,开发一个剧本杀DM预约平台小程序是至关重要的。下面,我们将详细介绍如何使用乔拓云第三方平台开发这样一个预约平台。 首先,打开乔拓云…

QLC SSD:LDPC纠错算法的优化方案

随着NAND TLC和QLC出现,LDPC也在不断的优化研究,提升纠错能力。小编看到有一篇来自Microchip发布的比较详细的LDPC研究数据,根据自己的理解分析解读给大家,如有错误,请留言指正! 文档中测试LDPC(Low-Density Parity-Check)码是为了评估其在不同配置下对数据错误的有效…

基于51单片机的四位并行数据主从机传输设计

基于51单片机的四位并行数据主从机传输设计[proteus仿真] 主从机通信系统这个题目算是课程设计和毕业设计中常见的题目了,本期是一个基于51单片机的四位并行数据主从机传输设计 需要的源文件和程序的小伙伴可以关注公众号【阿目分享嵌入式】,赞赏任意文…

spring 事务失效的 12 种场景

文章目录 spring 事务失效的 12 种场景一、事务不生效1.访问权限问题2. 方法用 final 修饰3.方法内部调用(自己玩自己)3.1 新加一个 Service 方法3.2 在该 Service 类中注入自己3.3 通过 AopContent 类 4.Bean没有纳入Spring IOC容器管理5.多线程调用&am…

时间轮奇妙旅程:深度解析Netty中的时间轮机制

欢迎来到我的博客,代码的世界里,每一行都是一个故事 时间轮奇妙旅程:深度解析Netty中的时间轮机制 前言时间轮的基本概念时间轮的工作机制netty中时间轮的实现时间轮实现细节:简化的源码示例: 时间轮的应用场景时间轮的…

调研图基础模型(Graph Foundation Models)

🍉 CSDN 叶庭云:https://yetingyun.blog.csdn.net/ 图基础模型(Graph Foundation Models,简称 GFMs) 是一种经过预训练的图大模型,旨在处理不同领域的图数据和任务。让我们详细探讨一下这个概念。 Github …

掌握Nodejs高级图片压缩技巧提升web优化

掌握Nodejs高级图片压缩技巧提升web优化 在当今的数字时代,图像在网络开发中发挥着至关重要的作用。它们增强视觉吸引力、传达信息并吸引用户。然而,高质量的图像通常有一个显着的缺点——较大的文件大小会减慢网页加载时间。为了应对这一挑战并确保快速加载网站,掌握 Node…

SQL注入漏洞,常用注入函数及其pakachu漏洞靶场演示

目录 SQL注入漏洞概述 SQL注入的常用函数 漏洞分类与利用 1.基于联合查询的SQL注入 2.盲注 时间盲注(base on bool)​编辑 3.宽字节注入 4.inset/update/delete注入 5.header注入 &…

【工作向】protobuf编译生成pb.cc和pb.py文件

序言 首先通过protoc --version查看protoc版本,避免pb文件生成方和使用方版本不一致 1. 生成pb.cc 生成命令 protoc -I${proto_file_dir} --cpp_out${pb_file_dir} *.proto参数: -I表示 proto 文件的路径; --cpp_out 表示输出路径&#xff…

关于Mybatis-Plus报错 Not Found TableInfoCache 解决办法

0. 接口结构&#xff1a;1. 方法报错&#xff1a;2. 解决方法&#xff1a;3. 原因分析&#xff1a; 0. 接口结构&#xff1a; 【接口】&#xff1a; public interface PurchaseOrderService extends IService<PurchaseOrder> {}【接口实现类】&#xff1a; public cla…

Java项目:39 springboot007大学生租房平台的设计与实现

作者主页&#xff1a;舒克日记 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文中获取源码 项目介绍 系统有管理员、房东和用户 【主要功能】 1、后台&#xff1a;房源管理、信息审批管理、订单信息管理、房东管理、用户管理 2、前台&#xff1a;注册登…

PackagesNotFoundError:学习利用报错信息找到解决方法

反思&#xff1a;之前看到报错经常是直接复制报错信息去网上搜&#xff0c;但很多情况下报错信息里其实就给出了解决方案 报错信息&#xff1a; Collecting package metadata (current_repodata.json): done Solving environment: unsuccessful initial attempt using frozen …

[LeetBook]【学习日记】有效数字——状态机

题目 有效数字 有效数字&#xff08;按顺序&#xff09;可以分成以下几个部分&#xff1a; 若干空格一个小数或者整数&#xff08;可选&#xff09;一个’e’或’E’&#xff0c;后面跟着一个整数若干空格 小数&#xff08;按顺序&#xff09;可以分成以下几个部分&#xff1a…

React Vite 构建工具如何查看代码占用体积

首先安装 Vite 中的 rollup-plugin-visualizer 插件 cnpm install rollup-plugin-visualizer 接着在你的 vite.config.ts 中引入并且使用到 plugins 中 import { visualizer } from "rollup-plugin-visualizer";export default defineConfig({plugins: [react(),vi…

JWT令牌实现登陆校验

一、JWT出现的背景 jwt令牌出现的背景&#xff0c;比如我们通过一个路由访问网站的时候&#xff0c;有些游客在知道url的情况下会跳过用户登录直接访问其他网页&#xff0c;这样不仅在逻辑上说不通&#xff08;我没登陆咋就能使用其他功能&#xff1f;&#xff09;还会造成信息…

第三天 Kubernetes进阶实践

第三天 Kubernetes进阶实践 本章介绍Kubernetes的进阶内容&#xff0c;包含Kubernetes集群调度、CNI插件、认证授权安全体系、分布式存储的对接、Helm的使用等&#xff0c;让学员可以更加深入的学习Kubernetes的核心内容。 ETCD数据的访问 kube-scheduler调度策略实践 预选与…

SSD LDPC纠错算法的重要性

固态硬盘&#xff08;Solid State Drives, SSD&#xff09;作为计算机行业中最具革命性的技术之一&#xff0c;凭借其更快的读写速度、增强的耐用性和能效&#xff0c;已经成为大多数用户的首选存储方案。然而&#xff0c;如同任何其他技术一样&#xff0c;SSD也面临自身的挑战…

SpringBoot约定大于配置

什么是约定大于配置 "约定大于配置"&#xff08;Convention Over Configuration&#xff09;是一种理念&#xff0c;旨在通过默认约定和规则来减少开发人员需要做的配置工作。在Spring Boot框架中&#xff0c;这一原则得到了充分应用&#xff0c;帮助开发者更快地构…

C++之获取Windows系统信息

目录 1. 操作系统版本 2. 获取CPU信息 3. 获取内存信息 4. 获取硬盘信息 5.获取网络接口信息 6.获取计算机名称、用户名 在C中&#xff0c;你可以使用Windows API函数来获取Windows系统的各种信息。以下是一些常见的API函数和示例代码&#xff0c;用于获取Windows系统信息…