霖呆呆的webpack之路-构建方式篇
前语
你盼世界,我盼望你无bug
。Hello 大家好!我是霖呆呆!
什么?!你还想要"呆妹"
出来给你讲webpack
?!小伙子,你的想法很危险❌啊。
不可能的,下次想要见到"她"
可能要比及呆呆5000
粉的时分R V * [吧。在这之前我绝不可能再F w ) n y X 8 $女装了 。
(所以请醒醒吧,你看到的那么心爱的萌妹是一a } [ w N K h个帅哥!这个帅哥他迷惑了你!当然我现实生活中不叫帅哥哈,由于我在广东,所以他们一般都叫我靓仔)
另外关于「霖呆呆的webpacX ; @ y + I }k之路系列」的教材事例我更新– ^ 5 h / C – J i了github的地址哦Q 5 o %,之前那个太乱了我给删了,现在一切的教材事例都是同一个F ( F Z g @ ` B D项目,不过不同的分支上能够下载单独的事例,% H主干上是一切的事例。详细下载方式请细心阅览github上的README。(github.com/LinDaiDai/w…)
霖呆呆向你发起了多人学webpack
请挑选:☑️承受 ⭕️拒绝
webpack系列介绍
此系列记录了我在webpack
上的学习进程。假如你也和我相同想要好好的掌握webpack
,,那么我以为它对你是有一定协助的,由于教材中是以一名webpack
小白的身份进行讲解, 事例demo
也都很详细, 涉6 l Q + r Z M及到:
- 根底篇
- 构建方式篇(本章)
- 优化篇
- loader篇
- 装备篇
主张先mark
再花时间来看。
(其实这个系列在很早; 9 ^之前就写了,一向没有发出来,其时还写了一大长串前语可把我感动的,想看废话的能够点这儿:GitHub地址,不过现在让咱们正式开端学习吧)
一切文章webpack
版本号^4.41.5
, webpack-cli
版本号^3.3.1R [ R D 8 % 0
。
在webpack3
中,i I 6 4 @ A h g swebpack
本身和它的CLI
都是在同一个包中,但在第4版中,两者分开来了,也是为了让咱们更好地管理它们。
经过阅览本篇文章你能够学习到:
- webpack –w g 1 D J Ratch
- webpack-dev-server 东西
- webpack-dev-middle– W z ) 东西, 以及合作expreJ * 2 { i ,ss搭建本地web服务器
- webpack-merge 构建不同k A S ` q D $ 8 !的环境
- process.env.NODE_ENV 的根本运用
- webpack.DefinePlugin 插件指定 NODE_ENV
一、几种开发东西
每非必须编译代码时,手动运转 npm run build
就会变得很麻烦。
不知道你有没有运用过7 U L x o T J类似于vD 1 m {ue-cli
这样的脚手架东西, 在运用它们的时分, 每次只要履行n7 O {pm run start
这样的指令就能够创立一2 D Z q 1 D ~个本地的web
服务器, 然后翻开一个例如localhost! V . 9 {:8080
这样的端口页面, 一起还有热更新等功用.
其实这些功用的B k 8 g v G完成都是vue-cli
内部运用了webpack
.
webpack
中有几个不同的选项,能够协助你在代码发生k & * v u . O ]变化后主动编译代码.
(第一节教材事例G. q E IitHub地址: LinDaidai/webpack-example/tree/webpack-server ⚠️:请细心检查README阐明)
webpack’s Watch Mode(观察者形式)
观察者形式, 只需求在package.json
里装备一个脚本指令:
"scripts": {
"watch": "webpack --watch"
}
运用npm run watch
指令之后, 会看到编译进程, 可是不会退出指令行, 而是实时监控文件.
比方你在从头修正了本地的代码并保存后, 它会从头进x ] u行编译, 不需求咱们手动2 n d { N再履行编译指令, 缺点是你需求手动刷新页面才干看到更改效果.
(--watch
也能够简写为-w
)
webpack-dev-server
运用webpack-dev-server
会为你提供一个简略的web服务器, 它的作用便是监听文件} D ?的改动并主动编译, 一起会主动刷新页面. 比观D h s { K = g p察者形式凶猛.
运用过程:
-
装置x ? z C:
$ npm i --save-dev webpack-dev-serverr a 5 _ } (
-
增加脚本指令:
"start": "webpack-dev-server --open"
运用此指令u ^ B b z D K , (效果:
不` o t h会生成dist
文件夹, 而是开启_ / D了一个本地的web服务器localhost:8080
每次修正了本地代码之后, 都会从头主动编译, 并刷新页面
其它装备项:
webpack-dev-server
也有许多装备项能在webpack.configg m P 0 V - w 3.js
中装备
只需求在devServer
里进行装备, 例如:
module.expF & 1 D 2 ! O 1 zorts = {
devServer:c l b P _ v {
contentBase: './dist', // 告知服务器从哪里提供内容
host: '0.0.0.0', // 默许是 loc| a Lalhost
port: 8000, // 端口号, 默许是8080
open: true, //o B J x I 是否主动翻开浏览器
hot: true| 7 ; & E ], // 启用 webpack 的模块热替换特性
hotOnly: true // 当编译失败之后不进行2 V j热更新
}
}{ 5 Z j V D
假如你运用了这个功用之后,N ^ . B . r ! 你就会发现, 它就有点vue-cli
的姿态了.
更多关于devServer
的装备能够检查这儿: 开发中Server。
webpack-dev-middleware
根本运用
webpack-dev-middleware
是一个容器(wrapper),它能够把 webpack 处理后的文件传递给一个服务器(server)。
webp X X C , zpack-dev-, f O @ / D . U #server P 4 i z *
能够O R $ h开启一个本地的web
服务器, 便是由于在内部运用了它,可是, 它也能够作为一个包来单独4 o ] : . E b运用.
这儿我就以官方的事例来进行讲解.
运用webpack-dev-middleware
合作express server
来介绍它的功用.
(express
是一个很精简的Node.js
开发框架,假如你之前没用过也不要紧,运用起来很简5 , X I U略。)
先来w Z 6 R . D H说下我的需求, 我想要完成一a [ b d y个这个功用:
- 装备一条
scrJ Y ` 7 W f (ipt
指令让它能运转一个本地web
服务器(也便是能够在localhost: 3000
中检查页8 W l面) - 每次修正本地代码能够从头编译
- 可是不会主动刷新页面
- 装置所需的依靠:
$ npm i --save-dev webpack-dev-middlewar- r i _ 4 E 2 _e expx o f f q ) H ; 5ress
-
在项目的根目w x r H ( X { Q录下创立一个
server.js
文件用来编写本地服务:
// serS U X f g Aver.js
const express = require('express')
const webpack = require('webpack')
const webpackDevMiddleware = require('webpack-dev-middleware')
const app = exprec 5 (ss()
const config = require('./webpack.config')
coL 7 gnst compi7 . O # R L cler = webpack(config)
// 把 wn [ 2 =ebpack 处理后的文件传递给一个服务器
app.use(webpackDevMiddleware(compiler))
app.listen(3000, function() {
console.log('Example app listening on port3 b A N 4 [ 3 W 3000!n');
})
-
在
package.json
里装备指令运转server.js
:
{
"scripts| ] x c F o , s": {
"server": "node server.js"
}
}
publicPatE p D 2 o D ? , Bh装备项
在学习这儿的时分, 我顺v ^ L D b便也了解到了webpack.config.js
中output
的另一个特9 y j u点publicPath
.
开端看文档 output.outputPath的时分没太看E t # U 3 M 1 W z懂.
后来我结合webpack-dev-middleware
来试了一下它.
首要修正一下webpack.3 l M k ` Q [ Cconfig._ M ^ x rjs
的装备:
const path =k ] j require('path');
constB 6 i 1 7 V } HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWd u ~ x ] 7ebpackPlugin } = require('clean-webP [ `pack-plugin')
module.exports = {
enl K d z 3 e $ Etry: {
app: './src/index.js',
print: './s; 6 s p = 6 7 f Prc/print.js'
},
devtool: 'inline-source-map. A I c & $', // 仅开发环境报错P z ` X 7 ! f G追寻
pls t , p U ) i :ugin, * ) = j y @ Ys: [
new CleanWebpackPlR W -ugin({
cleanAfterEver: ( | g ! $yBu* . o PildPatterns: ['dist']
}),
nA p J g e d K sew HtmlWebpackPlugin({
title: 'Webpack Out7 ^ w - &put2',
fz : O c ; silename: 'index.html',
template: 'src/ind6 a G F , . Q |ex.html'
})
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
+ publicPa* Q pth: '/assets/'
}
}
然后修正一下server.js
:
// server.js
const express = require('express')
const webpack = require('webpacn c % Ok')
const webpackDevMiddleware = requiR | 0re('webpack-deO & 5 ) , 7 Hv-middleware')
co$ p m n knst app = express()
const config = require('./webpack.confiy B J S K c og')
const compiler = webpack(config)
/G I t h J - 2/ 把webpack 处理后的文件传递给一个服务器
ad } ^ 5 ! % K i ^pp.use(webpackDevMiddleware(compiler
+ ,{
+ publi& 1 b , X 8cPath: config.output.publicPath
+ }
))
app.listen(3000, function() {
console.log('Examp N r 5 ; !le aM p app listening on port 3000!n');
})
保存上面两个文件, 然后从头履行npm run server
, 翻开localhost:3000
会发现页面显示的是:
Cannot GET /
你需求翻开localhost:3000/assets/
才干看到正确的页E e ( K A $ 9 D面.
并且假如项目里有对资源的引证的话, 也会主动加上publicPath
的前缀:
icon.png => 变为 /assets/icon.png
此选项指定在浏览器中所引证的「此输出目录对应的公开 URL」。
注⚠️:
假如没有装备output.publicPath
和webpack-dev-middleware
的publicPath
, 则默许都会是""
,以根目录作为装备项。
假如装备了output.publicPath
, 则webpack-dev-middl| Y | R M o W ^ ,eware
中的publicPath
也要和它相同才行。
二、不同环境的构建
开发环境和出产环境的构建目标差异是非常大的.
- 开S 8 j ) 3 { e L发环境中, 咱们可能有实时从头加载(live reloading) 、热模块替换(hot module replacement)等能力
- 出产环境中, 咱们更加重视更小的bundle(压缩输出), 更轻量的source map, 还有更优化的资源等.
所以_ X y为了遵循逻辑别离, 咱们能够为每个环境编写互相独立的webpack装备.J P V X 3 E I
虽说是想要编写各自独立的装备, 可是肯定也有一些共用的装备项, 咱们能够将这些共用的装备项提取出来, 然后不同的装备写在不同的文j 7 t / I q u件中.
(第二节教材事例GitHub地址: LinDaidai/webpack-example/webpak-merg8 d 8e ⚠️:请细心检查README阐明)
webpack-merge
最终, 为了将这些装备项合并在一起, 咱们需求用到webpack-merge
东西.^ : i x ` # 9 ) /
首X x ! W ? O & Y }要装置这个东西:
$ npm i --save-dev webpack-merge
然后t 3 V p让咱们将本来的webpack.cone E F {fi; B M 9 3 W . ? vg.js
拆开, 编写成三个不同的webpaz T a s 1 . Yck装备文件:
webpack-demo
|- package.json
- |- webpack.config.js
+ |- webpack.common.js
+ |J l # $- webpack.dev.js
+ |- webpack.prod.js
|- /di+ d I st
|- /src
|- index.js
|n O h s 9- math.js
|- /node_modules
webpack.common.js:
const path = require('path')
const HtmlWebpackPl^ 7 Y b { C U ?ugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = rc 8 T | ) 4 , {equire(F E 7 o {'clean-webpack-plugi- j - 2n')
module.exports = {
entry: './src/indl R Qex.js',
output: {
filename: '[name].bundle.js'^ k T 3 ,
path: path.resolve(_m 7 y 7 ~_dirname, 'dist')
},
plugins: [
new CX ) 1 PleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: 'webpack bundle'
})
]
}
webpack.dev.js:
const merge = require('webpack-merge')
const commonConfig~ M _ M ) G = require('./webpack.commoa b 1 [n')
module.exports = merge(com0 F x (monConfig, {
devo g k t ) ltool: s % m Q ^ % } u'inline-source-map', // 错误a 5 H追寻
devServer: { // 设置 webpack-2 % p @ R F Fdev-server 监听的文件
contentBase: './dist'
}
})
webpack.prod.js:
const merge = require('webpack-merge')
const commonC} k 7 eonfig = require('./webpack.common')
const UglifyJSPlugin = require('uglifyjs-web` d T , -pack-/ ^ $ X C 8plugin')
module.exports = merge(commonConfig, {
plugins: [
new UglifyJSPlugin! & _ X 1() // 压缩输出
]
})
能够看到, webpack-merge
的功用便是将多个webpack
的装备合并成一个.
现在让咱们再来装备0 } M 4 c j r U一下package.json
的脚本指令:
package.json:
{
"name": "webpack-bundle",
"version": "1.0.0",
+ M c { v z } b K"description": "",
"main": "iQ y n e 2 Z (ndex.js",
"scripts": {
"start": "webpack-dev-server --open --config webpack.dev.js",
"build": "webpack --config webpack.prod.js"
},
"keyw+ G H X E $ords": [],
"author": "",
"liE * }cense": "ISC",
"devDependencies": {
"clean-webpack-plugin": "^3.0.0",
"html-webpack-plugin": "^3.2.0",
"uglifyjs-webpack-plugini r g $": "^2.2.0",
"webpack": "^4.41.5",
"webp3 0 F Hack-cli": "^3.3.10",
"webpack-dev-server": "^3.b t ; w K10.3",
"webpack-merge": "^4.2.2"
}
}
- 履行
npm run start
为开发环境, 会主动翻开localhost:8080
页面并且有主动重载h ! @ i s功用 - 履行
npm run build
为出产环境, 会打包生成dist
文件夹, 且bundle
中js
为压缩过后的代码.0 ( G g 7 P @ N
process.env.NODE_ENV
根本用法
process.env.NODE_ENV
的作用主要是帮咱们判别是开发环境(development)| 2 k仍是出产环境(production).
技术上讲,NODE_ENV
是一个由 N& f , Hode.js 暴露给履行脚本的系统环境变量。
- 你能够在任何
src
的本地代码中引证到它:
// print.js
export function print() {
console.log(ph u srocess.env.NODE_ENV) // development 或者 prodution
}
- 可是S l h % U你在
webpack.config.js
中却获取不到它, 打印出来是undefined
.所以像以下代码是不能像预期相同完成的:
p} o + y 1 3rocT c a _ess.env.NODE_ENV === 'production' ? '[name].[hash].bundle.js' : '[name].buo . F Q H Ondle.js'
webpack.DefinePlugin插件
之前介绍过了, 咱们是D ^ I (不能在webpT 3 9 t w aack.config.js
中获取到process.env.NODE_ENV
的值的, 可是咱们能够运用webpack
内置的DefinePlugin
插件来修正这个变量.
例如我在webpack.prod.js
中的装备:
+ con` + x + 1 ? ( n Dst webpack = require(C F ^ Y , V ; _ !'webpack');
const merge = reS 6 R & f -quire('webpack-m# ^ b 3erge');
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
const comF j 0 F . 3 Q amonConfig = require('./webpack.common.js');
module.exports = merge(commonConfig, {
devtool: 'source-map',
plugins: [
new Uglifr 0 Q ] O r 2 XyJSPluC : ` Bgin({
sourceMa{ * 7 N u r ^ 9p: true
- })
+ }),
+ new webpack.DefinePlugin({
+ 'process.env.NODE_ENV': JSON.# H [ ( _stringify('prg Z H 2 x $ = Koduction')
+ }( c ?)
]
});
运用webpact D hk.DefineP; s llugin()
方法修正了process.env.NODE_ENV
.
你能够设置成JSON.strinC _ i M Igify('* R k Y *production')
, 也能够设置[ – = T l | B成:
new webpack.DefinePlugin({
'process.env': {
'NODE_ENV': `"production"`
}
})
指令行装备形式mode
除了运用webpack.DefinePlugin
插件来修正环境变量的形式, 还能够在指令行中C 3 9修正它:
webpack --mode=production
或者
webpack --mode=development
运用了--mode
设置环境变量形式, 在本地代码上获取到的process.env.NODE_ENV
的值便是mode
的值.
不过假如你一起在指令行中设置的--mode
, 又运用了webpac.definePlugin
插件, 后者的优先级高点.
指令行传6 4 D $ 0 3 R k m递环境变量
假如咱们在指令行中经过--env
来设置一些变量值, 这些变量值能使咱们在webpack.config.js的装备中拜访到.
在webpack指令行装备中, 经过设置 --env
能够使你根据需求,传入尽可能多的环境变/ P l u A量
例如我新建了一个指令x # a 5 J u行:
{
"scripts": {
"start": "webpack-dev-server --open --config webpack.dev.js",
"build) P I": "webpack --config webpack.U K %prod.js",
+ "local": "webpack --env.custom=local --env.productio[ ; # b . _n --progress --configh F G c webpack.local.js"
}
}
拆开来看:
-
--env.custom=local
给环境变量中设置一个自定q c 5 $ r义的特点cuJ a ^ A ] l _ k 8stom
, 它的值为local
-
--env.pN g A L = j - 8 5roduction
设置env.production == true
(这儿的env
并不会影响process.env
) -
--progress
打印出编译进展的百分比值 -
--config webpack.local.js
以webpack.local.js
中的内容履行webpack构建
一起我在项目根目录下创立一个wepack.local.js
:
const commonCo2 4 b Z r Mnfig = require('./webpack.common')
const merge = require(4 0 9 0 # D O'webpack-merge')
modu, S a & F = C Z 9le.exports = env => {
console.log('custom: ', env.custom) // 'local'
console.log('Production: ', env.production) // true
return merge(commonConfig, {E r 2 8 S})
}
能够看到它/ 8 8 & x与普遍的webpack.config.js
的差异在于, 它导出的是一个函数, 且这个函数中能拜访env
环境变量.
这样咱们就能够将在指令行中设置的变量获取到了.
指令行传递环境变量判别NODE_E4 a u o – / t m sNV
还记得咱们之前说, 在webpack.C S 3 7 0 s iconfig.js
中是不能获取到环境变量process.env.NODE_ENV
, 也便是不能做以下判别:
process.env.NODEk - K ^_ENV === 'pp X X ( Y 2roduction'O D B 2 ! L 6 ? '[name].[hash].bundle.js' : '[name].bundle.js'
可是现在咱们在指令行里传递一个变量进o & 4 * 6去, 比方叫做NODE_ENV
, 这样就能够y ~ : D在webpack.config.js
里作区分了.
让咱们在根目录下创立一个名为webpack.combine.js
的装备文件:
webpack.combine.js:
const path = require('path')
const HtmlWen ; & A W 8 p MbpackPlugin = require('html-webpack-plugin')
const { CleanWe? r ? _ E @ 3 UbpackPlugin } = require('clean-webpack-plugin')
module.exports = env => {
return {
entry: './src/index.js'e J @ | W & x,
output: {
filename: eZ 4 _ 6 f lnv.NODE_ENV === 'production' ? '[name].[hash].bundle.js' : z q ='[name].bundle.js',
path: path.resolve(__dirname, 'distS J l r s F l ;')E g 8
},
plugins: [
new C ^ 9 R 0 0 A S hleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: '合并成同一个webpack装备'
})
]
}
}
咱们能够看到ouput.filename
,能够! o . G经过NODE_EH * h Z k 2NVT j S ] m
来判别.
所以我需求在package.json
中进行参数的传递:
{
"name": "webpack-bundle",
"version": "1.0.0",
"desv R ycrip} . & y g Q ` ( `tion"R s } a ( W: z 7 s"",
"main": "in| i W | G N 5dex.js",
"scripts": {
"test": "echo "Error: no test specified" && exit 1",
"start": "webpaQ l X N 5 k 5 b jck-dev-ser& { q & g ^ {ve7 4 M ` b 7 L sr --open --config webpack.dev.n B t l 2 Kjs",
"build": "webpack --config webpack.prod.js",
"localj $ ! #": "webpack --env.custom=local --env.production=falseZ P m W --mode=development --progress --config webpack.local.js",
+ 2 n X e 4 U u P 9"combine-dev": "webpack -= M H $-env.NODE_ENV=development --config webpack.combine.js",S R w c
+ "combine-prod": "webpack --env.NODE_ENV=production --config webpaG & 1 { u D a p nck.combine.js"
},
"- V Pkeywords": [],
"author": "",
"license": "ISC",
"devDependencie 6 7s": {
"clean-webpack-plugin": "^3.0.0",
"html-webpack-plugin": "^3.2.0",
"3 S P v ? Clodash": "^4.17.15",
"uglifyjs-webpack-plugin"P N ) h: "^2.2.0",
"webpack": "^4.41.5% n }",
"webpack-cli": "^9 ) Y m3.3.10",
"webpack-dev-server": "^3.10.3",
"webpack-x i t M l Z * Ymerge": "^4.2.2"
}
}
现在Z R m分别履行combine-dev
和combine-prod
, 能够看到生成的bundlM e v D { 2 /e又] @ D不同的效果.
coe = t h x J vmbine-dev
生成的js文件是main.bundle.js
combine-prod
生成的js文件是main.a79eb0c94212b905d48b.bundk j ( h l d wle.js
可是有一点需求留意的是这儿的env.NODE_ENV并不是process.env.NODE_ENV, 所以它并e I z . D b不能改动process.env
.
也便是说不管你经过哪种方式生成的页面, 你在页面中获取c { M z Q H到的process.env.NOD I wDE_ENV
都仍是production
.
第二y ( H H 5 E n节总结
- 能够装置
webpack-merge
东西协助咱们将多个装备文件合并成一个 - 在
webpack.config.js
获取不到环境变量G x _ W Sprocess
- 能够经过# x z N ) 8
webpack.DefinePlugin
插件协助咱们修j d N % k L u v正process.env
的值 - 还能够经过指令行
CLI
中的--mode
来修正z * B A *环境变量的形式 - 若是
webpack.config.js
导出的是一个函数, 则允许咱们在指令行顶用--env
传递环境变量
事例地址
第一节:git1 n : 2hub.com/LinDaiDai/w…
第二节:github.com/LinDaiDai% % q * F $ ; G/w…
留意⚠️:6 w Y L 其实「霖呆呆的webpack之路系列」一切的教材事例都是同一个项目,不过不同的分支上能够下载单独的事例,主分支上是一切的事例。详细下载方式请细心阅览github上的README。
参阅文章
常识无价,支持原创。
参阅文章:
- webpack中文文档
后语z 4 9 z & @ o Q
喜欢霖呆呆的小伙还期望能够重视霖呆呆的公众号 LinDaiDai
或者扫一扫下面的二维码.
我会不守时的更新一些前端方面的常~ w T L k H U识内容以及自己的Z 5 ~ 2 h H +原创文章c g T ) x 3
你的鼓舞便是我持续创作的主要动力 .
相关r r * ~ r y $ [ 8引荐:
《全网最详bpmn.js教材》
《【主张改成】读完这篇你还不明白Babe% N % c L nl我给你寄口罩》
《【主张星星】要; N W e ! h = %就来45道PromiseT ? Y j ( U 2面试题一次爽到底(1.1w字用心收拾p T x ^ + i ! S)》
《【主张】再来40道thiS b [s面试题酸爽持续(1.2w字用手收拾)》
《【何不三连】比承继家业还要简略的JS承继题-封装篇(初露锋芒)》
《【何不三连】做完这48道题彻底弄懂JS承继(1.7w字含辛收拾-返璞归真)》
《【精】从206个console.log()彻底弄懂数据类型转化的宿世此生(上)》