hello~亲爱的看官老爷们大家好~ 最近一直在学习 webpack 的相关知识,当清晰地领悟到
就是不同
- webpack
和
- loader
组合起来打包之后,只作为工具使用而言,算是入门了。当然,在过程中碰到数之不尽的坑,也产生了想要深入一点了解
- plugin
的原理(主要是掉进坑能靠自己爬出来)。因而就从简单的入手,先看看使用
- webpack
打包后的
- webpack
文件是如何加载吧。
- JS
友情提示,本文简单易懂,就算没用过
问题都不大。如果已经了解过相关知识的朋友,不妨快速阅读一下,算是温故知新 ,
- webpack
既然需要用到
,还是需要简单配置一下的,这里就简单贴一下代码,首先是
- webpack
:
- webpack.config.js
- const path = require('path');
- const webpack = require('webpack');
- //用于插入html模板
- const HtmlWebpackPlugin = require('html-webpack-plugin');
- //清除输出目录,免得每次手动删除
- const CleanWebpackPlugin = require('clean-webpack-plugin');
- module.exports = {
- entry: {
- index: path.join(__dirname, 'index.js'),
- },
- output: {
- path: path.join(__dirname, '/dist'),
- filename: 'js/[name].[chunkhash:4].js'
- },
- module: {},
- plugins: [
- new CleanWebpackPlugin(['dist']),
- new HtmlWebpackPlugin({
- filename: 'index.html',
- template: 'index.html',
- }),
- //持久化moduleId,主要是为了之后研究加载代码好看一点。
- new webpack.HashedModuleIdsPlugin(),
- new webpack.optimize.CommonsChunkPlugin({
- name: 'manifest',
- })
- ]
- };
这是我能想到近乎最简单的配置,用到的两个额外下载的插件都是十分常用的,也已经在注释中简单说明了。
之后是两个简单的
文件:
- js
- // test.js
- const str = 'test is loaded';
- module.exports = str;
- // index.js
- const test = require('./src/js/test');
- console.log(test);
这个就不解释了,贴一下打包后,项目的目录结构应该是这样的:
至此,我们的配置就完成了。
开始看代码
- index.js
先从打包后的
文件看看两个
- index.html
文件的加载顺序:
- JS
- <body>
- <script type="text/javascript" src="js/manifest.2730.js"></script>
- <script type="text/javascript" src="js/index.5f4f.js"></script>
- </body>
可以看到,打包后
文件的加载顺序是先
- js
,之后才是
- manifest.js
,按理说应该先看
- index.js
的内容的。然而这里先卖个关子,我们先看看
- manifest.js
的内容是什么,这样可以带着问题去了解
- index.js
,也就是主流程的逻辑到底是怎样的,为何能做到模块化。
- manifest.js
- // index.js
- webpackJsonp([0], {
- "JkW7": (function(module, exports, __webpack_require__) {
- const test = __webpack_require__("zFrx");
- console.log(test);
- }),
- "zFrx": (function(module, exports) {
- const str = 'test is loaded';
- module.exports = str;
- })
- },
- ["JkW7"]);
删去各种奇怪的注释后剩下这么点内容,首先应该关注到的是
这个函数,可以看见是不在任何命名空间下的,也就是
- webpackJsonp
应该定义了一个挂在
- manifest.js
下的全局函数,
- window
往这个函数传入三个参数并调用。
- index.js
第一个参数是数组,现在暂时还不清楚这个数组有什么作用。
第二个参数是一个对象,对象内都是方法,这些方法看起来至少接受两个参数(名为
的方法只有两个形参)。看一眼这两个方法的内部,其实看见了十分熟悉的东西,
- zFrx
,尽管看不见
- module.exports
, 但有一个样子类似的
- require
,这两个应该是模块化的关键,先记下这两个函数。
- __webpack_require__
第三个参数也是一个数组,也不清楚是有何作用的,但我们观察到它的值是
,与参数2中的某个方法的键是一致的,这可能存在某种逻辑关联。
- JkW7
至此,
的内容算是过了一遍,接下来应当带着问题在
- index.js
中寻找答案。
- manifest.js
代码阅读
- manifest.js
由于没有配置任何压缩
的选项,因此
- js
的源码大约在 150 行左右,简化后为 28 行(已经跑过代码,实测没问题)。鉴于精简后的代码真的不多,因而先贴代码,大家带着刚才提出的问题,先看看能找到几个答案:
- manifest.js
- (function(modules) {
- window["webpackJsonp"] = function webpackJsonpCallback(chunkIds, moreModules, executeModules) {
- var moduleId, result;
- for (moduleId in moreModules) {
- if (Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
- modules[moduleId] = moreModules[moduleId];
- }
- }
- if (executeModules) {
- for (i = 0; i < executeModules.length; i++) {
- result = __webpack_require__(executeModules[i]);
- }
- }
- return result;
- };
- var installedModules = {};
- function __webpack_require__(moduleId) {
- if (installedModules[moduleId]) {
- return installedModules[moduleId].exports;
- }
- var module = installedModules[moduleId] = {
- exports: {}
- };
- modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
- return module.exports;
- }
- })([]);
首先应该看到的是,
内部是一个
- manifest.js
,就是自执行函数咯,这个函数会接受一个空数组作为参数,该数组被命名为
- IIFE
。之后看到我们在
- modules
中的猜想,果然在
- index.js
上挂了一个名为
- window
的函数。它接受的三个参数,分别名为
- webpackJsonp
,
- chunkIds
,
- moreModules
。对应了
- executeModules
中调用
- index.js
时传入的三个参数。而
- webpackJsonp
内究竟是有怎样的逻辑呢?
- webpackJsonp
先不管定义的参数,
先是
- webpackJsonp
遍历了一次
- for in
,将
- moreModules
内的所有方法都存在
- moreModules
, 也就是自执行函数执行时传入的数组。
- modules
之后是一个条件判断:
- if (executeModules) {
- for (i = 0; i < executeModules.length; i++) {
- result = __webpack_require__(executeModules[i]);
- }
- }
判断
, 也就是第三个参数是否存在,如存在即执行
- executeModules
方法。在
- __webpack_require__
调用
- index.js
方法时,这个参数当然是存在的,因而要看看
- webpackJsonp
方法是什么了。
- __webpack_require__
接受一个名为
- __webpack_require__
的参数。方法内部首先是一个条件判断,先不管。接下来看到赋值逻辑
- moduleId
- var module = installedModules[moduleId] = {
- exports: {}
- };
结合刚才的条件判断,可以推测出
是一个缓存的容器,那么前面的代码意思就是如果缓存中有对应的
- installedModules
,那么直接返回它的
- moduleId
,不然就定义并赋值一个吧。接着先偷看一下
- exports
的最后的返回值,可以看到函数返回的是
- __webpack_require__
,那么
- module.exports
又是如何被赋值的呢? 看看之后的代码:
- module.exports
- modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
刚才我们知道
就是
- modules[moduleId]
中的方法,此处就是将
- moreModules
指定为
- this
,再把
- module.exports
,
- module
,
- module.exports
传入去作为参数调用。这三个参数是不是很熟悉?之前我们看
- __webpack_require__
里面代码时,有一个疑问就是模块化是如何实现的。这里我们已经看出了眉目。
- index.js
其实
就是将每一个
- webpack
文件封装成一个函数,每个文件中的
- js
方法对应的就是
- require
,
- __webpack_require__
会根据传入的
- __webpack_require__
再去加载对应的代码。而当我们想导出
- moduleId
文件的值时,要么用
- js
,要么用
- module.exports
,这就对应了
- exports
,
- module
两个参数。少接触这块的童鞋,应该就能理解为何导出值时,直接使用
- module.exports
会导出失败了。简单举个例子:
- exports = xxx
- const module = {
- exports: {}
- };
- function demo1(module) {
- module.exports = 1;
- }
- demo1(module);
- console.log(module.exports); // 1
- function demo2(exports) {
- exports = 2;
- }
- demo2(module.exports);
- console.log(module.exports); // 1
粘贴这段代码去浏览器跑一下,可以发现两次打印出来都是1。这和
打包逻辑是一模一样的。
- wenpack
梳理一下打包后代码执行的流程,首先
会定义一个
- minifest.js
方法,待其他打包后的文件(也可称为
- webpackJsonp
)调用。当调用
- chunk
时,会先将该
- chunk
中所有的
- chunk
, 也就是每一个依赖的文件也可称为
- moreModules
(如
- module
)存起来。之后通过
- test.js
判断这个文件是不是入口文件,决定是否执行第一次
- executeModules
。而
- __webpack_require__
的作用,就是根据这个
- __webpack_require__
所
- module
的东西,不断递归调用
- require
,
- __webpack_require__
函数返回值后供
- __webpack_require__
使用。当然,模块是不会重复加载的,因为
- require
记录着
- installedModules
调用后的
- module
的值,只要命中缓存,就返回对应的值而不会再次调用
- exports
。
- module
打包后的文件,就是通过一个个函数隔离
- webpack
的作用域,以达到不互相污染的目的。
- module
以上就是
打包后
- webpack
文件是加载过程的简单描述,其实还是有很多细节没有说完的,比如如何异步加载对应模块,
- js
有什么作用等,但由于篇幅所限
- chunkId
。
- star
感谢各位看官大人看到这里,知易行难,希望本文对你有所帮助~谢谢!
来源: https://juejin.im/post/5a23b130f265da432003101a