Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

webpack-代码分割的原理 #10

Open
tianma630 opened this issue May 3, 2020 · 0 comments
Open

webpack-代码分割的原理 #10

tianma630 opened this issue May 3, 2020 · 0 comments
Labels

Comments

@tianma630
Copy link
Owner

tianma630 commented May 3, 2020

查看官方指南关于代码分割的文档,我们可以知道,实现代码分割有3种方式

  1. 入口起点:使用 entry 配置手动地分离代码。
  2. 防止重复:使用 CommonsChunkPlugin 去重和分离 chunk。
  3. 动态导入:通过模块的内联函数调用来分离代码。

前2种是陪过配置实现,第3种是通过代码动态分析实现,但是最后代码生成的结果是一样的,这里我们就那第3种方式为例。

动态导入也有2种写法,一种是基于ESMA提案的import()方法,另一种是webpack提供的require.ensure方法,这里选择第一种语法,实现一个例子。

index.js

setTimeout(() => {
    import(/* webpackChunkName: "print" */ './print').then((print) => {
        print.log();
    })
}, 3000);

print.js

export function log() {
    console.log('print log');
}

在webpack的配置的ouput选项中需要加上chunkFilename配置,用于生成需要动态导入的文件。

module.exports = {
    entry: {
        app: './src/index.js'
    },
   ...
    output: {
        filename: '[name].bundle.js',
        chunkFilename: '[name].bundle.js',
        path: path.resolve(__dirname, 'dist')
    }
};

执行webpack打包命令,可以看到最终生成了2个文件

  1. app.bundle.js
  2. print.bundle.js

在chrome中运行可以发现,先引入了app.bundle.js,3秒钟后引入了print.bundle.js。下面我们看下这个过程是怎么实现的。

先看下app.bundle.js的代码,删减后的代码

(function(modules) { // webpackBootstrap
	// 动态引入的方法
	__webpack_require__.e = function requireEnsure(chunkId) {
        // 因为动态引入的过程是异步的,所以promise化
		var promises = [];


        var installedChunkData = installedChunks[chunkId];
        // 是否已经引入过, 0表示已经引入
		if(installedChunkData !== 0) { // 0 means "already installed".

			// 是否已经创建了promise
			if(installedChunkData) {
				promises.push(installedChunkData[2]);
			} else {
				// 创建一个promise
				var promise = new Promise(function(resolve, reject) {
					installedChunkData = installedChunks[chunkId] = [resolve, reject];
				});
				promises.push(installedChunkData[2] = promise);

				// 通过动态创建script引入js文件
				var script = document.createElement('script');
				var onScriptComplete;

				script.charset = 'utf-8';
				script.timeout = 120;
				if (__webpack_require__.nc) {
					script.setAttribute("nonce", __webpack_require__.nc);
				}
				script.src = jsonpScriptSrc(chunkId);

				// create error before stack unwound to get useful stacktrace later
                var error = new Error();
                // 回掉函数
				onScriptComplete = function (event) {
					// avoid mem leaks in IE.
					script.onerror = script.onload = null;
					clearTimeout(timeout);
					var chunk = installedChunks[chunkId];
					if(chunk !== 0) {
						if(chunk) {
							var errorType = event && (event.type === 'load' ? 'missing' : event.type);
							var realSrc = event && event.target && event.target.src;
							error.message = 'Loading chunk ' + chunkId + ' failed.\n(' + errorType + ': ' + realSrc + ')';
							error.name = 'ChunkLoadError';
							error.type = errorType;
							error.request = realSrc;
							chunk[1](error);
						}
						installedChunks[chunkId] = undefined;
					}
				};
				var timeout = setTimeout(function(){
					onScriptComplete({ type: 'timeout', target: script });
				}, 120000);
				script.onerror = script.onload = onScriptComplete;
				document.head.appendChild(script);
			}
		}
		return Promise.all(promises);
	};

	// Load entry module and return exports
	return __webpack_require__(__webpack_require__.s = "./src/index.js");
})
/************************************************************************/
({

"./src/index.js":
(function(module, exports, __webpack_require__) {

    eval(```
        setTimeout(() => {
            // 动态引入print文件
            __webpack_require__.e("print")
            // 引入之后执行__webpack_require__,模块化处理(print文件是app文件的一个模块,具体逻辑可以参考之前的文章:https://github.com/tianma630/note-book/issues/9)
            .then(__webpack_require__.bind(null,"./src/print.js"))
            // 执行代码
            .then((print) => {
                print.log();
            })
        }, 3000);
        ```
    )
})
    
});

最后可以总结出,代码分割的执行流程可以分为3个步骤

  1. 动态创建script标签,引入被分割的js文件
  2. 调用模块化方法
  3. 执行业务逻辑代码
@tianma630 tianma630 reopened this May 17, 2020
@tianma630 tianma630 changed the title webpack中的一些重要概念 webpack-代码分割的原理 May 20, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant