webpack解惑

1、webpack的模块化原理

webpack 本身维护了一套模块系统,这套模块系统兼容了所有前端历史进程下的模块规范,包括 amd commonjs es6 等

(function(modules) {
  function __webpack_require__(moduleId) {
    var module =  {
      i: moduleId,
      l: false,
      exports: {}
    };
    modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
    return module.exports;
  }
  return __webpack_require__(0);
})([(function (module, __webpack_exports__, __webpack_require__) {
    // 模块的逻辑代码
    ...
  })]

原理的关键有两点:
1、所有的模块都被封装成function (module, webpack_exports, webpack_require){}的形式,
2、webpack_require函数,该函数内部有一个module对象,该对象有两个重要属性:i代表模块id,exports代表要暴露给外面的对象。每次调用webpack_require时都会传入一个数字作为模块id,这样不同模块就能被区分,另外该函数将module.exports作为返回值。每个模块的导出值都会被记录在module.exports对象里,其他依赖该模块的模块就能取到对应数据

2、webpack2是如何支持import和require两种引入模块的方式

首先webpack是基于node的,node的模块规范是commonjs,该规范就是使用require和module.exports来引入和导出模块的;其次,es6 module是静态的依赖,所以可以在运行前进行代码转换。import的本质其实就是require,webpack在进行静态分析时会将其转为require。这也说明了在webpack中可以混用import和require,因为二者本质上都是require

3、如何使用 webpack 的 tree-shaking 技术

webpack 的 tree-shaking 基于ES6的模块静态依赖机制,babel也是可以将ES6模块转换为commenjs模块的,但是你一旦这样做了就会失去tree-shaking技术。所以在使用babel转换ES6时一般会如下配置,即只使用bable的ES6语法转换能力,不使用它的模块处理,而是使用webpack2自己的模块处理。

presets: [['babel-preset-es2015', {modules: false}]]

需要说明的是,即使在 引入模块时使用了 es6 ,但是引入的那个模块却是使用 commonjs 进行输出,这也无法使用tree-shaking。
而第三方库大多是遵循 commonjs 规范的,这也造成了引入第三方库无法减少不必要的引入。
所以对于未来来说第三方库要同时发布 commonjs 格式和 es6 格式的模块。es6 模块的入口由 package.json 的字段 module 指定。而 commonjs 则还是在 main 字段指定。

4、import及export转换为require和module.exports的规则是啥样的

  • es6 的 export default 都会被转换成 exports.default
  • export 的所有输出都会添加到module.exports对象上
  • 使用require去引用ES6模块的export default输出时,注意用require('**').default
  • 导出
// ES6
export default 123;
export const a = 123;
const b = 3;
const c = 4;
export { b, c };
// 转换后
exports.default = 123;
exports.a = 123;
exports.b = 3;
exports.c = 4;
exports.__esModule = true;

babel 转换 es6 的模块输出逻辑非常简单,即将所有输出都赋值给 exports,并带上一个标志 __esModule 表明这是个由 es6 转换来的 commonjs 输出。

  • 导入
// 1、普通导入
import a from './a.js'     //引入一个 es6 模块中的 default 输出
// babel会这么转
function _interopRequireDefault(obj) {
    return obj && obj.__esModule
        ? obj
        : { 'default': obj };
}
var _a = require('./a.js');
var _a2 = _interopRequireDefault(_a);
var a = _a2['default'];
// 2、通配符引入
import * as a from './a.js'  //将 es6 模块的所有命名输出以及defalut输出打包成一个对象赋值给a变量
// Babel内部这么转
function _interopRequireWildcard(obj) {
    if (obj && obj.__esModule) {
        return obj;
    }
    else {
        var newObj = {}; // (A)
        if (obj != null) {
            for (var key in obj) {
                if (Object.prototype.hasOwnProperty.call(obj, key))
                    newObj[key] = obj[key];
            }
        }
        newObj.default = obj;
        return newObj;
    }
}
// 3、具名引入
import { a } from './a.js'  // 将 es6 模块的a引入
//babel这样转
require('./a.js').a

5、plugin && loader

  • compiler & compilation 对象

compiler 对象代表了完整的 webpack 环境配置。这个对象在启动 webpack 时被一次性建立,并在所有可操作的设置中被配置,包括原始配置,loader 和插件。当在 webpack 环境中应用一个插件时,插件将收到一个编译器对象的引用。可以使用它来访问 webpack 的主环境。

compilation 对象代表了一次单一的版本构建和生成资源。当运行 webpack 开发环境中间件时,每当检测到一个文件变化,一次新的编译将被创建,从而生成一组新的编译资源。一个编译对象表现了当前的模块资源、编译生成资源、变化的文件、以及被跟踪依赖的状态信息。编译对象也提供了很多关键点回调供插件做自定义处理时选择使用。

compiler 是 webpack 环境的代表,compilation 则是 webpack 构建内容的代表,它包含了每个构建环节及输出环节所对应的方法,存放着所有 module、chunk、asset 以及用来生成最后打包文件的 template 的信息。

  • 事件钩子

事件钩子其实就是类似 MVVM 框架的生命周期函数,在特定阶段能做特殊的逻辑处理。了解一些常见的事件钩子是写 webpack 插件的前置条件


compiler事件钩子
  • 插件模板
function MyPlugin(options) {}
  this.opt = options
  //1.函数原型上的 apply 方法会注入 compiler 对象
  MyPlugin.prototype.apply = function(compiler) {
  // 2.compiler 对象上挂载了相应的 webpack 事件钩子 
  compiler.plugin('emit', (compilation, callback) => { // 3.事件钩子的回调函数里能拿到编译后的 compilation 对象
    ...
  })
}
module.exports = MyPlugin
  • Loader

1、loader 用于对模块的源代码进行转换。loader 可以使你在 import 或"加载"模块时预处理文件。因此,loader 类似于其他构建工具中“任务(task)”,并提供了处理前端构建步骤的强大方法。loader 可以将文件从不同的语言(如 TypeScript)转换为 JavaScript,或将内联图像转换为 data URL。loader 甚至允许你直接在 JavaScript 模块中 import CSS文件!

2、css-loader 解析 @import 和 url()路径中指定的css内容

3、style-loader 会把原来的 CSS 代码插入页面中的一个 style 标签中

  • Loader 模版
// loaderUtils 可以获取 webpack.config.js 中的配置
var loaderUtils = require('loader-utils');
 
module.exports = function(source) {
  console.log("start process code...");
  var options = loaderUtils.getOptions(this) || {};
  if(options !== {}) {
    var replaceMap = options["replaceMap"];
    for(var key in replaceMap) {
      console.log(source);
      source = source.replace(key, replaceMap[key]);
      console.log(source);
    }
  }
  return source;
};

2、参考

简单易懂的 webpack 打包后 JS 的运行过程
简要分析webpack打包后代码

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容