前言
本篇文章主要还是入门教程,如果对webpack配置已经十分熟悉,想要了解怎么样合理利用缓存,提高打包效率,推荐这篇文章手摸手,带你用合理的姿势使用webpack4
一、什么是webpack
WebPack可以看做是模块打包机:它做的事情是,分析你的项目结构,找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Scss,TypeScript等),并将其打包为合适的格式以供浏览器使用。
const path = require('path'); //引入node的path模块
const webpack = require('webpack'); //引入的webpack,使用lodash
const HtmlWebpackPlugin = require('html-webpack-plugin') //将html打包
const ExtractTextPlugin = require('extract-text-webpack-plugin') //打包的css拆分,将一部分抽离出来
const CopyWebpackPlugin = require('copy-webpack-plugin')
//测试下 console.log(path.resolve(__dirname,'dist')); //物理地址拼接
module.exports = {
//webpack的配置文件是一个node.js的module,用CommonJS风格来书写
}
二、entey:配置入口文件的地址
所谓的入口,就是告诉webpack从哪里开始并通过依赖关系图得知要哪些文件要打包。你可以理解为应用程序最先读取的文件。读取这个文件之后,就会产生一个模块依赖关系图,那么当前这个文件,就是依赖关系的根。
key可以是简单的字符串,比如:’app’, ‘main’等。并且对应着output.filename配置中的[name]变量。key还可以是路径字符串。此时webpack会自动生成路径目录,并将路径的最后作为[name]。这个特性在多页面配置下也是很有用的。
entry: {
'path/of/entry': './deep-app.js',
'app': './app.js'
}
output: {
path: './output',
filename: '[name].js'
}
二、output:配置输出文件的地址
配置output属性就是告诉webpack把编译后的文件写在磁盘的哪个地方。注意一点,虽然入口中可以配置成多个文件的形式,但是输出只能指定一个。
— 打包之后资源,并不总是写在磁盘上,也可能是在内存中,比如使用webpack-dev-server的时候。
__dirname 总是指向被执行 js 文件的绝对路径。
const PROJECT_ROOT = path.resolve(__dirname, ‘../’);
output: {
path: path.resolve(PROJECT_ROOT, "./dist");,//定位,输出文件的目标路径
filename: `${JS_DIR}/[name].[${env.prod?'chunkhash':'hash'}:8].js`
//文件名[name].js默认,对应entry的key,也可自行配置
publicPath: "http://cdn.example.com/assets/"
//通常如果静态资源(非html 文件)由独立的静态资源服务器提供务,
// 或者是分配了单独的静态资源访问路径时需要设置此前缀。
// 通常如果静态资源与html文件保持构建时的目录结构时,走相对路径,无需设置。 }
filename属性表示的是如何命名生成出来的入口文件,规则有以下三种:
[name],指代入口文件的name,也就是上面提到的entry参数的key,因此,我们可以在name里利用/,即可达到控制文件目录结构的效果。
[hash],指代本次编译的一个hash版本,值得注意的是,只要是在同一次编译过程中生成的文件,这个[hash]的值就是一样的;由于每次使用 webpack 构建代码的时候,此 hash 字符串都会更新,因此相当于强制刷新浏览器缓存。
[chunkhash],指代的是当前chunk的一个hash版本,也就是说,在同一次编译中,每一个chunk的hash都是不一样的;而在两次编译中,如果某个chunk根本没有发生变化,那么该chunk的hash也就不会发生变化。这在缓存的层面上来说,就是把缓存的粒度精细到具体某个chunk,只要chunk不变,该chunk的浏览器缓存就可以继续使用。
三、module:配置模块,主要用来配置不同文件的加载器(loader)
当你为你的模块安装一个依赖模块时,正常情况下你得先安装他们(在模块根目录下npm install module-name),然后连同版本号手动将他们添加到模块配置文件package.json中的依赖里(dependencies)。
–save(-S)和–save-dev(-D)可以省掉你手动修改package.json文件的步骤。‘
npm install module-name -save 自动把模块和版本号添加到dependencies部分
npm install module-name -save-dev 自动把模块和版本号添加到devdependencies部分
好处:执行命令时,要寻找该模块定义,先从dev目录开始寻找,相比较于全局安装,速度就非常快了。
- test:匹配处理文件的扩展名的正则表达式
- use:loader名称,就是你要使用模块的名称
- include/exclude:手动指定必须处理的文件夹或屏蔽不需要处理的文件夹
- query:为loaders提供额外的设置选项
loader的三种写法 1. use 2.loader 3.use+loader
1.支持加载css文件
module: {
rules:[
{
test:/\.css$/,
use:['style-loader','css-loader'],
include:path.join(__dirname,'./src'),
exclude:/node_modules/
}
]
}
加载器的加载顺序为从右至左。即先用css-loader解析然后用style-loader将解析后的css文件添加到Head标签中。
2.支持图片
- file-loader 解决CSS等文件中的引入图片路径问题
-
url-loader 当图片较小的时候会把图片BASE64编码,大于limit参数的时候还是使用file-loader 进行拷贝
{ test: /\.(png|jpg|gif|svg|bmp|eot|woff|woff2|ttf)$/, loader: { loader: 'url-loader', options: { limit: 5 * 1024,// 图片大小 > limit 使用file-loader, 反之使用url-loader outputPath: 'images/'// 指定打包后的图片位置 } } }
3.编译less 和 sass(打包后在js中引入样式,会有延时问题,生产模式推荐plugins)
{
test: /\.css$/,
use: [
{
loader: "style-loader"
},
{
loader: "css-loader"
}
]
},
{
test: /\.less/,
use: [
{
loader: "style-loader"
},
{
loader: "css-loader"
},
{
loader: "less-loader"
}
]
},
{
test: /\.scss/,
use: [
{
loader: "style-loader"
},
{
loader: "css-loader"
},
{
loader: "sass-loader"
}
]
}
4.转义ES6/ES7/JSX
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
loader: 'babel-loader',
options: {
presets: ["env","stage-0","react"]// env --> es6, stage-0 --> es7, react --> react
// presets 由 .babelrc 配置
//
// import() 本来webpack支持,但是用了babel需要插件才能支持 plugins: [
"syntax-dynamic-import",
"dual-import",
["import", {"libraryName": "antd", "libraryDirectory": "es", "style": "css" }]
]
}
}
都8102了,请不要在使用babel-preset-esxxxx系列了,请用babel-preset-env
四、plugins:配置插件
1.自动产出html
我们希望自动能产出HTML文件,并在里面引入产出后的资源。
const HtmlWebpackPlugin = require('html-webpack-plugin');
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html', // 指定产出的模板
filename: 'base.html', // 产出的文件名
chunks: ['common', 'base'], // 在产出的HTML文件里引入哪些代码块
hash: true, // 名称是否哈希值
title: 'base', // 可以给模板设置变量名,在html模板中调用 htmlWebpackPlugin.options.title 可以使用
minify: { // 对html文件进行压缩
removeAttributeQuotes: true // 移除双引号
}
})
] #### 2.分离CSS 因为CSS的下载和JS可以并行,当一个HTML文件很大的时候,我们可以把CSS单独提取出来加载
const ExtractTextWebpackPlugin = require('extract-text-webpack-plugin');
let cssExtract = new ExtractTextWebpackPlugin(`${CSS_DIR}/[name].[contenthash:8].css`);
let lessExtract = new ExtractTextWebpackPlugin(`${CSS_DIR}/[name].[contenthash:8].css`);
let sassExtract = new ExtractTextWebpackPlugin(`${CSS_DIR}/[name].[contenthash:8].css`);
...
module: {
rules: [
{
test: /\.css$/,
loader: cssExtract.extract({
use: ['css-loader?minimize']
})
}, {
test: /\.less$/,
loader: lessExtract.extract({
use: ['css-loader?minimize', 'less-loader']
})
}, {
test: /\.scss$/,
loader: sassExtract.extract({
use: ['css-loader?minimize', 'sass-loader']
})
}
]
}
...
plugins: [
cssExtract,
lessExtract,
sassExtract
] > contenthash:它的出现主要是为了解决,让css文件不受js文件的影响。比如foo.css被foo.js引用了,所以它们共用相同的chunkhash值。但这样子是有问题的,如果foo.js修改了代码,css文件就算内容没有任何改变,由于是该模块的 hash 发生了改变,其css文件的hash也会随之改变。 这个时候我们就可以使用contenthash了,保证即使css文件所处的模块里有任何内容的改变,只要 css 文件内容不变,那么它的hash就不会发生变化。
3.拷贝静态文件
const CopyWebpackPlugin = require('copy-webpack-plugin');
plugins: [
new CopyWebpackPlugin([{
from: path.join(__dirname, 'public'), // 从哪里复制
to: path.join(__dirname, 'dist', 'public') // 复制到哪里
}])
]
4.打包前先清空输出目录
const CleanWebpackPlugin = require('clean-webpack-plugin');
plugins: [
new CleanWebpackPlugin([path.join(__dirname, 'dist')])
]
5.压缩js
const UglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin');
plugins: [
new UglifyjsWebpackPlugin()
] ### 五、devServer:配置开发服务器
devServer:{
Open:true,//则自动打开浏览器
Hot:true,//热模块更新作用
port:8080 // 开发服务器监听的端口,默认8080
host:'localhost',// 开发服务器监听的主机地址,默认localhost
compress:true, // 开发服务器是否启动gzip等压缩
contentBase:path.resolve(__dirname,'dist'),// 额外的静态资源目录,不受publicPath影响, 访问path为/ 根路径
publicPath:'/internal/',
proxy: {
'/proxy': {
target: 'http://your_api_server.com',
changeOrigin: true,
pathRewrite: {
'^/proxy': ''
}
}
}
-
devServer 里配置了 hot: true,即修改或模块后,保存会自动更新,webpack会自动添加 HMR(webpack.HotModuleReplacementPlugin) 插件,页面不用刷新呈现最新的效果。所以模块热更新最终还是 HMR 这个插件起的作用。
-
配置了 publicPath后, url = ‘主机名’ + ‘publicPath配置的’ +’原来的url.path’。这个 其实与 output.publicPath 用法大同小异。
output.publicPath 是作用于 js, css, img 。而 devServer.publicPath 则作用于请求路径上的。
六、devtool:打包方式
从上到下,打包速度越来越快,开发环境一般用eval-source-map,生产环境自行斟酌咯。毕竟打包越快,打包质量也就越差。还有,不知大家发现没,带eval都不支持生产模式哦。
if (env.prod) {
config.devtool = 'source-map';
七、resolve:解析
resolve: { //解析模块的可选项
// modules: [ ]//模块的查找目录 配置其他的css等文件
extensions: ["",".js", ".json", ".jsx",".less", ".css"], //用到文件的扩展名
alias: { //模快别名列表
utils: path.resolve(__dirname,'src/utils')
}
} #### 1.extensions 指定extension之后可以不用在require或是import的时候加文件扩展名,会依次尝试添加扩展名进行匹配 #### 2.alias 配置别名可以加快webpack查找模块的速度