Webpack学习笔记二:Webpack初体验

817 阅读6分钟

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.jsonscripts 中,方便我们后期可以直接使用 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的配置感到晦涩陌生。篇幅也许不会太长,但是希望对小伙伴们有所助益~