// app.bundle.js
(() => {
var e = {};
console.log(3), console.log('hello, webpack');
var o = exports;
for (var l in e) o[l] = e[l];
e.__esModule && Object.defineProperty(o, '__esModule', { value: !0 });
})();
这是生产环境输出的代码,就是在一个匿名函数中输出了结果,并且在{}上绑定了一个__esModule的对象属性,有这样一段代码var o = exports;主要是因为我们在output中新增了libraryTarget:commonjs,这个会决定js输出的结果。
我们再来看下如果mode:development那么是怎么样
// 这是在mode: development下生成一个bundle.js
/*
* ATTENTION: The "eval" devtool has been used (maybe by default in mode: "development").
* This devtool is neither made for production nor for readable output files.
* It uses "eval()" calls to create a separate source file in the browser devtools.
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
* or disable the default devtool with "devtool: false".
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
*/
/******/ (() => { // webpackBootstrap
/******/ var __webpack_modules__ = ({
/***/ "./src/app.js":
/*!********************!*\
!*** ./src/app.js ***!
\********************/
/***/ (() => {
eval("\nfunction twoSum(a, b) {\n return a+b\n}\nconst result = twoSum(1,2);\nconsole.log(result);\nconsole.log('hello, webpack');\n\n//# sourceURL=webpack://webpack-01/./src/app.js?");
/***/ })
/******/ });
/************************************************************************/
/******/
/******/ // startup
/******/ // Load entry module and return exports
/******/ // This entry module can't be inlined because the eval devtool is used.
/******/ var __webpack_exports__ = {};
/******/ __webpack_modules__["./src/app.js"]( ""./src/app.js"");
/******/
/******/ })()
;
正文开始...
前置
我们先了解下
webpack
能干什么webpack
是一个静态打包工具,根据入口文件构建一个依赖图,根据需要的模块组合成一个bundle.js
或者多个bundle.js
,用它来展示静态资源关于
webpack
的一些核心概念,主要有以下,参考官网entry
1、
entry
入口(依赖入口文件,webpack 首先根据这个文件去做内部模块的依赖关系)也可以是一个数组
在分离应用 app.js 与第三方包时,可以将第三方包单独打包成
vender.js
,我们将第三方包打包成一个独立的chunk
,内容hash
值保持不变,这样浏览器利用缓存加载这些第三方js
,可以减少加载时间,提高网站的访问速度。不过目前
webpack4.0.0
已经不建议这么做,主要可以使用optimization.splitChunks
选项,将app
与vendor
会分成独立的文件,而不是在入口处创建独立的entry
output
2、
output
输出(把依赖的文件输出一个指定的目录
下)主要会根据
entry
的入口文件名输出到指定的文件名目录中,默认会输出到dist
文件中module
3、
module
配制loader
插件,loader
能让webpack
处理各种文件,并把文件转换为可依赖的模块,以及可以被添加到依赖图中。其中test
是匹配对应文件类型,use
是该文件类型用什么loader
转换,在打包前运行。plugins
4、
plugins
主要是在整个运行时都会作用,打包优化,资源管理,注入环境mode
5、
mode
指定打包环境,development
与production
,默认是production
从零开始一个项目搭建
新建一个目录
webpack-01
,执行npm init -y
在
package.json
中配置scirpt
首先我们在在开发依赖安装
webpack
与webpack-cli
,执行npm i webpack webpack-cli --save-dev
在webpack5
中我们默认新建一个webpack
的默认配置文件webpack.config.js
我们在
src
目录下新建一个app.js
并写入一段js
代码在终端执行
npm run build
,这个命令我在package.json
的script
中配置此时就会生成一个在
dist
文件,并且名字就是app.bundle.js
我们打开一下生成的
app.bundle.js
,我们发现是这样的,这是在model:production
下生成的一个匿名的自定义函数。这是生产环境输出的代码,就是在一个匿名函数中输出了结果,并且在
{}
上绑定了一个__esModule
的对象属性,有这样一段代码var o = exports;
主要是因为我们在output
中新增了libraryTarget:commonjs
,这个会决定js
输出的结果。我们再来看下如果
mode:development
那么是怎么样这上面的代码就是运行
mode:development
模式下生成的,简化一下就是在开发环境就是会以文件路径为
key
,然后通过evel
执行app.js
的内容,并且调用这个webpackModules
执行evel
函数注意我们默认
libraryTarget
如果不设置,那么就是var
,主要有以下几种amd
、commonjs2
,commonjs
,umd
通过以上,我们会发现我们可以用配置不同的命令执行打包不同的脚本,在默认情况下,
npm run build
与执行npm run build:default
是等价的,我们会看到default
用--config webpack.config.js
指定了webpack
打包的环境的自定义配置文件。如果配置默认文件名就是
webpack.config.js
那么webpack
就会根据这个文件进行打包,webpack --config xxx.js
是指定自定义文件让webpack
根据xxx.js
输入与输出的文件进行一系列操作。除了以上,我们可以不使用配置
webpack --config webpack.config.js
这个命令,而是直接在命令行-cli直接打包指定的文件输出到对应的文件下会创建
dist2
目录并打包出来一个默认命名的main_ff7753e9dbb1e41a06a6.bundle.js
的文件我们会发现我们配置了诸如
webpack_test_dev_config.js
或者webpack_test_prd_config.js
z 这样的文件,通过build: test_dev
与build:test_prd
来区分,里面文件内容似乎大同小异,那么我可不可以复用一份文件,通过外面的环境参数来控制呢?这点在实际项目中会经常使用环境参数
我们可以通过
package.json
中指定的参数来确定,可以用--mode='xxx'
与--env a='xxx'
此时
webpack.config.js
需要改成函数的方式 第二参数argv
能获取全部的配置的参数因此我们就可以通过修改
package.json
里面的变量,从而控制webpack.config.js
运行整个项目
我们已经创建了一个
src/app.js
的入口文件,现在需要在浏览器上访问,因此需要构建一个index.html
,在根目录中新建public/index.html
,并且引入我刚打包的js
文件终于大功告成,我打开浏览器,打开页面终于可以访问了,【我本地装了 live server】插件
但是,当我每次修改
js
文件,我都要每次执行npm run build
这个命令,这就有些繁琐了,而且我本地是安装 vsode 插件的方式帮我打开页面的,这就有点坑了。于是在
webpack
中就有一个内置cli
watch 来监听文件的变化,我们只需要加上--watch
就可以了这种方式会一直监听文件的变化,当文件发生变化时,就会重新打包,页面会重新刷新。
当然还有一种方式,就是可以在
webpack.config.js
中加入watch
然后我们就改回原来的,将
--watch
去掉就行。--watch
这种方式确实提升我本地开发效率,因为只要文件一发生变化,就会重新打包编译,结合vscode
的插件就会重新加载最新的文件,但是随着项目的庞大,那么这种效率就很低了,因此除了webpack
自身的 watch 方案,我们需要去了解另外一个方案webpack-dev-server
webpack-dev-server
我们需要借助一个非常强大的插件工具来实现
本地静态服务
,这个插件就是webpack-dev-server,我们常常称呼为WDS
本地服务,他有热更新,并且浏览器会自动刷新页面,无需手动刷新页面并且我们还需要引入另一个插件
Html-webpack-plugins
这个插件,它可以自动帮我们引入打包后的文件。当我们启动本地服务,生地文件 js 文件会在内存中生成,并且被html
自动引入我们在
webpack.config.js
中引入html-webpack-plugin
并且在
package.json
中增加server
命令,注意我们加了server
,webpack-dev-server
内部已经有对文件监听,当文件发生变化时,可以实时更新生成在内存的那个js
,这个server
命令就是我安装的webpack-dev-server
的命令控制台运行
npm run server
默认打开 8080 端口,已经 ok 了模块热更新(Hot Module Replacement)
现在当我每次修改文件时,整个文件都会重新 build,并且是在虚拟内存中引入,如果修改的只是部分文件,全部文件重新加载就有些浪费了,因此需要
HMR
,热更新devServer hot,在运行时更新某个变化的文件模块,无需全部更新所有文件当我添加完后,发现热更新还是和以前一样,没什么用,官方这里有解释hot-module-replacement,通俗讲就是要指定某些文件要热更新,不然默认只要文件发生更改就得全部重新编译,从而全站刷新。
写了一段测试代码
在
app.js
中引入注意我们加了一段代码判断指定模块是否
HMR
这里注意一点
,指定的utils/index.js
必须是esModule
的方式输出,要不然不会生效 ,我们会发现,当我修改utils/index.js
时,会有一个请求 当你每改这个文件都会请求一个app.[hash].hot.update.js
这样的一个文件。webpack-dev-server
内置了HMR
,我们用webpack server
这个命令就启动静态服务了,并且还内置了HMR
,如果我不想用命令呢,我们可以通过 API 的方式启动dev-server
,具体示例代码如下,新建一个config/server.js
webpack-dev-middleware 代替 webpack-dev-server
然后命令行配置
node config/server.js
,可以参考官网webpack-dev-middleware加载 css[XHR 更新样式]
配置加载 css 的
loader
样式是内敛在
html
里面的,如何提取成单个文件呢?mini-css-extract-plugin 提取 css
我们把
style-loader
去掉了,并且换成了miniCssExtractPlugin.loader
,并且在plugins
中加入插件,将 css 文件提取了指定文件中,此时就会发现index.html
内敛的样式就变成一个文件加载了。图片资源加载
我们只知道
css
用了css-loader
与style-loader
,那么图片以及特殊文件也是需要特殊loader
才能使用,具体参考图片首先需要安装
file-loader
执行 npm i file-loader --save-dev可以参考
file-loader
,输出的图片文件可以加hash
值后缀,当打包上传后,如果文件没有更改,图片更容易从缓存中获取在
app.js
中加入引入图片看下引入的图片页面
大功告成,
css
与图片
资源都已经 OK 了总结
1、了解
webpack
是什么,它主要是前端构建工程化的一个工具,将一些譬如ts
,sass
,vue
,tsx
等等一些浏览器无法直接访问的资源,通过webpack
可以打包成最终浏览器可以访问的html
、css
、js
的文件。并且webpack
通过一系列的插件方式,提供loader
与plugins
这样的插件配置,达到可以编译各种文件。2、了解
webpack
编译入口的基本配置,entry
,output
、module
、plugins
以及利用devServer
开启热更新,并且使用module.hot.accept('path')
实现HMR
模块热替换功能3、我们了解在命令行
webpack --watch
可以做到实时监听文件的变化,每次文件变化,页面都会重新加载4、我们学会如何使用加载
css
以及图片资源
,学会配置css-loader
,style-loader
、file-loader
,以及利用min-css-extract-plugin
去提取css
,用html-webpack-plugin
插件实现本地WDS
静态文件与入口文件的映射,在html
中会自动引入实时打包的入口文件的app.bundle.js
5、熟悉从 0 到 1 搭建一个前端工程化项目
6、本文示例code-example
下一节会基于这个当下项目搭建
vue
、react
项目,以及项目的tree-shaking
,懒加载
、缓存
,自定义loader,plugins
等