Hibop / Hibop.github.io

Hibop 个人博客
https://hibop.github.io/
23 stars 1 forks source link

关于webpack学习之模块篇——五种模块化兼容方案学习 #16

Open Hibop opened 6 years ago

Hibop commented 6 years ago

webpack作为前端全能打包工具,在模块化解决方案上很全能。学习这五种模块化解决方案可以加深我们队前端模块化理解,也可以加深我们对webpack的工作原理的理解。

模块化规范分类:

// 错误语法 export 1; var a = 100; export a; // 不能解构{100: 100}

1.  import则不同,是编译时的(require是运行时的),必须放在文件开头;
2.  理解上,require是赋值过程,import是解构过程
3. 目前所有的引擎都还没有实现import,我们在node中使用babel支持ES6,也仅仅是将ES6转码为ES5再执行,import语法会被转码为require。
4. esModule要搞清楚几个关键词:as、*、default、{}
- 正常引入加括号: ` import {each,map} from 'jquery' `;
- as: 别名` import {fun as a} from './a' `; 
- *: 全量引入   `import * as a from './a' `;
- export default 引入时 import sth(不加括号解构) from './adda';
```js
import a from './d';
// 等效于,或者说就是下面这种写法的简写,是同一个意思
import {default as a} from './d';

【天坑】 export default fn 如果在router中require引入时必须:require(./dir/src...).default, 否则模块报错 因此在实际使用过程中定好标准:webpack使用require模块引入,而项目业务代码统一es6

模块化实现原理——IIFE(立即执行函数)

因为浏览器本身不支持模块化,那么webpack和require等模块化工具就用函数作用域来hack模块化的效果

整个打包生成的代码是一个IIFE(立即执行函数)。函数参数是我们写的各个模块组成的数组,只不过我们的代码,被webpack包装在了一个函数的内部,也就是说我们的模块,在这里就是一个函数。

在debug node代码时,你会发现一样的hack方式,node中的模块也是函数,跟模块相关的参数exports、require,或者其他参数filename和dirname等都是通过函数传值作为模块中的变量,模块与外部模块的访问就是通过这些参数进行的,当然这对开发者来说是透明的。

同样的方式,webpack也控制了模块的module、exports和require,那么我们就看看webpack是如何实现这些功能的。

webpack模块化代码生成分析:

  1. 打开webpack打包的代码,整体可以简化成下面的结构:
    (function (modules) {
    // 1、模块缓存对象
    var installedModules = {};
    // 2、webpack实现的require
    function __webpack_require__(moduleId) {
    // 3、判断是否已缓存模块
    if(installedModules[moduleId]) {
        return installedModules[moduleId].exports;
    }
    // 4、缓存模块
    var module = installedModules[moduleId] = {
        i: moduleId,
        loaded: false,
        exports: {}
    };
    // 5、调用模块函数,这里面call调用绑定的执行上下文是module.exports
    modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
    // 6、标记模块为已加载
    module.loaded = true;
    // 7、返回module.exports
    return module.exports;
    }
    // 8、require第一个模块,并执行入口函数
    return __webpack_require__(__webpack_require__.s = 0);
    })
    ([
    function (module, exports, __webpack_require__) {
    /* 模块index.js的代码,入口代码 */
    },
    function (module, exports, __webpack_require__) {
    /* 模块bar.js的代码 */
    }
    ]);

    整个打包生成的代码是一个IIFE(立即执行函数)。函数参数是我们写的各个模块组成的数组,只不过我们的代码,被webpack包装在了一个函数的内部,也就是说我们的模块,在这里就是一个函数。

初始化函数

在执行模块函数之前,IIFE函数体内实现需要函数参数的初始化:

  1. IIFE首先定义了installedModules ,这个变量被用来缓存已加载的模块。
  2. 定义了__webpack_require__ 这个函数,函数参数为模块的id。这个函数用来实现模块的require。
  3. __webpack_require__ 函数首先会检查是否缓存了已加载的模块,如果有则直接返回缓存模块的exports。
  4. 如果没有缓存,也就是第一次加载,则首先初始化模块,并将模块进行缓存。
  5. 然后调用模块函数,也就是前面webpack对我们的模块的包装函数,将module、module.exports和__webpack_require__作为参数传入。注意这里做了一个动态绑定,将模块函数的调用对象绑定为module.exports,这是为了保证在模块中的this指向当前模块。
  6. 调用完成后,模块标记为已加载。
  7. 返回模块exports的内容。
  8. 利用前面定义的__webpack_require__ 函数,require第0个模块,也就是入口模块。

模块函数

function(module, exports, __webpack_require__) {
    "use strict";
    var bar = __webpack_require__(1);
    bar();
}

对于模块化函数一般会有三个参数:

webpack的模块缓存:

在webpack中通过require加载模块,我们看到初始初始化时有installedModules,这是处理模块缓存问题 webpack管理着这些模块的缓存,如果一个模块被require多次,那么只会有一次加载过程,而返回的是缓存的内容,这也是commonjs的规范。

模块化规范历史

http://www.cnblogs.com/lvdabao/p/js-modules-develop.html

Hibop commented 6 years ago

附上大神的webpack模块化原理三连,搞懂这些,妈妈再也不用担心不理解模块化了

webpack模块化原理-commonjs: https://segmentfault.com/a/1190000010349749; webpack模块化原理-ES module: https://segmentfault.com/a/1190000010955254 webpack模块化原理-Code Splitting: https://segmentfault.com/a/1190000010349749

Hibop commented 6 years ago

webpack之build: dll(生产环境依赖包构建)

Hibop commented 6 years ago

Webpack 入门指迷 : https://segmentfault.com/a/1190000002551952

Hibop commented 6 years ago

怎样写自定义webpack-plugins:

Hibop commented 6 years ago

webpack之code-split

https://segmentfault.com/a/1190000007479892 http://foio.github.io/wepack-code-spliting/