zoukankan      html  css  js  c++  java
  • React: webpack模块组织关系

    现代前端开发离不开打包工具,以 webpack 为代表的打包工具已经成为日常开发必备之利器,拿 React 技术栈为例,我们 ES6 形式的源代码,需要经过 webpack 和 Babel 处理,才能生成发布版文件,在浏览器中运行。今天就结合 React 来梳理一下 webpack 打包时模块的组织结构,先给定下面一个简单的应用示例:

    import React from 'react';
    import ReactDOM from 'react-dom';
    
    import {greet} from './utils';
    
    const App = <h1>{greet('scott')}</h1>;
    
    ReactDOM.render(App, document.getElementById('root'));
    

    代码中的 utils 模块如下:

    export function greet(name) {
      return `hello ${name}`;
    }
    

    如果编译该示例代码,由于要将第三方库一起打包,最终生成的目标代码会比较多,所以我们先在 webpack.config.prod.js 中将 React 和 ReactDOM 配置为 externals,不将它们编译到目标代码中,而是在运行时直接从外部取值。配置如下:

    let appConfig = {
      entry: './src/index.js',
      externals: {
        'react': 'React',
        'react-dom': 'ReactDOM',
      },
      output: {
        path: './dist',
        filename: 'index.js'
      },
      module: {
        loaders: [
          {
            test: /.js$/,
            exclude: /node_modules/,
            loader: 'babel-loader'
          }
        ]
      },
    };
    

    运行 webpack,生成的目标代码如下:

    (function (modules) {
      // 存放已加载的模块
      var installedModules = {};
    
      // 加载函数
      function __webpack_require__(moduleId) {
        // 如果该模块已被加载 直接返回module.exports
        if (installedModules[moduleId]) {
          return installedModules[moduleId].exports;
        }
    
        var module = installedModules[moduleId] = {
          exports: {},
          id: moduleId,
          loaded: false
        };
    
        // 调用模块函数 加载相应的模块
        // 参数是(module, exports[, __webpack_require__])
        modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
    
        // 标记该模块已被加载
        module.loaded = true;
    
        // 最后也返回exports
        return module.exports;
      }
    
      // 暴露modules和installedModules对象
      __webpack_require__.m = modules;
      __webpack_require__.c = installedModules;
    
      // __webpack_public_path__
      __webpack_require__.p = "";
    
      // 加载主模块
      return __webpack_require__(0);
    })
    /* 以下是模块列表 作为参数被加载 */
    ([
      /* 0 主模块 */
      (function (module, exports, __webpack_require__) {
        'use strict';
    
        // 取索引为1的React模块
        var _react = __webpack_require__(1);
    
        var _react2 = _interopRequireDefault(_react);
    
        // 取索引为2的ReactDOM模块
        var _reactDom = __webpack_require__(2);
    
        var _reactDom2 = _interopRequireDefault(_reactDom);
    
        // 取索引为3的utils模块
        var _utils = __webpack_require__(3);
    
        // 模块取值 不包含__esModule:true的是默认导出
        function _interopRequireDefault(obj) {
          return obj && obj.__esModule ? obj : { default: obj };
        }
    
        // 开始渲染视图
        
        var App = _react2.default.createElement(
          'h1',
          null,
          (0, _utils.greet)('scott')
        );
    
        _reactDom2.default.render(App, document.getElementById('root'));
      }),
      /* 1 React模块 */
      (function (module, exports) {
        module.exports = React;
      }),
      /* 2 ReactDOM模块 */
      (function (module, exports) {
        module.exports = ReactDOM;
      }),
      /* 3 utils模块 */
      (function (module, exports) {
        "use strict";
    
        Object.defineProperty(exports, "__esModule", {
          value: true
        });
        
        exports.greet = greet;
        
        function greet(name) {
          return "hello " + name;
        }
      })
    ]);
    

    上面就是基本的模块加载机制。其实,有了 externals 配置,我们也可以不在代码中引入 React 和 React-DOM,下面稍微修改一下代码:

    import {greet} from './utils';
    
    const App = <h1>{greet('scott')}</h1>;
    
    ReactDOM.render(App, document.getElementById('root'));
    

    转译后的代码如下:

    (function (modules) {
      // ...
    })
    /* 以下是模块列表 作为参数被加载 */
    ([
      /* 0 主模块 */
      (function (module, exports, __webpack_require__) {
        'use strict';
    
        // 取索引为1的utils模块
        var _utils = __webpack_require__(1);
    
        // 开始渲染视图
        
        var App = React.createElement(
          'h1',
          null,
          (0, _utils.greet)('scott')
        );
    
        ReactDOM.render(App, document.getElementById('root'));
      }),
      /* 1 utils模块 */
      (function (module, exports) {
        "use strict";
    
        Object.defineProperty(exports, "__esModule", {
          value: true
        });
        
        exports.greet = greet;
        
        function greet(name) {
          return "hello " + name;
        }
      })
    ]);
    

    可以看到,代码清晰了不少,主模块中直接调用 React.createElement() 来创建虚拟 DOM 对象,最后调用 ReactDOM.render() 方法来渲染真实的 DOM 结点。访问下面的入口文件,我们就可以看到程序运行的结果:

    <!DOCTYPE html>
    <html>
      <body>
        <div id="root"></div>
        <script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
        <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
        <script src="index.js"></script>
      </body>
    </html>
    
  • 相关阅读:
    [debug] 解决在C++编写过程中的“找到一个或多个多重定义的符号”
    调试事件的收集
    [ida]查看某一函数在程序中被谁引用
    IDA+Windbg IDA+OD 连动调试插件
    一个简单的创建被调试进程的案例
    LOAD_DLL_DEBUG_EVENT 时读取 DllName
    【编译系统01】编译器
    [动态规划]石子合并问题
    xBIM 基础15 IFC导出Excel报表
    xBIM 基础14 使用LINQ实现最佳性能(优化查询)
  • 原文地址:https://www.cnblogs.com/liuhe688/p/10970764.html
Copyright © 2011-2022 走看看