web-vitals构建小结

202 阅读7分钟

web-vitals构建小结

web-vitals是一个用于测量前端性能的库,它提供了一些常用的性能指标,如FCP、LCP、FID、CLS等。 项目地址

让我们看下GoogleChrome开发团队如何构建一个库

用到构建的包前置

  1. typescript用于ts文件编译
  2. rollup用于打包成esm、umd、iife文件 rollupjs
  3. npm-run-all用于串并行执行脚本指令 npm-run-all
  4. eslint用于质量和规范性检查 eslint
  5. prettier用于代码格式化 prettier
  6. husky能使你的提交变得更好 🐶 汪! husky

以下代码是参照web-vitals写的一个demo构建项目,去除了业务代码逻辑 仓库

dev

dev调试,run-p并行执行指令

  1. dev 在这没做测试的脚本,实则watch文件变化执行编译
// package.json
  "scripts": {
    "dev": "run-p watch",
    "watch": "run-p watch:*",
    "watch:ts": "tsc -b -w",
    "watch:js": "rollup -c -w",
    "clean": "rimraf dist tsconfig.tsbuildinfo",
  },

buiid

buiid流程,run-s串行执行每一步指令

  1. clean 清除上次打包的文件
  2. tsc编译ts文件
  3. rollup根据上面编译出来的文件再打包成esm、umd、iife文件
// package.json
  "scripts": {
    "build:ts": "tsc -b",
    "build:js": "rollup -c",
    "build": "run-s clean build:ts build:js",
    "clean": "rimraf dist tsconfig.tsbuildinfo",
  },
// tsconfig.json
{
  "compilerOptions": {
    "composite": true, // 启用复合项目模式。在这种模式下,TypeScript 编译器会生成一个 .tsbuildinfo 文件,记录项目中每个文件的编译状态。这有助于在构建过程中提高性能,因为它允许编译器只重新编译自上次构建以来已更改的文件。
    "declaration": true, // 生成 TypeScript 类型声明文件(.d.ts),这些文件包含了类型的信息,可以被其他 TypeScript 文件导入和使用。
    "lib": ["es2017", "DOM"], // 指定编译器包含哪些 ECMAScript 标准库。"es2017" 表示包含 ECMAScript 2017(ES8)的特性,"DOM" 表示包含 Web DOM API。
    "module": "nodenext", // 指定模块系统的目标格式。"nodenext" 表示使用 Node.js 的下一个主版本模块系统。
    "moduleResolution": "nodenext", // 指定模块解析策略。"nodenext" 表示使用 Node.js 的下一个主版本的解析策略。
    "noFallthroughCasesInSwitch": true, // 在 switch 语句中,如果没有 explicit fallthrough(即没有 break)时,编译器会报错。
    "noImplicitReturns": true, // 如果函数的每个分支路径都有返回值,则编译器不会隐式地返回 undefined。
    "noUnusedLocals": true, // 报告未使用的局部变量。
    "noUnusedParameters": true, // 报告未使用的函数参数。
    "outDir": "./dist/modules", // 指定输出目录,编译后的 JavaScript 文件将被放置在这个目录下。
    "preserveConstEnums": true, // 保持 const 枚举的值不变。
    "rootDir": "./src", // 指定项目的根目录,编译器将从这个目录开始编译项目。
    "strict": true, // 启用所有严格类型检查选项,包括严格的 null 检查、严格的布尔类型检查等。
    "target": "esnext" // 指定 ECMAScript 目标版本。"esnext" 表示使用最新的 ECMAScript 草案版本。
  },
  "include": ["src/*.ts"], // 指定编译器应该包含的文件和目录。这里表示包含 src 目录下的所有 TypeScript 文件。
  "exclude": [] // 指定编译器应该排除的文件和目录。空数组表示不排除任何文件或目录。
}
// rollup.config.js
import babel from '@rollup/plugin-babel';
import terser from '@rollup/plugin-terser';

const configurePlugins = ({module}) => {
  return [
    babel({
      babelHelpers: 'bundled', // 使用_bundled_模式来嵌入Babel的辅助函数,避免在每个文件中重复引入
      presets: [
        [
          '@babel/preset-env', // 使用@babel/preset-env预设来转译代码,使其兼容目标浏览器环境
          {
            targets: {
              browsers: ['ie 11'], // 指定浏览器兼容性目标,这里指定了对IE 11的支持
            },
          },
        ],
      ],
    ]),
    terser({
      module, // 指示Terser插件是否在压缩模块代码时应用特定的优化
      mangle: true, // 是否启用变量名和函数名的压缩
      compress: true, // 是否启用代码的逻辑压缩,移除无用的代码和简化表达式
    }),
  ];
};

const configs = [
  {
    input: 'dist/modules/index.js', // 指定输入文件的路径
    output: {
      format: 'esm', // 指定输出模块的格式为ES模块
      file: './dist/web-vitals.js', // 指定输出文件的路径
    },
    plugins: configurePlugins({module: true}), // 应用配置好的插件数组,传入{module: true}表示压缩模块化代码
  },
  {
    input: 'dist/modules/index.js', // 指定输入文件的路径
    output: {
      format: 'umd', // 指定输出模块的格式为通用模块定义
      file: `./dist/web-vitals.umd.cjs`, // 指定输出文件的路径
      name: 'webVitals', // 指定全局变量的名称
    },
    plugins: configurePlugins({module: false}), // 应用配置好的插件数组,传入{module: false}表示不压缩模块化代码
  },
  {
    input: 'dist/modules/index.js', // 指定输入文件的路径
    output: {
      format: 'iife', // 指定输出模块的格式为立即执行函数表达式
      file: './dist/web-vitals.iife.js', // 指定输出文件的路径
      name: 'webVitals', // 指定全局变量的名称
    },
    plugins: configurePlugins({module: false}), // 应用配置好的插件数组,传入{module: false}表示不压缩模块化代码
  },
];

export default configs;

eslint

ESLint 是一个可配置的 JavaScript 检查器。它可以帮助你发现并修复 JavaScript 代码中的问题。问题可以指潜在的运行时漏洞、未使用最佳实践、风格问题等。

// .eslintrc
{
  "env": {
    "browser": true, // 指定代码运行环境为浏览器
    "es6": true, // 指定代码使用 ECMAScript 2015 (ES6) 规范
    "node": true, // 指定代码运行环境为 Node.js
    "mocha": true // 指定代码使用 Mocha 测试框架
  },
  "parserOptions": {
    "sourceType": "module", // 指定代码为 ES6 模块
    "ecmaVersion": "latest" // 使用最新的 ECMAScript 版本
  },
  "overrides": [
    {
      "files": "wdio.conf.js", // 指定覆盖的文件为 wdio.conf.js
      "extends": ["eslint:recommended"], // 继承 ESLint 推荐的规则集
      "rules": {
        "max-len": "off" // 关闭代码行长度的规则
      }
    },
    {
      "files": "src/*.ts", // 指定覆盖的文件为 src 目录下的所有 TypeScript 文件
      "parser": "@typescript-eslint/parser", // 使用 @typescript-eslint/parser 解析 TypeScript 代码
      "extends": ["plugin:@typescript-eslint/recommended"], // 继承 @typescript-eslint/recommended 规则集
      "rules": {
        "@typescript-eslint/no-non-null-assertion": "off", // 关闭非空断言的规则
        "@typescript-eslint/no-use-before-define": 2, // 开启变量在使用前定义的规则
        "@typescript-eslint/explicit-function-return-type": "off", // 关闭函数返回类型显式声明的规则
        "@typescript-eslint/explicit-module-boundary-types": "off", // 关闭模块边界类型显式声明的规则
        "@typescript-eslint/ban-ts-comment": "off", // 关闭 @ts- 评论的规则
        "@typescript-eslint/camelcase": "off", // 关闭驼峰命名的规则
        "comma-dangle": ["error", "always-multiline"], // 确保对象字面量属性的逗号在多行时正确悬挂
        "indent": ["error", 2], // 确保代码块的缩进为 2 个空格
        "node/no-missing-import": "off", // 关闭 Node.js 环境中缺少导入的规则
        "node/no-unsupported-features/es-syntax": "off", // 关闭 Node.js 环境中不支持的 ES 语法的规则
        "node/no-missing-require": "off", // 关闭 Node.js 环境中缺少 require 的规则
        "node/shebang": "off", // 关闭 Node.js 环境中 shebang 的规则
        "no-dupe-class-members": "off", // 关闭类成员重复的规则
        "prefer-spread": "off", // 关闭使用扩展运算符的规则
        "space-before-function-paren": [ // 确保函数圆括号前的空格正确
          "error",
          {
            "anonymous": "always", // 匿名函数圆括号前总是有空格
            "named": "never", // 具名函数圆括号前没有空格
            "asyncArrow": "always" // 异步箭头函数圆括号前总是有空格
          }
        ]
      },
      "parserOptions": {
        "ecmaVersion": 2018, // 使用 ECMAScript 2018 版本
        "sourceType": "module" // 指定代码为 ES6 模块
      }
    }
  ]
}

prettier

代码格式化工具

// package.json
{
  "prettier": {
    "arrowParens": "always", // 总是在箭头函数参数周围添加括号
    "bracketSpacing": false, // 在对象字面量括号内不添加空格
    "quoteProps": "preserve", // 保持对象属性的引号不变,除非它们需要被添加以满足语法要求
    "singleQuote": true // 使用单引号而不是双引号
  }
}

husky

在提交或推送时,自动化 检查提交信息、检查代码 和 运行测试。(母鸡典改,试了好多次才触发到pre-commit这个hooks)

  1. 使用 prepare 来进行 husky install生成.husky目录
  2. .husky目录下配置pre-commit
  3. 配置lint-staged 对暂存区中的文件执行指定的脚本
// package.json
"husky": {
    "hooks": {
      "pre-commit": "npm run lint"
    }
  },

以上基本能了解到库的打包流程,如需用到更多测试脚本和发包脚本可以看web-vitals的实现