webpack的使用依托于nodejs,本文及之后的篇幅里,使用的node版本为:
10.17.0
。
初始化node项目
开始之前,切换到存放项目的路径,在本地目录下初始化一个node项目,本文是在 webpack-study
目录下。初始化的命令如下:
cd webpack-study
npm init
根据步骤设置下项目名称等一些基础信息,其他的步骤按照默认配置一路回车到底即可。创建完成后,在 webpack-study
目录下会生成最基本的package.json文件。
创建代码目录
在 webpack-study
目录下创建 src
目录,用于存放源代码。并在src下创建index.js作为入口文件,这里先写一段简单的代码用作示例:
const add = (x, y) => x + y
console.log(add(1, 2))
安装Webpack
npm i webpack webpack-cli
这里除了用到 webpack
,我们也会用到 webpack-cli
, 它是webpack的脚手架。
创建Webpack配置文件
想要使用webpack对资源进行打包,首先我们需要配置下webpack的配置文件。我们在项目根目录下创建 webpack.config.js
文件。上一篇中,我们提到webpack的5个基本概念,(如果印象不是很深的小伙伴可以回看:Webpack学习笔记一:一些基础知识。)现在我们根据上一节中提到的5个基础概念来创建一个简单的webpack配置文件:
// 引入node的path模块
const { resolve } = require('path')
module.exports = {
// 入口文件
entry: './src/index.js',
// 文件输出配置
output: {
// 输出的文件名称
// 配置[name]则取的是入口文件的名称
filename: 'js/[name].js',
// 输出的文件路径
// 这里是指输出到根目录下的build目录下,如果对__dirname不是很了解的小伙伴可以去nodejs官网了解一下
path: resolve(__dirname, 'build')
},
// 用于配置loader
module: {},
// 用于配置插件信息
plugins: [],
// 启用开发模式
mode: 'development'
}
添加打包命令
webpack的打包命令就是 webpack
,我们可以在指令脚本中添加一些设置参数:
// 开发环境
webpack ./src/index.js -o ./build/index.js --mode=development
// 生产环境
webpack ./src/index.js -o ./build/index.js --mode=production
稍微解释下上面的脚本,上述脚本的含义是:webpack使用 开发/生产
模式( mode
),将 ./src/index.js
打包输出( -o
)到 ./build/index.js
中。
细心的小伙伴儿应该已经发现,上述两种指令的唯一区别在于 mode
的参数不同,一个是 development
,代表开发环境; 另一个是 production
,代表生产环境。那两者有什么区别呢?
我们先将两种指令配置到 package.json
的 scripts
中,方便我们后期可以直接使用 npm
或者 yarn
命令来执行它们。当然也可以直接将这两种指令复制到控制台中运行,但是显然这样就不是很方便了。
{
"name": "webpack_study",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build:dev": "webpack ./src/index -o ./build/index.js --mode=development",
"build:prd": "webpack ./src/index -o ./build/index.js --mode=production"
},
"author": "",
"license": "ISC",
"dependencies": {
"webpack": "4.41.6",
"webpack-cli": "3.3.11"
}
}
然后我们分别通过执行 build:dev
以及 build:prd
来看下打包后的区别。
Tips:
可能有小伙伴注意到了,在webpack.config.js
文件中配置了mode: 'development'
,而在我们package.json
的指令中也指定了--mode='development'
,那么两者究竟以谁为准呢?
答案是以package.json
中配置的为准。有兴趣的小伙伴可以去试试看~
build:dev
npm run build:dev
运行这个指令后,我们在根目录的 build/js
路径下可以看到打包后生成的 index.js
文件。打开这个文件:
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ }
/******/ };
/******/
/******/ // define __esModule on exports
/******/ __webpack_require__.r = function(exports) {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/
/******/ // create a fake namespace object
/******/ // mode & 1: value is a module id, require it
/******/ // mode & 2: merge all properties of value into the ns
/******/ // mode & 4: return value when already ns object
/******/ // mode & 8|1: behave like require
/******/ __webpack_require__.t = function(value, mode) {
/******/ if(mode & 1) value = __webpack_require__(value);
/******/ if(mode & 8) return value;
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ var ns = Object.create(null);
/******/ __webpack_require__.r(ns);
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ return ns;
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = "./src/js/index.js");
/******/ })
/************************************************************************/
/******/ ({
/***/ "./src/js/index.js":
/*!*************************!*\
!*** ./src/js/index.js ***!
\*************************/
/*! no static exports found */
/***/ (function(module, exports) {
eval("const add = (x, y) => x + y;\n\nconsole.log(add(1, 2));\n\n\n//# sourceURL=webpack:///./src/js/index.js?");
/***/ })
/******/ });
滑动到最底部,我们可以看到之前写的代码被转换成了字符串形式写入到了 eavl
方法中,并且以 源码路径
为key,同时将之作为value,以对象的形式作为入参,传入打包生成的方法中。
build:prd
我们再来看下通过运行 build:prd
打包生成的文件是什么样的呢:
npm run build:prd
运行完成后生成如下代码:
!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=0)}([function(e,t){console.log(1+2)}]);
我们发现代码精简了许多,没有了注释,没有了空格折行,显然代码被压缩过了。由此可以得出结论:
相较于development模式,production模式会对打包后的代码进行压缩。主要是因为production模式下启用了webpack的
UglifyJsPlugin
插件,这个插件的作用就是对代码进行压缩。
控制台输出信息
打包完成后,让我们回过头来,再来一起看下控制台中显示的信息,并对这些信息做一个简单的了解。我们先来看下刚才打包完成后的控制台输出:
我们依次看下这些参数的含义:
Hash: 打包之后生成的唯一ID,后续可用作文件名中的唯一表示,防止访问缓存;
Version: webpack的版本;
Time: 打包所耗的时间;
Built at: 打包开始的时间;
Asset:打包的资源;
Size:文件大小;
Chunks: 所属的chunk名称;
Chunk Names:chunk名称;
EntryPoint main: 入口文件名称;
[....]: 参与打包的模块,也就是上一节中提到的静态模块。
行文至此,我们已经实现了简单的webpack打包功能,并且对webpack的一些基本配置、打包命令以及不同模式下打包的区别做了一个简单的了解。与此同时,我们对控制台中输出信息的含义也了然于胸。
在后面的章节中,我会依次介绍webpack的其他功能,借此可以帮助大家逐步熟悉webpack的配置,让小伙伴们在之后的开发过程中不会再对webpack的配置感到晦涩陌生。篇幅也许不会太长,但是希望对小伙伴们有所助益~