Skip to content

自动化构建 ​

构建工具的选择 ​

从目前的实际业务角度去选择:

  • UI库可以选择rollup
  • 小型项目可以选择vite
  • 大型项目可以选择webpack vite
  • 自己的项目也可以选择turbopack、rspack、swc等等
  • 对于需要快速原型开发的项目,可以考虑使用Parcel,它的零配置特性非常适合快速启动项目。
  • 对于大型单页应用(SPA),Next.js或Nuxt.js这样的框架也值得考虑,它们提供了开箱即用的构建优化。

确定构建的流程 ​

  1. 需要一些什么样的loader、plugin?

  2. 你所用的plugin、loader可以反映出你所设计的技术选型(css in js 、tailwind...)

  3. 举例:

  • 如果我需要二进制文件的加载或者拷贝,我应该使用?(file-loader、url-loader、copy-webpack-plugin)
  • 如果我需要将静态资源打包到cdn,我应该使用?(cdn-loader、cdn-plugin)
  • 考虑使用babel-loader进行JavaScript转译,以支持新的语言特性。
  • 对于CSS,可以考虑使用PostCSS和Autoprefixer来处理浏览器兼容性问题。

确定构建产物输出 ​

  • 我们是需要单chunk输出,还是多chunk输出?

  • 是否需要生成sourcemap以便调试。

  • 是否需要生成压缩和非压缩版本的文件。

优化打包构建流程 ​

  1. 优化打包体积? 使用bundle分析工具(如webpack-bundle-analyzer)来可视化包大小,找出可优化的地方。

    1. 代码拆分(Code Splitting)
    • 描述:将代码分割成多个小块,按需加载。提取不怎么变动的公共模块(vendor chunk)
    • 工具:Webpack 的 splitChunks、Rollup 的 output.splitting。
    • 应用场景:适用于首屏加载时间要求较高的应用。
    1. 树摇(Tree Shaking)
    • 描述:通过静态代码分析,去除无用的代码。
    • 工具:Webpack 支持 ES6 模块的 Tree Shaking、Rollup 的 Tree Shaking。
    • 应用场景:减少 commonjs 库的使用,尽可能使用 esm 的库,适用于使用 ES6 模块的应用。
    1. 压缩代码
    • 描述:压缩代码,去除空白、注释等。
    • 工具:Terser、UglifyJS、Rollup 的 terser 插件
    • 应用场景:适用于所有构建场景,尤其是生产环境
    1. 按需加载(Lazy Loading)
    • 描述:仅在需要时加载代码,而不是一开始就加载所有代码。
    • 工具:Webpack 的 import()、React 的 React.lazy。
    • 应用场景:适用于路由较多、功能复杂的单页应用(SPA)。
  2. 提升打包构建速度?

    1. 使用增量构建
    • 描述:增量构建仅打包发生变化的文件,而不是每次都重新打包整个项目。
    • 工具:大多数现代构建工具(如 Webpack、Rollup)支持增量构建。
    • 应用场景:适用于开发阶段,当文件发生改动时快速反馈。
    1. 缓存机制
    • 描述:缓存编译结果,避免重复编译相同的代码。
    • 工具:Webpack 配置cache属性,使用dll plugin,Rollup 的 --watch 模式
    • 应用场景:适用于所有构建场景,尤其是在频繁修改代码的情况下
    1. 优化依赖解析
    • 描述:减少解析模块的时间,特别是在模块数量较多时。
    • 工具:Webpack 的 resolve.alias、resolve.modules。
    • 应用场景:适用于大型项目,模块依赖关系复杂的情况
    1. 使用并行构建(多线程)
    • 描述:利用多核 CPU 并行执行构建任务
    • 工具:Webpack 的 thread-loader、happy-pack(老项目) 插件
    • 应用场景:适用于具有大量计算密集型任务的项目
    1. 优化加载路径
    • 描述:减少加载路径,避免不必要的文件查找
    • 工具:Webpack 可以使用 externals、Rollup 的 external,配合cdn引入来去除部分模块。
    • 应用场景:适用于外部依赖项不需要打包的情况
    1. 云端构建
    • 描述:借助turbo,rspack等构建工具,将构建任务提交到云端,由云端进行构建,减少本地构建压力。
    • 工具:turbo、rspack、swc等构建工具。
    • 应用场景:适用于需要构建速度和资源占用低,但需要构建结果的一致性,如 CI/CD 等场景。

一些vscode插件推荐 ​

  1. Version Lens: 显示当前文件版本号,方便对比版本差异。

自动化测试 ​

概念 ​

为什么需要测试 ​

测试一定是保证程序稳定健壮的手段

但是在我们平常的工作中,测试很难落地:

  • 频繁变动的需求(产品)
  • 人力成本(工时)
  • 上层不重视(沟通)

单元测试的原理 ​

通过确定测试用例,来编写测试代码,保证测试用例通过

js
//test.mjs
import chalk from 'chalk'

//一般测试框架(jest)需要做的事情

//描述测试场景
export function describe(desc, fn) {
  console.log(chalk.red(desc))
  fn()
}

//单元用例测试描述
export function it(desc, fn) {
  console.log(chalk.blue(desc));

  fn()
}

//断言库,做比对
export function expect(actual) {
  return {
    toBe(expected) {
      if (actual !== expected) {
        console.log(chalk.red(`预期和实际不相符,预期的值:${actual} ,实际的值:${expected}`));
        throw new Error(`预期和实际不相符,预期的值:${actual} ,实际的值:${expected}`)
      }
      console.log(chalk.green('测试通过'));

    }
  }
}


//__home__.test.mjs
import { describe, it, expect } from './test.mjs'

function sum(a, b) {
    return a + b
}

//测试用例
describe('测试 home 模块', () => {
    it('测试 sum 函数', () => {
        expect(sum(1, 1)).toBe(2)
        expect(sum(1, 2)).toBe(3)
        expect(sum(1, 3)).toBe(4)
        expect(sum(1, 4)).toBe(9)
    })
})

测试分类 ​

  • 单元测试(Unit Test),我们可以理解为小的功能测试
  • 集成测试(Integration Test),我们可以理解为小的模块测试
  • UI 测试(UI Test),我们可以理解为从页面出发去测试整体功能(仅前端)
  • E2E 测试(UI + 接口)

测试框架 ​

  • 测试框架:Jest、@vue/test-utils、Mocha、Jesmine、Tape、Tyu、Ava
  • 测试运行工具:Karam
  • 断言库:Assert、Should、Chai
  • 测试辅助工具:Sinon
  • 测试覆盖率工具:Istanbul
  • 无头浏览器:puppeteer

Jest ​

常用API ​

  • describe(name, fn): 用于组织相关的测试用例。
  • it(name, fn) 或 test(name, fn): 定义一个测试用例。
  • expect(value): 创建一个断言。
  • beforeEach(fn) 和 afterEach(fn): 在每个测试用例之前和之后运行的函数。
  • beforeAll(fn) 和 afterAll(fn): 在所有测试用例之前和之后运行的函数。
  • jest.fn(): 创建一个模拟函数。
  • jest.spyOn(object, methodName): 创建一个对象方法的间谍。
  • jest.mock(moduleName): 模拟一个模块。
  • toBe(value): 断言值是否等于给定的值。
  • toEqual(value): 断言值是否等于给定的值,并且类型也相同。

示例 ​

假设我们有一个简单的计算器模块:

js
// calculator.js
export function add(a, b) {
  return a + b;
}

export function subtract(a, b) {
  return a - b;
}

export function multiply(a, b) {
  return a * b;
}

export function divide(a, b) {
  if (b === 0) throw new Error("Cannot divide by zero");
  return a / b;
}

现在,让我们为这个计算器模块编写测试:

js
// calculator.test.js
import * as calculator from './calculator';

describe('Calculator', () => {
  beforeAll(() => {
    console.log('Starting Calculator tests');
  });

  afterAll(() => {
    console.log('Finished Calculator tests');
  });

  beforeEach(() => {
    jest.spyOn(console, 'log');
  });

  afterEach(() => {
    console.log.mockRestore();
  });

  it('adds two numbers correctly', () => {
    expect(calculator.add(2, 3)).toBe(5);
  });

  it('subtracts two numbers correctly', () => {
    expect(calculator.subtract(5, 3)).toBe(2);
  });

  it('multiplies two numbers correctly', () => {
    expect(calculator.multiply(2, 3)).toBe(6);
  });

  describe('divide', () => {
    it('divides two numbers correctly', () => {
      expect(calculator.divide(6, 2)).toBe(3);
    });

    it('throws an error when dividing by zero', () => {
      expect(() => calculator.divide(6, 0)).toThrow('Cannot divide by zero');
    });
  });

  it('logs a message', () => {
    console.log('This is a test log');
    expect(console.log).toHaveBeenCalledWith('This is a test log');
  });

  it('uses a mock function', () => {
    const mockFn = jest.fn();
    mockFn();
    expect(mockFn).toHaveBeenCalled();
  });
});

持续集成 / 持续部署(CI/CD) ​

CI/CD 是一套自动化工具和技术,它可以帮助你自动化地构建、测试和部署你的应用程序。简单来说,CI/CD 可以分为两个部分:

  • 持续集成(Continuous Integration):每次代码有新的提交,就会自动构建并测试代码,确保代码的质量。
  • 持续部署(Continuous Deployment/Delivery):构建通过后,自动部署到生产环境或测试环境。

常见的 CI/CD 工具和服务包括 Jenkins、Travis CI、GitLab CI、GitHub Actions 等。

GitHub Actions ​

GitHub Actions 是 GitHub 提供的 CI/CD 服务,它允许你定义在代码仓库中发生特定事件时运行的任务。

详细步骤 ​

  1. 创建 .github/workflows 目录 在你的仓库根目录下创建一个名为 .github/workflows 的目录。这个目录是用来存放 GitHub Actions 的工作流文件的。

  2. 编写工作流文件 在 .github/workflows 目录下创建一个 .yml 文件,例如 ci.yml。这个文件将描述工作流的各个步骤。 下面是一个简单的示例,演示如何设置一个简单的 CI/CD 流程:

yaml
name: CI/CD Pipeline

on:
  push:
    branches:
      - main  # 或者你希望触发的分支名称

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest

    steps:
    - name: Checkout code
      uses: actions/checkout@v2

    - name: Set up Node.js
      uses: actions/setup-node@v2
      with:
        node-version: 16  # 或者你希望使用的 Node.js 版本

    - name: Install dependencies
      run: |
        npm install

    - name: Build project
      run: |
        npm run build

    - name: Deploy to production server
      uses: appleboy/ssh-action@master
      with:
        host: your-production-server-host
        username: your-username
        password: your-password
        port: 22
        script: |
          cd /path/to/your/deployment/directory
          rsync -avz --delete ./* ${{ steps.deploy.outputs.user }}@$SERVER_IP:/path/to/your/deployment/directory
  1. 解释工作流文件
  • name: 工作流的名称。
  • on: 触发工作流的事件。在这个例子中,当有新的代码推送到 main 分支时,工作流会被触发。
  • jobs: 定义的工作流中的任务。
  • runs-on: 执行任务的操作系统环境。
  • steps: 每个任务包含的具体步骤。
  1. 测试工作流

  2. 提交更改:将 .yml 文件提交到仓库。

  3. 查看工作流状态:前往 GitHub 仓库的 "Actions" 标签页,可以看到工作流的状态和输出。

  4. 自动化部署 在上面的例子中,我们使用了 appleboy/ssh-action 来通过 SSH 连接到生产服务器,并将构建好的代码部署到服务器上。你需要替换相应的变量,例如 host、username、password 和 script。

进阶技巧

  • 环境变量:你可以使用 GitHub 的 Secret 功能来存储敏感信息,如数据库密码等。
  • 并行执行:你可以设置多个任务并行执行,以加快构建速度。
  • 条件执行:根据不同的条件执行不同的任务。
  1. 使用 Docker 部署 如果你想使用 Docker 来部署你的应用,可以修改工作流文件如下:
yaml
name: CI/CD Pipeline

on:
  push:
    branches:
      - main

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest

    steps:
    - name: Checkout code
      uses: actions/checkout@v2

    - name: Set up Docker
      run: docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD

    - name: Build and push Docker image
      run: |
        docker build -t your-docker-image-name .
        docker push your-docker-image-name

    - name: Deploy to production server
      uses: appleboy/ssh-action@master
      with:
        host: your-production-server-host
        username: your-username
        password: your-password
        port: 22
        script: |
          docker pull your-docker-image-name
          docker stop your-service-container
          docker rm your-service-container
          docker run -d --name your-service-container your-docker-image-name

如何生成一个新的 SSH 密钥对用于 GitHub Action ​

这个过程主要在你的本地计算机上完成,使用命令行工具。以下是详细步骤:

  1. 打开终端(在 Windows 上,你可以使用 Git Bash 或 Windows Subsystem for Linux)。

  2. 确保你当前不在任何 Git 仓库目录内(可以用 cd ~ 命令回到你的主目录)。

  3. 运行以下命令来生成新的 SSH 密钥对:

bash
ssh-keygen -t rsa -b 4096 -C "actions@github.com" -f gh-pages-deploy

这个命令的含义是:

  • -t rsa: 指定密钥类型为 RSA
  • -b 4096: 指定密钥长度为 4096 位
  • -C "actions@github.com": 添加一个注释,帮助识别这个密钥的用途
  • -f gh-pages-deploy: 指定输出文件名为 gh-pages-deploy
  1. 当提示输入密码时,直接按回车跳过。我们不需要为这个密钥设置密码。

  2. 命令执行完毕后,在当前目录下会生成两个文件:

  • gh-pages-deploy:这是私钥文件
  • gh-pages-deploy.pub:这是公钥文件
  1. 你可以用以下命令查看生成的文件
bash
ls -l gh-pages-deploy*
dir gh-pages-deploy*  //windows cmd
  1. 查看公钥内容(后面会用到):
bash
cat gh-pages-deploy.pub
type gh-pages-deploy.pub  //windows cmd
  1. 查看私钥内容(后面会用到):
bash
cat gh-pages-deploy
type gh-pages-deploy  //windows cmd

现在你已经成功生成了一个新的 SSH 密钥对。公钥(.pub 文件)将被添加到 liuhuakawaii.github.io 仓库的部署密钥中,而私钥将被添加到 EnlightenCode 仓库的 secrets 中。

记住,这个密钥对是专门为 GitHub Actions 创建的,不要用于其他目的,也不要与他人分享私钥。

完成这些步骤后,你就可以继续设置 GitHub 仓库了,如我之前描述的那样:

  1. 将公钥(gh-pages-deploy.pub 的内容)添加到 liuhuakawaii.github.io 仓库的部署密钥中。

  2. 将私钥(gh-pages-deploy 的内容)添加到 EnlightenCode 仓库的 secrets 中,名称为 ACTIONS_DEPLOY_KEY。

这样,GitHub Actions 就能安全地访问和更新你的 liuhuakawaii.github.io 仓库了。