2.1 项目概览

工程化简介 && package.json

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

上一章节中用了 5 节篇幅 ElementUI 源码学习:从零开始搭建 Vue 组件库汇总 讲解了如何编写一个组件、发布 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 的优秀实践。

项目目录结构

|-- 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) 文章中提到了package.json配置信息,下面将对element 项目的各项属性进行分析。

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

  • 必备属性(nameversion)

  • 描述信息(descriptionkeywordshomepagerepositorybugs)

  • NPM 脚本(scripts)

  • 依赖(dependenciesdevDependenciespeerDependencies)

  • 协议(license) 指定软件的开源协议类型

  • 目录文件相关(mainfilestypingsfaasunpkgstyle)

必备属性

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

依赖

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

NPM 脚本

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

描述信息

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

协议

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

目录文件相关

main

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

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

files

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

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

typings

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

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

详细参考 TypeScript docs.

style

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

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

unpkg

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

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

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

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

main 属性值lib/element-ui.common.jscommonjs 规范,由build/webpack.common.js打包生成。unpkg属性值 lib/index.jsumd 规范,由build/webpack.conf.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属性值。

<!-- 引入样式 -->
<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)

# 早期版本
{
    "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 源码学习

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

1️⃣ 源码剖析之工程化:项目概览、package.json、npm script 2️⃣ 源码剖析之工程化:项目构建、MD 解析 3️⃣ 源码剖析之工程化:打包配置 4️⃣ 源码剖析之工程化:发布部署、持续集成 5️⃣ 源码剖析之工程化:主题构建、自动化测试、代码质量检查、类型声明 6️⃣ 源码剖析之工程化:项目网站 7️⃣ 源码剖析之工程化:命令之图解

最后更新于