webpack是什么
Webpack可以看做是模块打包机:它做的事情是,分析你的项⽬结构,找到JavaScript模块以及其它的⼀些浏览器不能直接运⾏的拓展语⾔(Scss,TypeScript等),并将其打包为合适的格式以供浏览器使⽤。
安装
环境:nodejs 官网地址 nodejs.org/en/
版本参考官⽹发布的最新版本,可以提升webpack的打包速度
全局安装(不推荐)
安装:npm install webpack webpack-cli -g
webpack-cli 可以帮助我们在命令⾏⾥使⽤npx,webpack等相关指令。
查看当前webpack版本:webpack -v
全局卸载webpack:npm uninstall webpack webpack-cli -g
局部安装(项目内安装)
安装:npm install webpack webpack-cli --save-dev
查看版本:
webpack -v // command not found 默认在全局环境中查找
npx webpack -v // npx帮助我们在项⽬中的node_modules⾥查找webpack
安装指定版本:
**npm info webpack ** //查看webpack的历史发布信息
npm install webpack@xxx webpack-cli -D
webpack 配置⽂件
零配置是很弱的,特定的需求,总是需要⾃⼰进⾏配置。
当我们使⽤npx webpack index.js时,表示的是使⽤webpack处理打包,名为index.js的⼊⼝模块。默认放在当前⽬录下的dist⽬录,打包后的模块名称是main.js,当然我们也可以修改webpack有默认的配置⽂件,叫webpack.confifig.js,我们可以对这个⽂件进⾏修改,进⾏个性化配置。
- 默认的配置文件:webpack.config.js
执⾏npx webpack命令后,webpack会找到默认的配置⽂件,并使⽤执⾏。
- 不使⽤默认的配置⽂件: webpack.config.js
指定webpack使⽤webpackconfig.js⽂件来作为配置⽂件并执⾏:
npx webpack --config webpack.config.js
- 修改package.json scripts字段
"scripts": {
"bundle":"webpack"
}
这个地⽅不要添加npx ,因为npm run执⾏的命令,会优先使⽤项⽬⼯程⾥
的包,效果和npx⾮常类似。执行脚本命令:npm run bundle
webpack核心概念
entry
指定打包⼊⼝⽂件:Webpack 执⾏构建的第⼀步将从 entry 开始,可抽象成输⼊
entry: {
main: './src/index.js'
}
==相当于简写===
entry:"./src/index.js"
output
打包后的⽂件输出结果位置:在 Webpack 经过⼀系列处理并得出最终想要的代码后输出结果。
output: {
publicPath:"xxx",
filename: "bundle.js", path: path.resolve(__dirname, "dist") // 必须是绝对路径
}
loader
模块转换器,⽤于把模块原内容按照需求转换成新内容。webpack是模块打包⼯具,⽽模块不仅仅是js,还可以是css,图⽚或者其他格式。但是webpack默认只知道如何处理js模块,那么其他格式的模块处理,和处理⽅式就需要loader。
moudle
模块,在 Webpack ⾥⼀切皆模块,⼀个模块对应着⼀个⽂件。Webpack 会从配置的 entry 开始递归找出所有依赖的模块。
module: {
rules: [
{
test: /\.xxx$/,
use: {
loader: 'xxx-loadr'
}
}
]
}
当webpack处理到不认识的模块时,需要在webpack中的module处进⾏配置,当检测到是什么格式的模块,使⽤什么loader来处理。
- file-loader
处理静态资源模块。原理是把打包⼊⼝中识别出的资源模块,移动到输出⽬录,并且返回⼀个地址名称。
所以我们什么时候⽤file-loader呢?
场景:就是当我们需要模块,仅仅是从源代码挪移到打包⽬录,就可以使⽤fifile-loader来处理,txt,svg,csv,excel,图⽚资源等等。
安装:npm install file-loader -D
案例:
module: {
rules: [
{
test: /\.(png|jpe?g|gif)$/,
//use使⽤⼀个loader可以⽤对象,字符串,两个loader需要⽤数组
use: {
loader: "file-loader",
// options额外的配置,⽐如资源名称
options: {
// placeholder 占位符 [name]⽼资源模块的名称
// [ext]⽼资源模块的后缀
// https://webpack.js.org/loaders/file-loader#placeholders
name: "[name]_[hash].[ext]",
//打包后的存放位置
outputPath: "images/"
}
}
}
]
}
- url-loader
可以处理file-loader所有的事情,但是遇到jpg格式的模块,会把该图⽚转换成base64格式字符串,并打包到js⾥。对⼩体积的图⽚⽐较合适,⼤图⽚不合适。
安装:npm install url-loader -D
案例:
module: {
rules: [
{
test: /\.(png|jpe?g|gif)$/,
use: {
loader: "url-loader",
options: {
name: "[name]_[hash].[ext]",
outputPath: "images/",
//⼩于2048,才转换成base64
limit: 2048
}
}
}
]
}
- 样式处理
css-loader 分析css模块之间的关系,并合成⼀个css。
style-loader 会把css-loader⽣成的内容,以style挂载到⻚⾯的head部分。
安装:npm install style-loader css-loader -D
案例:
{
test: /\.css$/,
use: ["style-loader", "css-loader"]
}
- sass样式处理
sass-loader 把sass语法转换成css ,依赖node-sass模块。
安装:npm install sass-loader node-sass -D
案例:
{ test: /\.scss$/,
use: ["style-loader", "css-loader", "sass-loader"]
}loader有顺序,从右到左,从下到上
- postcss-loader
样式⾃动添加前缀,兼容不停浏览器版本
安装:npm i postcss-loader -D
案例:
{
test: /\.css$/,
use: ["style-loader", "css-loader", "postcss-loader"]
}
根目录新建postcss.confifig.js文件,并安装autoprefixer
安装:npm i autoprefixer -D
module.exports = {
plugins: [require("autoprefixer")]
}
plugins
plugin 可以在webpack运⾏到某个阶段的时候,帮你做⼀些事情,类似于⽣命周期的概念。
在 Webpack 构建流程中的特定时机注⼊扩展逻辑来改变构建结果或做你想要的事情。
- HtmlWebpackPlugin
htmlwebpackplugin会在打包结束后,⾃动⽣成⼀个html⽂件,并把打包⽣成的js模块引⼊到该html中。
安装:npm install --save-dev html-webpack-plugin
配置:
title: ⽤来⽣成⻚⾯的 title 元素
filename: 输出的 HTML ⽂件名,默认是 index.html, 也可以直接配置带有⼦⽬录。
template: 模板⽂件路径,⽀持加载器,⽐如 html!./index.html。
inject: true | 'head' | 'body' | false ,注⼊所有的资源到特定的 template 或者
templateContent 中,如果设置为 true 或者 body,所有的 javascript 资源将被放置到 body
元素的底部,'head' 将放置到 head 元素中。
favicon: 添加特定的 favicon 路径到输出的 HTML ⽂件中。
minify: {} | false , 传递 html-minifier 选项给 minify 输出。
hash: true | false, 如果为 true, 将添加⼀个唯⼀的 webpack 编译 hash 到所有包含的脚本和
css⽂件,对于解除 cache 很有⽤。
cache: true | false,如果为 true, 这是默认值,仅仅在⽂件修改之后才会发布⽂件。
showErrors: true | false, 如果为 true, 这是默认值,错误信息会写⼊到 HTML ⻚⾯中。
chunks: 允许只添加某些块 (⽐如,仅仅 unit test 块)
chunksSortMode: 允许控制块在添加到⻚⾯之前的排序⽅式,⽀持的值:'none' | 'default' |
{function}-default:'auto'
excludeChunks: 允许跳过某些块,(⽐如,跳过单元测试的块)。
案例:
const path = require("path");
const htmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
...
plugins: [ new htmlWebpackPlugin({
title: "My App",
filename: "app.html",
template: "./src/index.html"
})
]
}
- clean-webpack-plugin
每次打包的时候用于删除/清理您的构建文件夹
安装:npm install --save-dev clean-webpack-plugin
案例:
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
...
plugins: [
new CleanWebpackPlugin()
]
- mini-css-extract-plugin
抽离css样式形成一个css文件,在head标签中link链接一个css文件
安装:npm install --save-dev mini-css-extract-plugin
案例:
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
...modules: [ {
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, "css-loader"]
}
]plugins: [
new MiniCssExtractPlugin({
filename: "[name].css"
})
]
sourceMap
源代码与打包后的代码的映射关系
在dev模式中,默认开启,关闭的话 可以在配置⽂件⾥
eval:速度最快,使⽤eval包裹模块代码,
source-map: 产⽣ .map ⽂件
cheap:较快,不⽤管列的信息,也不包含loader的sourcemap
Module:第三⽅模块,包含loader的sourcemap(⽐如jsx to js ,babel的sourcemap)
inline: 将 .map 作为DataURI嵌⼊,不单独⽣成 .map ⽂件
配置推荐:
devtool:"none"
devtool:"cheap-module-eval-source-map",// 开发环境配置
devtool:"cheap-module-source-map", // 线上⽣成配置
WebpackDevServer
每次改完代码都需要重新打包⼀次,打开浏览器,刷新⼀次,很麻烦。我们可以安装使⽤webpackdevserver来改善这块的体验。启动服务后,会发现dist⽬录没有了,这是因为devServer把打包后的模块不会放在dist⽬录下,⽽是放到内存中,从⽽提升速度。
安装:npm install webpack-dev-server -D
修改下package.json:
"scripts": {
"server": "webpack-dev-server"
}
在webpack.confifig.js配置:
devServer: {
contentBase: "./dist",
open: true,
port: 8081
}
Hot Module Replacement (HMR:热模块替换)
devServer {
contentBase: './dist',
open: true,
hot: true,
// 即便HMR不生效,浏览器也不自动刷新,就开启hotOnly
hotOnly:true
}
配置文件头部引入webpack,在插件配置处添加:
const webpack = require('webpack')
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
template: "src/index.html"
}),
new webpack.HotModuleReplacementPlugin()
]
Babel处理ES6
安装:npm i babel-loader @babel/core @babel/preset-env @babel/polyfill -D
babel-loader是webpack 与 babel的通信桥梁,不会做把es6转成es5的⼯作,这部分⼯作需要⽤到@babel/preset-env来做,@babel/preset-env⾥包含了es6转es5的转换规则。但是类似Promise, async/await之类的语法特性还没有转换过来,这时候需要借助@babel/polyfifill,把es的新特性都装进来,来弥补低版本浏览器中缺失的特性。
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader",
options: {
presets: ["@babel/preset-env"]
} }
// index.js 顶部
import '@/babel/polyfill'
const arr = [new Promise(() => {}), new Promise(() => {})];
arr.map(item => {
console.log(item);
});
会发现打包的体积⼤了很多,这是因为polyfifill默认会把所有特性注⼊进来,假如我想我⽤到的es6+,才会注⼊,没⽤到的不注⼊,从⽽减少打包的体积,可不可以呢?当然可以,修改webpack.config.js配置。
options: {
presets: [
[
"@babel/preset-env",
{
targets: {
edge: "17",
firefox: "60",
chrome: "67",
safari: "11.1"
},
useBuiltIns: "usage" }
]
]
}
@babel/plugin-transform-runtime
当我们开发的是组件库,⼯具库这些场景的时候,polyfill就不适合了,因为polyfill是注⼊到全局变量,window下的,会污染全局环境,所以推荐闭包⽅式:@babel/plugin-transform-runtime,它不会造成全局污染。
安装:
npm install --save-dev @babel/plugin-transform-runtime
npm install --save @babel/runtime
先注释掉index.js⾥的polyfill
// index.js 顶部
// import '@/babel/polyfill'
const arr = [new Promise(() => {}), new Promise(() => {})];
arr.map(item => {
console.log(item);
});
修改配置⽂件:注释掉之前的presets,添加plugins
options: {
"plugins": [ [
"@babel/plugin-transform-runtime",
{
"absoluteRuntime": false,
"corejs": 2,
"helpers": true,
"regenerator": true,
"useESModules": false
}
]
]
}
development vs Production模式区分打包
安装:npm install webpack-merge -D
案例:
const merge = require('webpack-merge')
const commonConfig = require('./webpack.common.js')
const devConfig = {
...
}
module.exports = merge(commonConfig, devConfig)
在package.json配置
"scripts":{
"dev":"webpack-dev-server --config ./build/webpack.dev.js",
"build":"webpack --config ./build/webpack.prod.js"
}