技术分享
首页
  • JavaScript

    • 构造函数和原型
    • Cookie和Session
    • Object.create(null)和{}
    • TypeScript配置
    • typescript入门到进阶
  • 框架

    • Vue-Router
    • React基础入门
  • 其它

    • Http协议
    • 跨域问题总结
  • 分析Promise实现
  • Axios源码分析
  • Webpack原理
  • vueRouter源码分析
  • Vue

    • Vite快速搭建Vue3+TypeScript项目
    • Cordova打包Vue项目的问题
    • Vue将汉字转为拼音,取出首字母
    • h5项目问题总结
  • JavaScript

    • new Function
  • 后端

    • Node.js中使用Crypto生成Token
    • Body-Parser处理多层对象的问题
  • 其它

    • 项目Demo汇总
    • Vuepress+Vercel搭建个人站点
    • 项目中能用到的
    • husky规范代码提交
  • Mongoose基础
  • Multer文件上传中间件的使用
  • JavaScript

    • 浅谈两数全等
    • JavaScript进制转换
    • 手写bind,apply,call和new
  • 算法

    • 数组去重和排序
    • 数组扁平化
    • 斐波那契数列
  • JavaScript 数据结构
  • 其它

    • webpack面试题
    • vite面试题
    • svg和canvas的优缺点
    • TypeScript面试题
    • Vue常见面试题
  • 计算机网络

    • 数据链路层
    • 网络层
  • Git的使用
  • Nginx的使用
  • CentOS7安装Nginx
  • 正则表达式
  • SEO搜索引擎优化
  • Serverless介绍
友链
GitHub (opens new window)

刘誉

总有人要赢,为什么不能是我
首页
  • JavaScript

    • 构造函数和原型
    • Cookie和Session
    • Object.create(null)和{}
    • TypeScript配置
    • typescript入门到进阶
  • 框架

    • Vue-Router
    • React基础入门
  • 其它

    • Http协议
    • 跨域问题总结
  • 分析Promise实现
  • Axios源码分析
  • Webpack原理
  • vueRouter源码分析
  • Vue

    • Vite快速搭建Vue3+TypeScript项目
    • Cordova打包Vue项目的问题
    • Vue将汉字转为拼音,取出首字母
    • h5项目问题总结
  • JavaScript

    • new Function
  • 后端

    • Node.js中使用Crypto生成Token
    • Body-Parser处理多层对象的问题
  • 其它

    • 项目Demo汇总
    • Vuepress+Vercel搭建个人站点
    • 项目中能用到的
    • husky规范代码提交
  • Mongoose基础
  • Multer文件上传中间件的使用
  • JavaScript

    • 浅谈两数全等
    • JavaScript进制转换
    • 手写bind,apply,call和new
  • 算法

    • 数组去重和排序
    • 数组扁平化
    • 斐波那契数列
  • JavaScript 数据结构
  • 其它

    • webpack面试题
    • vite面试题
    • svg和canvas的优缺点
    • TypeScript面试题
    • Vue常见面试题
  • 计算机网络

    • 数据链路层
    • 网络层
  • Git的使用
  • Nginx的使用
  • CentOS7安装Nginx
  • 正则表达式
  • SEO搜索引擎优化
  • Serverless介绍
友链
GitHub (opens new window)
  • 项目 Demo 汇总
  • node中使用crypto生成token
  • new Function
  • body-parser处理多层对象的问题
  • Vite快速搭建Vue3+TypeScript项目
  • Cordova打包Vue项目的问题
  • Vue将汉字转为拼音,取出首字母
  • 项目中能用到的
  • Vuepress+Vercel搭建个人站点
  • husky规范代码提交
  • h5项目问题总结
  • vue动态换行,行末省略
  • mocks项目复盘
    • 项目简介
    • 问题
  • 项目
coderly
2022-03-29

mocks项目复盘

# mocks项目复盘

项目地址 (opens new window)

# 项目简介

  1. 前端 mock 方案
  2. mock数据保存在前端
  3. 支持根据 ts 类型 生成对应的mock数据
  4. 文件变化,自动加载最新的数据

# 问题

# Access-Control-Allow-Credentials 问题

参看MDN (opens new window)

  1. 项目中使用到了cookie,所以启用了这个属性,axios 开启了 withCredentials = true(CORS请求默认不发送Cookie和HTTP认证信息,前端请求开启这个属性,请求默认发送cookie)
  2. 启用后会报 cros 问题,但实际上是开启了允许 cros,响应内容不会返回给请求的发起者 (opens new window)
  3. 此时不能将响应headers 的 origin 设置为 *,必须为实际的 origin(request请求时的origin)阮一峰的跨域资源共享 CORS 详解 (opens new window)
// koa
function (ctx: any) {
    let origin = '*'
    if (ctx.request.header && ctx.request.header.origin) (origin = ctx.request.header.origin)
    ctx.set({
        'origin': origin
    })
}
1
2
3
4
5
6
7
8

效果

# fs.watch 问题

  1. fs.watch 监听文件夹,如果修改了某个文件名,将会触发两次 rename 事件
  2. 第一次回调改名前的文件相对路径
  3. 第二次回调改名后的文件相对路径
  4. 这样还需要自己去辨别是删除了文件,还是仅仅修改了文件名
  5. 使用 chokidar (opens new window) 插件 代替 fs.watch,
  6. chokidar
    • 删除:unlink
    • 添加:add
    • 改变:change
const chokidar = require('chokidar');

chokidar.watch('.').on('all', (event, path) => {
  console.log(event, path);
});
1
2
3
4
5

# json-schema-faker + typescript-json-schema + typescript mock数据

  1. json-schema-faker (opens new window),原是基于 faker 生成mock数据,但 faker 原作者因某些原因已将该库删除
  2. typescript-json-schema (opens new window)
// 创建 ts 编译程序
// file:需要编译哪个文件
const program = ts.createProgram([file], {
    noEmit: true, // 不输出文件(write)
    allowJs: true, // 允许js
    emitDecoratorMetadata: true, // 为源文件中的修饰声明发出设计类型元数据
    experimentalDecorators: true, // 启用对 TC39 第 2 阶段草稿装饰器的实验性支持
    target: ts.ScriptTarget.ES5, // 为发出的 JavaScript 设置 JavaScript 语言版本并包含兼容的库声明
    module: ts.ModuleKind.CommonJS, // 指定生成什么模块代码
    allowUnusedLabels: true, // 禁用未使用标签的错误报告
    diagnostics: true, // 构建后输出编译器性能信息
    explainFiles: true, // 打印在编译期间读取的文件,包括包含它的原因
    resolveJsonModule: true, // 启用导入 .json 文件
});
// symbol 是需要从 file 产生 mock 数据的 interface 等
const schema = this.jsonSchema.generateSchema(program, symbol, {
   required: true, // 不加上,最后生成的数据,会有缺失、额外字段 
});
const result = await jsf.generate(schema); // jsonValue to string
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

eg:

// user/index.ts
type MyName = SchamName;

interface SchamName {
  school: string;
}
export interface Mime {
  age: number;
  name: string;
  info: MyName;
  sex?: string;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
// 创建 ts 编译程序
// file:需要编译哪个文件
const program = ts.createProgram(['./user/index.ts'], {
    noEmit: true, // 不输出文件(write)
    allowJs: true, // 允许js
    emitDecoratorMetadata: true, // 为源文件中的修饰声明发出设计类型元数据
    experimentalDecorators: true, // 启用对 TC39 第 2 阶段草稿装饰器的实验性支持
    target: ts.ScriptTarget.ES5, // 为发出的 JavaScript 设置 JavaScript 语言版本并包含兼容的库声明
    module: ts.ModuleKind.CommonJS, // 指定生成什么模块代码
    allowUnusedLabels: true, // 禁用未使用标签的错误报告
    diagnostics: true, // 构建后输出编译器性能信息
    explainFiles: true, // 打印在编译期间读取的文件,包括包含它的原因
    resolveJsonModule: true, // 启用导入 .json 文件
});
// symbol 是需要从 file 产生 mock 数据的 interface 等
const schema = this.jsonSchema.generateSchema(program, 'Mime', {
   required: true, // 不加上,最后生成的数据,会有缺失、额外字段 
});
const result = await jsf.generate(schema); // jsonValue to string
// 输出
// {
//     "age": 11019161.318964705,
//     "info": {
//         "school": "enim magna reprehenderit voluptate esse"
//      },
//     "name": "adipisicing sit dolor",
//     "sex": "in quis"
// }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

# 从一个 JSON 字符串中解析出 某个属性

  1. 针对包含特殊字符的 json 字符串, 例如:属性名有 $_+ 等
/**
 * 在一个json字符串中,解析出需要的那个字段
 * @param str 需要解析的源字符串
 * @param target 需要解析的属性
 * @returns 解析后获取到的 target JSON 对象
 */
export function getJsonFromStr(str: string, target: string) {
  let file = str;
  let idx = file.indexOf(target);
  if (idx > -1) {
    file = file.slice(idx + target.length + 1);
    idx = file.indexOf('{');
    const stack = [];
    const fileStack = [];
    if (idx > -1) {
      stack.push('{');
      fileStack.push('{');
      file = file.slice(idx + 1);
      while (stack.length > 0) {
        if (file.indexOf('{') > -1 || file.indexOf('}') > -1) {
          if (file.indexOf('{') > -1 && file.indexOf('{') < file.indexOf('}')) {
            // 先匹配到的是 {
            // 存入 stack
            idx = file.indexOf('{');
            fileStack.push(file.slice(0, idx));
            file = file.slice(idx + 1);
            fileStack.push('{');
            stack.push('{');
          } else if (file.indexOf('}') === -1) {
            // 未匹配到 } 直接 break
            break;
          } else {
            // 此时匹配到的是 } 用 { 去抵消
            // 取出 stack
            let char = stack.pop();
            if (char !== '{') {
              // 无法匹配,此时说明解析错误,直接推出
              throw new Error('replace解析错误');
            } else {
              idx = file.indexOf('}');
              fileStack.push(file.slice(0, idx));
              file = file.slice(idx + 1);
              fileStack.push('}');
            }
          }
        } else break;
      }
      // 解析完成,尝试拼接
      const finallyStr = fileStack.join('');
      try {
        const finallyJson = new Function('return' + finallyStr)(); // 转换成 json
        if (typeof finallyJson === 'object') {
          return finallyJson;
        }
        return '';
      } catch (error) {
        throw new Error('循环解析文本出错');
      }
    }
  }
  return '';
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62

eg:

let str = `
{
    replace: {
      $$_THOR_HOST_$$: {
        'dev-daily': '//h5.dev.weidian.com:7000',
        // 'dev-daily': '//thor.daily.weidian.com',
        'dev-pre': '//thor.pre.weidian.com',
        'dev-prod': '//thor.weidian.com'
      },
      $$_ENVIRONMENT_$$: {
        'dev-daily': 1,
        'dev-pre': 2,
        'dev-prod': 3
      }
    }
}
`
console.log(getJsonFromStr(str, 'replace:')) // 最终就能获得 replace 对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# http、https 创建代理服务器

  1. 本来应该在 mock 服务中 再使用代理服务器的(初衷是用来请求真实地址)
  2. 发现跨域,所以耽搁了下来,顺便记录下
  3. https 需要用到 ssl,所以这里用了 devcert (opens new window)
  4. 获取可用端口号使用了 portfinder (opens new window)
import Koa from 'koa';
import http from 'http';
import https from 'https';
import portfinder from 'portfinder';
const app = new Koa()

// isHttps 代表创建 https服务器,这里简化了
// 实际得定义 isHttps 变量

if (isHttps) {
 require('devcert')
    .certificateFor('coderly.cn', { getCaPath: true })
    .then((ssl: any) => {
        const { key, cert } = ssl;
        https.createServer({ key, cert }, app.callback()).listen(443);
    });
} else {
    getFreePort().then((port) => http.createServer(app.callback()).listen(port));
}

/**
 * 获取可用端口号
 * @param port 查找可用端口号起始 端口,默认 3000
 * @returns
 */
export function getFreePort(port: number = 3000) {
  portfinder.basePort = port;
  return new Promise<number>((resolve, reject) => {
    portfinder
      .getPortPromise()
      .then((port: number) => {
        resolve(port);
      })
      .catch(reject);
  });
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
上次更新: 2022/03/29, 11:07:22
vue动态换行,行末省略

← vue动态换行,行末省略

最近更新
01
代码片段
04-22
02
koa全家桶
03-29
03
可视化
03-22
更多文章>
Theme by Vdoing | Copyright © 2021-2022 coderly | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式