学一点Webpack配置:基本配置
source link: https://www.tuicool.com/articles/eQFvemz
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
这两天朋友圈流行这么一张图:
多么形象的展示了前端学习的曲线图。真可谓是一言难尽呀,现在的前端真不好学,乱而杂。如果你要是再看看@Kamran Ahmed整理的 2017年 、 2018年 和 2019年 现代Web开发者要掌握的 Roadmap ,估计更会泪崩:
现状是如此,未来可能会更混乱,但我们不应该去抱怨,应该更应该保持一颗爱学习的心,继续往前行走。
学点Webpack配置方面的知识
Webpack 是构建工具中必不可少的一部分:
作为现代Web开发者就需要对Webpack有所了解,哪怕掌握的不够深入,略知皮毛也对我们自己的工作或学习都是有所帮助的。比如说吧,前段时间折腾React环境下的CSS Modules,就是因为自己对Webpack不了解,有些坑踩了无法立刻解决,就算借助互联网,解的也是知半解(而且现在技术更新太快,网上有些教程根本走不通,不踩不知道,一踩只有泪)。正因为这个原因,促使自己去了解Webpack更多的知识。接下来的内容是一些基础,主要会介绍怎么用Webpack来构建自己的开发环境,感兴趣的请继续往下阅读。
Webpack是什么
一直以来,在我自己的印象和理解中,都认为 Webpack是一个构建工具 。主要用来构建开发的工程体系。但从其官网来看,告诉我Webpack是一个模块Bundler(捆绑器):
那么,Webpack到底是一个构建工具(或者说一个构建系统)还是一个模块Bundler(捆绑器)呢?答案是:
Webpack既是一个构建系统,也是一个捆绑器!
Webpack不是先构建你的资源(Assets),然后再bundle你的模块,它把你的资源本身就当做是一个模块。这些模块可以被 导入 、 修改 和 操作 等,最后才被打包到你最后的bundle。
简单地说,Webpack其最核心的功能就是 解决模板之间的依赖,把各个模块按照特定的规则和顺序组织在一起,最终合并成一个JS文件(比如 bundle.js
) 。这个整个过程也常常被称为是 模块打包 。换句话说,Webpack是一个指令集合的配置文档,然后通过配置好的这些指令去驱动程序做一些指令要求要做的事情。而这些动作都是通过自己写的规则去做编译,而且通过JavaScript的引入( import
)语法让Webpack知道需要它帮忙编译什么东西(比如Pug、Sass等等)。所以我们始终会有一个入口文件(比如 index.js
)注入那些Preprocess,让那些Preprocess可以通过这些入口文件的JavaScript让Webpack去根据相关的配置指令编译它,然后打包到一个出口文件中,比如 bundles.js
。
为什么要用Webpack
一直以来,在开发Web页面或Web应用程序的时候,都习惯性的将不同资源放置在不同的文件目录之中,比如图片放置在 images
(或 img
)下,样式文件放置在 styles
(或 css
)中,脚本文件放在 js
和模板文件放置在 pages
中。一直以来,发布的时候都会一次性的将所有资源打包发布,不管这些资源用到了还是没用到(事实上很多时候自己都分不清楚哪资源被使用)。用一句话来描述就是: 依赖太复杂,太混乱,无法维护和有效跟踪 。比如哪个样式文件引用了 a.img
,哪个样式文件引用了 b.img
;另外页面到底是引用了 a.css
呢还是 b.css
呢?
而Webpack这样的工具却能很多好的解决它们之间的依赖关系,使其打包后的结果能运行在浏览器上。其目前的工作方式主要被分为两种:
.js
相比于Parcel、Rollup具有同等功能的工具而言,Webpack还具有其他的优势:
- Webpack支持多种模块标准 :这对于一些同时使用多种模块标准的工程非常有用,Webpack会帮我们处理好不同类型模块之间的依赖关系
- Webpack有完备的代码分割解决方案 :它可以分割打包后的资源,首屏只加载必要的部分,不太重要的功能放到后面动态地加载
- Webpack可以处理各种类型的资源 :除了JavaScript之外,Webpack还可以处理样式、模板、图片等资源。开发者要做的只是将之些资源导入,而无需关注其他
另外,Webpack还拥有一个强大的社区。这也是其受开发者青眯的原因之一。接下来,我们还是实际一点,动手来撸码。
从零开始构建你自己的开发环境
为了更好的理解Webpack能帮我们做什么,我打算从零开始构建一个属于自己的开发环境。可能在后面的内容中会涉及到很多关键词,比如Webpack、loaders、Babel、sourcemaps、React、TypeScript,CSS Modules等等。接下来一步一步的学习中会了解到这些单词和相关技术。
后面会一步一步的带大家如何使用Webpack配置适合自己的开发环境,会涉及到一些相关技术,但不会深入到具体技术细节中。
在写这篇文章所具备的环境是:Node是 v10.9.0
,NPM是 v6.9.2
,Webpack是 v4.34.0
,React是 v16.8.6
,TypeScript是 v3.4.4
,Sass是 v.6.9.0
,PostCSS是 v.6.9.0
等。接下来的内容会以配置React + TypeScript + CSS Modules + PostCSS为主线,从零开始一个项目。另外,接下来的内容会以不同分支的形式将 代码放置在Github上 。感兴趣的可以直接将仓库克隆下来,切换到对应步骤的分支,查看代码。
Step01:初始化项目
请将Git分支切换到 step1
分支查看代码 。
首先在你的本地创建一个项目,比如我这里创建了一个 webpack-sample
项目:
⇒ mkdir webpack-sample && cd webpack-sample
进入到新创建的项目目录下,执行 npm init
或者 npm init -y
命令来初始化项目,执行完该命令之后,在你的命令终端会看到类似下图这样的命令询问,你可以根据你自己的需要去输入你想要的内容,或者一路 Enter
键执行下去:
此时你的项目根目录下会增加一些文件和文件夹:
|--webpack-sample/ |----node_modules/ |----package.json |----package-lock.json
其中 package.json
文件里将包含一些项目信息:
注意,这个文件随着后面的步骤完成,会增加更多的内容。
而 package-lock.json
文件是当 node_modules/
或 package.json
发生变化时自动生成的文件,它的主要功能是 确定当前安装的包的依赖,以便后续重新安装的时候生成相同的依赖,而忽略项目开发过程中有些依赖已经发生的更新 。
在Step01中,我们对 package.json
文件只做一个修改,删除 "main": "index.js"
入口,并添加 "private":true
选项,以便确保安装包是私有的,这样可以防止意外发布你的代码。
Step02:安装Webpack和初始配置Webpack
请将分支切换到 step2
查看代码 。
在这一步,先来安装Webpack。执行下面的命令安装Webpack配置所需要的包:
⇒ npm i webpack webpack-cli webpack-dev-server -D
此时打开 package.json
文件,你会发现在文件中有一个新增项 devDependencies
:
{ // 其他项信息在这省略,详细请查看该文件 "devDependencies": { "webpack": "^4.35.0", "webpack-cli": "^3.3.5", "webpack-dev-server": "^3.7.2" } }
注意,在命令终端使用 npm i
安装依赖关系时,如果带后缀 -D
(或 --save-dev
) 安装的包会记录在 "devDependencies"
下;如果使用 --save
后缀(我们后面会用到)安装的包会记录在 "dependencies"
下。两者的区别是:
-
"devDependencies"
:dev
开发时的依赖包 -
dependencies
:程序运行时的依赖包
为了验证Webpack是否能正常工作,这个时候我们需要创建一些新的文件。在 webpack-sample
根目录下创建 /src
目录,并且在该目录下创建一个 index.js
文件:
⇒ mkdir src && cd src && touch index.js
执行完上面的命令,你会发现你的项目目录结构变成下图这样:
我们在新创建的 /src/index.js
文件下添加一行最简单的JavaScript代码:
console.log("Hello, Webpack!(^_^)~~")
保存之后回到命令终端,第一次执行有关于Webpack相关的命令:
⇒ npx webpack src/index.js --output dist/bundle.js
执行完上面的命令后,如果看到下图这样的结果,那么要恭喜你,Webpack的安装已经成功,你可以在你的命令终端执行有关于Webpack相关的命令:
回到项目中,会发现项目根目下自动创建了一个 /dist
目录,而且该目录下包含了一个 bundle.js
文件:
执行完上面的命令之后,可以看到有相关的警告信息。那是因为 Webpack4增加了 mode
属性 ,用来表示不同的环境。 mode
模式具有 development
, production
和 none
三个值,其默认值是 production
。也就是说,在执行上面的命令的时候,我们可以带上相应的 mode
属性的值,比如说,设置 none
来禁用任何默认行为:
⇒ npx webpack src/index.js --output dist/bundle.js --mode none
执行到这里,只知道我们可以运行Webpack相关命令。并不知道 /src/index.js
的代码是否打包到 /dist/bundle.js
中。为此,我们可以在 /dist
目录下创建一个 index.html
⇒ cd dist && touch index.html
并且将生成出来的 bundle.js
引入到新创建的 index.html
:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Hello Webpack (^_^) ~~~</title> </head> <body> <script src="./bundle.js"></script> </body> </html>
在浏览器中打开 /dist/index.html
,或者在命令行中执行:
⇒ npm i -g http-server ⇒ http-server dist
http-server
是一个启动服务器的 npm
包,执行上面的命令之后,就可以在浏览器中访问 http://127.0.0.1:8080/
(访问的 /dist/index.html
),在浏览器的 console.log
控制台中,可以看到 src/index.js
的脚本输出的值:
上面的过程足以验证,你的Webpack能正常的工作了。
不过,当你需要构建的东西越复杂,需要的标志就会越多。在某种程度上说,就会变得难以控制。这个时候我们就需要一个文件来管理这些配置。接下来我们需要创建一个 webpack.config.js
这样的一个文件,用来配置Webpack要做的事情。注意,这个文件是一个 node.js
文件,所以你可以在任何节点文件中执行任何你能够执行的操作。你也可以写成 json
文件,但是node文件更强大一些。
首先们先创建Webpack的配置文件,在 webpack-sample
根目录下创建一个 /build
目录,然后在该目录下添加一个名为 webpack.config.js
文件:
⇒ mkdir build && cd build && touch webpack.config.js
执行完上面的命令之后,你会发现你的项目文件目录结构变成下面这样了:
这个时候,新创建的 webpack.config.js
文件里面是一片空白,它就是Webpack的配置文件,将会导出一个对象的JavaScript文件。我们需要在这个文件中添加一些配置:
var webpack = require('webpack'); var path = require('path'); var DIST_PATH = path.resolve(__dirname, '../dist'); // 声明/dist的路径 module.exports = { // 入口JS路径 // 指示Webpack应该使用哪个模块,来作为构建其内部依赖图的开始 entry: path.resolve(__dirname,'../src/index.js'), // 编译输出的JS入路径 // 告诉Webpack在哪里输出它所创建的bundle,以及如何命名这些文件 output: { path: DIST_PATH, // 创建的bundle生成到哪里 filename: 'bundle.js', // 创建的bundle的名称 }, // 模块解析 module: { }, // 插件 plugins: [ ], // 开发服务器 devServer: { } }
Webpack配置是标准的 Node.js CommonJS模块 ,它通过 require
来引入其他模块,通过 module.exports
导出模块,由Webpack根据对象定义属性进行解析。
上面很简单,到目前为止只通过 entry
设置了入口起点 ,然后通过 output
配置了打包文件输出的目的地和方式 。你可能也发现了,在配置文件中还有 module
、 plugins
和 devServer
没有添加任何东西。不需要太急,后面会一步一步带着大家把这里的内容补全的,而且随着配置的东西越来越多,整个 webpack.config.js
也会更变越复杂。
完成 webpack.config.js
的基础配置之后,回到 package.json
文件,并在 "scripts"
下添加 "build": "webpack --config ./build/webpack.config.js"
:
// package.json { // ... "scripts": { "build": "webpack --config ./build/webpack.config.js", "test": "echo \"Error: no test specified\" && exit 1" }, }
这样做的,为让我们直接在命令终端执行相关的命令就可以实现相应的功能。比如上面配置的 build
,在命令终端执行:
⇒ npm run build
上面的命令执行的效果前面提到的 npx webpack src/index.js --output dist/bundle.js --mode none
等同。同样有警告信息,主要是 mode
的配置没有添加。在上面的配置中添加:
{ "scripts": { "build": "webpack --config ./build/webpack.config.js --mode production", "test": "echo \"Error: no test specified\" && exit 1" }, }
再次执行 npm run build
,不会再有警告信息。你可以试着修改 /src/index.js
的代码:
alert(`Hello, Webpack! Let's Go`);
重新编译之后,打开 /dist/index.html
你会发现浏览器会弹出 alert()
框:
为了开发方便,不可能通过 http-server
来启用服务。我们可以把这部分事件放到开发服务器中来做,对应的就是 devServer
,所以我们接着在 webpack.config.js
中添加 devServer
相关的配置:
// webpack.config.js // 开发服务器 devServer: { hot: true, // 热更新,无需手动刷新 contentBase: DIST_PATH, // host: '0.0.0.0', // host地址 port: 8080, // 服务器端口 historyApiFallback: true, // 该选项的作用所用404都连接到index.html proxy: { "/api": "http://localhost:3000" // 代理到后端的服务地址,会拦截所有以api开头的请求地址 } }
有关于 devServer
更详细的配置参数描述, 可以查阅读Webpack官网相关文档 。
和 build
类似,需要在 package.json
的 scripts
中添加相关的命令:
// package.json "scripts": { "build": "webpack --config ./build/webpack.config.js --mode production", "dev": "webpack-dev-server --config ./build/webpack.config.js --mode development --open", "test": "echo \"Error: no test specified\" && exit 1" },
保存所有文件,在命令行中执行 npm run dev
就可以启动服务器:
你可以验证一下,修改 /src/index.js
:
document.addEventListener('DOMContentLoaded', () => { const h1Ele = document.createElement('h1') document.body.append(h1Ele); h1Ele.innerText = 'Hello Webpack (^_^)' h1Ele.style.color = '#f46'; })
保存该文件之后,浏览器会立刻刷新,你将看到修改之后的变化:
Step03: 优化Webpack配置
请将分支切换到 step3
查看代码 。
在 Step02
中,开发和生产环境相关的配置都集成在 webpack.config.js
一个文件中。为了更好的维护代码,在 Step03
中做一些优化。把 webpack.config.js
拆分成三个部分:
- 公共配置 :把开发和生产环境需要的配置都集中到公共配置文件中,即
webpack.common.js
- 开发环境配置 :把开发环境需要的相关配置放置到
webpack.dev.js
- 生产环境配置 :把生产环境需要的相关配置放置到
webpack.prod.js
先在 /build
目录下创建上面提到的三个配置文件。在命令终端执行下面的命令即可:
⇒ cd build && touch webpack.common.js webpack.dev.js webpack.prod.js
这个时候,整个项目目录结构变成下图这样:
从 Step02
中遗留下来的 webpack.config.js
文件将会从 /build
目录中移除。
为了更好的管理和维护这三个文件,需要安装一个 webpack-merge
插件 :
⇒ npm i webpack-merge -D
执行完上面的命令之后, package.json
文件中的 devDependencies
会增加 webpack-merge
相关的配置:
// package.json { //... 省略的信息请查看原文件 "devDependencies": { "webpack": "^4.35.0", "webpack-cli": "^3.3.5", "webpack-dev-server": "^3.7.2", "webpack-merge": "^4.2.1" } }
接下来分别给 webpack.common.js
、 webpack.dev.js
和 webpack.prod.js
文件添加相关的配置:
Webpack公共配置
在公共配置文件 webpack.common.js
文件中添加相应的配置:
const webpack = require('webpack'); const path = require('path'); const DIST_PATH = path.resolve(__dirname, '../dist/'); // 声明/dist的路径 module.exports = { // 入口JS路径 // 指示Webpack应该使用哪个模块,来作为构建其内部依赖图的开始 entry: path.resolve(__dirname,'../src/index.js'), // 编译输出的JS入路径 // 告诉Webpack在哪里输出它所创建的bundle,以及如何命名这些文件 output: { path: DIST_PATH, // 创建的bundle生成到哪里 filename: 'bundle.js', // 创建的bundle的名称 }, // 模块解析 module: { }, // 插件 plugins: [ ] }
Webpack开发环境配置
接着给Webpack开发环境配置文件 webpack.dev.js
添加下面的相关配置:
const webpack = require('webpack'); const path = require('path'); const merge = require('webpack-merge'); const commonConfig = require('./webpack.common.js'); const DIST_PATH = path.resolve(__dirname, '../dist/'); // 声明/dist的路径 module.exports = merge(commonConfig, { mode: 'development', // 设置webpack mode的模式 // 开发环境下需要的相关插件配置 plugins: [ ], // 开发服务器 devServer: { hot: true, // 热更新,无需手动刷新 contentBase: DIST_PATH, // host: '0.0.0.0', // host地址 port: 8080, // 服务器端口 historyApiFallback: true, // 该选项的作用所用404都连接到index.html proxy: { "/api": "http://localhost:3000" // 代理到后端的服务地址,会拦截所有以api开头的请求地址 } } })
Webpack生产环境配置
继续给Webpack生产环境配置文件 webpack.prod.js
添加相关配置:
const webpack = require('webpack'); const path = require('path'); const merge = require('webpack-merge'); const commonConfig = require('./webpack.common.js'); module.exports = merge(commonConfig, { mode: 'production', // 设置Webpack的mode模式 // 生产环境下需要的相关插件配置 plugins: [ ], })
上面的配置信息只是将 Step02
中 webpack.config.js
分成三个文件来配置,随着后续添加相应的配置信息,那么这三个文件中的配置信息会越来越多,也会越来越复杂。
修改完Webpack的配置之后,对应的 package.json
中的 scripts
中的信息也要做相应的调整:
// package.json { // ... 其他配置信息请查看原文件 "scripts": { "build": "webpack --config ./build/webpack.prod.js --mode production", "dev": "webpack-dev-server --config ./build/webpack.dev.js --mode development --open", "test": "echo \"Error: no test specified\" && exit 1" }, }
这个时候重新在命令终端执行
// 执行build命令,重新打包 ⇒ npm run build // 执行dev命令 ⇒ npm run dev
这仅仅是最基础部分的优化,因为我们的配置还是最简单的,后续我们添加了别的配置之后,也会在相应的步骤做相应的优化。
Step04: 配置React开发环境
请将分支切换到 step4
查看代码 。
经过前面三步,我们完成了Webpack的基本配置,知道文件入口,出口,打包以及开发,生产等环境。接下来,我们来给工程配置React相关的环境。
React的环境需要先安装 react
和 react-dom
。所以先在命令终端中执行下面的命令:
⇒ npm i react react-dom --save
执行完上面的命令之后,在 package.json
文件中的 dependencies
增加了 ract
和 react-dom
相应的信息:
// package.json { "dependencies": { "react": "^16.8.6", "react-dom": "^16.8.6" }, }
为了验证React相关的环境是否能正常工作,将 /src/index.js
中的内容做一些修改:
// /src/index.js import React from 'react'; import ReactDOM from 'react-dom'; import App from './components/App' ReactDOM.render(<App />, document.getElementById('root'))
在这个 index.js
中引用了 App
组件。所以我们在 /src
目录下新增 components/
目录,并在该目录下新增 App.js
:
// src/components/App.js import React from 'react'; export default class App extends React.Component { render() { return ( <h1>Hello Webpack and React! (^_^)</h1> ) } }
另外在 /src/
新增一个模板文件 index.html
:
<!-- /src/index.html --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Hello Webpack (^_^) ~~~</title> </head> <body> <div id="root"></div> </body> </html>
这个时候你在命令终端不管是执行 npm run build
还是 npm run dev
都无法正常运行,会报错:
首先我要告诉你的是 Step04
这一步的操作并没有任何问题,主要是在编译的过程中缺少必要的东西。那就是Babel相关的配置。接下来的 Step05
将会添加Babel相关的配置。
Step05:添加Babel相关的配置
请将分支切换到 step5
查看代码 。
在 Step04
中会失败主要是因为Webpack只识别JavaScript文件,而且只能编译ES5。实际上ES6(甚至后面要说的JSX),Webpack它根本不认识。那么要解决这个问题,就需要借助Babel来处理。先安装需要的插件:
⇒ npm i babel-loader @babel/core @babel/preset-env @babel/plugin-transform-runtime @babel/plugin-transform-modules-commonjs @babel/preset-react -D ⇒ npm i @babel/runtime --save
执行完上面的命令之后, package.json
文件在 dependencies
和 devDependencies
添加了新的配置信息:
// package.json { // ...省略的信息可以查看原文件 "dependencies": { "@babel/runtime": "^7.4.5", "react": "^16.8.6", "react-dom": "^16.8.6" }, "devDependencies": { "@babel/core": "^7.4.5", "@babel/plugin-transform-modules-commonjs": "^7.4.4", "@babel/plugin-transform-runtime": "^7.4.4", "@babel/preset-env": "^7.4.5", "@babel/preset-react": "^7.0.0", "babel-loader": "^8.0.6", "webpack": "^4.35.0", "webpack-cli": "^3.3.5", "webpack-dev-server": "^3.7.2", "webpack-merge": "^4.2.1" } }
接着在 webpack-sample
根目录下创建 .babelrc
文件来配置Babel:
{ "presets": [ [ "@babel/preset-env", { "targets": { "browsers": [ "> 1%", "last 5 versions", "ie >= 8" ] } } ], "@babel/preset-react" ], "plugins": [ "@babel/plugin-transform-runtime", "@babel/plugin-transform-modules-commonjs" ] }
有关于Babel更详细的配置 可以点击这里阅读 ,这里不再做过多的阐述。
最后在 webpack.common.js
配置文件中的 module
中添加 rules
来处理 .js
和 .jsx
文件,这也是我们添加的第一个有关于Webpack的Loader相关的东西:
// webpack.common.js module.exports = { // ... 省略的信息查看原文件代码 // 模块解析 module: { rules: [ { test: /\.(js|jsx)$/, exclude: /node_modules/, use: { loader: "babel-loader" } } ] }, }
这个时候执行 npm run build
可以正常的执行,但是执行 npm run dev
还是会报错:
执行 npm run dev
不成功是因为我们的模板文件没有自动插入到 /dist
目录下。为了让 /src/
目录下的模板文件 index.html
能自动编译到 /dist
目录下,并且所有的 .js
引用能自动插入到 index.html
中。我们需要使用Webpack的两个插件:
- 生成
index.html
文件,并且自动插入到/d
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK