JavaScript系列文章: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中将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/[email protected]/umd/react.production.min.js"></script>
    <script crossorigin src="https://unpkg.com/[email protected]/umd/react-dom.production.min.js"></script>
    <script src="index.js"></script>
  </body>
</html>

原文地址:https://www.cnblogs.com/liuhe688/p/9602414.html

时间: 2024-08-05 11:45:20

JavaScript系列文章:React总结之Webpack模块组织的相关文章

JavaScript系列文章:变量提升和函数提升

第一篇文章中提到了变量的提升,所以今天就来介绍一下变量提升和函数提升.这个知识点可谓是老生常谈了,不过其中有些细节方面博主很想借此机会,好好总结一下. 今天主要介绍以下几点: 1. 变量提升 2. 函数提升 3. 为什么要进行提升 4. 最佳实践 那么,我们就开始进入主题吧. 1. 变量提升 通常JS引擎会在正式执行之前先进行一次预编译,在这个过程中,首先将变量声明及函数声明提升至当前作用域的顶端,然后进行接下来的处理.(注:当前流行的JS引擎大都对源码进行了编译,由于引擎的不同,编译形式也会有

JavaScript系列文章:详解正则表达式之二

在上一篇文章中我们讲了正则表达式的基本用法,接下来博主想聊聊其中的细节,今天就从正则修饰符开始吧. 正则修饰符又称为正则标记(flags),它会对正则的匹配规则做限定,进而影响匹配的最终结果.在上次的文章中我们也提到过,正则修饰符一共有以下几种,可以单独使用,也可以组合使用: /\w+/g; // global search /\w+/i; // ignore case /\w+/m; // multi-line /\w+/u; // unicode /\w+/y; // sticky /\w+

JavaScript系列文章:详解正则表达式之一

正则表达式是一个精巧的利器,经常用来在字符串中查找和替换,JavaScript语言参照Perl,也提供了正则表达式相关模块,开发当中非常实用,在一些类库或是框架中,比如jQuery,就存在大量的正则表达式,所以说学好正则表达式,是提高开发技能的一项基本要求.那么今天博主就来详细总结一下正则表达式的相关知识,希望不熟悉的同学们,也能够掌握正则表达式的原理及应用. 在JS中,创建正则表达式有两种方式,一种是字面量方式,一种是构造器方式,如下所示: var regex = /\w+/; // 或者 v

JavaScript系列文章:自动类型转换-续

在上一篇文章中,我们详细讲解了JavaScript中的自动类型转换,由于篇幅限制,没能覆盖到所有的转换规则,这次准备详细讲解一下. 上次我们提到了对象类型参与运算时转换规则: 1). 在逻辑环境中执行时,会被转换为true 2). 在字符串环境和数字环境中,它的valueOf()方法和toString()方法会依次被调用,然后根据返回值进行再次转换.首先,valueOf()方法会被调用,如果其返回值是基础类型,则将这个返回值转为目标类型,如果返回值不是基础类型,则再试图调用toString()方

JavaScript系列文章:自动类型转换

我们都知道,JavaScript是类型松散型语言,在声明一个变量时,我们是无法明确声明其类型的,变量的类型是根据其实际值来决定的,而且在运行期间,我们可以随时改变这个变量的值和类型,另外,变量在运行期间参与运算时,在不同的运算环境中,也会进行相应的自动类型转换. 自动类型转换一般是根运行环境和操作符联系在一起的,是一种隐式转换,看似难以捉摸,其实是有一定规律性的,大体可以划分为:转换为字符串类型.转换为布尔类型.转换为数字类型.今天我们就介绍一下这几种转换机制. 1. 转换为字符串类型(to s

深入理解JavaScript系列(47):对象创建模式(上篇)

介绍 本篇主要是介绍创建对象方面的模式,利用各种技巧可以极大地避免了错误或者可以编写出非常精简的代码. 模式1:命名空间(namespace) 命名空间可以减少全局命名所需的数量,避免命名冲突或过度.一般我们在进行对象层级定义的时候,经常是这样的: var app = app || {}; app.moduleA = app.moduleA || {}; app.moduleA.subModule = app.moduleA.subModule || {}; app.moduleA.subMod

深入理解JavaScript系列(17):面向对象编程之概论

介绍 在本篇文章,我们考虑在ECMAScript中的面向对象编程的各个方面(虽然以前在许多文章中已经讨论过这个话题).我们将更多地从理论方面看这些问题. 特别是,我们会考虑对象的创建算法,对象(包括基本关系 - 继承)之间的关系是如何,也可以在讨论中使用(我希望将消除之前对于JavaScript中OOP的一些概念歧义). 英文原文:http://dmitrysoshnikov.com/ecmascript/chapter-7-1-oop-general-theory/ 概论.范式与思想 在进行E

深入理解JavaScript系列(22):S.O.L.I.D五大原则之依赖倒置原则DIP

前言 本章我们要讲解的是S.O.L.I.D五大原则JavaScript语言实现的第5篇,依赖倒置原则LSP(The Dependency Inversion Principle ). 英文原文:http://freshbrewedcode.com/derekgreer/2012/01/22/solid-javascript-the-dependency-inversion-principle/ 依赖倒置原则 依赖倒置原则的描述是: A. High-level modules should not

深入理解JavaScript系列(7):S.O.L.I.D五大原则之开闭原则OCP

前言 本章我们要讲解的是S.O.L.I.D五大原则JavaScript语言实现的第2篇,开闭原则OCP(The Open/Closed Principle ). 开闭原则的描述是: Software entities (classes, modules, functions, etc.) should be open for extension but closed for modification. 软件实体(类,模块,方法等等)应当对扩展开放,对修改关闭,即软件实体应当在不修改的前提下扩展.