(function(modules) { // webpackBootstrap
// The module cache
var installedModules = {};
// The require function
function __webpack_require__(moduleId) {
// Check if module is in cache
if(installedModules[moduleId]) {
return installedModules[moduleId].exports;
}
// Create a new module (and put it into the cache)
var module = installedModules[moduleId] = {
i: moduleId,
l: false,
exports: {}
};
// Execute the module function
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
// Flag the module as loaded
module.l = true;
// Return the exports of the module
return module.exports;
}
// Load entry module and return exports
return __webpack_require__(__webpack_require__.s = "./src/index.js");
})
({
/***/ "./src/index.js":
/*!**********************!*\
!*** ./src/index.js ***!
\**********************/
/*! no static exports found */
/***/ (function(module, exports) {
eval("console.log('hahaha!!!')\n\n\n//# sourceURL=webpack:///./src/index.js?");
})
});
// The require function
function __webpack_require__(moduleId) {
// Check if module is in cache
if(installedModules[moduleId]) {
return installedModules[moduleId].exports;
}
// Create a new module (and put it into the cache)
var module = installedModules[moduleId] = {
i: moduleId,
l: false,
exports: {}
};
// Execute the module function
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
// Flag the module as loaded
module.l = true;
// Return the exports of the module
return module.exports;
}
// node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js
// 避免贴过多代码,以下有删减
function insertStyleElement(options) {
var style = document.createElement('style');
if (typeof options.insert === 'function') {
options.insert(style);
} else {
var target = getTarget(options.insert || 'head');
if (!target) {
throw new Error("Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.");
}
target.appendChild(style);
}
return style;
}
function applyToTag(style, options, obj) {
var css = obj.css;
var media = obj.media;
/* istanbul ignore if */
if (style.styleSheet) {
style.styleSheet.cssText = css;
} else {
while (style.firstChild) {
style.removeChild(style.firstChild);
}
style.appendChild(document.createTextNode(css));
}
}
每次提起 webpack,都会被它一大堆配置搞得头都大了,本身就不算很熟,如果面试被问起来,就更难了。
相信 webpack 大家都有使用过,但是多少人知道它的原理呢?有没有去看过它打包后的代码是怎么样的?本文讨论的是 webpack 4,也会忽略掉 webpack 的一些基本的配置(当作你是会用的啦~),这次就通过打包一些简单的文件,看看 webpack 的打包过程是怎么样的。
下面是我们当前的目录结构(tree 生成)
我在 src 新建一个叫 index.js 的文件,并且将其作为 webpack 的打包入口,index.js 我只写了一行代码,主要是看
此时,我们来 webpack 一下,然后我们的 dist 就会有我们的 bundle.js 文件了( bundle 这个名字是来自 entry 的 key,因为我们写了 [name] )
大概会有 100 行代码,这里我省去一些暂时不需要了解的代码(一些对 __webpack_require__ 函数拓展的方法),把一些关键的代码贴出来
删完之后,大概就剩下40多行了。这时看就很方便了
先看整体,整个 js 文件是一个自执行函数,Like this
再来看下入参,入参是一个对象,里面的 key 是文件路径,就是我们在 webpack.config.js 里 entry 配置的文件入口,上面我只配置了一个 index.js 的入口,所以这里入参对象只有一项,而对应的 value 是一个函数对象,函数体是执行一个 eval 函数,然后其执行的内容,就是这个文件的内容!
然后再来看下自执行函数里面是做了什么
一个对象,表面上去像是缓存已经加载过的 module(事实也是如此)
紧接着是,可以说 webpack 最关键的一个的一个函数 __webpack_require__ ,它正是我们平常 require 文件的关键
函数体做的事情不复杂,首先是检查之前外部声明的 installedModules 对象(没错吧,就是用来缓存模块的,一个文件一个模块,也就是一个 k-v 对),假如存在,直接读取其 export 出来的值。
否则在 installedModules 以其当前模块的 id (也就是文件的路径)创建一个值。
接着,会将该文件 export 出的内容通过 call 方法进行调用,调用完毕设置其标志位,并将其 export 值返回,假如是当前文件中有 require, __webpack_require__ 将会递归地调用,Like this,index.js 及自执行函数参数部分如下
require 被编译成 __webpack_require__ 方法,同时匿名函数多了个参数,See? 我们的文件就是这样递归地进行调用,然后被打包成一个文件。
除了 require 方法,对于 import,webpack 又是怎么打包的呢? 我来新建一个 util 文件,并引用其中的一个方法
这时我们再打包一下,会发现我们原本
"./src/index.js"
这个模块的 eval 内容有点不一样了,最顶处多了这些代码这里使用了
__webpack_require__.r
检查当前环境是否支持 Symbol,若支持则将其
[[Class]]
值设为Module
(这个值可以通过Object.prototype.toString
方法获得),然后设置一个__esModule
属性,值为 true.这个函数看起来就是给 export 增加一个标记
再来看下我们的 utils 文件打包后的样子
这里主要是用到了
__webpack_require__.d
这个函数可以看到,这里主要是将 utils 定义的方法定义到 exports 里面,getter 就是返回相关函数的引用,然后引用的页面就读到这些 export 出来的引用了,这个 export 就是开始定义的
大致的流程就是这样子,是不是还挺容易理解的?
延伸
有时候我们也可以在 JS 引入 css,由于 webpack 只认识 js 文件,如果需要打包 css 文件,那就需要额外加入能让webpack 认识的 loader,css-loader(如果是 sass, less 那些,还需要特定的 loader),尝试一下
可以看到,入参多了两个 k-v 对,其中一个是我们之前理解的以我们的文件路径为 key 的值,另外一个则是一个 api
看到 css 文件执行内容如下
通过上面可以看到,我们会先通过加载一个 loader ,然后再通过 loader 去获取我们的 css 内容,具体实现可以查看 css-loader/dist/runtime/api.js 的源码,这里就不再展开
要注意的是,这里 css-loader 只是获取到了样式内容,并没有在文档上应用里面的样式,如果需要把样式应用到文档中,可以使用 style-loader,这个 loader 会把样式内容以 style 标签插入到 head 标签末尾,下面是 style-loader 的一些关键代码
webpack 中,loader 的加载顺序是从右到左
所以正确使用 style-loader 和 css-loader 的姿势是
这里会先使用 css-loader 处理,然后将处理完的结果递交给 style-loader 处理,以此类推
优化
webpack 有一些自带的优化项,比如
打包之后,生成的文件如下(我们的代码部分)
在编译阶段 webpack 会自动把一些常量计算结果,以结果的形式输出
DllPlugin 和 DllReferencePlugin
这两个是 webpack 提供的插件,可以用来拆分业务代码,提取一些外部库,使其不会打包进我们的业务代码中,利用 CDN 缓存起来,同时也降低业务代码包的体积,加快构建速度 使用:
需新建一个 webpack 配置文件,专门用于生成抽取出来的独立文件,以及映射表 manifest.json
以我自己个人的项目来说,优化前 app.js 为 153kb,使用 dll 技术后 app.js 仅剩 23kb