前言
很多前端同学在快速实现产品经理验证性需求的时候,往往把功能点写在一个 html 页面上。单独的 html 页面确实可以快速搭建一个 demo,但是随着迭代的推进,越来越多的功能堆砌在了这个页面上,最终导致整个工程变得难以扩展、难以维护。
不仅如此,很多前端同学会直接在 html
页面上,使用 ES6+语法或者新的CSS语法,也会凭着自己的心情随意转换代码风格。这就导致了同样一套代码在不同版本的浏览器上会有不同的表现,大大降低了页面的稳定性和代码的可维护性。
开发人员一定要有远见!无论什么项目都应使用一个易扩展、易维护的架构。
避免给自己挖坑,将来反复代码重构。
随着前端技术的快速发展,越来越多成熟稳定的开源工具可以应用到项目当中。这些工具可以帮助我们提升开发效率,增强代码的可读性、健壮性,例如babel
和postcss
这样的代码转义工具,以及eslint
和 stylelint
这样的语法审查工具。
如果添加新的插件后,引起了文件报错,不要慌~ 吃口药,还能救!
。我们可以把出现的问题当成普通 bug,例如添加 eslint 之后,大部分的文件报错,我们就把每一个报错的文件当成是一个"bug"。相信我,前端同学花不了多长时间就能解决一个这样的"bug"。
架构未必最优!如有好的建议,欢迎指出~
【step-by-step】1. webpack 工程
目录:
这是根据 官方文档 中的示例搭建的工程,只是增加了几个常用的 plugin
。在 /examples 目录下的所有后继示例工程都是基于这个工程扩展的。我们会逐步在这个工程中添加 babel 、 eslint 、 postcss 、 stylelint 等插件。
如果,前端同学对于这个工程有诸多不清楚的地方,那么我强烈建议你先系统地学习一下 webpack
,可以参考 官方文档,也可以看一下中文文档,里面的教程非常棒!!!
注意:中文文档更新不及时,里面的很多示例不适用于新版本的 webpack。因此,建议可以多多查看官方文档。
1.1 目录结构
示例工程的目录结构:
|-- examples
|-- index.html // html模板
|-- package.json
|-- build // 配置文件文件夹
| |-- webpack.base.js // webpack的通用配置
| |-- webpack.dev.js // webpack开发环境的配置
| |-- webpack.prod.js // webpack生产环境的配置
|-- src
|-- index.js // 入口文件
|-- assets
|-- style.css // 样式
1.2 文件分析
1.2.1 webpack 配置
开发环境和生产环境的构建目标差异很大:
- 在开发环境中,我们需要强大的 热更新 功能,及 dev server 和 proxy 。
- 在生产环境中,我们的目标则转向于关注更小的 bundle、更轻量级的 source map 和优化资源,以改善加载时间。
热更新:
- 保留在完全重新加载页面期间丢失的应用程序状态。
- 只更新变更内容,以节省宝贵的开发时间。
- 在源代码中 CSS/JS 产生修改时,会立刻在浏览器中进行更新,这几乎相当于在浏览器 devtools 直接更改样式。
配置
遵循逻辑分离,我们通常为每个环境编写彼此独立的webpack 配置:
build/webpack.base.js:
const path = require('path')
module.exports = {
entry: {
app: './src/index.js'
},
output: {
filename: '[name].[hash].js',
path: path.resolve(__dirname, '../dist')
},
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}
]
}
}
在 webpack.base.js
中,我们设置了 entry
和 output
,并且在文件中引入了两个环境共用的插件。
注意: output.filename 不能使用 [chunkhash],只能使用 [hash],否则在 开发模式(dev) 下会报错。
build/webpack.dev.js:
// 合并数组和对象,但不是覆盖!!!
// https://www.npmjs.com/package/webpack-merge
const { merge } = require('webpack-merge')
// 生成 html5,并在body中使用script标签引入打包后的js文件。
// https://www.npmjs.com/package/html-webpack-plugin
const HtmlWebpackPlugin = require('html-webpack-plugin')
const base = require('./webpack.base')
module.exports = merge(base, {
mode: 'development', // 声明开发模式
devServer: {
contentBase: '../dist',
hot: true, // 热模块更新 - 局部更新
host: '0.0.0.0', // 设置后,其他机器可以通过ip访问
// port: '8080', // 端口
quiet: false,
clientLogLevel: 'warning',
proxy: {} // 跨域代理
},
// 'cheap-module-eval-source-map'低开销的source-map,但只映射行数。
// 'eval-source-map',初始化source map的时候比较慢,但是重新构建时,提供比较快的速度,并能正确映射出报错的位置
// https://www.webpackjs.com/configuration/devtool/
devtool: 'cheap-module-eval-source-map',
plugins: [
new HtmlWebpackPlugin({
filename: 'index.html', // 生成的文件名
template: 'index.html', // 使用的模板文件
inject: true // 生成的script插到body底部
})
]
})
在 webpack.dev.js
中,我们将 mode
设置为 development
,并且为此环境添加了强大的 devtool (强大的 source map)和 devServer 。
build/webpack.prod.js
:
const path = require('path')
// 合并数组和对象,但不是覆盖!!!
// https://www.npmjs.com/package/webpack-merge
const { merge } = require('webpack-merge') // 合并数组和对象,但不是覆盖!!!
// 生成 gzip 文件
// https://www.npmjs.com/package/compression-webpack-plugin
const CompressionPlugin = require('compression-webpack-plugin')
// 分析打包出来的文件
// https://www.npmjs.com/package/webpack-bundle-analyzer
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
// 将CSS提取为独立的文件的插件,对每个包含css的js文件都会创建一个CSS文件,支持按需加载css和sourceMap
// https://www.npmjs.com/package/mini-css-extract-plugin
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
// 生成 html5,并在body中使用script标签引入打包后的js文件。
// https://www.npmjs.com/package/html-webpack-plugin
const HtmlWebpackPlugin = require('html-webpack-plugin')
// 清理 dist 文件夹
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
// 压缩器 用于代替webpack自带的压缩器。
const TerserJSPlugin = require('terser-webpack-plugin')
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
const base = require('./webpack.base')
const config = {
bundleAnalyzerReport: false,
productionGzip: true
}
const webpackConfig = merge(base, {
mode: 'production',
output: {
filename: '[name].[chunkhash].js',
path: path.resolve(__dirname, '../dist')
},
// source-map: 整个source map 作为独立文件生成,并未bundle添加一个引用注释。
// https://www.webpackjs.com/configuration/devtool/
devtool: 'source-map',
optimization: {
minimizer: [new TerserJSPlugin({}), new OptimizeCSSAssetsPlugin({})],
splitChunks: { chunks: 'all' }
},
module: {
rules: [
{
test: /\.css$/i,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
esModule: true
}
},
'css-loader'
]
}
]
},
plugins: [
new CleanWebpackPlugin(),
new MiniCssExtractPlugin({
filename: '[name].[chunkhash].css'
}),
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'index.html',
minify: {
removeComments: true, // 移除html中的注释
collapseWhitespace: true, // 去掉留白部分
removeAttributeQuotes: true // 去掉属性中的引号
},
inject: true
})
]
})
// 生成 gzip 文件
// https://www.npmjs.com/package/compression-webpack-plugin
if (config.productionGzip) {
const CompressionWebpackPlugin = require('compression-webpack-plugin')
webpackConfig.plugins.push(
new CompressionWebpackPlugin({
test: new RegExp('\\.(js|css)$'), //只打包 js和css 文件
threshold: 10240,
minRatio: 0.8
})
)
}
// 分析打包出来的文件
// https://www.npmjs.com/package/webpack-bundle-analyzer
if (config.bundleAnalyzerReport) {
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
webpackConfig.plugins.push(new BundleAnalyzerPlugin())
}
module.exports = webpackConfig
在 webpack.prod.js
中,添加了 optimization.slitChunks.chunks
( SlitChunksPlugin ) ,可以将公共的依赖模块抽象到 chunk 中,或者抽象到一个新生成的 chunk 中。
mode
webpack 4+ 提供了 mode 配置选项,能够自动调用 webpack 的优化策略。默认值为 production。
string = 'production': 'none' | 'development' | 'production'
在 webpack
配置文件中
module.exports = {
mode: 'development' // 默认是 production
}
- 在 development(开发模式)下,会自动引用 NamedChunksPlugin 和 NameModulesPlugin。
- 在 production(生产模式)下,会自动引用 FlagDependencyUsagePlugin、FlagIncludedChunksPlugin、ModuleConcatenationPlugin、NoEmitOnErrorsPlugin、OccurrenceOrderPlugin、SideEffectsFlagPlugin 及 TerserPlugin。
devtool
devtool 选项控制是否生成以及如何生成 source map 。
Source map 就是一个信息文件,里面储存着位置信息。也就是说,source map 能把转换后代码还原成转换前的样子。有了 source map,debug工具可以把压缩代码显示为原始代码。详细可参考:阮一峰-JavaScript Source Map 详解
在 webpack.dev.js
中,推荐使用以下两种 source map
:
- cheap-module-eval-source-map ✅ : 低开销的 source-map ,但只映射行数。(推荐)
- eval-source-map: 初始化 source map 的时候比较慢,但是重新构建时,提供比较快的速度,并能正确映射出报错的位置。
在 webpack.prod.js
中,推荐使用以下三种 source map
:
- source-map ✅ : 整个 source map 作为独立文件生成,并为 bundle 添加一个引用注释。(推荐!需在nginx中,设置可访问.map 文件的ip 白名单,以保护代码安全。)
- hidden-source-map: hidden-source-map 与 source-map 相同,但是不会为 bundle 添加引用注释。
- nosource-source-map: 创建的 source map 不包括 源代码内容。只映射客户端上的堆栈信息,不会暴露所有源代码。
1.2.2 package.json
通常而言,当我们阅读一个项目的源代码时,最好是从 package.json
开始。这样做可以方便我们找到项目的入口,也就是起始位置。
package.json
:
{
"name": "01-base",
"version": "1.0.0",
"description": "",
"scripts": {
"dev": "webpack-dev-server --config build/webpack.dev.js",
"build": "webpack --config build/webpack.prod.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/core": "^7.11.4",
"@babel/plugin-transform-runtime": "^7.11.0",
"@babel/preset-env": "^7.11.0",
"babel-loader": "^8.1.0",
"clean-webpack-plugin": "^3.0.0",
"compression-webpack-plugin": "^5.0.1",
"css-loader": "^4.2.2",
"html-webpack-plugin": "^4.3.0",
"mini-css-extract-plugin": "^0.10.0",
"optimize-css-assets-webpack-plugin": "^5.0.4",
"style-loader": "^1.2.1",
"terser-webpack-plugin": "^4.1.0",
"webpack": "^4.44.1",
"webpack-bundle-analyzer": "^3.8.0",
"webpack-cli": "^3.3.12",
"webpack-dev-server": "^3.11.0",
"webpack-merge": "^5.1.2"
}
}
在 package.json
中,注册了两个 脚本命令
,以便于快速进行启动 devServer
及 打包文件。
{
"scripts": {
"dev": "webpack-dev-server --config build/webpack.dev.js",
"build": "webpack --config build/webpack.prod.js"
}
}
同时,引用一些常用的plugin
:
- clean-webpack-plugin :在 webpack 构建时,清除 dist 文件夹。
- compression-webpack-plugin :打包时,自动生成 js、css 的 gzip 文件。
- html-webpack-plugin :生成 html5 文件。并能以 script 标签的形式引入打包后的
bundle.js
文件。 - mini-css-extract-plugin :抽取 css,并生成文件。
- webpack-bundle-analyzer :分析
bundle.js
内的组件成分。 - webpack-merge : 合并数组和对象。(但
不是
覆盖!!!) - terser-webpack-plugin :用
terser
压缩打包后的 JS 代码。 - optimize-css-assets-webpack-plugin :压缩打包后的 CSS 文件。
1.2.3 其他文件
src/index.js
:
import './assets/style.css'
function foo() {
document.body.innerText = 'hello world'
}
foo()
src/assets/style.css
:
body {
background-color: red;
}
index.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>模板</title>
</head>
<body></body>
</html>
1.3 运行结果
运行以下命令,查看结果
> yarn
> npm run dev
打开 http://0.0.0.0:8080/ 地址查看结果:
1.4 示例工程
示例工程:
|-- examples
|-- index.html
|-- package.json
|-- build
| |-- webpack.base.js // 通用配置
| |-- webpack.dev.js // 开发环境下的配置
| |-- webpack.prod.js // 生成环境下的配置
|-- src
|-- index.js
|-- assets
|-- style.css
【step-by-step】02. 使用 babel 转译 ES6 + 语法
本篇文档的目的是希望前端同学能够以
复制粘贴
的方式,快速在自己项目中添加插件。因此,一些说明性质的知识将以推荐阅读
的方式推荐给大家。
示例工程:02-add-babel-loader
Babel 是一个 JavaScript 编译器,允许我们在项目中使用下一代 JS 语法(ES 6 7 8 9...)。Babel
将 ECMAScript 2015+ 版本的代码转译成旧版本浏览器也能运行的代码。
比如箭头函数 :
// Babel 输入: ES2015 箭头函数
;[1, 2, 3].map((n) => n + 1)
// Babel 输出: ES5 语法实现的同等功能
;[1, 2, 3].map(function (n) {
return n + 1
})
你可以通过安装预设(presets,一系列同类插件组合)或 插件(plugins)告诉 Babel 应该如何进行代码转译,例如:@babel/preset-env
(转译 ES6+ 的语法)、@babel/preset-react
(转译 React )。
2.1 Babel 工具介绍
注意:如果安装
Babel
后不使用任何plugin
,那么Babel
是不会转译ES6+ 语法
的。
@babel/preset-env
Babel 推崇功能的单一性,例如我们想使用 ES6 的箭头函数,需要对应的转化插件。
yarn add @babel/plugin-transform-arrow-functions -D
并在 babel.config.js
中注册
// babel.config.js
module.exports = {
plugins: ['@babel/plugin-transform-arrow-functions']
}
但是我们不可能一个个的设置所有需要的plugin
,例如 ES2015
就包含了二十几个转译插件。为了解决这个问题,babel 还提供了一组插件的套餐
,也就是 preset
。
我们在项目中添加的是 @babel/preset-env
, 它会根据目标浏览器(2.3.4 指定 browserslist)自动引入对应的插件列表,然后再进行编译。可以简单理解,它是一堆 Plugin 的豪华全家桶
,包含了我们常用的 ES2015、ES2016、ES2017 等最新语法的转化插件。
@babel/polyfill
由于 babel 只进行语法转换(如箭头函数),你可以使用 polyfill
来支持新的全局变量,如 Promise
、Symbol
或 新的原生方法。
@babel/preset-env 与 @babel/polyfill
@babel/polyfill
在 @babel 7.4 已经废弃了,可以通过配置 @babel/preset-env
来引入 polyfill。
主要是以下两个参数:
targets
: 本工程需要支持的目标浏览器列表。useBuiltIns
: 此参数决定了 babel 打包时如何处理@babel/polyfill 语句。entry
: 去掉目标浏览器已支持的 polyfill 模块,将浏览器不支持的都引入对应的 polyfill 模块。usage
✅: 打包时会自动根据实际代码的使用情况,结合 targets 引入代码里实际用到部分 polyfill 模块false
(默认) : 不会自动引入 polyfill 模块,对 polyfill 模块屏蔽
建议使用 useBuiltIns: usage。Babel会根据目标浏览器的支持情况和源代码中出现的语言特性,按需引入用到的 polyfill 文件,这确保了最终包里 polyfill 数量的最小化。
@babel/plugin-transform-runtime
babel-polyfill
会污染全局变量,它给很多类的原型链上添加新的方法。如果我们也开发一套工具类库或者临时在原型链上添加了方法,那么这些自定义方法有可能会跟 polyfill
中的方法互相影响,使得整个工程中的代码都变得不可控。
@babel/plugin-transform-runtime
以沙箱垫片的方式防止污染全局,并抽离公共的 helper function,以节省代码的冗余。
以 async/await
举例,如果不使用这个 plugin (即默认情况),转换后的代码大概是:
// babel 添加一个方法,把 async 转化为 generator
function _asyncToGenerator(fn) {
return function () {
// ....
}
}
// 具体使用处
var _ref = _asyncToGenerator(function* (arg1, arg2) {
yield (0, something)(arg1, arg2)
})
在使用了 babel-plugin-transform-runtime
了之后,转化后的代码会变成
// 从直接定义改为引用,这样就不会重复定义了。
var _asyncToGenerator2 = require('babel-runtime/helpers/asyncToGenerator')
var _asyncToGenerator3 = _interopRequireDefault(_asyncToGenerator2)
// 具体使用处是一样的
var _ref = _asyncToGenerator3(function* (arg1, arg2) {
yield (0, something)(arg1, arg2)
})
使用 babel-plugin-transform-runtime
避免了代码重复的问题。
推荐阅读:
2.2 添加步骤
根据 babel-loader 中的示例说明,我们在项目中添加 babel-loader
的步骤如下:
- 安装依赖
- 添加
babel-loader
的配置 - 添加
.babelrc
配置文件 - 指定
browserslist
2.3 具体流程
2.3.1 安装依赖
在 webpack 项目中使用 babel
转义 ES6 以上的语法,最好的方式当然是使用 babel-loader 了。babel-loader
可以自动帮我们转译 ES6 以上的语法(Loader makes the life easier~ 🎉)。
运行以下命令来安装 babel-loader
yarn add babel-loader @babel/core @babel/preset-env @babel/plugin-transform-runtime core-js -D
2.3.2 调整 webpack 配置
由于开发环境和生产环境都需要使用 babel
转译。因此,我们在 webpack.base.js
中添加 babel-loader
的配置。可参考 官方例子。
build/webpack.base.js:
const path = require('path')
module.exports = {
module: {
// ...
rules: [
// ---- 在此添加 babel-loader 的配置 ----
{
test: /\.m?js$/,
exclude: /(node_modules)/,
use: {
loader: 'babel-loader'
}
}
// **** 在此添加 babel-loader 的配置 END ****
]
}
// ...
}
2.3.3 添加配置文件
在根目录下添加 .babelrc
文件:
{
"presets": [
[
"@babel/preset-env",
{
"useBuiltIns": "usage",
"corejs": "3"
}
]
],
"plugins": [
[
"@babel/plugin-transform-runtime"
]
]
}
在上面的配置中
- @babel/preset-env:是一系列插件的集合,包含了我们在 babel6 中常用的 es2015、es2016、es2017 等最新的语法转换插件。允许我们使用最新的 js 语法,比如 let、const、箭头函数等。
- "useBuiltIns": "usage":在项目中按需添加 polyfills 中的方法。由于
babel
只进行语法转换(如箭头函数),你可以使用polyfill
来支持新的全局变量,如 Promise 或 新的原生方法。@babel/polyfill 在 @babel 7.4 已经废弃了,可以通过使用@babel/preset-env
来引入 polyfill,即在.babelrc
中,配置 userbuiltIns: 'usage'。具体可以参考 polyfill 及 useBuiltIns。 - "corejs": "3": useBuiltIns 使用 usage 的时候,需要声明 corejs 的版本
- @babel/plugin-transform-runtime:以沙箱垫片的方式防止污染全局,并抽离公共的 helper function,以节省代码的冗余。
2.3.4 指定 browserslist
通过 browserslist 指定了项目的目标浏览器的范围。这个值会被 @babel/preset-env 和 Autoprefixer 用来确定需要转译的 JavaScript 特性和需要添加的 CSS 浏览器前缀。
browserslist
广泛应用于多个库中:
- Babel
- postcss-preset-env
- Autoprefixer
- ESLint
- StyleLint
在 package.json
中配置 browserslist
。
{
"browserslist": ["> 0.25%", "not dead", "last 2 versions", "not ie <= 8"]
}
2.4 测试
调试
我们在 index.js
中添加 ES6+
语法,然后进行 debug
或者 打包
,查看 ES6+
的语法是否被转译成了 ES5
语法 :
const bar = {
a: {
b: 123,
c: {
d: 'hello',
e() {
console.info(123)
}
}
}
}
const bb = {
...bar,
app: [1, 2, 3, 4],
bpp: 'hello world'.includes('ll')
}
document.body.innerText = `这个是 ${JSON.stringify(bb)}`
测试
打开 http://0.0.0.0:8080/ 页面显示:
这个是 {"a":{"b":123,"c":{"d":"hello"}},"app":[1,2,3,4],"bpp":true}
运行 npm run build
查看打包后的文件:
// app.41ff3a5a6bab98ef169d.js
// ...
function u(e) {
for (var r = 1; r < arguments.length; r++) {
var t = null != arguments[r] ? arguments[r] : {}
r % 2
? c(Object(t), !0).forEach(function (r) {
o()(e, r, t[r])
})
: Object.getOwnPropertyDescriptors
? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t))
: c(Object(t)).forEach(function (r) {
Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r))
})
}
return e
}
var i = u(
u(
{},
{
a: {
b: 123,
c: {
d: 'hello',
e: function () {
console.info(123)
}
}
}
}
),
{},
{
app: [1, 2, 3, 4],
bpp: 'hello world'.includes('ll')
}
)
document.body.innerText = '这个是 '.concat(i)
// ...
通过打包后的文件,可以分析出,ES6 语法已经被转义了。其中 String.prototype.includes 被定义在 vendor~app.js 中。
2.5 示例工程
示例工程:
|-- examples
|-- .babelrc // babel 配置文件
|-- index.html
|-- package.json
|-- build
| |-- webpack.base.js
| |-- webpack.dev.js
| |-- webpack.prod.js
|-- src
|-- index.js
|-- assets
|-- style.css
2.6 总结
在项目中添加 babel-loader
的步骤:
- 安装依赖
- 添加
babel-loader
的配置 - 添加
.babelrc
配置文件 - 指定
browserslist
【step-by-step】3. 使用 .editorconfig 设置 IDE 的配置
我们期待能有一套自动化工具,帮助我们自动调整代码风格,自动审查代码语法。使我们能够把更多的精力投放到业务开发中,而不是千奇百怪的代码风格上。
因此,我们在工程中添加几个工具:
- .editorconfig : 让IDE遵循同样的编写规则。
- prettier : 代码格式化工具。
- eslint : 审查 js 语法。
- stylelint : 审查 css 语法。
- commitlint : 审查 git commit 信息格式。
TL;DR
.editorconfig
文件是为了在不同的开发环境上遵循 .editorconfig
的配置,以达到拥有同样的代码风格表现。
在前端工程中,即使已经有 eslint
及 prettier
这样的代码格式化工具,也建议使用 .editorconfig
。 .editorconfig
可以对其他文件格式的代码风格进行控制,例如 .py
、.md
。
3.1 添加 .editorconfig 文件
在工程的根目录下添加 .editorconfig
文件
# editorconfig.org
# 表示是最顶层的配置文件,IDE发现root设为true时,才会停止查找.editorconfig文件
root = true
[*]
# 设置缩进风格(tab是硬缩进,space为软缩进)
indent_style = space
# 用一个整数定义的列数来设置缩进的宽度,如果indent_style为tab,则此属性默认为tab_width
indent_size = 2
# 设置换行符,值为lf、cr和crlf
end_of_line = lf
# 设置编码,值为latin1、utf-8、utf-8-bom、utf-16be和utf-16le,不建议使用utf-8-bom
charset = utf-8
# 设为true表示会去除换行行首的任意空白字符。
trim_trailing_whitespace = true
# 设为true表示使文件以一个空白行结尾
insert_final_newline = true
3.2 配置 IDE
webstorm配置如下:
VS Code 配置
参考这篇文章:VS Code 配置文档
推荐文章
【step-by-step】4. 使用 prettier 格式化代码
我们期待能有一套自动化工具,帮助我们自动调整代码风格,自动审查代码语法。使我们能够把更多的精力投放到业务开发中,而不是千奇百怪的代码风格上。
因此,我们在工程中添加几个工具:
- .editorconfig : 让IDE遵循同样的编写规则。
- prettier : 代码格式化工具。
- eslint : 审查 js 语法。
- stylelint : 审查 css 语法。
- commitlint : 审查 git commit 信息格式。
TL;DR
Building and enforcing a style guide
Prettier 是一个代码格式工具,支持主流的前端语言(js, ts, ES6, ES7, markdown 等等)。 Prettier
会根据书写的代码,重新解析和构建代码的显示格式,确保团队使用统一的代码风格。
4.1 安装步骤
根据 prettier.io 中的示例说明,我们在项目中添加 prettier
的步骤如下:
- 安装依赖
- 创建
.prettierrc.js
文件 - 添加 IDE 插件
4.2 具体流程
4.2.1 安装依赖
yarn add prettier --dev --exact
4.2.2 添加配置文件
在工程的根目录下,创建 .prettierrc.js
文件。
module.exports = {
printWidth: 120, // 每行代码最大长度 默认为80
tabWidth: 2, //一个tab代表几个空格数
useTabs: false, //是否使用tab进行缩进
semi: false, // 声明后带分号
singleQuote: true, // 使用单引号
trailingComma: 'none',
endOfLine: 'auto'
}
由于在windows与mac系统中的换行符不同,建议在
.prettierrc.js
文件中添加endOfLine
属性。
4.2.3 添加脚本命令(可选)
在 package.json
中,添加 prettier
命令:
{
"script": {
"prettier": "prettier --write ./src/**.{js,css}"
}
}
运行 npm run prettier
命令,prettier
可以按照配置文件调整 src
文件夹中的代码。
4.3 测试
为了测试 prettier
是否安装成功,我们弄乱代码中的格式,然后使用 prettier
进行修复。
调整文件
调整 index.js
的文件格式
// 两个问题:1. 多行; 2. 段位分号;
import './assets/style.css';
function foo() {
document.body.innerText = 'hello world';
}
foo();
以及 src/assets/style.css
文件的格式
body {
background-color: red;
}
.example {
background-color: red;
}
运行 prettier
运行命令 npx prettier --write ./src/**
后,查看 src文件夹
中的 js 文件和 css 文件是否格式化了。
body {
background-color: red;
}
.example {
background-color: red;
}
done!
4.4 在 IDE 中使用插件
webstorm
在 webpack 中使用 prettier插件
,在保存(ctrl + S)的时候,自动调整文件的格式。
vscode
vscode 可参考:
- 安装方法是点击“Extension”图标,然后搜索 “prettier”,找到官方插件并安装:
-
代码的vscode窗口中,使用快捷键“CTRL + Shift + P”打开vscode命令框,在框中输入“format”关键字,可以看到有2个选项:
-
Format Document (快捷键 Shift+Alt+F)对整个文档做格式化
-
Format Selection (快捷键Ctrl+K, Ctrl+F)对选择代码做格式化
-
4.5 示例工程
示例工程:
|-- examples
|-- .babelrc // babel配置
|-- .editorconfig // editorconfig 的配置
|-- .prettierrc.js // prettier 的配置
|-- index.html
|-- package.json
|-- build
| |-- webpack.base.js
| |-- webpack.dev.js
| |-- webpack.prod.js
|-- src
|-- index.js
|-- assets
|-- style.css
推荐阅读
【step-by-step】5. 使用 ESLint 审查 JS 代码
我们期待能有一套自动化工具,帮助我们自动调整代码风格,自动审查代码语法。使我们能够把更多的精力投放到业务开发中,而不是千奇百怪的代码风格上。
因此,我们在工程中添加几个工具:
- .editorconfig : 让IDE遵循同样的编写规则。
- prettier : 代码格式化工具。
- eslint : 审查 js 语法。
- stylelint : 审查 css 语法。
- commitlint : 审查 git commit 信息格式。
.editorconfig
与 prettier
可以自动调整代码风格,却无法约束语法。因此,还是需要 ESLint 这样专业的代码语法检查工具。
TL;DR
ESLint 是在 ECMAScript/JavaScript 代码中识别和报告模式匹配的工具,它的目标是保证代码的一致性和避免错误。在 webpack 项目中,使用 eslint-loader
自动检查 JS 代码。
在安装 eslint-loader
之前,我们需要先安装 ESLint 及其配置文件。
5.1 步骤
根据 ESLint 文档中的示例说明,我们在项目中添加 ESLint
的步骤如下:
- 安装依赖
- 生成
.eslintrc
配置文件 - 添加
eslint-plugin-prettier
插件 - 调整
.eslintrc
文件
5.2 具体流程
5.2.1 安装 ESLint
yarn add eslint -D
5.2.2 生成 .eslintrc 文件
创建 .eslintrc.js
文件有两种方法:
- 创建
.eslintrc.js
文件 - 执行脚本命令自动生成
方法一:创建 .eslintrc.js
配置文件
安装依赖
yarn add eslint-config-standard eslint-plugin-standard eslint-plugin-promise eslint-plugin-import eslint-plugin-node -D
在工程的根目录下创建 .eslintrc.js
文件
module.exports = {
env: {
browser: true,
es2020: true
},
extends: ['standard'],
parserOptions: {
ecmaVersion: 12,
sourceType: 'module'
},
rules: {}
}
方法二:运行脚本
安装完 eslint
后,运行 npx eslint --init
可以自动生成 .eslintrc
配置文件。
npx 的用法:阮一峰-npx 使用教程
在运行 npx eslint --init
后,出现下面三个选项,选择第三项:
npx eslint --init
? How would you like to use ESLint? …
To check syntax only
To check syntax and find problems
❯ To check syntax, find problems, and enforce code style
module
的类型中,选择 JavaScript modules (import/export)
npx eslint --init
✔ How would you like to use ESLint? · style
? What type of modules does your project use? …
❯ JavaScript modules (import/export)
CommonJS (require/exports)
None of these
framework
中选择 None of these
npx eslint --init
✔ How would you like to use ESLint? · style
✔ What type of modules does your project use? · esm
? Which framework does your project use? …
React
Vue.js
❯ None of these
然后选择在 Browser
上运行代码。
npx eslint --init
✔ How would you like to use ESLint? · style
✔ What type of modules does your project use? · esm
✔ Which framework does your project use? · none
✔ Does your project use TypeScript? · No / Yes
? Where does your code run? … (Press <space> to select, <a> to toggle all, <i> to invert selection)
✔ Browser
✔ Node
代码风格选择第一项
npx eslint --init
✔ How would you like to use ESLint? · style
✔ What type of modules does your project use? · esm
✔ Which framework does your project use? · none
✔ Does your project use TypeScript? · No / Yes
✔ Where does your code run? · browser
? How would you like to define a style for your project? …
❯ Use a popular style guide
Answer questions about your style
Inspect your JavaScript file(s)
代码规范选择 Standard
标准的格式
npx eslint --init
✔ How would you like to use ESLint? · style
✔ What type of modules does your project use? · esm
✔ Which framework does your project use? · none
✔ Does your project use TypeScript? · No / Yes
✔ Where does your code run? · browser
✔ How would you like to define a style for your project? · guide
? Which style guide do you want to follow? …
Airbnb: https://github.com/airbnb/javascript
❯ Standard: https://github.com/standard/standard
Google: https://github.com/google/eslint-config-google
配置文件的类型中,选择 JavaScript
npx eslint --init
✔ How would you like to use ESLint? · style
✔ What type of modules does your project use? · esm
✔ Which framework does your project use? · none
✔ Does your project use TypeScript? · No / Yes
✔ Where does your code run? · browser
✔ How would you like to define a style for your project? · guide
✔ Which style guide do you want to follow? · standard
? What format do you want your config file to be in? …
❯ JavaScript
YAML
JSON
自此,在项目的根目录下,生成了 .eslintrc.js
文件
module.exports = {
'env': {
'browser': true,
'es2020': true
},
'extends': [
'standard'
],
'parserOptions': {
'ecmaVersion': 12,
'sourceType': 'module'
},
'rules': {}
}
动态例子
5.2.3 添加 eslint-plugin-prettier 与 eslint-config-prettier 插件
为了让 prettier
能够更好的配合 eslint
检查代码,我们安装以下插件:
- eslint-config-prettier: Prettier 与 Linter 工具配合的时候,插件间的配置会彼此冲突。为了解决这个问题,我们使用 eslint-config-prettier 插件,关闭部分
ESLint配置
,让 Prettier 的配置覆盖 ESLint 的配置。 - eslint-plugin-prettier:ESLint 会使用
prettier
的规则,对工程进行检查。
运行以下的命令,安装 plugin
与 配置
yarn add eslint-plugin-prettier eslint-config-prettier eslint-config-recommended -D
5.2.4 调整 .eslintrc.js 文件
我们可以使用 prettier 提供的配置 plugin:prettier/recommended
,它会做三件事:
- 开启
eslint-plugin-prettier
- 设置
prettier/prettier
rule 为 "error" - 继承
eslint-config-prettier
的配置
因此,我们在工程中继承 ESLint(eslint:recommended) 与 Prettier(plugin:prettier/recommended) 的配置,即在 extends
列表中,添加 eslint:recommended
和 plugin:prettier/recommended
。
.eslintrc.js
module.exports = {
env: {
browser: true,
es2020: true
},
extends: ['eslint:recommended', 'plugin:prettier/recommended'],
parserOptions: {
ecmaVersion: 12,
sourceType: 'module'
}
}
5.3 测试
我们调整 index.js
文件,故意搞错代码后。执行 ESLint
的脚本,查看 ESLint
是否能够检查出相关的错误。
调整 src/index.js
src/index.js
const bar = {
a: {
b: 123,
c: {
d: 'hello',
e () {
// XXXXXX e后括号的格式不对
console.info(123)
}
}
}
}
// XXXXXX规则中,不能以分号结尾。
const bb = {
...bar,
app: [1, 2, 3, 4],
bpp: 'hello world'.includes('ll')
};
document.body.innerText = `这个是 ${JSON.stringify(bb)}`
运行命令
运行 npx eslint src/**/*.js
命令来检查工程中的 js 语法,来验证 eslint 是否配置成功。
文件的匹配规则可以参考 glob 库中的介绍 。
查看结果
npx eslint src/**/*.js
/Users/CodingNutsZac/Documents/founder/git/test/test-webpack-tutorial/src/index.js
7:8 error Delete `·` prettier/prettier
19:2 error Delete `;` prettier/prettier
✖ 2 problems (2 errors, 0 warnings)
2 errors and 0 warnings potentially fixable with the `--fix` option.
到目前为止,eslint配置
正确。
5.4 总结
添加 eslint
的步骤
- 安装依赖
- 生成
.eslintrc
文件 - 添加
eslint-plugin-prettier
插件 - 调整
.eslintrc
文件
5.5 示例工程
示例工程:
|-- examples
|-- .babelrc
|-- .editorconfig
|-- .eslintrc.js // eslint 的配置
|-- .prettierrc.js
|-- index.html
|-- package.json
|-- build
| |-- webpack.base.js
| |-- webpack.dev.js
| |-- webpack.prod.js
|-- src
|-- index.js
|-- assets
|-- style.css
推荐阅读
【step-by-step】6. 使用 eslint-loader 自动审查代码
在 使用 ESLint 审查 JS 代码
文档中,我们添加了 eslint 和 prettier。我们通过在项目中添加 eslint-loader ,让webpack
自动检查 JS 代码中的问题,自动提醒、自动报错。
6.1 步骤
-
推荐阅读 根据 eslint-loader 官方文档中的示例说明,我们在 webpack工程03 中添加
eslint-loader
: -
安装依赖
-
调整 webpack 的配置
6.2 具体流程
6.2.1 安装 eslint-loader
yarn add eslint-loader -D
6.2.2 调整 webpack.base.js
中的配置
在 module.rules
中添加 babel-loader
的配置:
build/webpack.base.js
module.exports = {
// ...
module: {
rules: [
// 在此添加 eslint-loader 的配置
{
enforce: 'pre',
test: /\.js$/,
exclude: /node_modules/,
loader: 'eslint-loader',
options: {
cache: true,
fix: false // 自动修复,对 ide 不够友好。如果,webstorm也开启了自动修复,会与eslint-loader互相冲突。
// failOnError: true, // 如果有格式错误,不进行编译
// failOnWarning: true
}
}
// END 在此添加 eslint-loader 的配置
]
}
// ...
}
6.3 测试 eslint 是否生效
在开发模式下,我们故意编写错误代码,以验证 webpack 是否能够自动审查代码,并提示我们报错信息。
运行 yarn run dev
启动工程后,改写 index.js
const bar = {
a: {
b: 123,
c: {
d: 'hello',
e () {
// XXXXXX e后括号的格式不对
console.info(123)
}
}
}
}
// XXXXXX规则中,不能以分号结尾。
const bb = {
...bar,
app: [1, 2, 3, 4],
bpp: 'hello world'.includes('ll')
};
document.body.innerText = `这个是 ${JSON.stringify(bb)}`
修改index.js
文件后,webpack
在控制台中提示了代码的格式错误。
ERROR in ./src/index.js
Module Error (from /Users/CodingNutsZac/Documents/gitee/webpack-project-tutorial/node_modules/eslint-loader/dist/cjs.js):
/webpack-project-tutorial/examples/04-add-eslint/src/index.js
6:8 error Delete `·` prettier/prettier
17:2 error Delete `;` prettier/prettier
✖ 2 problems (2 errors, 0 warnings)
2 errors and 0 warnings potentially fixable with the `--fix` option.
ℹ 「wdm」: Failed to compile.
如果控制台显示了上面的提示信息,说明我们已经成功地在webpack工程中,添加了 eslint-loader
。工程中的 webpack 可以通过 eslint-loader
自动审查代码错误,并提示我们报错信息。
使用 webstorm 快捷键、或者 vscode 快捷键,或者手动修复错误后,项目恢复正常。
6.4 示例工程
示例工程:
|-- examples
|-- .babelrc
|-- .editorconfig
|-- .eslintrc.js // 新添加的 eslint 配置
|-- .prettierrc.js
|-- index.html
|-- package.json
|-- build
| |-- webpack.base.js
| |-- webpack.dev.js
| |-- webpack.prod.js
|-- src
|-- index.js
|-- assets
|-- style.css
6.5 总结
添加 eslint-loader 的步骤:
- 安装依赖
- 调整 webpack 的配置
推荐阅读
- 轻松上手 Eslint: juejin.cn/post/686192…
- ESlint 使用总结:juejin.cn/post/684490…
- ESLint 工作原理探讨: juejin.cn/post/684490…
- 深入浅出 eslint: juejin.cn/post/684490…
【step-by-step】7. 通过 postcss 使用新一代的 CSS 语法
本篇文档的目的是希望前端同学能够以
复制粘贴
的方式,快速在 webpack 工程 中添加插件。因此,一些说明性质的知识将以推荐阅读
的方式推荐给大家。
TL;DR
postcss 是一个利用 JavaScript 的强大编程能力对 CSS 代码进行转换的工具。它负责把 CSS 代码解析成抽象语法树结构(Abstract Syntax Tree,AST),再交由插件来进行处理。插件基于 CSS 代码的 AST 所能进行的操作是多种多样的,比如可以支持变量和混入(mixin),增加浏览器相关的声明前缀,或是把使用将来的 CSS 规范的样式规则转译(transpile)成当前的 CSS 规范支持的格式。从这个角度来说,PostCSS 的强大之处在于其不断发展的插件体系。目前 PostCSS 已经有 200 多个功能各异的插件。开发人员也可以根据项目的需要,开发出自己的 PostCSS 插件。
- postcss 能够自动为css 规则添加前缀 ( autoprefixer 插件 )。
- postcss 能够将最新的 css 语法转换成大部分版本的浏览器都能理解的语法( postcss-preset-env 插件 )。
- postcss 能够使用
stylelint
强化一致性约束避免样式表中的错误( stylelint )。
Autoprefixer
Autoprefixer 是一款自动管理浏览器前缀的插件,它可以解析CSS文件并且添加浏览器前缀到CSS内容里,使用 Can I Use 的数据来决定哪些前缀是需要的。在 如何处理CSS3属性前缀 文章中,作者详细介绍了为什么要用浏览器前缀。
把Autoprefixe添加到资源构建工具(例如Webpack)后,可以完全忘记有关CSS前缀的东西,只需按照最新的W3C规范来正常书写CSS即可。如果项目需要支持旧版浏览器,可修改browsers参数设置
Autoprefixer将使用基于当前浏览器支持的特性和属性数据去为你添加前缀。你可以尝试下Autoprefixer的demo: Autoprefixer CSS online
7.1 步骤
根据 postcss-loader 文档中的示例,我们在项目中添加 postcss-loader
的步骤如下:
- 安装 postcss-loader
- 创建
.postcssrc
文件 - 调整
webpack
的配置
7.2 具体流程
7.2.1 安装 postcss-loader
同样的,我们在项目中直接添加 postcss-loader 来使用postcss
的功能。 具体配置可参考: postcss-loader
yarn add postcss-loader -D
安装 postcss
相关的 plugins
yarn add postcss-load-config postcss-preset-env postcss-import -D
- postcss-load-config: 允许我们在工程中,使用更多种类的配置文件。
- postcss-preset-env: 允许我们在工程中,使用 新一代的 css 语法 ,根据
browserslist
进行转译。 - postcss-import:通过内联内容来转换@import规则。
7.2.2 创建 .postcssrc.js
文件
在工程的根目录下,创建 .postcssrc.js
文件
// .postcssrc.js
module.exports = {
plugins: [
require('postcss-import'), // 需要放到最上面
// require('autoprefixer'), // 自动添加浏览器前缀(已经包含在 postcss-preset-env 中)
require('postcss-preset-env')() // 使用下一代css语法
]
}
7.2.3 调整 webpack.base.js
根据 postcss-loader 文档中的示例说明,我们在 webpack.base.js
中,添加 postcss-loader
在 build/webpack.base.js
中, 我们把 postcss-loader
添加到 .css 文件的loader列表中。
// webpack.base.js
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
// 在此添加 postcss-loader 的配置
{
loader: 'css-loader',
options: {
importLoaders: 1
}
},
{
loader: 'postcss-loader'
}
// END 在此添加 postcss-loader 的配置
]
}
]
}
}
7.2.4 调整 webpack.prod.js
由于 webpack.prod.js
中 css 的配置与 base 中的不一样,所以需要分别配置。
build/webpack.prod.js
const webpackConfig = merge(base, {
module: {
rules: [
{
test: /\.css$/i,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
esModule: true
}
},
// 在此添加 postcss-loader 的配置
{
loader: 'css-loader',
options: {
importLoaders: 1
}
},
{
loader: 'postcss-loader'
}
// END 在此添加 postcss-loader 的配置
]
}
]
}
})
7.3 测试
7.3.1 调整项目
我们在项目中添加 最新的 CSS 语法后,查看 postcss-loader
是否能够帮助我们自动转译这些新的 CSS 语法。
在 style.css
文件中添加最新的 CSS 语法,并在 index.js
中引用这个 css 文件。
src/assets/style.css
::placeholder {
color: gray;
}
body {
background-color: red;
}
.example {
display: flex;
position: relative;
transform: translate(10, 10);
}
src/index.js
import './assets/style.css'
document.body.innerHTML = `<div class="example">hello world</div>`
7.3.2 测试打包
运行打包命令
npm run build
查看 dist/app.css
,原来的 css 文件已经被转译。
::-webkit-input-placeholder {
color: gray;
}
::-moz-placeholder {
color: gray;
}
:-ms-input-placeholder {
color: gray;
}
::-ms-input-placeholder {
color: gray;
}
::placeholder {
color: gray;
}
body {
background-color: red;
color: white;
}
.example {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
position: relative;
-webkit-transform: translate(10, 10);
transform: translate(10, 10);
}
/*# sourceMappingURL=app.css.map*/
done!
7.4 示例工程
示例工程:
|-- examples
|-- .babelrc
|-- .editorconfig
|-- .eslintrc.js
|-- .postcssrc.js // postcss 配置文件
|-- .prettierrc.js
|-- index.html
|-- package.json
|-- build
| |-- webpack.base.js
| |-- webpack.dev.js
| |-- webpack.prod.js
|-- src
|-- index.js
|-- assets
|-- style.css
7.5 总结
添加 postcss-loader
的步骤
- 安装
postcss-loader
- 创建
.postcssrc
文件 - 调整
webpack
的配置
推荐阅读
【step-by-step】8. 添加 stylelint
我们期待能有一套自动化工具,帮助我们自动调整代码风格,自动审查代码语法。使我们能够把更多的精力投放到业务开发中,而不是千奇百怪的代码风格上。
因此,我们在工程中添加几个工具:
- .editorconfig : 让IDE遵循同样的编写规则。
- prettier : 代码格式化工具。
- eslint : 审查 js 语法。
- stylelint : 审查 css 语法。
- commitlint : 审查 git commit 信息格式。
TL;DR
stylelint 是强大的 css 代码审查工具,由PostCSS
提供技术支持。与 ESLint
类似,是通过定义一系列的编码风格规则帮助我们避免在样式中出现错误。目前,我们可以把stylelint
作为postcss
的plugin
来使用。
8.1 步骤
根据 stylelint 文档中的示例说明,我们在项目中,添加 stylelint
的步骤如下:
- 安装依赖
- 添加配置文件
- 调整 postcss 的配置
8.2 具体流程
8.2.1 安装 stylelint
运行脚本命令安装 stylelint
yarn add stylelint stylelint-config-standard -D
8.2.2 添加配置文件
创建 .stylelintrc
文件
{
"extends": "stylelint-config-standard"
}
8.2.3 调整 .postcssrc.js
文件
调整 .postcssrc.js
文件,添加 stylelint
plugin
module.exports = {
plugins: [
require('stylelint'), // 添加 stylelint 插件,需要放在最上面。
require('postcss-import'), //
// require('autoprefixer'), // 自动添加浏览器前缀(已经包含在 postcss-preset-env 中了)
require('postcss-preset-env')() // 使用下一代css语法
]
}
stylelint 插件需要放在
plugin
列表的最上面。
8.3 测试
我们故意破坏 style.css
的格式后,运行 stylelint
的命令查看审查结果。
::placeholder {
color: gray;
}
body {
background-color: #00;
}
.example {
display: flex;
position: relative;
transform: translate(10, 10);
}
运行 stylelint
命令
文件的匹配规则可以参考 glob 库中的介绍 。
npx stylelint src/**/*.css
在控制台会出现以下提醒
npx stylelint src/**/*.css
src/assets/style.css
6:21 ✖ Unexpected invalid hex color "#00" color-no-invalid-hex
9:1 ✖ Expected no more than 1 empty line max-empty-lines
调整后,程序恢复正常
8.4 添加 stylelint-prettier
通过安装 stylelint-prettier ,stylelint
会使用 prettier 中的配置规则对工程进行检查。
安装依赖
yarn add stylelint-prettier stylelint-config-prettier -D
调整配置文件
调整配置文件 .stylelintrc
, 把原来的配置替换成下面的这个
{
"extends": ["stylelint-prettier/recommended"]
}
再次进行测试
8.5 示例工程
示例工程:
|-- examples
|-- .babelrc
|-- .editorconfig
|-- .eslintignore
|-- .eslintrc.js
|-- .postcssrc.js
|-- .prettierignore
|-- .prettierrc.js
|-- .stylelintrc // 新添加的 stylelint 配置文件
|-- index.html
|-- package.json
|-- build
| |-- webpack.base.js
| |-- webpack.dev.js
| |-- webpack.prod.js
|-- src
|-- index.js
|-- assets
|-- style.css
8.6 总结
添加 stylelint 的 步骤
- 安装依赖
- 添加配置文件
- 调整 postcss 的配置
- 安装 stylelint-prettier
- 调整配置文件
【step-by-step】9. 使用 husky 和 lint-staged 来调用 Git Hook (Pre-commit)
TL;DR
为了避免 一千个程序员,就有一千种代码风格
!我们期待在提交代码时,Git Hook
自动调用 prettier
统一代码风格,然后调用 eslint
和 stylelint
审查代码语法。同时,我们也期待 eslint
这类工具只审查本地修改过的文件,而不是所有文件,以此来提升执行效率。
我们借助下面两个工具来达成目标:
- husky :
husky
是一个Git Hook工具(对!就是二哈的英文名)。 Git 能在特定的重要动作发生时触发Husky
中的自定义脚本。在本工程中,主要是pre-commit
,也就是在执行 git commit 之前,先执行一些自定义操作。可参考:Git 钩子 。 - lint-staged :
lint-staged
可以让我们只对 Git 缓冲区 中的文件执行操作。
9.1 安装步骤
根据 pretter.io 文档中的建议,以及 husky 和 lint-staged 说明文档中的示例说明,我们在项目中添加 Git Hook
的步骤如下所示:
- 安装依赖
- 在
package.json
中添加husky
和lint-staged
的配置 - 添加
ignore
文件
9.2 具体流程
9.2.1 安装依赖
运行命令安装 husky
和 lint-staged
yarn add husky lint-staged -D
安装完 husky
后,在 根目录
下查看 .git/hooks
文件夹下面的脚本:
例如 .git/hooks/pre-commit
#!/bin/sh
# husky
# Created by Husky v4.3.0 (https://github.com/typicode/husky#readme)
# At: 9/14/2020, 3:31:42 PM
# From: /Users/CodingNutsZac/Documents/founder/git/webpack-tutorial/node_modules/husky (https://github.com/typicode/husky#readme)
. "$(dirname "$0")/husky.sh"
从注释中可以看出,这个脚本是由 husky
生成的,由此证明 husky
安装成功。
9.2.2 在 package 中添加配置
在 package.json
中添加 husky
和 lint-staged
的配置:
{
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"src/**/*.js": ["prettier --write", "eslint --cache --fix"],
"src/**/*.css": ["prettier --write", "stylelint --cache --fix"]
}
}
husky
在 pre-commit 阶段(也就是 commit 之前)执行 lint-staged 命令。lint-staged
里面定义了需要对 Git 暂存区中的文件执行的任务。先优化暂存区中的 js 代码格式,再进行 eslint 检测。- 如果在
eslint
执行阶段抛错了,则表示报错代码不符合eslint 规范
,进而导致 pre-commit 钩子抛错,最终导致整个commit 操作
失败。
lint-staged
将把git缓冲区
中文件的绝对路径作为参数传给命令行,这样我们就可以只操作本地修改的文件了。
9.2.3 添加 ignore 文件
至此,我们就定义完 Git Hook 中的指令了。为了使整个工程更易于扩展,建议在工程中添加下面的 ignore
文件:
.eslintignore
: ESLint 的ignore文件。.gitignore
: Git 的ignore文件。.prettierignore
: prettier 的ignore文件。
9.3 验证
9.3.1 验证 prettier
我们故意弄乱代码风格后执行git commit
,查看 husky
和 lint-staged
是否会调用 prettier
对代码格式化。
修改文件
修改 src/index.js
以及 src/assets/style.css
。如果在 git commit
时,prettier
修复了代码格式,证明 husky
和 lint-staged
添加成功。
index.js
中,有两处错误: 1. 两行代码之间有多个换行符;2. 段位的分号问题;
// 两行代码之间,添加多个换行。这是违背eslint规则的,理应报错。
import './assets/style.css';
document.body.innerHTML = `<div class="example">hello world, zac</div>`;
style.css
中,有两处错误:1. 第8行的分号;2. 第9行到第12行的换行符。
::placeholder {
color: #fff;
}
body {
background-color: #000;
color: white;
};
.example {
display: flex;
position: relative;
transform: translate(10, 10);
}
提交代码
控制台输出如下
如果,执行git commit
后,两个修改过的文件被格式化,证明 husky
和 lint-staged
安装成功。
9.3.2 验证 eslint
我们故意在js代码当中添加错误的语法调用。如果在 git commit
的时候,eslint 提示报错信息,证明 husky
和 lint-staged
安装成功。
修改文件
// 两行代码之间,添加 `return`,eslint应该会报错。
import './assets/style.css'
return
document.body.innerHTML = `<div class="example">hello world, zac</div>`
提交 git 进行验证
控制台展示如下:
9.4 示例工程
示例工程:
|-- examples
|-- .babelrc
|-- .editorconfig
|-- .eslintignore // eslint的ignore文件
|-- .eslintrc.js
|-- .gitignore // git的ignore文件
|-- .postcssrc.js
|-- .prettierignore // prettier的ignore文件
|-- .prettierrc.js
|-- .stylelintrc
|-- index.html
|-- package.json
|-- build
| |-- webpack.base.js
| |-- webpack.dev.js
| |-- webpack.prod.js
|-- src
|-- index.js
|-- assets
| |-- style.css
|-- js
|-- utils.js
9.5 总结
添加 husky
和 lint-stage
的步骤:
- 安装依赖
- 在
package.json
中添加husky
和lint-staged
的配置 - 添加
ignore
文件
推荐阅读
- prettier.io - Precommit-Hook: prettier.io/docs/en/pre…
- lint-staged: www.npmjs.com/package/lin…
- lint-staged 使用教程: www.cnblogs.com/jiaoshou/p/…
- Git 钩子: www.git-scm.com/book/zh/v2/…
【step-by-step】10. 使用 commitizen 规范 Git 日志格式
本篇文档的目的是希望前端同学能够以
复制粘贴
的方式,快速在 webpack 工程 中添加插件。因此,一些说明性质的知识将以推荐阅读
的方式推荐给大家。
TL;DR
在项目开发中,我们经常能看到:
- 一连串
一模一样
的 commit 日志; - 无法分辨
提交意图
的 commit 信息; - commit 信息与更变代码之间
毫无关联
;
这些习惯会导致代码回滚、issue
追溯变得十分困难,让日志信息变得毫无意义。理想的 commit 信息应该是能较好的解决如上问题的。
- 发生问题时快速让 PM 识别问题代码并回滚。
- commit 和 代码之间建立联系,并和相关的
issue
予以关联。
因此,我们期待的流程是:
- commitizen : 生成 commit message 的约定模板。
- commitlint: 检查 commit message 是否符合提交格式。
- standard-version : 每次发版的时候,生成
changelog.md
,方便查看发版信息
以及工单与代码的对应关系
。
当前比较推荐的日志格式是 Angular Git Commit Guidelines ,我们后面所使用的插件也是基于 angular
日志格式的。
接下来将介绍如何在系统安装 commitizen
,并使用它生成 Git 提交日志。
10.1 期待的日志格式
Augular
推荐的日志格式:
<type>(<scope>): <subject>
<BLANK LINE>
<body>
<BLANK LINE>
<footer>
type
:本次 commit 的类型,诸如 feat、fix、docs 等。scope
:本次 commit 涉及的范围,例如组件、文件等。subject
:日志的标题。body
:详细说明本次 commit 的内容,动机等,如需换行使用\n
。footer
:描述与之关联的 issue 或 change- 不兼容变动:如果当前代码与上一个版本不兼容,则 Footer 部分以 BREAKING CHANGE 开头,后面是对变动的描述、以及变动理由和迁移方法。
- 关闭 Issue:如果当前 commit 针对某个 issue,那么可以在 Footer 部分关闭这个 issue。例如:
closes #123
。
例如:
feat(\$browser): onUrlChange event (popstate/hashchange/polling)
Added new event to \$browser:
- forward popstate event if available
- forward hashchange event if popstate not available
- do polling when neither popstate nor hashchange available
Breaks \$browser.onHashChange, which was removed (use onUrlChange instead)
fix(\$compile): couple of unit tests for IE9
Older IEs serialize html uppercased, but IE9 does not...
Would be better to expect case insensitive, unfortunately jasmine does
not allow to user regexps for throw expectations.
Closes #392
Breaks foo.bar api, foo.baz should be used instead
feat(directive): ng:disabled, ng:checked, ng:multiple, ng:readonly, ng:selected
New directives for proper binding these attributes in older browsers (IE).
Added coresponding description, live examples and e2e tests.
Closes #351
更多的例子:Git Commit Message Conventions
10.2 使用 commitizen 规范日志格式
commitzen 是格式化 commit message 的工具,它以问询
的方式获取所需的信息。
10.3 添加步骤
根据 commitizen 文档 中的说明,我们在项目中添加 commitizen
:
- 安装依赖
- 调整 package.json 文件
10.3.1 安装依赖
安装格式化工具 commitizen:
yarn add commitizen -D
我们需要按照一定的标准来规范日志的格式,也就是需要遵照 约定格式
的 适配器
,这样 commitizen
才能按照固定格式进行交互式提问。
npx commitizen init cz-conventional-changelog --yarn --dev --exact
注意
运行上面的命令后,脚本会在 package.json
中添加 config.commitizen
属性。此时,我们需要调整 path
属性,避免在windows下运行 commitizen
脚本时报错!
"config": {
"commitizen": {
- "path": "./node_modules/cz-conventional-changelog"
+ "path": "cz-conventional-changelog"
}
}
10.3.2 调整 package.json 文件
在 script 中添加 ct
命令。
{
"script": {
"ct": "git add . && git-cz"
}
}
10.4 测试
提交代码进行验证,由于 git add 命令已经添加到 ct
中了,因此直接在 terminal 中运行 npm run ct
来提交代码。
在 gitlab 上看结果:
10.5 总结
添加 commitizen
的步骤:
- 安装依赖
- 调整 package.json 文件
10.6 示例工程
示例工程:
|-- examples
|-- .babelrc
|-- .editorconfig
|-- .eslintignore
|-- .eslintrc.js
|-- .gitignore
|-- .postcssrc.js
|-- .prettierignore
|-- .prettierrc.js
|-- .stylelintrc
|-- index.html
|-- package.json
|-- build
| |-- webpack.base.js
| |-- webpack.dev.js
| |-- webpack.prod.js
|-- src
|-- index.js
|-- assets
| |-- style.css
|-- js
|-- utils.js
推荐
【step-by-step】11. 使用 commitlint 审查 Git 日志规范
我们期待能有一套自动化工具,帮助我们自动调整代码风格,自动审查代码语法。使我们能够把更多的精力投放到业务开发中,而不是千奇百怪的代码风格上。
因此,我们在工程中添加几个工具:
- .editorconfig : 让IDE遵循同样的编写规则。
- prettier : 代码格式化工具。
- eslint : 审查 js 语法。
- stylelint : 审查 css 语法。
- commitlint : 审查 git commit 信息格式。
TL;DR
对于 Git 日志,我们期待的流程是:
-
commitizen
: 生成 commit message 的约定模板。 -
commitlint
: 检查 commit message 是否符合提交格式。 -
standard-version
: 每次发版的时候,生成changelog.md
,方便查看发版信息
以及工单与代码的对应关系
。
当前比较推荐的日志格式是 Angular Git Commit Guidelines ,我们后面所使用的插件也是基于 angular
日志格式的。
接下来将介绍如何在系统安装 commitlint
,并使用它审查日志格式。
11.1 添加的步骤
- 安装依赖
- 生成配置
- 调整
package.json
11.2 具体流程
根据 github:commitlint 中的示例,我们在项目中通过以下的流程来安装 commitlint
。
- 安装依赖
- 配置文件
- 调整 package.json
11.2.1 安装依赖
安装 commitlint
cli 以及配置的 adapter。
yarn add @commitlint/config-angular @commitlint/cli -D
11.2.2 生成配置文件
用以下命令生成配置文件 commitlint.config.js
echo "module.exports = {extends: ['@commitlint/config-angular']}" > commitlint.config.js
注意:请务必检查一下已生成的
commitlint.config.js
文件。在windows下,生成的commitlint.config.js
文件可能会存在格式错误,例如在内容两端有双引号 (")。
11.2.3 调整 package.json
在 package.json
中添加 husky
的 hook:
{
"husky": {
"hooks": {
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
}
}
}
11.3 测试
运行命令来验证 commitlint
是否安装成功。
git add . && git commit -m "测试"
# 在 windows 下,请分开运行
git add .
git commit -m "测试"
在 terminal 中会出现以下错误,证明安装成功。
> git add . && git commit -m "测试"
warning ../../../../package.json: No license field
husky > pre-commit (node v10.21.0)
⚠ Some of your tasks use `git add` command. Please remove it from the config since all modifications made by tasks will be automatically added to the git commit index.
ℹ No staged files match any configured task.
warning ../../../../package.json: No license field
husky > commit-msg (node v10.21.0)
⧗ input: 测试
✖ subject may not be empty [subject-empty]
✖ type may not be empty [type-empty]
✖ found 2 problems, 0 warnings
ⓘ Get help: https://github.com/conventional-changelog/commitlint/#what-is-commitlint
husky > commit-msg hook failed (add --no-verify to bypass)
error Command failed with exit code 1.
11.4 通过 commitizen 来提交 git
如下:
11.5 示例工程
示例工程:
|-- examples
|-- .babelrc
|-- .editorconfig
|-- .eslintignore
|-- .eslintrc.js
|-- .gitignore
|-- .postcssrc.js
|-- .prettierignore
|-- .prettierrc.js
|-- .stylelintrc
|-- commitlint.config.js // 新添加的 commitlint 的配置
|-- index.html
|-- package.json
|-- build
| |-- webpack.base.js
| |-- webpack.dev.js
| |-- webpack.prod.js
|-- src
|-- index.js
|-- assets
| |-- style.css
|-- js
|-- utils.js
11.6 总结
添加 commitlint 的步骤
- 安装依赖
- 生成配置
- 调整
package.json
推荐
【step-by-step】12. 使用 standard-version 生成 CHANGELOG.md
本篇文档的目的是希望前端同学能够以
复制粘贴
的方式,快速在 webpack 工程 中添加插件。因此,一些说明性质的知识将以推荐阅读
的方式推荐给大家。
TL;DR
对于 Git 日志,我们期待的流程是:
- commitizen : 生成 commit message 的约定模板。
- commitlint: 检查 commit message 是否符合提交格式。
- standard-version : 每次发版的时候,生成
changelog.md
,方便查看发版信息
以及工单与代码的对应关系
。
当前比较推荐的日志格式是 Angular Git Commit Guidelines ,我们后面所使用的插件也是基于 angular
日志格式的。
接下来将介绍如何在工程中安装 standard-version,并用它根据 Git 日志信息自动生成 changelog.md
。
12.1 步骤
根据 standard-version 和 用工具思路来规范化 git commit message 中的示例,我们在项目中添加 standard-version
的步骤:
- 安装依赖
- 调整
package.json
中的配置
12.2 具体流程
12.2.1 安装依赖
yarn add standard-version replace -D
12.2.2 添加命令
在 package.json
文件中,添加 script.release
{
"script": {
"release": "standard-version --no-verify --header '# Changelog'"
}
}
12.2.3 添加 standard-version 的配置
在 package.json
中添加 standard-version
字段,并把 issue 的地址换掉( standard-version
默认将 git 地址作为 issue 地址。通常情况下,我们有自己的工单系统,需要把 issue 地址替换成工单系统地址)。
{
"standard-version": {
"scripts": {
"postchangelog": "replace 'https://git.fzyun.io/frontend/templates/webpack-sample/issues/' 'https://kb.fzyun.io/issues/' CHANGELOG.md"
},
"skip": {
"commit": true,
"tag": true
}
}
}
12.2.4 私建 gitlab 仓库
git地址不完整
在私建 gitlab 仓库中,使用 standard-version
生成的 CHANGELOG.md
文件后,文件中的 git地址
显示不完整。
例如,在 CHANGELOG.md
文件中,我们可以看到 git的地址是
https://git.xx.xx///commit/7166e3cafa2757315a039c734f02dc347db1f7dd
而实际的地址应该是
https://git.xx.xx/frontend/templates/webpack-sample/-/commit/7166e3cafa2757315a039c734f02dc347db1f7dd
解决办法
根据 standard-version/issues/384 中的建议,我们在工程根目录下,创建 .versionrc
文件,并在文件中指定 commitUrlFormat
字段和 issueUrlFormat
字段。
.versionrc
{
"commitUrlFormat": "https://git.xx.xxo/frontend/templates/webpack-sample/-/commit/{{hash}}",
"issueUrlFormat": "https://git.xx.xx/issues/{{id}}"
}
同时,删掉 package.json
文件中的 standardVersion.scripts.postchangelog
属性:
{
"standard-version": {
- "scripts": {
- "postchangelog": "replace 'https://git.xx.xx/frontend/templates/webpack-sample/issues/' 'https://kb.fzyun.io/issues/' CHANGELOG.md"
- },
"skip": {
"commit": true,
"tag": true
}
}
}
12.3 测试
运行下面的命令,插件将帮我们生成 CHANGELOG.md
文件。
npm run release
12.4 示例工程
|-- examples
|-- .babelrc
|-- .editorconfig
|-- .eslintignore
|-- .eslintrc.js
|-- .gitignore
|-- .postcssrc.js
|-- .prettierignore
|-- .prettierrc.js
|-- .stylelintrc
|-- CHANGELOG.md // 新生成的 changelog 文件
|-- commitlint.config.js
|-- directoryList.md
|-- index.html
|-- package.json
|-- build
| |-- webpack.base.js
| |-- webpack.dev.js
| |-- webpack.prod.js
|-- src
|-- index.js
|-- assets
| |-- style.css
|-- js
|-- utils.js
12.5 总结
在工程中添加 standard-version
的步骤:
- 安装依赖
- 调整
package.json
中的配置
推荐
- 用工具思路来规范化 git commit message: github.com/pigcan/blog…
- 私建gitlab issue地址指向问题(由许青军提供解决方案): github.com/conventiona…