webpack入门讲解

本文将介绍webpack的作用、入门使用,以及webpack打包原理

模块开发

javascript诞生之初,就是服务于网页的脚本语言,相较于c++,java等缺少模块开发功能。在不使用webpack工具,javascript文件只能通过在html文件中使用script标签导入同时还要留意导入的顺序。当然在es6是支持使用原生模块开发,但不够完善

<!-- index.html -->
<script src="a.js"></script>
<script scr="b.js"></script>

<!-- es6原生导入 -->
<script type="module">
  import 'a.js';
  import 'b.js';
</script>

但我们注意到,这时javascript文件里的代码作用域是处于全局的,不同js文件的同名变量可能会造成冲突或覆盖

// a.js
var num = 114514;
const str = 'this is a.js';

// b.js
var num = 5211314;
const str = 'this is b.js';

// b.js 中的num覆盖 a.js 中的num,而 str 为常量,修改值会报错

此时我们想到使用函数闭包,但是问题在于手动使用函数闭包使得模块开发变得复杂且难以维护,这时使用webpack就可以解决这个问题------模块开发、构建打包,当然除了webpack构建工具外,还有gulp、vite、rollup等。就目前开发生态来说,webpack拥有大量的第三方插件支持和更好的js模块标准(AMD、CMS、ES6等)兼容,学习webpack之后再学习其他构建也能很快上手

简单打包一个应用

说明:使用 npm 还是 yarn 全靠个人喜好哈

  1. 初始化项目(项目名:webpack-demo)

    npm init -y
    or
    yarn init -y
    
  2. 目录结构如下

    .
    ├── package.json
    ├── public
    │   └── index.html  # 模版文件
    ├── src
    │   ├── a.js
    │   ├── b.js
    │   └── index.js    # 入口js文件
    └── webpack.config.js
    
  3. 下载 一些库

    npm install -D html-webpack-pluigin webpack webpack-cli webpack-dev-server
    or
    yarn add -D html-webpack-pluigin webpack webpack-cli webpack-dev-server
    
  4. 所有文件内容

    // package.json 文件内容如下
    // 注意:json 不能使用注释,这里只是描述
    {
      "name": "webpack-demo",
      "version": "1.0.0",
      "main": "index.js",
      "license": "MIT",
      "scripts": {
        "build": "webpack",								// 构建   npm run bulid or yarn build
        "dev": "webpack-dev-server"				// 热部署 npm run dev or yarn dev
      },
      "devDependencies": {
        "html-webpack-plugin": "^5.5.0",	// 使用html模版文件,链接js和html模版
        "webpack": "^5.74.0",							// 核心库 
        "webpack-cli": "^4.10.0",					// 使命令行使用 webpack 命令
        "webpack-dev-server": "^4.11.0"		// 热部署,适合开发时随时查看构建状态
      }
    }
    
    // index.html 文件内容如下
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>webpack-demo</title>
      <style>
        body {
          width:100%;
        }
        div {
          width: 50%;
          margin: 20% auto;
          font-size:32px;
          color: red;
          text-align:center;
        }
      </style>
    </head>
    <body>
      <div>webpack-demo</div>
    </body>
    </html>
    
    // a.js 文件内容如下
    var num = 114514;
    const str = 'this is a.js';
    export { num, str };
    
    // b.js 文件内容如下
    var num = 5211314;
    const str = 'this is b.js';
    export { num, str };
    
    // index.js 文件内容如下
    import { num as num_a, str as str_a } from './a.js';
    import { num as num_b, str as str_b } from './b.js';
    let a = `import a.js, and num_a is ${num_a}, str_a is '${str_a}' `;
    let b = `import b.js, and num_b is ${num_b}, str_b is '${str_b}' `;
    console.log(a);
    console.log(b);
    
    // webpack.config.js 文件内容如下
    const path = require('path');
    
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    
    module.exports = {
      // entry 默认入口为src目录下的 index.js
      entry: './src/index.js',
      output: {
        filename: '[name].js',
        // 默认 构建到 dist 目录下
        path: path.resolve(__dirname, 'dist'),
      },
      mode: 'development',
      plugins: [
        new HtmlWebpackPlugin({
          template: path.join(__dirname, 'public', 'index.html'),
          filename: 'index.html',
        }),
      ],
      devServer: {
        hot: true,
        port: 8080,
        open: true,
        compress: true,
      },
    };
    
  5. 热部署和构建

    npm run dev or yarn dev # 在默认浏览器打开网页,控制台可以看到打印信息,
    npm run build or yarn build # 构建项目,在 dist 目录下
    
  6. 小结

    查看 webpack.config.js 文件,可以大致了解 webpack 核心配置信息

    entry:构建入口

    output:构建出口

    plugins:插件集,功能有构建优化、资源管理、环境注入,devServer 是官方提供的插件

    还有 loader,让webpack处理其他类型文件,例如

      module: {
        rules: [
          // 处理图片文件
          {
            test: /\.(jpe?g|png|gif|bmp)$/i,
            type: 'asset/resource',
          },
          // 处理 css 样式文件
          {
            test: /\.css$/,
            use: ['vue-style-loader', 'css-loader', 'postcss-loader'],
          },
          // 处理 scss 样式文件
          {
            test: /\.scss$/,
            use: ['vue-style-loader','css-loader','sass-loader','postcss-loader',],
          }
        ]
      }
    
  7. 更多

    webpack更多配置知识请查看官网 或者 等我更新下一篇博客(很慢)

webpack打包原理

打包完上面的项目后,查看 dist目录下的 main.js (bundle)文件(简略)

// 立即执行匿名函数
(() => {
  // 开启 严格模式
  'use strict';
  // 以 key-value 形式存储 所有被打包的模块
  var __webpack_modules__ = {
    './src/a.js': (__unused_webpack_module,__webpack_exports__,__webpack_require__) => {/* 省略 */},
    './src/b.js': (__unused_webpack_module,__webpack_exports__,__webpack_require__) => {/* 省略 */},
    './src/index.js': (__unused_webpack_module,__webpack_exports__,__webpack_require__) => {/* 省略 */},
  };
	// 模块缓存
  var __webpack_module_cache__ = {};
	// 实现 require
  function __webpack_require__(moduleId) {
    // 省略
  }
  // 执行入口模块的加载
  var __webpack_exports__ = __webpack_require__('./src/index.js');
})();

内容可以分为以下几个部分:

  • 最外层匿名函数,包裹整个bundle文件,形成自身的作用域
  • __webpack_modules__项目产生依赖关系的模块都会以 key-value 形式放在这里。key就是模块id,value是由一个匿名函数包裹的模块实体,匿名函数的参数赋予每个模块导出和导入能力
  • __webpack_module_cache__每个模块只在第一次被加载的时候执行,之后其导出值就被存储到这个对象里面,当再次被加载的时候webpack就会直接从这里取值,而不会重新执行该模块
  • __webpack_require__ 对模块加载的实现,在浏览器中可以调用__webpack_require__(moduleId) 完成模块导入

bundle在浏览器中执行步骤:

  1. 在最外层匿名函数中初始化浏览器执行环境,包括定义__webpack_modules__对象、__webpack_require__ 函数等,为模块的加载和执行做一些准备工作
  2. 加载入口模块。每个bundle都仅有一个入口模块,上面例子中 index.js 就是入口模块,在浏览器中会从它开始执行
  3. 执行模块代码。如果执行到了module.exports则记录下模块的导出值;如果中间遇到require函数(__webpack_require__ ),则会暂时停止执行,进入__webpack_require__函数内进行加载其他模块的逻辑
  4. __webpack_require__中判断即将加载的模块是否存在于__webpack_module_cache__中,如果存在则直接取值,否则返回到第三步,执行该模块的代码来获取导出值
  5. 所有依赖的模块都执行完毕,最后的执行权又回到入口模块,当入口模块中的代码执行完毕后也就意味着整个bundle执行结束

我们可以看到第三步和第四步是一个递归过程。webpack为每个模块创造了一个可以导出和导入模块的环境,但本质并没有修改代码的执行逻辑,因此代码执行的顺序与模块加载的顺序是完全一致的,这也就是webpack模块打包的原理

posted @ 2022-09-20 08:32  晨米酱  阅读(178)  评论(0编辑  收藏  举报