Preface
require.context It's actually a very useful api.But 3-4 years later, many people still don't know how to use it.
And what does this api mainly do for us?It helps us dynamically load the files we want, and is very flexible and powerful (recursive directories).Can do things import can't do.Today I'll take you all to analyze how webpack requires.context is implemented.
Dead work
Before analyzing this api, we need to understand the simplest file that the webpack will compile into.
-- src -- index.ts
// index.ts console.log(123)
After compiling, we can see that the webpack compiles into the following code
// Source https://github.com/MeCKodo/require-context-sourece/blob/master/simple-dist/bundle-only-index.js (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; } // expose the modules object (__webpack_modules__) __webpack_require__.m = modules; // expose the module cache __webpack_require__.c = installedModules; // define getter function for harmony exports __webpack_require__.d = function(exports, name, getter) { if(!__webpack_require__.o(exports, name)) { Object.defineProperty(exports, name, { configurable: false, enumerable: true, get: getter }); } }; // define __esModule on exports __webpack_require__.r = function(exports) { Object.defineProperty(exports, '__esModule', { value: true }); }; // getDefaultExport function for compatibility with non-harmony modules __webpack_require__.n = function(module) { var getter = module && module.__esModule ? function getDefault() { return module['default']; } : function getModuleExports() { return module; }; __webpack_require__.d(getter, 'a', getter); return getter; }; // Object.prototype.hasOwnProperty.call __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; // __webpack_public_path__ __webpack_require__.p = ""; // Load entry module and return exports return __webpack_require__(__webpack_require__.s = "./src/index.ts"); }) ({ "./src/index.ts": (function(module, exports) { console.log('123'); }) });
At first glance it's messy, so in order to sort out the structure, I'll help you get rid of something that doesn't matter to this article.In fact, the main structure is just like this, the code is not much for later understanding, we must carefully look at each line
// Source address https://github.com/MeCKodo/require-context-sourece/blob/master/simple-dist/webpack-main.js (function(modules) { // Cache all loaded modules (files) var installedModules = {}; // Module (File) Loader moduleId is usually the file path function __webpack_require__(moduleId) { // Walk cache if (installedModules[moduleId]) { return installedModules[moduleId].exports; } // Create a new module (and put it into the cache) explained better than I did var module = (installedModules[moduleId] = { i: moduleId, l: false, exports: {} }); // Executing our module (file) is now. /src/index.ts and passes in three parameters modules[moduleId].call( module.exports, module, module.exports, __webpack_require__ ); // Flag the module as loaded explained better than I did module.l = true; // Return the exports of the module explained better than I did return module.exports; } // Start loading entry file return __webpack_require__((__webpack_require__.s = './src/index.ts')); })({ './src/index.ts': function(module, exports, __webpack_require__) { console.log('123'); } });
__webpack_require__
Is a module loader, and all of our modules are read and loaded as objectsmodules = { './src/index.ts': function(module, exports, __webpack_require__) { console.log('123'); } }
We temporarily refer to such a structure as a module structure object
Positive
Once you understand the body structure, you can write a require.context to see the effect.Let's first add two new TS files and modify our index.ts to test our dynamic loading.
--- src --- demos --- demo1.ts --- demo2.ts index.ts
// index.ts // Later we'll look at why this was written through source analysis function importAll(contextLoader: __WebpackModuleApi.RequireContext) { contextLoader.keys().forEach(id => console.log(contextLoader(id))); } const contextLoader = require.context('./demos', true, /\.ts/); importAll(contextLoader);
Looking at our compiled source code, we found that there are more than one module structure object
// Compiled code address https://github.com/MeCKodo/require-context-sourece/blob/master/simple-dist/contex-sync.js#L82-L113 { './src/demos sync recursive \\.ts': function(module, exports, __webpack_require__) { var map = { './demo1.ts': './src/demos/demo1.ts', './demo2.ts': './src/demos/demo2.ts' }; // context loader, which loads modules (files) from previous module loaders function webpackContext(req) { var id = webpackContextResolve(req); var module = __webpack_require__(id); return module; } // Find the true path of a module (file) by moduleId // Individuals don't like some variable naming inside a webpack. moduleId compiles it as request function webpackContextResolve(req) { // id is the real file path var id = map[req]; // To tell you the truth, this wave of operations did not understand, the current guess is that the webpack will compile into a file like 0.js 1.js and if it can't find a misload, send an error if (!(id + 1)) { // check for number or string var e = new Error('Cannot find module "' + req + '".'); e.code = 'MODULE_NOT_FOUND'; throw e; } return id; } // Traverse to get all moduleId s webpackContext.keys = function webpackContextKeys() { return Object.keys(map); }; // Get File True Path Method webpackContext.resolve = webpackContextResolve; // The module returns a context loader module.exports = webpackContext; // moduleId for the module loader webpackContext.id = './src/demos sync recursive \\.ts'; }
I wrote detailed comments in the source code.After reading this code, it's easy to understand that the document says require.context returns a function with three API s (webpackContext).
Next let's look at the source code for compiled index.ts
'./src/index.ts': function(module, exports, __webpack_require__) { function importAll(contextLoader) { contextLoader.keys().forEach(function(id) { // Get all moduleId s and load each module through the context loader return console.log(contextLoader(id)); }); } var contextLoader = __webpack_require__( './src/demos sync recursive \\.ts' ); importAll(contextLoader); }
Simply, you can find that require.context is compiled as a u webpack_require_u loader and loads modules with id. /src/demos sync recursive \.ts, sync indicates that we are loading these modules synchronously (we will introduce this parameter later), and recursive indicates that recursive directory lookup is required.From then on, we can fully understand how the webpack builds all the modules and loads them dynamically.
Advanced Deep Exploration of webpack Sources
We know that after webpack version 2.6, when loading modules, you can specify the webpackMode l module loading mode, and there are several ways we can control which modules we want to load.The common model is sync lazy lazy-once eager
So the same applies to require.context, so if you look at @types/webpack-env, it's not difficult to see that it has a fourth parameter.
Briefly speaking
- sync packages directly into the current file, loads and executes synchronously
- lazy delayed loading separates individual chunk files
- lazy-once delayed loading separates individual chunk files and loads code directly in memory the next time.
- eager does not detach a separate chunk file, but returns promise, which executes code only when promise is called, which can be understood as loading code first, but we can control the delay in executing that part of the code.
The document is here https://webpack.docschina.org....
This part of the documentation is obscure, and the documentation group may not keep up with it, so if we look at the source of the webpack, we can see that there are actually six mode s.
Model type definition
https://github.com/webpack/we...
So how does a webpack actually achieve recursive access to our files?We can find such a line of code in the source address just above.
It's about finding the modules we need.So we follow this line to find specific sources.
This is how require.context loads into our file.It's just fs.readdir.Our module structure object is generated through the context loader after the last file is obtained.Such code is responsible for generating our sync-type context loader.You can see the other five types in detail.
Six types of loading logic and generating module structure objects for context loaders
https://github.com/webpack/we...
summary
1. Learn how the webpack organizes loading a module, how the webpack loader works, and finally how to generate compiled code.
2. Just to find out how require.context works, we found that its third parameter has six mode s, which are not available in the webpack documentation.
3. Starting from a practical API, we explored the implementation principle of the api, and read some webpack source code together.
4. Exploring the nature is much more important than being a porter of the API.You can discover the secrets of the world only if you keep searching for the essence.
Finally, follow this line of thought to learn the code compiled by another six mode ls.
The compiled code in this article is here >>> https://github.com/MeCKodo/re...
Personal Site >>> http://www.meckodo.com
Last perennial recruitment
Xiamen RingCenter Foreign Enterprises, the Top Benefit in Xiamen
5:30 Off Work 5.30 Off Work 5.30 Off Work
Contact me if necessary~