构建自己的front-end-cli

安装

全局安装(不推荐)

1
npm install webpack webpack-cli -g

全局的话容易发生webpack3和webpack4的版本冲突

局部安装(推荐)

1
2
npm install webpack webpack-cli -D
# npm install webpack webpack-cli --save-dev

查看版本

1
npx webpack -v

查看包版本

1
npm info webpack

配置

项目目录文件下新建

webpack.config.js

entry & output

webpack.config.js

1
2
3
4
5
6
7
8
module.exports = {
mode:'development', // default 'production' for compression
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'bundle')
}
}

多入口

1
2
3
4
5
6
7
8
9
10
11
module.exports = {
mode:'development', // default 'production' for compression
entry: {
main: './src/index.js',
sub: './src/index.js'
}
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'bundle')
}
}

浏览器缓存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
module.exports = {
mode:'development', // default 'production' for compression
entry: './src/index.js',
optimization: {
runtimeChunk: {
name: 'runtime' // 提取runtime
}
}
output: {
filename: '[name].[contenthash].js',
chunkFilename: '[name].[contenthash].chunk.js',
path: path.resolve(__dirname, '../dist')
}
}

publicPath

1
2
3
4
5
6
7
8
9
module.exports = {
mode:'development', // default 'production' for compression
entry: './src/index.js',
output: {
publicPath: 'http://cdn.com.cn',
filename: 'bundle.js',
path: path.resolve(__dirname, 'bundle')
}
}

loader

file-loader

说明

将文件上的import / require()解析为url,并将文件发送到输出目录中

安装

1
npm install file-loader -D

代码

webpack.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
module.exports = {
module: {
rules: [{
test: /\.(eot|ttf|svg|woff)$/,
use: {
loader: 'file-loader',
options: {
name: '[name]_[hash].[ext]',
outputPath: 'font/'
}
}
}]
}
}

url-loader

说明

用于将文件转换为base64 URI

安装

1
npm install url-loader -D

代码

webpack.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
module.exports = {
module: {
rules: [{
test: /\.(png|jpg|gif)$/,
use: {
loader: 'url-loader',
options: {
name: '[name]_[hash].[ext]',
outputPath: 'images/',
limit: 8192
}
}
}]
}
}

style-loader && css-loader

说明

style-loader通过注入标记将DOM添加到DOM

css-loader解释@ import和url(),如import / require()并解析它们。

安装

1
npm install style-loader css-loader -D

代码

webpack.config.js

1
2
3
4
5
6
7
8
module.exports = {
module: {
rules: [{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}]
}
}

sass-loader && node-sass

说明

sass-loader加载Sass / SCSS文件并将其编译为CSS

node-loader是Node.js附加组件加载程序模块,用于增强需求。在enhanced-require中执行附加组件

安装

1
npm install sass-loader node-sass -D

代码

webpack.config.js

1
2
3
4
5
6
7
8
9
10
11
12
module.exports = {
module: {
rules: [{
test: /\.scss$/,
use: [
'style-loader',
'css-loader',
'sass-loader'
]
}]
}
}

postcss-loader

说明

postcss-loader使用PostCSS处理CSS的webpack的加载器(产商前缀)

安装

1
npm install postcss-loader -D

代码

webpack.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
module.exports = {
module: {
rules: [
{
test: /\.scss$/,
use: [
'style-loader',
'css-loader',
'postcss-loader',
'sass-loader'
]
},
{
test: /\.css$/,
use: [
'style-loader',
'css-loader',
'postcss-loader'
]
}]
}
}

使用

1
npm install autoprefixer -D

项目目录下创建postcss.config.js

1
2
3
4
5
module.exports = {
plugins: [
require('autoprefixer')
]
}

option: importLoaders

importLoaders选项允许您在将css-loader应用于@imported资源之前配置多少个加载器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
module.exports = {
module: {
rules: [{
test: /\.scss$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2, // 0 => no loaders (default); 1 => postcss-loader; 2 => postcss-loader, sass-loader
},
},
'postcss-loader',
'sass-loader'
]
}
]
}
}

option: modules

modules选项启用/禁用CSS模块规范并设置基本行为。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
module.exports = {
module: {
rules: [{
test: /\.scss$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2,
modules: true
},
},
'sass-loader',
'postcss-loader'
]
}
]
}
}

babel-loader

Bebel是一个工具链,主要用于将ECMAScript 2015+代码转换为当前和旧版浏览器或环境中的向后兼容版本的JavaScript。以下是Babel可以为您做的主要事情:

安装

1
npm install --save-dev babel-loader @babel/core

代码

1
2
3
4
5
6
7
8
9
10
module.exports = {
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader"
}]
}
}

在项目目录下新建.babelrc,相当如babel中options的配置项

preset-set
说明

preset-set一个转化ES2015+的预设

安装
1
npm install @babel/preset-env --save-dev
代码

并在.babelrc下输入

1
2
3
{
"presets": ["@babel/preset-env"]
}

在webpack.config.js配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
module.exports = {
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader",
options: {
presets: ["@babel/preset-env"]
}
}
]
}
}

polyfill

说明

Polyfill是一个js库,主要抚平不同浏览器之间对js实现的差异

安装
1
npm install --save @babel/polyfill
使用

在业务代码中进行引入

1
import "@babel/polyfill";
问题

引入所有纠正由此带来代码臃肿

解决

在webpack.config.js键入以下内容以根据业务代码加载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
module.exports = {
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader",
options: {
presets: [
["@babel/preset-env",{
useBuiltIns: 'usage'
}]
]
}
}]
}
}
warning 及 解决

WARNING: We noticed you’re using the useBuiltIns option without declaring a core-js version. Currently, we assume version 2.x when no version is passed. Since this default version will likely change in future versions of Babel, we recommend explicitly setting the core-js version you are using via the corejs option.

1
npm install --save core-js@2

plugin-transform-runtime

说明

plugin-transform-runtime一个插件,可以重复使用Babel注入的帮助程序代码来节省代码

用于写类库代码而不污染全局变量

安装
1
2
3
npm install --save-dev @babel/plugin-transform-runtime
npm install --save @babel/runtime
npm install --save @babel/runtime-corejs2
使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
module.exports = {
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader",
options: {
plugins: [["@babel/plugin-transform-runtime"],{
"corejs": 2,
"helpers": true,
"regenerator": true,
"useESModules": false
}]
}
}]
}
}

plugin

html-webpack-plugin

安装

1
npm install html-webpack-plugin -D

说明

HtmlWebpackPlugin简化了HTML文件的创建,以便为您的webpack包提供服务。 这对于包含文件名中的哈希的webpack包特别有用,它会更改每个编译。 您可以让插件为您生成HTML文件,使用lodash模板提供您自己的模板,或使用您自己的加载器。

生成html并自动将output注入到htmlzhogn

代码

webpack.config.js

1
2
3
4
5
6
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
plugins: [new HtmlWebpackPlugin({
template: 'src/html/index.html' // 模板
})],
}

MiniCssExtractPlugin

说明

此插件将CSS提取到单独的文件中。它为每个包含CSS的JS文件创建一个CSS文件。它支持CSS和SourceMaps的按需加载。

安装

1
npm install --save-dev mini-css-extract-plugin

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
module:{
rules: [
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader'
]
},
{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
importLoaders: 2,
modules: true
},
},
'postcss-loader',
'sass-loader'
]
}
]
},
plugins: [
new MiniCssExtractPlugin({
filename: '[name].css',
chunkFilename: '[name].chunk.css'
})
]
}

防止tree shaking

package.json

1
2
3
4
5
{
"sizeEffects": [
"*.css"
],
}

workboxWebpackPlugin

说明

官方文档

Workbox provides two webpack plugins: one that generates a complete service worker for you and one that generates a list of assets to precache that is injected into a service worker file.

The plugins are implemented as two classes in the workbox-webpack-plugin module, named GenerateSW and InjectManifest. The answers to the following questions can help you choose the right plugin and configuration to use.

用于在webpack中使用PWA

安装

1
npm install workbox-webpack-plugin -D

使用

1
2
3
4
5
6
7
8
const WorkboxWebpackPlugin = require('workbox-webpack-plugin');
module.exports = {
plugins: [
new WorkboxWebpackPlugin.GenerateSW({
clientsClaim: true,
skipWaiting: true
})]
}

测试

先安装http-server

1
npm install http-server -D

输入以启用服务

1
npx http-server dist

http://localhost:8080/访问该服务

clean-webpack-plugin

安装

1
npm install clean-webpack-plugin -D

说明

非官方插件,用于清除制定目录文件下的内容

代码

1
2
3
4
5
6
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
plugins: [
new CleanWebpackPlugin()
],
}

devtool

说明

此选项控制是否以及如何生成源映射。

代码

开发

1
2
3
4
module.exports = {
mode:'development', //default production to compression
devtool: 'cheap-module-eval-source-map',
}

生产

1
2
3
4
module.exports = {
mode:'production',
devtool: 'cheap-module-source-map',
}

WebpackDevServer

说明

webpack-dev-server可用于快速开发应用程序。请参阅开发指南以开始使用。

安装

1
npm install webpack-dev-server -D

执行

1
npx webpack-dev-server

配置说明

devServer.contentBase告诉服务器从哪里提供内容。只有在您想要提供静态文件时才需要这样做

devServer.publicPath将用于确定应该从哪个服务器提供服务,并优先使用

devServer.proxy当您拥有单独的API后端开发服务器并且希望在同一域上发送API请求时,代理某些URL会很有用。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
module.exports = {
devServer: {
contentBase: './dist',
open: true,
port: 8080,
proxy: {
'/api': {
target: 'http://localhost:3000',
pathRewrite: {'^/api' : ''},
secure: false,
bypass: function(req, res, proxyOptions) {
if (req.headers.accept.indexOf('html') !== -1) {
console.log('Skipping proxy for browser request.');
return '/index.html';
}
}

}
}
}
}

webpack-dev-middleware

可用于自启动一个webpack-dev-server

项目目录新建server.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const express = require('express');
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const config = require('./webpack.config.js');
const complier = webpack(config);

const app = express();

app.use(webpackDevMiddleware(complier, {
publicPath: config.output.publicPath
}))

app.listen(3000, () => {
console.log('server is running');
})

webpack.config.js中添加publicPath项

1
2
3
4
5
6
7
module.exports = {
output: {
publicPath: '/',
filename: '[name].js',
path: path.resolve(__dirname, 'dist')
}
}

执行

1
node server.js

Hot Module Replacement

说明

hot启用webpack的热模块替换功能

hot在没有页面刷新的情况下启用热模块替换(请参阅devServer.hot)作为构建失败时的后备

代码

1
2
3
4
5
6
7
8
9
10
11
const webpack = require('webpack')
module.exports = {
devServer: {
contentBase: './dist',
open: true,
port: 8080,
hot: true,
hotOnly: true,
}
plugins: [new webpack.HotModuleReplacementPlugin()],
}

optimization

optimize-css-assets-webpack-plugin

说明

一个插件的WebPack优化\压缩CSS文件。

安装

1
npm install --save-dev optimize-css-assets-webpack-plugin

使用

1
2
3
4
5
6
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
module.exports = {
optimization: {
minimizer: [new TerserJSPlugin({}), new OptimizeCSSAssetsPlugin({})],
}
}

UglifyjsWebpackPlugin

说明

这个插件使用uglify-js来缩小你的JavaScript。

安装

1
npm install uglifyjs-webpack-plugin -D

使用

1
2
3
4
5
6
7
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');

module.exports = {
optimization: {
minimizer: [new UglifyJsPlugin()],
},
};

Tree shaking

说明

Tree shaking用于JavaScript上下文中常用于消除死代码

Tree shaking只支持ES module的引入,即import,不支持require

mode: ‘production’自带Tree shaking

mode: ‘development’需要配置

代码

1
2
3
4
5
module.exports = {
optimization: {
usedExports: true
},
}

package.json 中 sizeEffects 用于免于tree shaking的文件

1
2
3
4
{
"sizeEffects": false,
// "sizeEffects": ['@babel/polly-fill', '*.css']
}

Code Splitting

同步代码分割

需要在webpack.common.js中做optimization的配置

1
2
3
4
5
6
7
module.exports = {
optimization: {
splitChunks: {
chunks: 'all'
}
}
}

异步代码分割

不需要做任何配置,会自动进行代码分割,但是需要安装babel-plugin-dynamic-import-webpack进行ES6转化

babel-plugin-dynamic-import-webpack

说明

非官方

安装
1
npm install babel-plugin-dynamic-import-webpack -D
代码

.babelrc

1
2
3
{
plugins: ["dynamic-import-webpack"]
}

plugin-syntax-dynamic-import

说明

官方

安装
1
npm install --save-dev @babel/plugin-syntax-dynamic-import
代码
1
2
3
{
"plugins": ["@babel/plugin-syntax-dynamic-import"]
}

SplitChunksPlugin

说明

使用前必须安装上述plugin-syntax-dynamic-import

最初,块(以及在其中导入的模块)通过内部webpack图中的父子关系进行连接。CommonsChunkPlugin用于避免跨越它们的重复依赖,但无法进一步优化。

代码

main.js

1
2
3
4
5
6
7
8
9
10
function getComponent(){
return import(/* webpackChunkName:"lodash" */'lodash').then(({default: _})=>{
var element = document.createElement('div');
element.innerHTML = _.join(['Dell', 'Lee'], '-');
return element
})
}
getComponent().then(element=>{
document.body.appendChild(element);
})

webpack.common.js,同步的代码会读取到cacheGroups而异步的不会

1
2
3
4
5
6
7
8
9
10
11
module.exports = {
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
vendors: false,
default: false
}
}
}
}

结果:会在dist目录单独生成lodash.js

配置同步代码和异步代码的加载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
module.exports = {
optimization: {
splitChunks: {
chunks: 'all',
minSize: 30000, // 代码分割最小大小
minChunks: 1,
maxAsyncRequests: 5,
maxInitialRequests: 3,
automaticNameDelimiter: '~',
name: true,
cacheGroups: {
// 同步引入
vendors: {
test: /[\\/]node_modules[\\/]/, // 把node_modules的文件打包到vendors.js中
priority: -10,
},
default: {
priority: -20,
reuseExistingChunk: true,
filename: 'common.js'
}
}
}
}
}

区分development和production打包

webpack-merge

安装

1
npm install webpack-merge -D

在文件目录下创建build文件夹

创建webpack.common.js、webpack.dev.js、webpack.prod.js

webpack.commom.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const CleanWebpackPlugin = require('clean-webpack-plugin')

module.exports = {
entry: './src/index.js',
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader"
},
{
test: /\.(png|jpg|gif)$/,
use: {
loader: 'url-loader',
options: {
name: '[name]_[hash].[ext]',
outputPath: 'images/',
limit: 8192
}
}
},
{
test: /\.(eot|ttf|svg|woff)$/,
use: {
loader: 'file-loader'
}
},
{
test: /\.css$/,
use: [
'style-loader',
'css-loader',
'postcss-loader'
]
},
{
test: /\.scss$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2,
modules: true
},
},
'postcss-loader',
'sass-loader'
]
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: 'src/html/index.html'
}),
new CleanWebpackPlugin(),
],
output: {
filename: '[name].js',
path: path.resolve(__dirname, '../dist') // 上层目录
}
}

webpack.dev.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
const webpack = require('webpack')
const merge = require('webpack-merge')
const commonConfig = require('./webpack.common.js')

const devConfig = {
mode: 'development',
devtool: 'cheap-module-eval-source-map',

devServer: {
contentBase: './dist',
open: true,
port: 8080,
hot: true,
proxy: {
'/api': {
target: 'http://localhost:3000',
pathRewrite: {'^/api' : ''}
}
}
},

plugins: [
new webpack.HotModuleReplacementPlugin()
],
optimization: {
usedExports: true
},

}

module.exports = merge(commonConfig, devConfig)

webpack.prod.js

1
2
3
4
5
6
7
8
9
const merge = require('webpack-merge')
const commonConfig = require('./webpack.common.js')

const prodConfig = {
mode: 'production',
devtool: 'cheap-module-source-map',
}

module.exports = merge(commonConfig, prodConfig)

npm script

dev && build

package.json中

1
2
3
4
5
6
{
"scripts": {
"dev": "webpack-dev-server --config ./build/webpack.dev.js",
"build": "webpack --config ./build/webpack.prod.js"
}
}

analyse

使用

参见官网

package.json中加入scripts

1
2
3
4
5
{
"scripts": {
"analyse": "webpack --profile --json > stats.json --config ./build/webpack.dev.js"
}
}

打开分析网站或者webpack-chart,将项目下生成的stat.json文件上传,更多分析工具看这里

Eslint

安装

1
2
npm install eslint -D
npm install eslint-loader -D

快速初始化

1
npx eslint --init

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
module.exports = {
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: ["babel-loader", {
loader: 'eslint-loader',
options: {
fix: true
}
}]
}
}
}

git钩子做eslint

安装

1
npm install husky --save-dev

使用

package.json中

1
2
3
4
5
6
7
{
"husky": {
"hooks": {
"pre-commit": "npm run lint-fix"
}
}
}

当执行commit的时候会先执行npm run lint-fix

其它

Preloading && Prefetching

说明

webpack推荐的一种代码分离异步加载的方式

使用

index.js

1
2
3
4
5
document.addEventListener('click', () => {
import(/* webpackPrefetch: true*/ './js/click.js').then(({default: func})=>{
func();
})
})

click.js

1
2
3
4
5
6
7
function handleClick(){
const element = document.createElement('div');
element.innerHTML = 'Dell Lee';
document.body.appendChild(element);
}

export default handleClick;