webpack学习笔记

webpack 注意点:

1.起步:

  1. webpack配置文件自定义位置时: npm run start – “start”: “webpack –config ./deploy/webpack.prod.conf.js”
  2. 在dist/index.html中引入js:
    1
    2
    3
    4
    5
    6
    7
    8
    dist/index.html
    <script src="./bundle.js"></script>

    deploy/webpack.prod.config.js
    output: {
    filename: 'bundle.js' -- 就是dist/index.html引入的js名称
    path: path.resolve(__dirname, '../dist') -- !!注意输入路径
    }

2.管理资源

  1. 比如管理css资源,需要加载相应的css loader
    1
    npm install --save-dev style-loader css-loader

webpack配置文件中如何使用css loader:

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

业务中如何引入css:

1
2
// dist/bundle.js
import './css/style.css'; -- 直接引入,之后会被css loader解析,作为<style>插入到引入该js文件的html文件的<header>

具体见 资源管理.html / webpack.prod.config_css.js。 其他的资源管理,比如image / data file / …一样的逻辑,只是loader换一下

3.输出管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 多入口配置及使用HtmlWebpackPlugin
entry: {
app: './src/index.js',
print: './src/log.js'
},
output: {
filename: 'bundle_[name].js',
path: path.resolve(__dirname, '../dist')
},
plugins: [
new HtmlWebpackPlugin({
title: 'HtmlWebpackPlugin',
filename: 'HtmlWebpackPlugin2.html',
template: path.resolve(__dirname, '../dist/index.html')
}),
/** 等同于
* new HtmlWebpackPlugin({
title: 'HtmlWebpackPlugin',
filename: '../dist/HtmlWebpackPlugin2.html'
}),
*/
]

4.Tree Shaking

为了学会使用 tree shaking,你必须……

  1. 使用 ES2015 模块语法(即 import 和 export)。
  2. 在项目 package.json 文件中,添加一个 “sideEffects” 入口。!!!
  3. 引入一个能够删除未引用代码(dead code)的压缩工具(minifier)(例如 UglifyJSPlugin)。 或者在webppack4.0以上直接设置mode: ‘prodution’就会自动调用UglifyJSPlugin压缩代码

5.生产环境构建

  1. webpack-merge的使用
  2. 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
    // webpack.base.js
    const path = require('path');
    const CleanWebpackPlugin = require('clean-webpack-plugin');
    const HtmlWebpackPlugin = require('html-webpack-plugin');

    module.exports = {
    entry: {
    app: './src/index.js'
    },
    plugins: [
    new CleanWebpackPlugin(['dist']),
    /**相对路径的时候,一定要设置root
    new CleanWebpackPlugin([
    'dist/*'
    ],{
    root: path.resolve(__dirname, '../../'),
    verbose: true,
    dry: false
    }),
    */
    new HtmlWebpackPlugin({
    title: 'Production'
    })
    ],
    output: {
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, 'dist')
    }
    };

    webpack.dev.js
    module.exports = merge(common, {
    devtool: 'inline-source-map', //建议可以使用cheap-module-eval-source-map
    devServer: {
    contentBase: './dist'
    }
    });

    webpack.prod.js
    module.exports = merge(common, {
    devtool: 'source-map',
    plugins: [
    new UglifyJSPlugin({
    sourceMap: true
    }),
    // 构建的时候制定环境
    new webpack.DefinePlugin({
    'process.env.NODE_ENV': JSON.stringify('production')
    })
    ]
    });
  3. package.json

    1
    2
    "start": "webpack-dev-server --open --config webpack.dev.js",
    "build": "webpack --config webpack.prod.js"
  4. 使用 ExtractTextPlugin 进行split css

6.code splitting – 代码分离:主要目的是分离出公共的代码,不重复加载

  1. 使用CommonsChunkPlugin自动提取功能的代码模块
    1
    2
    3
    4
    // webpack.config.js
    new webpack.optimize.CommonsChunkPlugin({
    name: 'common' // 指定公共 bundle 的名称。
    })

7.动态导入(dynamic imports) 和 懒加载

  1. 在业务js代码中动态地import

8.缓存

  1. 浏览器有缓存,在生产环境上线新代码的情况避免走缓存,需要通过hash来来避免缓存,因此可以使用chunkhash

    1
    2
    3
    4
    5
    // webpack.config.js
    output: {
    filename: '[name].[chunkhash].js', // chunkhash,当文件内容变更时,hash值就会发生变化
    path: path.resolve(__dirname, 'dist')
    }
  2. 使用HashedModuleIdsPlugin,修复改一个文件全部文件的hash值全部改变的bug

    1
    2
    3
    4
    // webpack.config.js
    plugins: {
    new webpack.HashedModuleIdsPlugin()
    }

注:dllPlugin的使用,对不频繁改动的代码进行单独编译,用于打包出一个个动态连接库,目的是提高 构建 时的速度

9. 注意点:

  1. 可以使用resolve.extensions自动解析扩展,使用resolve.alias自定义别名
    1
    2
    3
    4
    5
    6
    7
    webpack.config.js
    resolve: {
    extensions: ['.js', '.json']
    alias: {
    '@': path.resolve(__dirname, '../../src')
    }
    }

10. 完整工程示例:

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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
// webpack.conf.base.js
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin'); // 编译时有详细的报错信息

const ROOT_PATH = path.resolve(__dirname, '../../');

module.exports = {
resolve: {
extensions: ['.js', '.json'], // 自动解析扩展
alias: {
pro: path.join(ROOT_PATH, 'src') // 定义src目录别名
}
},
entry: path.join(ROOT_PATH, 'src/javascript/main.js'), // spa 入口js文件
module: {
rules: [
{
test: /\.html$/,
include: [
path.join(ROOT_PATH, 'src/javascript'),
path.join(ROOT_PATH, 'src/template/pages')
],
use: 'html-loader'
},
{
test: /\.js$/,
include: path.join(ROOT_PATH, 'src/javascript'),
use: 'babel-loader'
},
{
test: /\.(png|svg|jpg|gif)$/,
include: path.join(ROOT_PATH, 'src'),
use: ['file-loader']
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
include: path.join(ROOT_PATH, 'src'),
use: ['file-loader']
}
]
},
optimization: {
runtimeChunk: {
name: "manifest"
},
splitChunks: {
chunks: "all",
minSize: 3000,
minChunks: 1,
maxAsyncRequests: 5,
maxInitialRequests: 5,
automaticNameDelimiter: '~',
name: true,
cacheGroups: {
// vendors: {
// test: /[\\/]node_modules[\\/]/,
// priority: -10,
// name: "vendors",
// },
vendors: {
test: /node_modules\/(regularjs|regular-state|nek-ui)/,
priority: -8,
name: "vendors",
},
axios: {
test: /node_modules\/axios/,
priority: -9,
name: "axios",
},
echarts: {
test: /node_modules\/echarts/,
priority: -10,
name: "echarts",
},
common: {
test: /node_modules/,
priority: -11,
name: "common",
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
}
}
}
},
plugins: [
new webpack.HashedModuleIdsPlugin(),
new HtmlWebpackPlugin({
title: 'demo',
filename: path.join(ROOT_PATH, 'dist-template/pages/index.ftl'),
template: path.join(ROOT_PATH, 'src/template/pages/index.ftl'),
}),
new FriendlyErrorsPlugin()
]
};
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
// webpack.conf.dev.js
const path = require('path');
const merge = require('webpack-merge');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const WebpackBaseConf = require('./webpack.base.conf.js');

const ROOT_PATH = path.resolve(__dirname, '../../');

module.exports = merge(WebpackBaseConf, {
devtool: 'cheap-module-eval-source-map',
output: {
filename: '[name].js',
path: path.join(ROOT_PATH, 'dist'),
publicPath: '/dist/',
},
module: {
rules: [
{
test: /\.css$/,
include: [
path.join(ROOT_PATH, 'src/css'),
path.join(ROOT_PATH, 'src/javascript'),
],
use: [
'style-loader',
'css-loader'
]
}
]
},
plugins: [
new CleanWebpackPlugin([
'dist/*'
],{
root: ROOT_PATH,
verbose: true,
dry: false
}),
],
mode: 'development'
});
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
// webpack.conf.prod.js
const path = require('path');
const webpack = require('webpack');
const merge = require('webpack-merge');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const WebpackBaseConf = require('./webpack.base.conf.js');

const ROOT_PATH = path.resolve(__dirname, '../../');

module.exports = merge(WebpackBaseConf, {
devtool: 'source-map',
output: {
filename: '[name].[chunkhash:8].js',
path: path.join(ROOT_PATH, 'dist'),
publicPath: '/dist/',
},
module: {
rules: [
{
test: /\.css$/,
include: [
path.join(ROOT_PATH, 'src/css'),
path.join(ROOT_PATH, 'src/javascript')
],
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: "css-loader"
})
}
]
},
plugins: [
new CleanWebpackPlugin([
'dist/*',
'dist-template/*'
],{
root: ROOT_PATH,
verbose: true,
dry: false
}),
new ExtractTextPlugin('[name].[chunkhash:8].css'),
new UglifyJSPlugin({
sourceMap: true
}),
// 构建的时候制定环境
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production')
})
],
mode: 'production'
});
1
2
3
4
5
// package.json
'script': {
'build_dev': 'cross-env NODE_ENV=dev && webpack --config ./src/deploy/build/webpack.dev.conf.js',
'build_prod': 'cross-env NODE_ENV=prod && webpack -- config ./src/deploy/build/webpack.prod.conf.js'
}