本文转载自微信公众号「前端万有引力」,作者一川。转载本文请联系前端万有引力公众号。
写在前面
我们知道当前生产中主流的模块化打包工具有Webpack、Parcel和Rollup。作为模块化打包工具,它们基本的特点有:
- 能够将散落的模块打包在一起
- 能够编译转换代码中的新特性,使得可以兼容各种生产环境
对于主流的webpack而言,webpack作为一个模块打包工具,自身就可以实现模块化代码打包的问题,通过webpack可以将零散的JS代码打包到一个JS文件中。对于有环境兼容性问题的代码,webpack可以在代码打包过程中通过loader机制对其实现编译转换,然后再进行打包。对于不同类型的前端模块,webpack支持在js中以模块化的方式载入任意类型的资源文件,例如:可以通过webpack实现在JS中加载CSS文件,被加载的CSS文件会以style标签的方式工具。
webpack还具备代码拆分的能力,能够将应用中所有的模块按需分块打包,不用担心全部代码打包到一起,产生单个文件过大,导致加载慢的问题。这种模块按需分块打包非常适合大型web项目。
Webpack上路
Webpack作为主流的前端模块打包器,提供了一整套前端项目模块化方案,而不仅仅局限于JS的模块化,可以实现对前端项目开发过程中涉及到的资源进行模块化。
我们知道ES Modules中制定的html中使用script导入js模块,需要设定type="module",用来区分加载的是普通JS脚本还是一个模块。对于支持ES Modules的浏览器可以直接使用,但是对于不支持的浏览器就会报错,因此需要使用webpack模块打包工具避免报错。
- <script src="./index.js" type="module"></script>
- // heading.js
- export default function(){
- const element = document.createElement("div");
- element.textContent = "hello yichuan";
- element.addEventListener("click",()=>{
- console.log("hello onechuan");
- return element
- })
- }
- // index.js
- import createHeader from "./heading.js";
- document.body.append(createHeader())
对于,我们需要先安装webpack的核心模块和cli程序,用于在命令行中调用webpack。npx是npm5.2以后新增的一个命令,可以更方便地执行远程模块或者项目node_modules中的cli程序。
- $ npm init --yes
- $ npm i webpack webpack-cli -D
- $ npx webpack --version
- $ npx webpack
通过执行npx webpack实现自动化打包,webpack会默认从src/index.js文件开始打包,打包完毕后会在根目录下生产dist目录,所有打包文件会在dist目录。
- |--dist
- |--main.js
- (()=>{"use strict";document.body.append(function(){const e=document.createElement("div");e.textContent="hello yichuan",e.addEventListener("click",(()=>(console.log("hello onechuan"),e)))}())})();
webpack4.0以后的版本支持零配置的方式直接启动打包,整个过程会按照约定将src/index.js文件作为打包入口,最终打包的结果会存放到dis/main.js中。但是,很多时候webpack的默认规则并能满足我们实际需求,对此我们需要对webpack进行自定义配置。
webpack支持在项目中创建webpack.config.js文件进行自定义打包配置,由于webpack是运行在node.js环境,对应应该遵守CommonJS规则进行导入导出。
- |--backpack
- |--src
- |--heading.js
- |--main.js
- |--index.html
- |--package.json
- |--webpack.config.js-------webpack配置文件
webpack自定义的配置文件:
- // webpack.config.js
- const path = require("path");
- module.exports = {
- entry:"./src/index.js",//打包入口
- output:{//打包出口
- filename:"bundle.js",//命名打包后的文件
- path:path.join(__dirname,"output")
- },
- // 打包模式
- mode:"none"
- }
执行打包命令后会在项目根目录下自动生成打包后的文件目录,我们看到下面是执行打包命令后生成的文件,生成的是一个立即执行函数。
- /******/ (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/index.js");
- /******/ })
- /************************************************************************/
- /******/ ({
- /***/ "./src/heading.js":
- /*!************************!*\
- !*** ./src/heading.js ***!
- \************************/
- /*! exports provided: default */
- /***/ (function(module, __webpack_exports__, __webpack_require__) {
- "use strict";
- eval("__webpack_require__.r(__webpack_exports__);\n// heading.js\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(){\n const element = document.createElement(\"div\");\n element.textContent = \"hello yichuan\";\n element.addEventListener(\"click\",()=>{\n console.log(\"hello onechuan\");\n return element\n })\n});\n\n//# sourceURL=webpack:///./src/heading.js?");
- /***/ }),
- /***/ "./src/index.js":
- /*!**********************!*\
- !*** ./src/index.js ***!
- \**********************/
- /*! no exports provided */
- /***/ (function(module, __webpack_exports__, __webpack_require__) {
- "use strict";
- eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _heading_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./heading.js */ \"./src/heading.js\");\n// index.js\n\ndocument.body.append(Object(_heading_js__WEBPACK_IMPORTED_MODULE_0__[\"default\"])())\n\n//# sourceURL=webpack:///./src/index.js?");
- /***/ })
- /******/ });
在上面打包之后的文件中,src目录中的每个模块对应打包后文件中的一个函数,从而实现模块的私有作用域,在此文件中还挂载了一些其他的数据和工具函数。
写在后面
这篇文章主要写了webpack上路使用,对于webpack的基本使用并不是很复杂,特别是在webpack4.0后,很多配置都已经被简化。在这种配置并不复杂的前提下,开发人员对它的掌握程度,主要体现在是否能够理解它的工作机制和原理。