从零配置你的Webpack
前言
上篇文章简单的介绍了一下,前端进阶之脚手架的搭建。其实我个人认为重要的还是Webpack的配置出来的template,至于脚手架的交互体验可以后期去优化,也可以更加的个性化。但是我们的核心还是放在Webpack等一系列的配置上。
这篇文章的目的
- 可以给自己一个回顾的地方
- 加强对Webpack的理解,每个知识点都会认认真真的彻查!尽量让每一步都是非常清晰明了的!
- 如果可以帮助到大家那是更好不过了
好了,收!话不多说开始!
前提
相信来到这里的小伙伴,都是有一些些前端的经验了,至于什么node安装环境变量这里就不赘述了。本人使用的是mac os,如果是Windows的并且碰到问题的话,可以留言,或者直接Google。
开始
建立一个空的文件夹📁
新建一个文件夹,名为【webpackInit】
并且使用你的编辑器打开他,然后打开命令行执行:
1 | npm init -y |
这个命令是node帮你初始化一个项目用的,帮你新建一个package.json
。
至于-y
是用于默认都以yes继续执行。如果想了解一下里面到底有什么的同学可以不用-y继续跑一遍。其实里面的东西后期都可以自己修改package.json
。所以不需要太在意。
安装Webpack🔧
现在是北京时间:2020/06/07 06:42:49。
Webpack5有Beta版,这里就不考虑了,后续上正式版的话我应该会出新的文章介绍。
因为我们使用的是 webpack 4+
版本,还需要安装 webpack-cli
,执行以下命令:
1 | npm install --save-dev webpack webpack-cli |
因为Webpack主要是编译时使用所以放到“devDependencies”。
确认一下现在的目录结构,以防有同学掉队!
1 | webpackInit |
这里说下题外话。
package.json和package-lock.json的区别
package.json 文件只能锁定大版本,也就是版本号的第一位,并不能锁定后面的小版本,npm install
都是拉取的该大版本下的最新的版本,为了稳定性考虑我们几乎是不敢随意升级依赖包的,这将导致多出来很多工作量,测试/适配等,所以 package-lock.json 文件出来了,当你每次安装一个依赖的时候就锁定在你安装的这个版本。
那如果我们安装时的包有bug,后面需要更新怎么办?
在以前可能就是直接改 package.json 里面的版本,然后再 npm install
了,但是 5 版本后就不支持这样做了,因为版本已经锁定在 package-lock.json 里了,所以我们只能 npm install xxx@x.x.x
这样去更新我们的依赖,然后 package-lock.json 也能随之更新。
新建配置文件📃
我们在根目录新建文件夹【config】用于存储一些相关的配置文件,然后在【config】里面新建一个文件夹【webpack】表示,专门用于存储webpack的配置文件。然后在【webpack】这个文件夹下面新建文件【webpack.common.config.js】
并敲入以下代码
1 | const path = require('path'); |
webpack 配置是标准的 Node.js的CommonJS 模块,它通过require来引入其他模块,通过module.exports导出模块,由 webpack 根据对象定义的属性进行解析。
在根目录新建【src】文件夹📁
在【src】文件夹下新建文件index.js
ok👌,确认一下目录结构
1 | webpackInit |
那我们怎么打包呢?在 package.json
中配置如下属性:
1 | "scripts": { |
好了,我们试试怎么打包吧,虽然你的 index.js
中什么代码也没有。
在控制台中输入以下代码:
1 | npm run start |
npm run xxxx 会去执行当前目录下package.json里面的script同名脚本
我们的【npm run start】相当于直接执行了我们写在【start】里面的代码。
执行之后,你会发现根目录多出了一个文件夹: dist/js
,其中有一个js文件: bundle.js
,那么至此,我们已经成功编译打包了一个js文件,即入口文件: index.js
。
安装React
在控制台输入以下代码:
1 | npm install --save react react-dom |
–save就是运行时会用到的代码
具体和–dev-save的区别可以自己Google一下
在【src/index.js】里面加入以下代码
1 | import React from 'react'; |
在根目录加入文件夹【public】,然后在【public】里面加入【index.html】
目录如下:
1 | webpackInit |
然后在【index.html】加入以下代码:
1 |
|
OK
万事俱备,我们运行:
1 | npm run start |
打包失败了。。。为什么呢?
使用babel
为什么我们上面写jsx会打包不了呢,因为webpack根本识别不了jsx语法,那怎么办?使用loader对文件进行预处理。
其中,babel-loader,就是这样一个预处理插件,它加载 ES2015+ 代码,然后使用 Babel 转译为 ES5。那开始配置它吧!
首先安装babel相关的模块:
1 | npm install --save-dev babel-loader @babel/preset-react @babel/preset-env @babel/core babel-plugin-import |
- babel-loader:使用Babel和webpack来转译JavaScript文件。
- @babel/preset-react:转译react的JSX
- @babel/preset-env:转译ES2015+的语法
- @babel/core:babel的核心模块
- babel-plugin-import:按需加载所需要的babel解析
理论上我们可以直接在 webpack.common.config.js
中配置”options”,但最好在当前根目录,注意,一定要是根目录!!! 新建一个配置文件 .babelrc
配置相关的”presets”:
1 | { |
这里有关bebel的配置可上官网查询文档。
再修改 webpack.common.config.js
,添加如下代码:
1 | const path = require('path'); |
接下来激动人心的时刻:
1 | npm run start |
是不是能打包成功了呢?
打开【dist/】你的html页面,看一下是否是“Hello World!”吧!
使用webpack-merge🈴️
我们将使用一个名为 webpack-merge 的工具。通过“通用”配置,我们不必在环境特定(environment-specific)的配置中重复代码。简单来说就是生产环境不同,我们要给的配置也有所不同,但是可以共用一个共有的配置。
我们先从安装 webpack-merge 开始:
1 | npm install --save-dev webpack-merge |
安装结束之后,我们在 config
这个文件夹下新建两个文件,分别为 webpack.prod.config.js
和 webpack.dev.config.js
,这两个文件分别对应生产和开发两个环境的配置。当然你也可以添加test环境。名字也可以自己取,尽量保持一致。
现在的目录结构:
1 | webpackInit |
在【webpack.prod.config.js】加入
1 | const merge = require('webpack-merge'); // 版本为4.x |
然后修改【package.json】
1 | { |
然后
删除【dist文件夹】
之后
1 | npm run build |
是不是也build也打包成功了!
html是不是也可以正常访问!
但是没有显示Hello World,仔细一看还报错了。。。为什么呢。。因为
1 | // 如果不加哈希值,浏览器会有缓存,可能你部署了,但是用户看到的还是老页面 |
我们给js文件设置了hash值。不能够直接在html里面加上【】这么一句,而且每次生成的hash值都会变,那么我们要怎么处理这个问题呢?
使用HtmlWebpackPlugin
在控制台执行以下代码:
1 | npm install --save-dev html-webpack-plugin |
修改【webpack.prod.config.js】
1 | const merge = require('webpack-merge'); // 版本为4.x |
现在我们再来打包试试
1 | npm run build |
看看dist中是不是多出了html文件,并且自动引入了script,用浏览器打开它试试看是不是能正确输出内容了!
起飞!!🛫️
使用clean-webpack-plugin
有些同学已经厌倦了每次都需要删除dist文件夹来验证是否成功。
其实假如我们不删除的话,我们需要修改js文件,这样让webpack知道我们改了东西,他就会重新打包,但是我们每次测试都没有去修改,所以我们需要删除。
但是,如果说我们不去删除dist文件夹的话,我们修改了【src/index.js】。然后再重新build,就会发现【dist/js】下面会又多出一个js文件,这样的话我们就需要观察日志,查看新鲜“出炉”的是哪一个,然后删掉别的。这样非常麻烦。
OK,我们现在就来解决一下这个问题
安装clean-webpack-plugin
1 | npm install --save-dev clean-webpack-plugin |
修改【webpck.prod.config.js】
1 | const merge = require('webpack-merge'); // 版本为4.x |
我们先查看现在的js文件前面的hash值。然后我们修改【src/index.js】,随便改成什么,再重新
1 | npm run build |
就会发现文件并没有新增,而且换了新鲜的哈希值。
当然了,我之前说的那些话,同学不相信的话,可以把【new CleanWebpackPlugin()】这一行注释掉,然后再修改【src/index.js】,会发现【dist/js】下面的js文件会增多一条。
使用webpack-dev-server
既然刚刚都提到优化了,我们每次都需要build一下,感觉很呆。而webpack官方也提供热部署,那我们现在就使用起来
安装webpack-dev-server
1 | npm install webpack-dev-server --save-dev |
新建文件【webpack.dev.config.js】📃
1 | webpackInit |
然后加入如下代码
1 | const path = require('path'); |
修改文件【package.json】📃
1 | "scripts": { |
然后我们
1 | npm run start |
是不是自动开了一个端口为9000的网页,上面是我们写的页面内容,这和我们的配置都是一一对应的。
现在你随意修改index.js中的代码,再回到页面看下是不是也跟着变了,那我们就整合webpack-dev-server成功!
使用source-map
source-map可以展示我们代码的错误位置,因为我们的代码都是被webpack打包过的,只有机器看得懂,我们人类无法正常识别。所以需要他。
想试一下未开启是什么状态的同学可以自己故意把代码写错,然后看看控制台的报错。
开启也十分简单。
它的配置非常简单,只需要在 【webpack.dev.config.js】 中增加一个 devtool
属性即可!
1 | module.exports = { |
因为我们只有自己写代码的时候才需要查看,到生产环境就要关闭啦,不然我们的智慧结晶就要被【窃取】啦!所以我放在了
中场休息
这里回顾一下知识点。
- 我们先npm init 新建一个空白项目。
- 然后安装webpack,react。
- 发现无法编译jsx。
- 所以我们寻求了babel的帮助,并配置了所需要解析的内容。
- 觉得在html里面加入js很呆,所以引入了HtmlWebpackPlugin
- 因为觉得每次删除打包出来的东西很呆,所以引入了clean-webpack-plugin
- 因为每次都需要重新打包,所以使用webpack-dev
- 至于webpack-merge是为了打包和编译两个或者说多个状态做预备的,在上述的例子只有本地的dev和build两个环境。
基本上上述的操作过程我都有解释,或者是注释,或者是在文章中说明。大家可以跟着节奏一步一步来,因为我也是一边写blog一边跑代码一边回顾知识点。
至此,webpack算是告一……
诶诶诶诶!js是可以解析了,那css呢!
哦哦,好的,那我们继续启程
重新起航
使用css-loader和style-loader两兄弟
假如我们直接引用css的话,会报错了。这里就不演示了,有兴趣的同学可以自己试试。
所以,我们先走命令行敲击
1 | npm install --save-dev style-loader css-loader |
两兄弟的关系
来说说css-loader和style-loader他们这对鸳鸯的关系。
首先css-loader会把你的CSS文件进行解析,因为webpack是用JS写的,运行在node环境,所以默认webpack打包的时候只会处理JS之间的依赖关系!
所以我们之前的react里面的jsx需要babel的帮助,或者说需要【babel-loader】的帮助,所以我们的css同样需要【css-loader】的帮助,那么又关【style-loader】什么事?可不可以不装他呢?
答案是:可以的,但是你使用起来会非常的麻烦。怎么个麻烦法呢?
如果只用了【css-loader】解析出来的是这样的
1 | ["./src/index.css", ".test{↵ color: red;↵}", ""] |
这样咋用嘛,你是解析了,可是你解析的是个XX。
这个时候就需要我们的天降猛男【style-loader】
style-loader 是通过一个JS脚本创建一个style标签,里面包含一些样式。style-loader是不能单独使用的,应为它并不负责解析 css 之前的依赖关系,每个loader的功能都是单一的,各自拆分独立。
上手!
加入index.css
1 | src |
index.css文件的内容如下
1 | .test{ |
修改index.js
1 | import React from 'react'; |
修改【webpack.common.config.js】📃
1 | //... |
记得重新运行,因为webpack的配置他读取一次,所以如果你修改了配置,需要【ctrl+c】关闭重新运行。
嘿嘿……是不是运行不了啊。
其实我是故意的,我想告诉大家一个知识点。
loader的加载顺序是从右往左。这里的编译顺序是先用css-loader将css代码编译,再交给style-loader插入到网页里面去。所以css-loader在右,style-loader在左。
虽然我们的数组换行了,但是仔细看不难看出顺序。
所以我们只需要将他们换个位置就可以了。代码我就不贴了。
现在大家应该记忆很深刻了吧!
大家重新【ctrl+c】关闭重新运行就行了。我们的hello world是不是变红啦~
安装less-loader
说到css,说句实在话,没几人真的是在写纯css的吧?现在谁不是less,sass或者其他css预处理呢?而且这些预处理的好处我就不细说了,感兴趣的自己Google吧,本文用的是less(因为ant design用的也是less,哈哈,假装是蚂蚁的一员)
在命令行输入
1 | npm install --save-dev less less-loader |
less没什么好说的,用他肯定要装,less-loader,顾名思义,就是less的解析者。
在【webpack.common.config.js】增加
1 | { |
依旧是顺序问题,先解析less,把less解析成常规的css,然后再解析css,最后插入到网页中去。
修改【index.js】引入自己写的【.less】文件
大家重新【ctrl+c】关闭重新运行就行了。至于你写了什么less特性,只要有效果就行了。
安装url-loader和file-loader
说完CSS,美丽的网页当然离不开我们动人的图片啦。
【file-loader】的作用是,把你的文件打包起来,和js文件放在一起,这样用户访问我们的网页的时候,其实也需要访问我们的url,既增加了服务器的压力,也增加了用户升级流量的压力,需要去下载这个文件。
【url-loader】
如果页面图片较多,发很多http请求,会降低页面性能。这个问题可以通过url-loader解决。url-loader会将引入的图片编码,生成dataURl并将其打包到文件中,最终只需要引入这个dataURL就能访问图片了。
url-loader和file-loader两兄弟的搭配可以有效的减少不必要的url请求,因为有的图片你要去请求url获取,如果小的话完全可以用base64替代。如果图片很大的话就用file-loader,这样可以减少编码的压力。
在命令行输入
1 | npm install file-loader url-loader --save-dev |
修改【webpack.common.config.js】📃
1 | module: { |
进阶
基本上我们的webpack可以正常运行了,css,js,jsx都可以解析了。但是我们需要考虑一些进阶的东西,优化。
使用uglifyjs-webpack-plugin
在控制台执行以下代码:
1 | npm install uglifyjs-webpack-plugin --save-dev |
在【webpack.prod.config.js】添加代码
1 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); |
使用splitChunks
其实我们写的代码,有些库的代码是不需要每次都编译的,最简单的例子就是React,这个我们几乎每个js文件都会用到。所以我们可以将它们单独打包,这样只需要打包一次。
修改【webpack.common.config.js】
1 | entry: { |
修改【webpack.prod.config.js】
1 | const merge = require('webpack-merge'); // 版本为4.x |
不想写diff啦,直接CV啦。。。
为什么我需要把react提取出来,因为哪里都需要用,而且他几乎不可能会变,所以我特别提出来做了缓存,其余的还是使用的默认配置(除了chunk改为了‘all’)。
Tobias Koppers@Wsokra:optimization. splitchunks. chunks: althe only option you need for vendor and commons splitting in webpackBest combine it with html-webpack-plugin or equivalent html generation18120
作者都发推特说了,那我们也就接受吧~就改个all,然后补一下react~
再重新打包,你会发现index.bundle.js(不被缓存)的hash值变了,但是common.bundle.js(能被缓存)的hash值没变。
使用mini-css-extract-plugin
js都独立📦,那我css也要!
其实如果把CSS打包成一个文件然后让html引用的话可以减小html文件的大小,暗合了HTTP2的多路复用,多文件小数量。包括之前的splitChunks里面我们配置的react也是为了HTTP2。
在命令行输入:
1 | npm install --save-dev mini-css-extract-plugin |
修改【webpack.common.config.js】
1 | + const MiniCssExtractPlugin = require('mini-css-extract-plugin'); |
使用CSS Module
其实很简单,只需要修改一下配置【wbpack.common.config.js】
1 | { |
使用PostCSS
postcss 一种对css编译的工具,类似babel对js的处理,常见的功能如: 1 . 使用下一代css语法 2 . 自动补全浏览器前缀 3 . 自动把px代为转换成rem 4 . css 代码压缩等等 postcss 只是一个工具,本身不会对css一顿操作,它通过插件实现功能,autoprefixer 就是其一。
安装postcss
1 | npm install postcss postcss-loader --save-dev |
安装postcss某个插件,以Autoprefixer举例
1 | npm install postcss-aspect-ratio-mini postcss-write-svg postcss-px-to-viewport postcss-viewport-units postcss-flexbugs-fixes postcss-preset-env cssnano --save-dev |
在根目标新建文件【postcss.config.js】
1 | /* eslint-disable import/no-extraneous-dependencies */ |
修改【webpack.common.config.js】
1 | { |
然后,修改我们的css文件,看看我们写的单位为px的有没有被改为vw的自适应单位。
顺便试一下带有【notTransform】的是不是还是px作为单位。
结束
到此,我们的webpack配置,就算是入门了,对于webpack的配置我们还有很长的路要走。大家加油!
如果有哪里写的不好或者写错了,欢迎大家在评论区讨论。