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

目录

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如何解析过滤器。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Android Studio Iguana | 2023.2.1版本

Android Gradle 插件和 Android Studio 兼容性 Android Studio 构建系统基于 Gradle,并且 Android Gradle 插件 (AGP) 添加了一些特定于构建 Android 应用程序的功能。下表列出了每个版本的 Android Studio 所需的 AGP 版本。 如果特定版本的 Android Studio 不支持…

基于可变分辨率的半透明特效渲染优化方案

粒子效果在游戏中无处不在。大颗粒系统常见于烟雾、火灾、爆炸、灰尘和雾。如果这些效果填满屏幕,overdraw会非常严重,帧率会掉得很快,即使是在技术成熟的 3A 游戏中也是如此。以至于来半透明渲染的优化问题一直都是难题。 这里的解决方案是…

ruoyi-vue-plus4.X版本实现内嵌swagger文档(简单解决方法)

1.在common模块中添加pom依赖 <dependency><groupId>org.webjars</groupId><artifactId>swagger-ui</artifactId><version>4.15.5</version></dependency>结果如下&#xff1a; 2.在ResourcesConfig配置类的addResourceHandl…

CRM客户体验建设三剑客:构建旅程的必备策略

在企业越来越重视客户体验的今天&#xff0c;客户体验建设包含客户认知、客户旅程设置、NPS客户满意度调查三大版块&#xff0c;在工具上分别对应Zoho CRM的路径探查器、旅程构建器和NPS。上期介绍了路径探查器的作用和价值&#xff0c;本文将围绕客户旅程构建展开&#xff0c;…

学习JAVA的第十三天(基础)

目录 API之Arrays 将数组变成字符串 二分查找法查找元素 拷贝数组 填充数组 排序数组 Lambda表达式 集合的进阶 单列集合 体系结构 Collection API之Arrays 操作数组的工具类 将数组变成字符串 //将数组变成字符串char[] arr {a,b,c,d,e};System.out.println(Arra…

【Spring底层原理高级进阶】Spring Kafka:实时数据流处理,让业务风起云涌!️

&#x1f389;&#x1f389;欢迎光临&#x1f389;&#x1f389; &#x1f3c5;我是苏泽&#xff0c;一位对技术充满热情的探索者和分享者。&#x1f680;&#x1f680; &#x1f31f;特别推荐给大家我的最新专栏《Spring 狂野之旅&#xff1a;从入门到入魔》 &#x1f680; 本…

RS编码的FPGA实现

RS编码&#xff0c;即Reed-solomon codes&#xff0c;是一类纠错能力很强的特殊的非二进制BCH码&#xff08;BCH码是一种有限域中的线性分组码&#xff0c;具有纠正多个随机错误的能力&#xff09;。对于任选正整数S可构造一个相应的码长为nqS-1的 q进制BCH码&#xff0c;而q作…

[Python人工智能] 四十二.命名实体识别 (3)基于Bert+BiLSTM-CRF的中文实体识别万字详解(异常解决中)

从本专栏开始,作者正式研究Python深度学习、神经网络及人工智能相关知识。前文讲解如何实现中文命名实体识别研究,构建BiGRU-CRF模型实现。这篇文章将继续以中文语料为主,介绍融合Bert的实体识别研究,使用bert4keras和kears包来构建Bert+BiLSTM-CRF模型。然而,该代码最终结…

力扣每日一题 用栈实现队列

Problem: 232. 用栈实现队列 文章目录 思路复杂度&#x1f496; 朴素版&#x1f496; 优化版 思路 &#x1f468;‍&#x1f3eb; 路飞题解 复杂度 时间复杂度: 添加时间复杂度, 示例&#xff1a; O ( n ) O(n) O(n) 空间复杂度: 添加空间复杂度, 示例&#xff1a; O ( …

Python + Selenium —— 使用cookie绕过验证码!

使用 cookie 绕过验证码这种方式前提是必须要有长时间保存 cookie 的功能&#xff0c;比如登录时会有勾选项"保存本次登录信息"&#xff0c;“下次自动登录”&#xff0c;"记住我"等。 当你勾选类似的选项后&#xff0c;登录成功后服务器会要求浏览器将登录…

面试经典150题【41-50】

文章目录 面试经典150题【41-50】49.字母异位词分组1. 两数之和202.快乐数219. 存在重复元素II128.最长连续序列228. 汇总区间56.合并区间&#xff08;华为面试题&#xff09;57.插入区间452.用最少的箭引爆气球20.有效的括号 面试经典150题【41-50】 49.字母异位词分组 用这种…

关于vue创建项目以及关于eslint报错的问题

vue创建完项目以后如果报parsing error no babel config file。。。这样的错误的话&#xff0c;关闭项目&#xff0c;用vscode进入项目中打开项目就可以解决了。 1 代码保存的时候会自动将单引号报错为双引号 导致eslint报错的问题&#xff0c; 解决思路&#xff1a; 在项目根…

游戏寻路之A*算法(GUI演示)

一、A*算法介绍 A*算法是一种路径搜索算法,用于在图形网络中找到最短路径。它结合了Dijkstra算法和启发式搜索的思想,通过综合利用已知的最短路径和估计的最短路径来优化搜索过程。在游戏自动寻路得到广泛应用。 二、A*算法的基本思想 在图形网络中选择一个起点和终点。维护…

LED球泡灯高压线性恒流驱动芯片SM2235EGH原理与应用

高压线性恒流是一种LED恒流驱动芯片电子元件&#xff0c;它能够在高电压环境下提供稳定的电流输出。这种芯片采用线性恒流设计&#xff0c;能够确保电流的稳定性&#xff0c;适用于各种LED照明和其他需要恒流源的应用场景。 在设计高压线性恒流LED恒流驱动芯片时&#xff0c;需…

YOLOv9来了,YOLOv5和YOLOv8还香不香?

在目标检测领域&#xff0c;YOLO&#xff08;You Only Look Once&#xff09;一直是一种突破性算法。自YOLO算法问世以来&#xff0c;它已经演变为许多版本&#xff0c;其中最受欢迎的版本是YOLOv5和YOLOv8。这两个版本都有独特的特点和优势&#xff0c;使它们在各自的领域表现…

【短时交通流量预测】基于双层BP神经网络

课题名称&#xff1a;基于双层BP神经网络的短时交通流量预测 版本时间&#xff1a;2023-04-27 代码获取方式&#xff1a;QQ&#xff1a;491052175 或者 私聊博主获取 模型简介&#xff1a; 城市交通路网中交通路段上某时刻的交通流量与本路段前几个时段的交通流量有关&…

H5双人五子棋小游戏

H5小游戏源码、JS开发网页小游戏开源源码大合集。无需运行环境,解压后浏览器直接打开。有需要的,私信本人,发演示地址,可以后再订阅,发源码,含60+小游戏源码。如五子棋、象棋、植物大战僵尸、开心消消乐、扑鱼达人、飞机大战等等 <!DOCTYPE html> <html> <…

C++--机器人的运动范围

目录 1. 题目 2. 思路 3. C代码测试 4. 测试结果 1. 题目 地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动&#xff0c;每一次只能向左&#xff0c;右&#xff0c;上&#xff0c;下四个方向移动一格&#xff0c;但是不能进入行坐标和列坐标的数位之和大于k的格…

解决在 Mac 上安装 Adobe 软件弹出提示:安装包已经被损坏并且不能被打开。

问题&#xff1a; “INSTALLER” is damaged and can’t be opened. You should eject the disk image. 解决方法和步骤&#xff1a; 打开安装包&#xff1b;将安装包 “INSTALLER” 拖动复制到某个文件夹&#xff0c;复制后的文件路径例如像这样&#xff1a;/Users/michael…

Javaweb之SpringBootWeb案例之自动配置案例的自定义starter分析的详细解析

3.2.4.1 自定义starter分析 前面我们解析了SpringBoot中自动配置的原理&#xff0c;下面我们就通过一个自定义starter案例来加深大家对于自动配置原理的理解。首先介绍一下自定义starter的业务场景&#xff0c;再来分析一下具体的操作步骤。 所谓starter指的就是SpringBoot当…