霖呆呆的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面)
  • 每次修正本地代码能够从头编译
  • 可是不会主动刷新页面
  1. 装置所需的依靠:
$ npm i --save-dev webpack-dev-middlewar- r i _ 4 E 2 _e expx o f f q ) H ; 5ress
  1. 在项目的根目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');
})
  1. 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.jsoutput的另一个特9 y j upublicPath.

开端看文档 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.publicPathwebpack-dev-middlewarepublicPath, 则默许都会是"",以根目录作为装备项。

假如装备了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文件夹, 且bundlejs为压缩过后的代码.0 ( G g 7 P @ N

process.env.NODE_ENV

根本用法

process.env.NODE_ENV的作用主要是帮咱们判别是开发环境(development)| 2 k仍是出产环境(production).

技术上讲,NODE_ENV 是一个由 N& f , Hode.js 暴露给履行脚本的系统环境变量。

  1. 你能够在任何src的本地代码中引证到它:
// print.js
export function print() {
console.log(ph u srocess.env.NODE_ENV) // development 或者 prodution
}
  1. 可是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.jswebpack.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 ~ : Dwebpack.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-devcombine-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 ) 8webpack.DefinePlugin插件协助咱们修j d N % k L u vprocess.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 或者扫一扫下面的二维码.

霖呆呆向你发起了多人学习webpack-构建方式篇(2)

我会不守时的更新一些前端方面的常~ 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()彻底弄懂数据类型转化的宿世此生(上)》