浅析前端工程化

前言

如今前端的能力与日俱增,所面对的业务也日益复杂化和多元化,相随而来的是庞大的代码量、更多人员的协作、要求更高的性能、更复杂的维护等。这就意味着手动进行前端开发将面临越来越大的挑战。

定义

前端工程化是指使用软件工程的方法来规范前端开发流程,包括四部分:模块化、组件化、规范化、自动化。通过在前端开发过程中,将前端开发的流程、工具和规范化,并使用相关技术实现自动化,包括但不限于代码编写、测试、构建、部署等环节,以提高前端开发效率、提高代码质量和可维护性。

实现

实现前端工程化,就是将前端项目进行模块化、组件化、规范化、自动化。面对这个问题前端社区一直在做努力,已有非常多的优秀框架、工具,如react、vue、webpack、vite、rollup、typescript、eslint、prettier、commitlint、less、sass等等。

模块化

在模块化编程中,开发者将程序分解成离散功能块(discrete chunks of functionality),并称之为模块。每个模块具有比完整程序更小的接触面,使得校验、调试、测试轻而易举。精心编写的模块提供了可靠的抽象和封装界限,使得应用程序中每个模块都具有条理清楚的设计和明确的目的。模块化是一种处理复杂系统分解成为更好的可管理模块的方式,它可以把系统代码划分为一系列职责单一,高度解耦且可替换的模块,系统中某一部分的变化将如何影响其它部分就会变得显而易见,系统的可维护性更加简单易得。而前端模块化又分为JS模块化、css模块化、资源模块化。

  1. js模块化:历史上,JavaScript一直没有模块(module)体系,无法将一个大程序拆分成互相依赖的小文件,再用简单的方法拼装起来。在 ES6 之前,社区制定了一些模块加载方案,最主要的有CommonJS和AMD两种。前者用于服务器,后者用于浏览器。ES6出现后,在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代CommonJS和AMD规范,成为浏览器和服务器通用的模块解决方案。
  2. css模块化:CSS模块化的解决方案有很多,可以分为两类:一类是彻底抛弃CSS,使用JS或JSON来写样式,如Radium,jsxstyle等。优点是能给 CSS 提供 JS 同样强大的模块化能力;缺点是不能利用成熟的CSS预处理器(或后处理器) Sass/Less/PostCSS。另一类是依旧使用CSS,但使用JS来管理样式依赖,代表是CSS Modules。CSS Modules则是通过JS来管理依赖,最大化的结合了JS模块化和CSS生态,API 简洁到几乎零学习成本。发布时依旧编译出单独的 JS 和 CSS。比如 Vue 中的 style scoped。
  3. 资源模块化:任何资源都能以模块的形式进行加载,将项目中的字体文件、图片等可以直接通过 JS 做统一的依赖关系处理。

组件化

组件是描述了 UI 的一部分,例如按钮或复选框。它既可以提高可维护性,也允许代码重用。多个组件也可以组合成更大的组件。而前端的组件化,其实是对项目进行自上而下的拆分,把通用的、可复用的功能中的模型(Model)、视图(View)和视图模型(ViewModel)以黑盒的形式封装到一个组件中,然后暴露一些开箱即用的函数和属性配置供外部组件调用,实现与业务逻辑的解耦,来达到代码间的高内聚、低耦合,实现功能模块的可配置、可复用、可扩展。除此之外,还可以再由这些组件组合更复杂的组件、页面。
组件化≠模块化。模块化是从文件层面上,对代码或资源进行拆分;而组件化是从设计层面上,对用户界面进行拆分。前端组件化更偏向UI层面,更多把逻辑放到页面中,使得UI元素复用性更高。
得益于技术的发展,目前三大框架在构建工具(例如 webpack、vite…)的配合下都可以很好的实现组件化。例如 Vue,使用 *.vue 文件就可以把 template、script、style 写在一起,一个 *.vue 文件就是一个组件。而如果不想使用任何框架和构建工具,亦可以通过Web Components实现组件化,它是浏览器原生支持的组件化标准。

规范化

浅析前端工程化

规范化即是提前约定好的执行标准,用于构建健壮、易维护的程序。具体我们可以制定哪些规范呢?我们可以看下软件开发的基本流程是:需求分析、系统设计、编码、测试、部署、维护等,一般地,在我们进行前端开发过程中由我们完成的应该包括设计、编码、测试、部署、维护这几个流程,那么在前端工程化中就应该对这几部分做出规范化。但现实中如果每个流程都按照规范来走,没有工具辅助,那么可能每天我们得花费大量的时间在执行规范上面。所以大部分公司会做出一些取舍,但是大家都不会绕开编码规范。而编码规范不仅仅包括代码编写规范,还应该包括工程目录结构、目录命名、文件命名等规范。并且前端作为多语言项目,那么代码编写规范上,还分HTML、JS、CSS等规范。

自动化

前端自动化是指前端项目的自动化构建、打包、测试及部署等流程。

示例

下面我们通过一个vue示例看看前端工程化实现。

初始化

首先,我们通过vue提供的手脚架创建并初始化项目

npm create vue@latest

浅析前端工程化

安装依赖并启动

npm i && npm run dev

好了,现在我们已经实现了前端工程化了。是的vue框架里面已经集成了一系列工程化工具,使得我们的项目支持模块化、组件化、规范化、自动化开发。

那么vue做了什么?又集成了什么呢?

ES6

通过设置package.json中type=“module”定义项目使用ESM规范,实现js模块化

浅析前端工程化

css scoped、css modules

vue实现css模块化有两种方式,一种是使用组件作用域 CSS,当 style 标签带有 scoped attribute 的时候,它的 CSS 只会影响当前组件的元素,和 Shadow DOM 中的样式封装类似。它的实现方式是通过 PostCSS 给声明了scoped的样式中选择器命中的元素添加一个自定义属性,再通过属性选择器实现作用域隔离样式的效果。

<style>
.example[data-v-f3f3eg9] {
  color: red;
}
</style>

<template>
  <div class="example" data-v-f3f3eg9>hi</div>
</template>

另一种则是CSS Modules,一个 <style module> 标签会被编译为 CSS Modules 并且将生成的 CSS class 作为 $style 对象暴露给组件:

<template>
  <p :class="$style.red">This should be red</p>
</template>

<style module>
.red {
  color: red;
}
</style>

vue默认使用scoped方式,而要启用css modules需要在插件vite-plugin-vue额外的配置。

资源模块化

vue使用vite作为构建、打包工具,使得静态资源可以像模块一样通过import引入,以此来管理项目对静态资源的依赖。

import imgUrl from './img.png' // imgUrl 在开发时会是 /img.png,在生产构建后会是 /assets/img.2d8efhg.png
document.getElementById('hero-img').src = imgUrl

组件化

Vue 实现了自己的组件模型,使我们可以在每个组件内封装自定义内容与逻辑。一般地,一个vue页面就是一个巨大的组件,然后由上自下拆分出一个个独立结构(dom)、独立表现(css)、独立行为(js)的组件。可以看成是层层嵌套的树状结构:
浅析前端工程化

规范化

我们可以看到vue项目已经有了良好的目录结构了,我们可以遵循它。
浅析前端工程化

从项目的初始化流程可以看到,我们可以集成eslint、prettier等。然后选择(或自己制定)我们的代码规范,通过eslint做代码质量检测、prettier做代码格式工具。

自动化

  1. 编译、打包:通过vite实现自动化编译与打包;
  2. 测试:通过vitest我们只要创建测试集即可实现自动化测试,还有cypress能够实现vue的视图表现测试,与vitest一样创建测试集即可实现自动化测试。

通过huskycommitlint做提交校验

安装husky

npm install --save-dev husky
npx husky init

husky是一个Git hooks工具,它提供了一系列git钩子,当初始化完成后,我们可以看到在项目根目录下生成了一个.husky目录,并在package.json添加了一个运行脚本配置。
浅析前端工程化

现在我们修改.husky下面的pre-commit文件为npm run lint,然后提交一个更改
浅析前端工程化
可以看到,变更在commit仓库之前触发了pre-commit钩子,执行了npm run lint。

安装commitlint,并搭配Angular的commit规范

npm i @commitlint/config-conventional @commitlint/cli -D

然后在.husky下添加commit-msg钩子文件,并写入:npx –no-install commitlint –edit “$1″,添加配置文件.commitlintrc.json

{ "extends": ["@commitlint/config-conventional"] }

现在我们提交一个abc的变更,会提示我们commit不规范

浅析前端工程化

改为chore: abc就可以。

CI/CD

CI(Continuous Integration,持续集成)/CD(Continuous Delivery,持续交付/Continuous Deployment,持续部署)属于DevOps的概念,指将传统开发过程中的代码构建、测试、部署以及基础设施配置等一系列流程的人工干预转变为自动化。

持续集成(CI)是指自动且频繁地将代码更改集成到共享源代码存储库中的做法。持续交付和/或持续部署(CD)是一个由两部分组成的过程,涉及代码更改的集成、测试和交付。持续交付不会自动部署到生产环境,持续部署则会自动将更新发布到生产环境。

CI始终是指持续集成,这是一种面向开发人员的自动化流程,有助于更频繁地将代码更改合并回共享分支或“主干”。进行这些更新时,会触发测试步骤的自动执行,以确保合并代码更改的可靠性。

CI/CD 中的“CD”指的是持续交付和/或持续部署,这些相关概念有时会交叉使用。二者均与管道中的更多阶段的自动化相关,但有时会分开使用,以说明自动化的程度。选择持续交付还是持续部署取决于开发团队和运维团队的风险承受能力及具体需求。

通过GitHub Actions实现CI/CD

我们通过Github Actions实现代码合并或推送到主分支,dependabot机器人升级依赖等动作,会自动触发测试和发布版本等一系列流程。

在项目根目录创建.github/workflows文件夹,然后在里面新建ci.yml文件和cd.yml文件

在ci.yml文件中写入:

name: CI

on:
  push:
    branches:
      - '**'
  pull_request:
    branches:
      - '**'
jobs:
  linter:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v2
        with:
          node-version: 16
      - run: npm ci
      - run: npm run lint
  tests:
    needs: linter
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v2
        with:
          node-version: 16
      - run: npm ci
      - run: npm run test:unit

上面配置大概意思就是,监听所有分支的push和pull_request动作,自动执行linter和tests任务。

现在我们将代码推送到GitHub上面,在Actions页签下面就可以看到我们的CI脚本运行了,正在对我们推送的变更做自动化校验与测试了。

浅析前端工程化

在cd.yml文件写入:

name: CD

on:
  push:
    branches:
      - 'master'
  pull_request:
    branches:
      - 'master'
jobs:
  deploy:
    needs: ci
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Use Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 16
      - name: Strict Install dependencies
        run: npm ci --ignore-scripts
      - name: Run build task
        run: npm run build
      - name: deploy pipelines
        uses: cross-the-world/ssh-scp-ssh-pipelines@latest
        with:
          host: ${{ secrets.DC_HOST }}
          user: ${{ secrets.DC_USER }}
          pass: ${{ secrets.DC_PASS }}
          scp: |
            ./dist/* => /www/wwwroot/web/vue/test
          last_ssh: |
            nginx -t
            nginx -s reload

上面配置大概意思就是,监听所有主分支的push和pull_request动作,在ci结束后进行部署,通过ssh-scp-ssh-pipelines将部署文件推送到服务器上,如果还需要重启服务等操作可以在last_ssh中配置上需要执行的指令。

此外DC_HOST、DC_USER、DC_PASS需要在GitHub上做配置。
浅析前端工程化

结语

篇幅、文笔有限,许多的细节没有体现出来。前端工程化是一个庞大的架构,需要我们深入了解,不断实践,以此寻找最佳实现。文中有错漏之处,欢迎指出与交流。

参考
es6.ruanyifeng.com/#docs/modul…
juejin.cn/post/697181…
xiangzhihong.blog.csdn.net/article/det…
segmentfault.com/a/119000003…
www.redhat.com/zh/topics/d…
docs.github.com/zh/actions

原文链接:https://juejin.cn/post/7350549827674079244 作者:jimi1126

(0)
上一篇 2024年3月30日 下午4:34
下一篇 2024年3月30日 下午4:39

相关推荐

发表回复

登录后才能评论