现代前端开发离不开打包工具,以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中将ReactReactDOM配置为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配置,我们也可以不在代码中引入ReactReact-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>

React系列文章:Webpack模块组织关系的更多相关文章

  1. React系列文章:Babel编译JSX生成代码

    上次我们总结了React代码构建后的Webpack模块组织关系,今天来介绍一下Babel编译JSX生成目标代码的一些规则,并且模拟整个生成的过程. 我们还是拿最简单的代码举例: import {gre ...

  2. React: webpack模块组织关系

    现代前端开发离不开打包工具,以 webpack 为代表的打包工具已经成为日常开发必备之利器,拿 React 技术栈为例,我们 ES6 形式的源代码,需要经过 webpack 和 Babel 处理,才能 ...

  3. React 系列文章(1): npm 手动搭建React 运行实例 (新手必看)

    摘 要 刚接触React 开发, 在摸索中构建react 运行环境,总会遇到各种坑:本文,将用最短时间解决webpack+react 环境搭建问题. 1.如果你还没有React基础 看这里. 2.如果 ...

  4. React系列文章:无状态组件生成真实DOM结点

    在上一篇文章中,我们总结并模拟了JSX生成真实DOM结点的过程,今天接着来介绍一下无状态组件的生成过程. 先以下面一段简单的代码举例: const Greeting = function ({name ...

  5. React系列文章:JSX生成真实DOM结点

    在上一篇文章中,我们介绍了Babel是如何将JSX代码编译成可执行代码的,随后也实现了一个自己的解析器,模拟了Babel编译的过程. 现在我们再来回顾一下,假定有如下业务代码: const style ...

  6. 关于Webpack详述系列文章 (第二篇)

    1.缩小文件搜索范围 1.1.1 include & exclude module:{ rules:[ { test:/\.js$/, use:['babel-loader?cacheDire ...

  7. 关于Webpack详述系列文章 (第三篇)

    1. 类图 1. 模块 Module是webpack中最核心的类,要加载定的一切和依赖都是Module. 它有很多子类 RawModule NormalModule MultiModule Conte ...

  8. [React] react+redux+router+webpack+antd环境搭建一版

    好久之前搭建的一个react执行环境,受历史影响是webpack3.10.0和webpack-dev-server2.7.1的环境,新项目准备用webpack4重新弄弄了,旧的记录就合并发布了(在没有 ...

  9. React系列之--props属性

    版权声明:本文为博主原创文章,未经博主允许不得转载. PS:转载请注明出处作者:TigerChain地址:http://www.jianshu.com/p/fa81cebac3ef本文出自TigerC ...

随机推荐

  1. C++:MSVCRTD.lib(crtexe.obj) : error LNK2019: 无法解析的外部符号 _main,该符号在函数 ___tmainCRTStart

    在VS2013中Build一个C++程序报这个错,解决方案如下: 在解决方案管理器中选择该项目,项目/属性/连接器/系统/子系统 把控制台 (/SUBSYSTEM:CONSOLE)改为 窗口 (/SU ...

  2. Expm 2_2 查找中项问题

    对于长度为n的整型数组A,随机生成其数组元素值,然后实现一个线性时间的算法,在该数组中查找其中项. package org.xiu68.exp.exp3; import java.util.Array ...

  3. PYTHON-有参装饰器,无参装饰器,语法糖

    装饰器 装饰器就是闭包函数的一种应用场景 一 为何要用装饰器 #开放封闭原则:对修改封闭,对扩展开放 二 什么是装饰器 装饰器他人的器具,本身可以是任意可调用对象,被装饰者也可以是任意可调用对象. 强 ...

  4. mysql命令行怎么清屏

    例如: 怎么清屏? 哈哈 我也百度了半天,之后发现,这是个坑啊,dos(面向磁盘的操作命令)下面我们都是 cls 清屏,所以习惯性的用cls结果报错,打脸了吧.. mysql 命令行窗口不想看到那一堆 ...

  5. 分享我对JS插件开发的一些感想和心得

    本文阅读目录: •起因•如何开发一个轻量级的适用性强的插件•总结 起因 如果大家平时做过一些前端开发方面的工作,一定会有这样的体会:页面需要某种效果或者插件的时候,我们一般会有两种选择: 1.上网查找 ...

  6. three.js 相机camera位置属性设置详解

    开始很懵逼,完全不能理解,有个position,还要up和lookAt干嘛. [黑人问号脸❓❓❓] 既然是位置属性不明白,那默认其它属性都懂了. 上坐标轴: 先来第一个position属性,可以设置x ...

  7. 【linux】16进制格式查看命令hexdump

    test.txt内容 asdfsg ewtwfsdf1Hello World! hexdump -Cv test.txt   输出 |asdfsg ewtwfsdf1| 6c 6c 6f 6f 6c ...

  8. VIM 键盘符号

    :h key-notation //查询键盘符号说明<>> 等于shift + > % 是跳到对应的括号 x 是删除当前字符,即右括号 '' 是跳回左括号 x 删除左括号

  9. 认识js运动

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  10. jmeter在返回的json串中提取需要的值

    接口测试时我们需要对某条信息进行修改,如我们先创建了一篇文章,然后进行修改操作 我们就需要找到该文章的唯一标志,如id 示例:我们要将下图返回的json 中id进行提取 注:可输入$.加需要的key即 ...