随着现在项目越来越复杂,加上对项目性能的要求越来越高。webpack,也越来越体现其重要性。 所以我们必须得学会怎样使用webpack。
webpack应什么而生?
为什么会有webpack?它的作用是什么?这应该是很多所谓的cli工程师想了解的。很多同学做了很多vuexiangmu或者react项目,但一问到他webpack,他只会说脚手架里面集成了,然后对webpack的概念也仅仅停留在知道是个“构建打包工具”。 在以前,我们开发项目基本上就是一个html,燃火会在底部引入N多个js脚本文件。这样就有很多问题,比如开发人员粗心大意把a.js在b.js后引入了,但是a.js里面有用到b.js里面的内容,这样肯定就会出错,再加之,后来对模块化的需求越来越高。开发风格也就慢慢变成了html里面只引入一个index.js,然后在index.js中去通过import引入a.js,b.js...,但是浏览器中并不支持这种写法,那怎么办呢?webpack就可以解决这个问题。
index.html
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="webpack"></div>
<script type="text/javascript" src="index.js"></script>
</body>
</html>
header.js
function Header() {
console.log('创建header')
}
export default Header;
content.js
function Header() {
console.log('创建content')
}
export default Content;
index.js
import Header from './header';
import Content from './content';
new Header();
new Content();
就这样一个简单的页面,然后在浏览器中运行。
可以看到,浏览器不支持import。所以,不管是在使用脚手架还是自己搭项目的时候,不可避免的是项目得与浏览器打交道,也所以,我们必须得知道webpack解决了什么以及怎样使用webpack。
webpack的基本使用
webpack.config.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
mode: "production",
entry: "./index.js",
module: {
rules: [
{
test: /\.jpeg$/,
use: {
loader: 'file-loader',
options: {
name: '[name].[hash].[ext]'
}
}
}
]
},
plugins: [new HtmlWebpackPlugin()],
output: {
pubulichPath: "http://xxx.com",
filename: "bundle.js",
path: path.resolve(__dirname, 'dist')
},
}
webpack默认运行的是webpack.confg.js,你也可以自定义文件名,例如webpack.test.js,但是你在运行webpack的时候就得使用webpack --config webpack.test.js
上面这段代码中基本上包含了webpack的所有基本知识,下面依次介绍一下:
mode: mode是webpack编译的类型,有development和production两个类型。很明显,development就是开发环境,production就是产线环境,所以当你要使用webpack打包项目的时候,不同环境的配置也就有所有不同。webpack针对于这两个类型自身默认的属性也不同,比如production时,webpack会默认开启tree-shaking等等。
entry: entry就是webpack打包的入口文件。另一种写法是entry:{main:"xxx.js"},只是webpack默认入口文件名为main.js。entry也不只是只能写一个文件,webpack当然也可以多入口,并不吱声针对于单页面entry: {main:"xxx.js",test:"xxxx.js"},这样就会打包出两个文件。
loader: loader可以理解为它可以帮助webpack打包除js文件以外的文件。比如文件中使用到了css或者引入了图片,他们后缀名说.css或.jpg,而webpack只知道打包js的文件,这时候它就不知道该怎么处理,loader就可以帮助它告诉它这些文件该怎么处理,所以这些文件的处理就全权交给了loader。处理css文件引入css-loader,style-loader.处理.jpg使用file-loader或者url-loader。
在同时使用多个loader的时候,
use:[style-loader,css-loader],loader的执行顺序是从右到左,从下到上。 plugin: plugin插件可以理解为它能做到loader做不到的事。loader仅仅负责文件的转换,而plugin从打包到抽离样式到压缩到。。。,plugin可以在webpack不同的生命周期做对应的事,webpack也内置了很丰富的插件供使用。
output: output就是输出文件,与entry相对应。filename就是打包后文件的名字。path就是打包后文件的位置。
webpack的高级特性
publicPath: 基础路径。
当通过上面运行webpack.confg.js文件打包后,会在根目录下的dist文件生成一个html(html-webpack-plugin会自动生成一个html文件),然后它会通过script标签自动引入打包后的bundle.js( <script src='./bundle.js'></script>),如果你把bundle.js存放在xxx.com中,你只需要配置一个publicPath:"xxx.com",打包后的html引入的js自动就会变为<script src='http://xxx.com/bundle.js'></script>。
mini-css-extract-plugin: 我又看到很多同事在配置产线环境的webpack的css loader的时候用的style-loader,这个肯定能解析没问题,但是它是将样式解析到快html行内,很明显,这不是一个好的体验,所以,使用mini-css-extract-plugin代替style-loader。它会将css独立解析到一个文件中。然后可以配合terser-webpack-plugin,optimize-css-assets-webpack-plugin去做css的压缩。
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const TerserWebpackPlugin = require('terser-webpack-plugin')
const OptimizeCssWebpackPlugin = require('optimize-css-assets-webpack-plugin')
module.exports = {
mode: "production",
entry: "./index.js",
module: {
rules: [
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, css-loader]
}
]
},
plugins: [new HtmlWebpackPlugin()],
output: {
pubulichPath: "http://xxx.com",
filename: "bundle.js",
path: path.resolve(__dirname, 'dist')
},
optimization: {
minimizer: [new TerserWebpackPlugin({}), new OptimizeCssWebpackPlugin({})]
}
}
cacheDirectory: cacheDiretory的作用是对babel编译过的es6语法做缓存,这样,后面再编译时,没有修改过的es6语法就不需要再重新进行编译。通过include和exclude来确定需要做缓存的范围。
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
mode: "production",
entry: "./index.js",
module: {
rules: [
{
test: /\.js$/,
use: ['babel-loader?cacheDirectory'],
include: path.resolve(__dirname, 'src')
}
]
},
plugins: [new HtmlWebpackPlugin()],
output: {
pubulichPath: "http://xxx.com",
filename: "bundle.js",
path: path.resolve(__dirname, 'dist')
},
}
ignorePlugin: ignorePlugin的作用是拒绝引入没有使用到的包,拿moment插件举例,它里面有各个国家几十种语言的包,而我们只需要使用其中一种或者两种,但是你只用引入moment这个包就相当于全部引入,所以,需要使用ignorePlugin这个插件来指定哪些文件不引入。 webpack.config.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
mode: "production",
entry: "./index.js",
module: {
rules: [
{
test: /\.js$/,
use: ['babel-loader?cacheDirectory'],
include: path.resolve(__dirname, 'src')
}
]
},
plugins: [new HtmlWebpackPlugin(), new webpack.ignorePlugin(/\.\locale/, /moment/)],
output: {
pubulichPath: "http://xxx.com",
filename: "bundle.js",
path: path.resolve(__dirname, 'dist')
},
}
这样就不会引入moment下面locale这个包。
index.js
import moment from 'moment'
import 'moment/locale.zh-cn'
然后再你需要使用的文件里面去引入指定的包就可以了。
noParse: noParse的作用的避免重复打包。比如react.min.js是已经做好模块化打包的文件,就不需要再打包。
haapyPack: happyPack的意思是开启多进程解析文件,合理利用cpu资源。
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const HappyPack = require('happypack');
module.exports = {
mode: "production",
entry: "./index.js",
module: {
rules: [
{
test: /\.js$/,
use: ['happypack/loader?id=babel'],
exclude: /node_modules/
}
]
},
plugins: [new HtmlWebpackPlugin(),
new webpack.ignorePlugin(/\.\locale/, /moment/),
new HappyPack({
id: 'babel',
loaders: ['babel-loader?cacheDirectory']
})],
output: {
pubulichPath: "http://xxx.com",
filename: "bundle.js",
path: path.resolve(__dirname, 'dist')
},
}
parallelUglifyPlugin: parallelUglifyPlugin的作用是开启多进程压缩js。
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const HappyPack = require('happypack');
module.exports = {
mode: "production",
entry: "./index.js",
module: {
rules: [
{
test: /\.js$/,
use: ['happypack/loader?id=babel'],
exclude: /node_modules/
}
]
},
plugins: [new HtmlWebpackPlugin(),
new webpack.ignorePlugin(/\.\locale/, /moment/),
new HappyPack({
id: 'babel',
loaders: ['babel-loader?cacheDirectory']
}),
new ParallelUglifyPlugin({
uglifyJS: {
output: {
beautify:false, // 最紧凑的输出
comments:false, // 删除所有的注释
}
}
})
],
output: {
pubulichPath: "http://xxx.com",
filename: "bundle.js",
path: path.resolve(__dirname, 'dist')
},
}
HotModuleReplacementPlugin: HMR就是热更新的意思,在不配置这个插件时,当你在开发环境中,你开启了webpack-dev-server,你修改一次代码,页面就会刷新一次,如果你有表单,当你填写好了内容,然后修改了下代码,页面一刷新,表单内容也就清空了。HMR也就是解决这个问题,它只会刷新修改的代码,不会刷新页面,不会改变状态。
以上只是webpack中比较常用的方法,而且各个方法需选择性使用,一些方法适用于development,一些方法适用于production。所以在使用时,先考虑能给你带来什么优化,而不是盲目使用。