最近由于学习需求,手撸了个简单的SPA框架并使用Webpack4做打包和预览调试,由于没有怎么系统学习过Webpack所以遇到坑挺多,索性直接开始从头学一波顺便记录下来.
写Webpack文章不写版本都是耍流氓,这篇文章基于当下最新的 webpack v4.22.0 以及 webpack-cli v3.1.2 编写.
本章以 环境搭建 -> 练习1 -> 理论学习 -> 练习2
的步骤进行:
- 跟随官方文档 Getting-Started, 由简单项目配置搭建开始;
- 完成后直接进入练习1--默认Webpack配置打包项目;
- 完成一波实战操作之后再回头了解 基本概念;
- 完成概念学习之后再进入练习2--学习使用配置文件实现项目打包;
Tip:全过程将记录在Github仓库 webpack-stepbystep 中,边看文章边看commit学习食用效果更佳哟~
1. Init:简易Webpack项目搭建
想要学习Webpack的小伙伴大概都知道Webpack是用来打包的,嗯暂时知道这些就足够了,马上来开始第一次打包的体验吧~
开始实战肯定要创建工程的,使用下面的命令开始(需要 Node.js ):
mkdir webpack-stepbystep
cd webpack-stepbystep
npm init -y
npm i -D webpack webpack-cli
webpack -v
webpack-cli -v
上面的命令语句分别是:
- 创建项目
- 进入项目
- 初始化 package.json
- 在项目中安装 webpack & webpack-cli
- 检查 webpack 版本
- 检查 webpack-cli 版本
本文的环境是webpack v4.22.0 & webpack-cli v3.1.2, 最后两句版本检查如果运行失败的话,可以考虑使用全局方式安装 Webpack & Webpack-cli, 之前在Windows上开发的时候遇到过这个问题, 全局安装命令如下:
npm install -g webpack webpack-cli
webpack -v
webpack-cli -v
其他安装问题暂且按下不表, 可自行参考 Installation.
完成以上项目环境搭建之后,项目的文件结构大致如下所示:
webpack-stepbystep
└─ node_modules
└─ package-lock.json
└─ package.json
项目环境搭建至此全部完成, 以上环境搭建的项目提交为init(project): finish base enviroment setup, 马上进入实战环节.
2. 练习1:默认Webpack配置打包项目
2.1 从刀耕火种的写法开始
首先添加index.html
& src/index.js
, 项目的文件结构大致如下所示:
webpack-stepbystep
└─ node_modules
└─ src
└─ index.js
└─ index.html
└─ package-lock.json
└─ package.json
编辑index.html
, 其中用script引用了第三方工具库 lodash:
<!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>Started</title>
<script src="https://unpkg.com/lodash@4.16.6"></script>
</head>
<body>
<body>
<script src="./src/index.js"></script>
</body>
</body>
</html>
然后编辑 src/index.js
, 代码中的 _
即对lodash库的引用:
function component () {
let element = document.createElement('div');
element.innerHTML = _.join(['Hello', 'Webpack'], '');
return element;
}
document.body.appendChild(component());
可以看出 index.html
中的两个 <script>
之间存在这隐式依赖的关系, 即 index.js
默认全局存在lodash. 这种刀耕火种时期的写法存在三个问题:
- 模块间的依赖关系不明显,工程变大难以查找依赖;
- 如果依赖丢失,将造成运行问题;
- 如果引用了依赖但是程序并无调用,下载无用库造成浪费并且影响速度;
有错就要改,所以我们开始使用现代方案来对当前项目进行修改~
至此项目提交为 feat(project): add index.html & index.js with implicit dependencies .
2.2 现代化解决方案
现代的思想是用npm将库下载到本地,在js中显示声明依赖并调用,最后用Webpack完成打包,了解了之后就开始吧:
- 第零步、我们先使用Webpack的默认配置打包项目,Webpack默认以
./src/index.js
为起点开始构建,获取依赖并完成打包到./dist/main.js
. (文章第三节”学习:Webpack概念“将会介绍); -
第一步、将依赖安装到项目中. 之后 Webpack 会将依赖一起打包到
main
防止依赖丢失造成的运行问题:npm i -S lodash
-
第二步、在文件
index.js
顶部用import显式声明依赖. 方便依赖的查找的同时明确了项目所需的依赖:import _ from 'lodash'; function component () { let element = document.createElement('div'); element.innerHTML = _.join(['Hello', 'Webpack'], ''); return element; } document.body.appendChild(component());
-
第三步、执行打包. 打开终端在当前项目下运行
webpack
,运行成功之后发现当前项目目录多出了dist
文件夹,项目的文件结构大致如下所示:webpack-stepbystep └─ dist └─ main.js └─ node_modules └─ src └─ index.js └─ index.html └─ package-lock.json └─ package.json
完美~Webpack的打包操作现在已经完成了!只剩完成最后一步就可以完美运行程序了!
-
第四步、按照 Webpack 的默认规则修改
index.html
中的引用. 先删除两个<script>
标签,再添加对于Webpack生成main.js
的引用;<!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>Started</title> </head> <body> <body> <script src="main.js"></script> </body> </body> </html>
由于 Webpack 的默认规则只能打包js,所以我们先用一下比较粗暴的手段,直接把index.html
复制一份,放到打包完生成的dist
文件夹下. 在浏览器运行该index.html
.
WOW! 浏览器出现了 Hello Webpack的字样,运行成功!Webpack首战成功结束~接下来来了解一下Webpack的概念.
至此项目提交为 feat(project): combat:project bundle without configuration file .
3. 学习:Webpack概念
Concepts部分告诉了我们Webpack的定义、目的以及核心概念:
- 定义:静态模块打包器
- 目的:通过递归构建依赖关系图(dependency graph),将应用需要的模块打包,形成一个或者多个bundle
-
核心概念:
-
入口(entry): 告诉 Webpack 从那个模块(文件)开始构建dependency graph. Webpack将会以此为起点,找到所有的依赖并打包最终形成bundle.
- Tip: 如果不在
webpack.config.js
中配置entry,Webpack将会默认使用路径./src/index.js
作为起点开始构建打包.
- Tip: 如果不在
-
输出(ouput): 告诉Webpack打包结果的创建位置&命名方式.
- Tip: 如果不在
webpack.config.js
中配置output,Webpack将会默认在路径./dist
文件夹下创建打包结果main.js
.
- Tip: 如果不在
-
加载器(loader): loader的作用是将所有类型的文件转换为可以被 dependency graph 直接应用的文件,也就是Webpack封面图--将所有依赖模块打包成静态资源
- 使用方式: 每种类型的模块基本都有对应的loader负责其转换,但是Webpack并不具备loader. 使用一个新的loader,首先需要用npm或者yarn安装, 然后
webpack.config.js
的module.rules
中进行手动配置,声明希望进行转换的文件类型及其对应的处理器loader(test、use). - 列表链接:loader列表
- 使用方式: 每种类型的模块基本都有对应的loader负责其转换,但是Webpack并不具备loader. 使用一个新的loader,首先需要用npm或者yarn安装, 然后
-
插件(plugin): 执行范围更广的任务,例如:优化打包、压缩、注入新的环境变量等等.
- 使用方式:使用一个新的plugin, 首先需要用npm或者yarn安装, 然后在
webpack.config.js
的顶部用require
进行声明,接着在module.plugins
中初始化并根据plugin的文档进行配置. - 列表链接:Plugin列表
- 使用方式:使用一个新的plugin, 首先需要用npm或者yarn安装, 然后在
-
模式(Mode): 当前项目的打包模式,现有三种
none
、development
、production
模式可供选择. 现在暂时不管这个属性的配置.
-
4. 练习2:配置文件实战练习
Webpack提供了方便开箱即用的默认配置,但是想要充分发挥Webpack的能力,还是需要使用配置文件.
4.1. 使用配置文件复刻默认Webpack效果
如果 webpack.config.js
存在,则 webpack
命令将默认选择使用它,于是我们在项目根目录添加文件 webpack.config.js
,项目的文件结构大致如下所示:
webpack-stepbystep
└─ dist
└─ main.js
└─ node_modules
└─ src
└─ index.js
└─ index.html
└─ package-lock.json
└─ package.json
└─ webpack.config.js
为 webpack.config.js
添加配置,使之实现默认Webpack配置的效果:
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist')
}
};
删除dist
文件夹,保存 webpack.config.js
,并打开终端在当前项目下运行 webpack
,运行成功之后发现当前项目目录再次生成了 dist
文件夹,里面有 main.js
, 至此我们成功使用 webpack.config.js
成功复刻了默认Webpack配置的效果~
4.2. 使用配置文件实现项目自动化打包
当然这次实战的目标肯定不止如此,第一次实战中遗留着一个问题,就是Webpack的默认配置无法帮助我们将 index.html
一起打包到 dist
中,这导致项目无法全自动打包. 现在就来尝试借助 webpack.config.js
来解放Webpack的能力,实现项目的全自动化打包.
实现过程分为两个步骤:
-
第一步、每次打包之前清除
dist
内容。已有打包内容容易对新打包内容造成影响,所以在这里使用一个叫做clean-webpack-plugin
的插件,这个插件将帮助我们在每次打包之前清理dist
文件夹的内容,插件的配置方法如下:-
插件安装:首先通过
npm
再项目中安装插件:npm i -D clean-webpack-plugin
-
配置文件修改:在
webpack.config.js
文件开头用require引用插件,再在plugins
数组中添加其插件的实现,完整配置文件代码如下所示:const path = require('path'); const CleanWebpackPlugin = require('clean-webpack-plugin'); module.exports = { entry: './src/index.js', output: { filename: 'main.js', path: path.resolve(__dirname, 'dist') }, plugins: [ new CleanWebpackPlugin(['dist']) ] };
- 测试: 完成配置文件插件添加之后,复制根目录下的
index.html
文件到dist
中,再回到终端运行webpack
, 执行成功后发现dist
下只有一个刚被创建的main.js
, 即每次打包之前插件都会自动清理打包目标文件夹~
- 测试: 完成配置文件插件添加之后,复制根目录下的
-
-
第二步、在Webpack打包过程中,将根目录下的
index.html
文件复制到dist
中. 这里需要用到一个叫做html-webpack-plugin
的插件,这个插件功能很多,但是我们暂时需要它帮我们将根目录的index.html
复制到dist
中,配置方法如下:-
插件安装:
npm i -D html-webpack-plugin
-
配置文件修改:在
webpack.config.js
文件开头用require引用插件,再在plugins
数组中添加其插件的实现,完整配置文件代码如下所示:const path = require('path'); const CleanWebpackPlugin = require('clean-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: './src/index.js', output: { filename: 'main.js', path: path.resolve(__dirname, 'dist') }, plugins: [ new CleanWebpackPlugin(['dist']), new HtmlWebpackPlugin({ inject: false, template: 'index.html', filename: 'index.html' }) ] };
- 测试:完成配置文件插件添加之后,回到终端运行
webpack
, 执行完成后发现dist
下已经出现了我们想要的两个文件index.html
&main.js
,浏览器打开index.html
, 再次看到熟悉的 Hello Webpack!成功~至此第二次Webpack实战练习也成功结束了~这次我们完成了使用一句webpack
就完成了项目打包,无需粗暴的复制黏贴,真正做到了全自动化打包项目!
- 测试:完成配置文件插件添加之后,回到终端运行
-
至此项目提交为 feat(project): combat: project bundle with webpack.config.js .
5. 项目地址
To be continued...
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。