webpack就是为打包而生的。
一、准备
学习webpack首先需要安装nodejs,并且作者也强调新版本的nodejs会提高webpack的打包速度。
安装好node后就可以在项目命令行中执行下面的代码
npm init
然后根据需要设置,也可以一直回车,当然如果都使用默认配置也可以
npm init -y
这样就可以在项目中生成package.json
文件了。这样做的目的就是为了使项目符合node规范。
二、安装webpack
安装webpack有两种方式
1.全局安装webpack(不推荐)
在命令行中输入
npm install webpack webpack-cli -g
安装成功后可以通过命令 webpack -v
查看是否安装成功
但是非常不推荐全局方式安装webpack,因为如果机器上有多项目用不同版本webpack就无法做到兼容。
全局卸载webpack
npm uninstall webpack webpack-cli -g
2.项目内安装webpack(==推荐==)
命令行进入项目目录,执行下面命令
npm install webpack webpack-cli -D
这种方式安装成功后无法通过webpack -v
查看webpack版本信息,因为默认node会查找全局下的webpack。这里就要通过npx命令查看。
npx webpack -v
npx命令会在当前项目的node_modules
中找到我们安装的webpack
3.安装制定版本webpack
npm install webpack@4.16.0
可以通过webpack info 查看所有版本
三、开始打包
安装好webpack后就可以开始打包我们的项目代码了,如我们项目中有一个xxx.js
文件。
npx webpack xxx.js
这样就会将某一个js文件进行打包,打包后的文件就存放在项目根目录的dist文件夹中。这样就是webpack最基础的打包方式,使用的也是webpack默认的配置。如果想要更多的功能就要在webpack.config.js
文件中进行配置
四、配置文件
在项目根目录中新建一个webpack.config.js
文件。
const path = require('path');
module.exports = {
mode: 'production', // 打包模式,production为生产模式,development为开发模式 开发模式下js不会被压缩
// entry: 指定入口文件,
entry: { // 打包输出两个文件
home: './src/index.js',
index: './src/index.js'
},
output: { // 输入配置
publicPath:'http://cdn.com.cn',// 指定打包的文件前缀地址
filename:'[name].js', // 打包后的文件名,可以使用placeholder占位符方式,[name]获取到entry中配置的名称
path: path.resolve(__dirname, 'dist') // 打包好的文件夹,默认就是dist
}
}
这就是最近本的打包配置,指定的打包模式,入口,输出配置等。
配置好后就可以通过
npx webpack
直接运行,不用再写具体文件,而是通过配置的入口去找到指定文件。webpack.config.js
是webpack默认的配置文件,当然也可以手动修改
命令行执行如下面命令即可,npx webpack --config webpackconfig2.js
在平时开发的时候可能很少有人用到npx webpack
这种命令进行打包,这是因为在package.json
文件中对打包命令进行了简化,在scripts中添加如下代码
"scripts": {
"bundle": "webpack"
},
这样改写后执行npm run bundle
相当于执行了webpack打包命令。
在scripts中使用webpack会优先在项目的node_modules中找webpack。
我们之前在安装webpack的时候,安装了一个webpack-cli
,这个webpack-cli
包的作用就是让我们可以在命令行中运行webpack命令(npx webpack
等命令)。
五、loader
webpack的作用就是打包,但是webpack只能识别js文件,如果我们的js文件中引入了样式,或者尝试打包一个css文件、图片文件等,那么webpack就会报错。因为webpack不知道如何处理这样的文件。这时webpack就需要一个东西帮他处理,就是loader。
==loader就是帮助webpack打包不能识别的文件==
打包css
css-loader & style-loader
const path = require('path');
module.exports = {
mode: ……
entry: ……,
output: ……,
module:{
rules:[
{
test: /\.css$/,
use: ['style-loader','css-loader']
}
]
}
}
想打包css文件就要安装style-loader和css-loader。
npm install style-loader css-loader -D
rules
数组中就是很多打包规则,通过正则表达式匹配到css文件,然后告诉webpack一旦遇到css文件就使用style-loader
和css-loader
处理。css-loader
的作用是帮助webpack识别css。分析文件关系,然后合并。style-loader
的作用是得到css-loader分析出来的结果,将css挂载到html-header-style中。
==所以loader的调用是有顺序的,顺序是从后向前==,先执行css-loader再执行style-loader。
sass-loader
打包sass文件需要用到sass-loader。首先安装sass-loader和node-sass。npm i sass-loader node-sass -D
然后改动一下rules
rules:[
{
test: /\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 1,
modules: true // 默认false,引入的css属于全局的样式,开启true后,css变为模块化,引入css时可以通过style.xxx
}
},
'sass-loader'
]
}
]
sass-loader的调用一定要在css-loader
之前,所以要放到数组最后。但是如果一个scss文件中通过import了另一个scss文件,那么在打包第二个scss文件就会直接走到css-loader
,导致打包失败。所以要在css-loader
的options
中加入importLoaders
,表示走css-loader
前还要走另外一个loader。
postcss-loaderpostcss-loader
可以自动添加css3前缀,需要新建一个postcss.config.js
文件,并且要安装插件, npm i autoprefixed -D
将postcss-loader放到rules中sass-loader
之前执行,也就是数组最后。
postcss.config.js
module.exports = {
plugins: [require('autoprefixer')]
}
这样我们在打包css代码时,就会自动添加css3前缀。因为autoprefixer中内置了浏览器兼容表,默认>5%兼容。
打包文件
file-loader & url-loader
先安装npm i file-loader -D
。打包图片文件如下
rules: [
{
test: /\.(jpg|png|gif)$/,
use: {
loader: 'file-loader',
option: {
name: '[name]_[hash].[ext]',
outputPath: 'images/'
}
}
}
]
name命名使用placeholder-占位符
,不指定则打包后的文件是随机生成的名字。
对于特别小的图片,可以使用url-loader
打包成base64格式放到js文件中。
先安装npm i url-loader -D
,file-loader的功能url-loader同样也可以实现,通过配置大小限制,如果超过则使用传统方式打包,否则就使用base64方式打包,十分方便。
rules: [
{
test: /\.(png|jpg|gif)$/,
use:{
loader: 'url-loader',
options:{
name: '[name]_[hash].[ext]',
outputPath: 'images/',
limit: 2048 // 限制文件大小小于2048字节,使用base64方式打包
}
}
}
]
打包es6
安装babel-loader 和 babel/core 包。npm i babel-loader @babel/core -D
babel-loader是帮助webpack打包的工具
babel/core是babel的核心库。可以让babel识别js中的语法,将js语法转换为AST抽象语法树。
添加babel-loader分析js语法,打通babel和webpack,同时还需要借助其他babel模块才能将es6 转换为 es5。安装babel/preset-envnpm i @babel/preset-env -D
现在就可以将es6转换为es5了,但是一些方法在低版本的浏览器中依然不支持。还需要借助polyfill模块。npm i @babel/polyfill -D
然后在业务代码中引入polyfillimport "@babel/polyfill"
然后改写webpack.config.js
rules: [
{
test: /\.js$/,
exclude: /node_modules/, // 排除node_modules中的代码
loader: 'babel-loader',
options:{
presets:[["@babel/preset-env"],{
targets:{
chrome: ">67" // 可以指定兼容浏览器版本
},
// 当使用polyfill的时候,会将所有特性都加入,而不是根据业务代码决定加什么,导致打包后的文件过大。所以通过如下配置解决这个问题,根据业务代码的需要将使用的特性打包进去
useBuiltIns: 'usage'
}]
}
}
]
现在就可以正常打包es6的代码了。不过依然会有一个潜在的问题,就是polyfill再注入方法的时候通过全局变量的形式完成的,会污染全局环境,不适用组件打包。
解决办法:npm i @babel/plugin-transform-runtime @babel/runtime @babel/runtime-corejs2 -D
在babel options中添加plugins配置。
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options:{
// 库代码使用此配置,解决polyfill的问题
"plugins": [["@babel/plugin-transform-runtime"], {
"corejs": 2,
"helpers": true,
"regenerator": true,
"useESModules": false
}]
}
}
]
补充:options内的配置可以单独放到.babelrc文件中
六、plugin
plugin类似一些框架中的生命周期函数,在打包特定的时间点触发去做一些事情。
HtmlWebpackPlugin
如html-webpack-plugin是在打包结束后。安装这个插件。npm i html-webpack-plugin -D
HtmlWebpackPlugin插件会在打包结束后,自动生成一个html文件,并把打包生成的js自动引入到这个html中,html中没有标签。也可以通过模版template,这样在生成html文件时会以配置的html文件为模版生成。
const path = require('path');
module.exports = {
mode: ……
entry: ……,
output: ……,
module: ……,
plugins: [
new HtmlWebpackPlugin({
// 根据模版生成html文件
template: 'src/index.html'
})
]
}
CleanWebpackPlugin
CleanWebpackPlugin插件会在打包前将dist目录删除(非官方)cnpm i clean-webpack-plugin -D
module.exports = {
mode: ……
entry: ……,
output: ……,
module: ……,
plugins: [
// 重新打包先删除dist目录
new CleanWebpackPlugin(['dist'])
]
}
七、sourceMap
sourceMap 是一个映射关系,他知道dist目录下main.js文件出错位置,对应的是src下的index.js文件的位置。在webpack.config.js中添加devtool配置。
module.exports = {
mode: ……
devtool: 'none'
}
设置devtool: 'none'
,js报错只会提示在打包后的js文件中,在开发的时候定位错误就变得异常困难。
设置devtool: 'source-map'
,js报错会显示在原来的文件中,因为在打包后的目录中会生成一个map.js文件,里面存的就是映射关系。报错时通过映射关系找到原文件。
设置devtool: 'inline-source-map'
,inline这种方式不会生成map文件,而是生成base64字符串保存在打包后的js文件底部。
设置devtool: 'cheap-source-map'
,如不设置cheap,报错信息会具体到行列,导致映射关系复杂,耗费性能。加上cheap解决这个问题,同时不去处理第三方代码,只处理业务代码。到一些业务需要将我们自己写的模块或者第三方代码进行处理。那就要用到module。
设置devtool: 'cheap-module-source-map'
,设置module后解决cheap不处理第三方模块的问题。
设置devtool: 'cheap-module-eval-source-map'
,设置eval后,不会生成map文件,也不会有base64编码的字符串,而是改变代码的执行方式,通过sourceURL指向原文件。性能最好。
==开发环境最佳实践==:devtool: 'cheap-module-eval-source-map'
==生产环境最佳实践==:devtool: 'cheap-module-source-map'
八、devServer
每次修改后都要重新手动打包,这样非常浪费时间。有三种方式解决这个问题
1.在package.json中添加"watch": "webpack --watch"
scripts:{
"watch": "webpack --watch"
}
打包的时候运行命令npm run watch
这时webpack就会监听要打包的文件,如果文件变化就会重新打包。
2.配置 devServer
安装dev-server 执行。npm i webpack-dev-server -D
在package.json中添加配置
scripts:{
"start": "webpack-dev-server"
}
打包的时候运行命令npm run start
同时也可以在webpack.config.js中添加配置,
module.exports = {
……
devServer: {
contentBase: './dist', // 打包文件目录
open: true, // 是否自动打开浏览器
prot: 8080 // 启动服务端口
}
}
==webpack-dev-server 打包后不会生成dist目录,而是在内存中。提升打包速度==
使用dev-server时可以搭配使用Hot Module Replacement (热模块替换)
引入HotModuleReplacementPlugin插件,可开启hmr功能,hmr可以热更js、css等代码。
module.exports = {
……
devServer: {
contentBase: './dist', // 打包文件目录
open: true, // 是否自动打开浏览器
prot: 8080, // 启动服务端口
hot: true, // 开启hmr
hotOnly: true // 阻止浏览器自动刷新
},
plugins: [
new webpack.HotModuleReplacementPlugin()
],
}
3.自己编写node服务
自己编写服务如server.js ,在package.json中配置server命令"server": "node server.js"
,相当于在node中使用webpack
打包时执行命令npm run server
即可。
九、总结
webpack基础用法就是这些,文中涉及到的内容足够满足一个小型项目的打包需求。基础用法熟练掌握后,可以通过阅读webpack官方文档进行更详细的打包配置。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。