【原理分析】Mock.js:我是如何“科学造假”的

2,029 阅读4分钟

Mock.js 主要干了两件事:

  • 生成随机数据
  • 拦截 Ajax 请求

走走,这就去看看是咋回事儿

image.png

如何生成随机数据

Mock.mock(template) 的工作原理

// 属性名   name
// 生成规则 rule
// 属性值   value
"name|rule": value

原理概述

正则拆分,分类处理

  1. 根据 template 的类型,来调用对应的 handler 处理
  2. 借助正则,在步骤 1 中,将当前处理的属性名拆成 name, rule 两部分
  3. handler 中,依据生成规则 rule,占位符 placeholder 处理生成对应的 value
  4. 如为对象,各属性按步骤 1 继续处理

举个例子

以常见的情况为例,看源码中的相关处理方式

可以在控制台中实验一波:mockjs.com/examples.ht…

Mock.mock({
  "apples": {
      "weight": "@range(5)",
      "color|5": ["red"]
  },
  "sum": "@./apples/weight",
  "copy": "@sum"
})

1. Mock.mock(template) ->源码直通车

只传入模版,调用 Handler.gen(template) ->源码直通车

2. Handler.gen(template, name, context)

此时 template 的类型为 object,接下来调用 Handler['object']

Handler['object'] 的返回值会作为 mock 的结果

3. Handler['object'] ->源码直通车

字段中包含 template,表示包含生成规则的 **

path 指不包含生成规则的访问路径;templatePath指包含生成规则的访问路径

依次处理属性,递归调用 Handler.gen(template, name, context)

template 为当前处理到的属性名 name(包含生成规则)对应的属性值

在处理过程中维护 context 中记录的字段:

  • path / templatePath:记录当前访问路径
  • currentContext / templateCurrentContext:处理完的数据
  • root / templateRoot:根对象

首次调用会将对应的数据保存到 root / templateRoot

4. Handler['string'] ->源码直通车

"weight": "@range(5)" 或 "sum": "@./apples/weight" 或 "copy": "@sum"

空字符串

  • 如指定生成规则,则取对应个数的随机字符;否则,直接取空字符串

    "ASCII|1-10": "" => 1~10 内取一随机数作为个数

非空字符串

  • 根据生成规则中指定的数字,得到一待处理的完整字符串

  • 依次处理可能的占位符,调用 Handler.placeholder(placeholder, obj, templateContext, options) 得到对应的值,替换原有占位符

    "@range(5)@test" => ["@range(5)", "@test"]

  • placeholder,待处理的占位符,如"@range(5)"

  • obj / templateContext,对应前文 context 中保存的 currentContext / templateCurrentContext,处理完的数据

  • options,传给该处理函数的所有字段,如当前处理的属性值类型 type,属性值模版 template,上下文 context 等等 ->源码直通车

5. Handler.placeholder(placeholder, obj, templateContext, options) ->源码直通车

拆分占位符,得到对应的名称(下文以 key 代指)及参数

引用模版中属性

"copy": "@sum"

  • obj[key] 已处理完,则直接使用(示例情况)
  • key 还未处理到,则调用 Handler.gen,先计算被引用的属性值

绝对 / 相对路径

"sum": "@./apples/weight"

Handler.gen 首次执行时,将 1 作为路径根 ->源码直通车

依次处理 path 路径数组

  • 占位符拆分,得到路径数组,拼接成绝对路径

    • 当前路径:[1, "sum"]
    • 相对路径:[".", "apples", "weight"]
    • 绝对路径:[1, "apples", "weight"]
  • 根据路径,从 options 中的 context 里,依据 root / templateRoot,依次向下取值

    • 已处理,直接用
    • 未处理,调用 Handler.gen,先计算被引用的属性值
  • 路径没找到,返回 undefined

特定占位符

"weight": "@range(5)"

Random 文件夹下,内置了许多随机生成函数

  • Random 中有对应 key

    • 函数类型,传参数,直接调用
    • 数组类型,随机取一个
  • 无对应 key,占位符原样返回

6. Handler['array'] ->源码直通车

"color|5": ["red"]

空数组

直接返回 ""

非空数组

  • 如无生成规则,直接遍历元素,递归调用 Handler.gen
  • 如有,则会在遍历过程中处理,如从数组中随机取一个生成,重复添加多次等

关系总览

mock(template).jpg

如何拦截 Ajax 请求

Mock.mock( rurl, rtype?, template|function(options)) 的工作原理

原理概述

从 1.0 开始,Mock.js 通过覆盖模拟原生 XMLHttpRequest 的行为来拦截 Ajax 请求

->源码直通车

  1. MockXMLHttpRequest 上有一属性 Mock,其中的 _mocked 会记录需要拦截的请求

  2. MockXMLHttpRequest 覆盖原生 window.XMLHttpRequest

  3. open 中,如果初始化的 urlmethod,在 _mocked 中找到,则会打上拦截标记(原型属性 match,置为 true),否则使用原生 XHR

  4. send 中,通过拦截标记判断是否使用原生 XHR,如拦截请求,根据 template|function(options) ,设置响应数据:

    1. 传入函数,返回函数调用结果
    2. 传入 template,利用 Mock.mock,返回生成的假数据

关系总览

mock 拦截请求.jpg

字节跳动幸福里前端团队急缺人才(校招/社招大量HC),欢迎加我微信 orca_o 咨询~

相关文章:双非大三,无实习经历,如何以 hard 模式逆袭字节跳动(附春招笔记/字节内推)