> For the complete documentation index, see [llms.txt](https://anduril.gitbook.io/learning-element/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://anduril.gitbook.io/learning-element/chapter02_fee/2.1-xiang-mu-gai-lan.md).

# 2.1 项目概览

工程化简介 && package.json

> 原文地址： <https://juejin.cn/post/6969258163136626725>

上一章节中用了 5 节篇幅 [ElementUI 源码学习:从零开始搭建 Vue 组件库汇总](https://juejin.cn/post/6950907020069306399) 讲解了如何编写一个组件、发布 npm 以及生成展示文档。接下来将分析 Element 项目的代码结构，学习其工程化思想。

## 前端工程化是什么？

前端开发早已从瘦客户端（thin client）架构/中心服务器（server-centric）架构进入到胖客户端（fat client）架构，各家技术百花齐放，让人目不暇接，直呼学不动了！伴随项目开发的复杂度日益增高，开发中需要直面各种问题： 开发效率、产品质量、多人协作等。

**前端工程化** 就是为了应对上述问题，把软件工程相关的方法和思想应用到前端日常开发中，以 **系统化的、规范化的、可度量的方法** 用于前端项目的开发、运行和维护等阶段，从而提高开发效率、提高产品质量、减少不必要的重复工作时间、降低开发难度/风险、降低企业成本(降本增效)。

如今前端项目的开发、构建、部署等主要环节，涉及了项目构建、代码开发、分支管理、自动化测试、持续集成、项目部署、性能等内容。如何用工程化的思想让开发更加系统化、标准化？主要分为 **模块化（组件化）、规范化、自动化** 等多个方面。

### 1️⃣ 模块化

项目按照其功能/业务拆分成相互独立的模块，可以独立运行。每个模块只包含与其功能相关的内容，模块之间通过接口调用，降低模块间的耦合。 将一个大的系统模块化之后，每个模块都可以被高度复用。模块不等于功能，一个功能可能包含多个模块(功能 > 模块)。

模块化让项目便于依赖管理、利于性能优化、提高可维护性。 各技术实现方案如下：

* JS 的模块化(CommonJS、 AMD、 CMD、UMD、ES6 Module)
* CSS 的模块化(BEM 命名规范、CSS Module、CSS In JS)
* 资源模块化(webpack loaders)

### 2️⃣ 组件化

组件化是为了解决项目代码重复问题，将其拆分成多个独立的组件给不同的功能使用，提高系统的代码重用（复用）性和易维护性。

### 📝 模块化 vs 组件化

组件化和模块化的中心思想都是 `分而治之` , 将一个项目拆分成更小的颗粒度的单元(`组件/模块`)，降低业务开发的复杂度。

> 模块化、组件化它们术语是相似的，作为分治思想的体现，最终都实现了 `高内聚，低耦合`。 通常认为“模块”比“组件”大。 项目模块化不一定要求组件化，在进行模块化拆分时可以完全不考虑代码重用。一般不会这么做,这不是 best practice。 组件化就如 UI 控件，可以在各个模块中使用。而模块化就比如一个消息列表界面，引用 table 组件实现。虽然它没有复用的需求，但我们也要把它封装成独立模块。

### 3️⃣ 规范化

规范是团队基本约定的内容,必须严格遵循,旨在增强团队开发协作、提高代码质量。

* 目录结构 (约定俗称)
* 编码规范\[HTML、CSS、JS、图片、命名等规范] (eslint、prettier)
* 前后端接口规范 (Swagger RESTful)
* 组件文档规范
* Git 分支管理 (Git Flow )
* Commit 描述规范 (Commitizen)
* 设计规范 (Material Design、Ant Design)

### 4️⃣ 自动化

将工作流程内容标准化，通过工具实现全/半自动化完成重复的工作，减少人的操作,实现标准统一、高质量交付。

* 文件构建 (webpack)
* 持续集成/构建/部署 (Travis CI)
* 自动化测试 (Jasmine、Mocha+chai、Jest)

***

下面将通过解析 element 项目源码，从结构、功能、源码方面逐一解析，学习其模块化、组件化、规范化、自动化等多维度学习 element 的优秀实践。

## 项目目录结构

```bash
|-- build  // 工程化构建脚本和配置（开发，测试，构建，部署，持续集成）
    |-- bin  // 项目文件构建
    |-- md-loader  // 自定义markdown-loder,用于组件文档展示
|-- examples // 存放组件示例
|-- lib // 构建后生成的文件，发布到npm包
|-- packages // 存放组件源码 及主题（样式）
|-- src  //存放入口文件以及各种工具文件
    |-- directives // 指令
    |-- locale // 国际化功能
    |-- mixins // 混入方法
    |-- transition // 动画
    |-- utils // 工具方法
|-- test  // 存放单元测试文件
|-- types  // 存放声明文件，方便引入 typescript 写的项目中，需要在 package.json 中指定 typing 属性的值为 声明的入口文件才能生效。
|-- components.json  // 完整组件清单,标注了组件的文件路径，方便 webpack 打包时获取组件的文件路径
|-- .travis.yml // 持续集成（CI）的配置文件，在代码提交时，根据该文件执行对应脚本。
|-- CHANGELOG.XX.md // 更新日志，共计 4 个不同语言版本。
|-- FAQ.md // 开发者对常见问题的解答。
|-- LICENSE // 开源许可证
|-- Makefile  // 通过 make new 创建组件目录结构，包含测试代码、入口文件、文档。其中 make new 就是 make 命令中的一种。make 命令是一个工程化编译工具。
|-- package.json  // 项目所需种模块以及配置信息
```

## package.json

接下来将从`package.json`文件看起，快速了解分析项目 。

`package.json` 是项目的清单, 定义了这个项目所需要的各种模块,以及项目的配置信息（比如名称、版本、许可证、git 仓库等元数据）。

之前在 [05.项目发布配置（github pages\&npm package）](https://juejin.cn/post/6955841948607315982#heading-7) 文章中提到了`package.json`配置信息,下面将对`element` 项目的各项属性进行分析。

项目的 `package.json` 中有非常多的属性,可以大致分为以下几类：

* 必备属性(`name`、`version`)
* 描述信息(`description`、`keywords`、`homepage`、`repository`、`bugs`)
* NPM 脚本(`scripts`)
* 依赖(`dependencies`、`devDependencies`、`peerDependencies`)
* 协议(`license`) 指定软件的开源协议类型
* 目录文件相关(`main`、`files`、`typings`、`faas`、`unpkg`、`style`)

### 必备属性

`name` 和 `version` 属性是必须的字段，这两个属性组成一个 npm 模块的唯一标识。\
`name` 是一个包的唯一标识，包名不能重复，可以执行 `npm view packageName` 查看包名是否已被占用，并可以查看一些基本信息

![image.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ac01ea528d86497c8c0c2825ad491706~tplv-k3u1fbpfcp-watermark.image)

### 依赖

根据此配置信息，运行 `npm || yarn install` 命令，自动下载所需的模块，也就是配置项目所需的运行和开发环境。

### NPM 脚本

指定了运行脚本命令的 npm 命令行缩写，覆盖整个项目的生命周期。下文将重点着墨讲解。

### 描述信息

记录项目的简介、关键字、项目主页、代码仓库、反馈 issues 等元信息。

### 协议

开源协议很多有很多，如何为自己的项目选合适的开源协议呢？ 可以到 <https://choosealicense.com/> 获取更详细的说明和指引。

不想劳神费力的可以使用速查图，参考自 [如何选择开源许可证？](https://www.ruanyifeng.com/blog/2011/05/how_to_choose_free_software_licenses.html) ![image.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a5adc4d1e939437c96b2928f9e0d069b~tplv-k3u1fbpfcp-watermark.image)

### 目录文件相关

#### main

`main` 属性指定程序的主入口文件，当在应用程序中导入此软件包时，应用程序会在该位置搜索模块的导出。在代码中引入整个 Element `import ElementUI from 'element-ui';`，实际上引入的就是 `lib/element-ui.common.js` 中暴露出去的模块。

```json
{
  "main": "lib/element-ui.common.js"
}
```

#### files

`files` 属性用于描述 `npm publish`后推送到 npm 服务器的文件列表，如果指定文件夹，则文件夹内的所有内容都会包含进来。也可以通过配置 `.npmignore` 文件来忽略文件上传。

```json
{
  "files": ["lib", "src", "packages", "types"]
}
```

#### typings

`typings`属性指定针对 typescript 的声明文件入口。

```json
{
  "main": "lib/element-ui.common.js",
  "typings": "types/index.d.ts"
}
```

详细参考 [TypeScript docs](https://www.typescriptlang.org/docs/handbook/declaration-files/publishing.html#including-declarations-in-your-npm-package).

#### style

`style`属性指定了样式入口文件。

```json
{
  "style": "lib/theme-chalk/index.css"
}
```

#### unpkg

`unpkg` 是一个前端常用的公共 CDN，它通过 URL 语法可以访问 NPM 上任何包的任何文件。

```js
unpkg.com/:package@:version/:file
```

当把包发布到 npm 上时， 不仅可以 NodeJs 环境使用，也可以通过 unpkg 获取在浏览器环境执行，不过需要符合 `umd` 规范。

```json
{
  "unpkg": "lib/index.js"
}
```

`main` 属性值`lib/element-ui.common.js`是 `commonjs` 规范，由`build/webpack.common.js`打包生成。`unpkg`属性值 `lib/index.js`是 `umd` 规范，由`build/webpack.conf.js`打包生成。关于打包模块功能会稍后详细说明。

```js
//pkg 指 package.json
//定义了 unpkg 属性时
https://unpkg.com/:package@:latestVersion/[pkg.unpkg]

//未定义 unpkg 属性时，将回退到 main 属性
https://unpkg.com/:package@:latestVersion/[pkg.main]
```

设置 `unpkg` 属性后，访问 <https://unpkg.com/element-ui>，按照上述规则自动访问 <https://unpkg.com/element-ui@2.15.1/lib/index.js>。

在之前的`me-vue-ui`包发布中，由于没有配置 `unpkg` 属性，访问 <https://unpkg.com/me-vue-ui> 时按照 `main` 属性定义的文件路径自动访问 <https://unpkg.com/me-vue-ui@0.1.2/lib/me-vue-ui.common.js>。

#### CDN 引入

若浏览器环境引入组件,只需要通过 unpkg.com/element-ui 获取到资源,在页面上引入 js 和 css 文件即可开始使用。 CSS 文件的路径是 `style` 属性值；js 文件的路径是基于`umd`规范的打包文件的路径--`unpkg`属性值。

```js
<!-- 引入样式 -->
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
<!-- 引入组件库 -->
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
```

#### faas

用于`faas deploy` 配置。NPM 脚本的`pub`命令存在 `sh build/deploy-faas.sh`调用，用于站点`element.eleme.io`的发布部署,不过在 2.15 版本之后被移除了（~~**具体使用情况无法重现**~~）。详见 commit [feat: add change log 2.15.0 (#20692)](https://github.com/ElemeFE/element/commit/9eb0a3d55a6f17378b1c33655e2ad5568fd4e9c1#diff-7ae45ad102eab3b6d7e7896acd08c427a9b25b346470d7bc6507b6481575d519)

```json
# 早期版本
{
    "pub": "npm run bootstrap && sh build/git-release.sh && sh build/release.sh && node build/bin/gen-indices.js && sh build/deploy-faas.sh",
}
```

## 关联阅读

点击以下链接，可以快速查看本系列其他文章：\
[专栏/Element 2.X 源码学习](https://juejin.cn/column/6961321064110489631)

👇 项目工程化系列文章链接如下，推荐按照顺序阅读文章 👇。

1️⃣ [源码剖析之工程化：项目概览、package.json、npm script](https://juejin.cn/post/6969258163136626725)\
2️⃣ [源码剖析之工程化：项目构建、MD 解析](https://juejin.cn/post/6969933702759940133)\
3️⃣ [源码剖析之工程化：打包配置](https://juejin.cn/post/6970691644114862111/)\
4️⃣ [源码剖析之工程化：发布部署、持续集成](https://juejin.cn/post/6971054455139598366/)\
5️⃣ [源码剖析之工程化：主题构建、自动化测试、代码质量检查、类型声明](https://juejin.cn/post/6971434343516340238)\
6️⃣ [源码剖析之工程化：项目网站](https://juejin.cn/post/6972180649625157645)\
7️⃣ [源码剖析之工程化：命令之图解](https://juejin.cn/post/6991347082552573965)
