- 2021/01/09 update
- 2021/07/27 update
navigation
[Deep 01] Execution context [Deep 02] Prototype chain [Deep 03] Inheritance [Deep 04] Event loop [Deep 05] Curri Bias function [Deep 06] Function memory [Deep 07] Implicit conversions and operators [Deep 07] Browser caching mechanism (HTTP caching mechanism) [Deep 08] Front-end security [Deep 09] Deep copy [Deep 10] Debounce Throttle [Deep 10] Front-end routing [Deep 12] Front-end modularization [Deep 13] Observer mode Publish subscribe mode Bidirectional data binding [Deep 14] Canvas [Deep 15] webSocket Webpack HTTP and HTTPS CSS- Interview Handwriting Promise
[react] Hooks
[Deployment 01] Nginx [Deployment 02] Docker deployVue project [Deployment 03] gitlab-CI
[source code – Webpack01 – precompiler] AST abstract syntax tree [source code – Webpack02 – Precompiler] Tapable [source code – Webpack03] hand written webpack-compiler simple compilation process [source code] Redux React-redux01 [source] Axios [source] vuex [source -vue01] Data reactive and initialize render [source -vue02] Computed responsive – Initialize, access, Update Procedure [source -vue04] Watch Listening properties – Initialize and update [source -vue04] vue. set and vm.$set [source -vue05] vue.extend
Vue. NextTick and VM.$nextTick
Front knowledge
Some words
Compile the compiler:Copy the code
npm link
- (1) the need to link first BaoZaiGen directory: — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —
npm link
- NPM Link allows you to link packages globally
- The package needs to have
bin/wpack.js
- Set it in package.json
Bin: {wpack: 'path '}
- (2) In the root directory of the project where you want to use the package, run the command ——————-
npm link wpack
- The Wpack package is installed into node_modules
- (3) the validation
- In projects that use the WPack package, run the command: ————————-
npx wpack
- In projects that use the WPack package, run the command: ————————-
Process.cwd () ———————— Current working directory
- Process.cwd () returns the current working directory of the Node.js process
process.cwd() === path.resolve()
- process.cwd()
Fs. readFileSync(path[, options]) —– Read files
- Return the contents of path
- Parameters:
- Path: File name or file descriptor
- Options: configuration items, object | string
- Encoding: The encoding format is optional
Path. Relative (the from and to) — — — — — — — — — — — — — — — the from to the to line the path
- The path.relative() method returns the relative path from (to) to the current working directory.
Path.dirname (path) —————— Parent directory of the last section
- The path.dirname() method returns the directory name of path
- Return to the folder where the last file or folder in the path is located, i.e. the parent directory of the last file or folder.
Path.extname (path) —————— Returns the extension of path
- Path.extname (PATH) Returns the extension of path
- Ext is extend
The arguments. The callee — — — — — — — — — — — — — — — — — — — — – to the currently executing function (mode strictly prohibited)
- The arguments. The callee — — — — — — — — — — — — — — — — — — — — – to the currently executing function (mode strictly prohibited)
AST explorer
Source: the require ('. / a. s) AST: {" type ":" the Program ", "start" : 0, "end" : 17, "body" : {/ / -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- the body array may contain more than one statement state object "type" : "ExpressionStatement", "start" : 0, "end" : 17, "expression" : {" type ":" CallExpression ", / / -- -- -- -- -- -- -- -- -- -- - call expression "start" : 0, "end" : 17, "the callee" : { // ------------------------- callee.name = 'require' "type": "Identifier", "start": 0, "end": 7, "name": "Require"}, "the arguments" : / / / -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- parameter list {" type ":" Literal ", "start" : 8, "the end" : 16, "value" : "./a.js", "raw": "'./a.js'" } ] } } ], "sourceType": "module" }Copy the code
Babel related AST plug-in
- @babel/core
- The core file
- @babel/parser
- Turn the source string into an AST
- @babe/traverse
- Traverse the AST
Enter (path) to enter
和The exit (path)
Such as hook
- @babel/types
- Modify, add, and delete the AST
- The Class Lodash library for AST, which encapsulates a large number of AST-related methods, greatly reduces the cost of transforming aN AST
babelTypes.stringLiteral(modulePath)
- @bebe/generator
- Convert the modified AST into the source string
const options = loaderUtils.getOptions(this)
Loader – Write a custom Loader
-
(loader) is a (function) whose first argument represents (the source code of the file that the loader matches)
-
Loader cannot be written as (arrow function) because more apis need to be obtained through this
-
loader-utils
- The (options) object used to get the loader in Module -> rules
- The (options) object is obtained through (getOptions) in (loader-utils)
- Installation:
npm install loader-utils -D
- Use:
const options = loaderUtils.getOptions(this)
- loader-utils
-
this.callback
- The first argument: err // Error or null
- The second parameter: content // String or buffer, which is the processed source code
- The third argument: sourceMap? // Optional, it must be a source map that can be parsed by this module
- Fourth parameter: Meta? // Optionally, metadata
- This. Callback – WebPack official website document
-
this.async
- This. async is mainly used to handle asynchronous operations in the Loader
- The return value is: this.callback()
-
How to introduce the loader in webpack.config.js?
- Create a new “loaders” folder in the root directory to store “replace-loader.js”
- A single loader
module.exports = { ... module: { rules: [{ test: /\.js$/, use: [{ loader: Path. Resolve (__dirname, 'loaders/replace-loader'), options: {name: 'aaaaa'}}]}]}}Copy the code
- Multiple loader
module.exports = { ... {// resolveLoader modules: ['node_modules', path.resolve(__dirname, 'loaders')] // Tell webPack where to look for loader modules // Find node_modules first, // modules: ['node_modules', './loaders/']}, module: {rules: [{test: /\.js$/, use: [{loader: 'upper-loader', options: { name: 'aaaaa' } },{ loader: 'replace-loader', options: { name: 'hi!!!!???&&&&'} // Load the replace-loader.js file in the loaders folder, just write the loader name}]}]}}Copy the code
-
User-defined Loader instance
- loaders/replace-loader.js
LoaderUtils = require('loader-utils') const loaderUtils = require('loader-utils') // Loader-utils plugin Module.exports = function(source) {// source is the source of the file matched by the loader const options = loaderUtils. GetOptions (this) // through Const callback = this.async() // this.async() is used to handle asynchronous operations in the loader, -------- returns: this.callback() // this.callback(err, content, sourceMap? , meta?) setTimeout(function() { const result = source.replace('hello', options.name) callback(null, result) }, 1000) }Copy the code
- webpack.config.js
const path = require('path') module.exports = { mode: 'development', entry: { index: './src/index.js' }, output: { path: path.resolve(__dirname, 'dist'), filename: 'index.js' }, module: { rules: [ // { // test: /\.js$/, // use: [{ // loader: path.resolve(__dirname, 'loaders/replace-loader.js'), // options: { // name: 'woow_wu7' // } // }] // } { test: /\.js$/, use: [{ loader: Options: {name: 'replace-loader', // where the name is the file name of the replace-loader.js file in the loaders folder options: {name: 'woow_wu77' } }] } ] }, resolveLoader: {// specify where to load the loader in the node_modules folder, and in the './loaders/' folder // look for node_modules first and then './loaders/' modules: ['node_modules', './loaders/'] } }Copy the code
Compiler – The lifecycle hook function
- entryOption
- After the entry configuration item in the WebPack options is processed, the plug-in is executed
- afterPlugins
- After setting up the initial plug-in, execute the plug-in
- run
- The compiler.run() method is triggered during execution – before starting to read records, hook into the compiler
- compile
- BuildMoudle () triggers before execution – triggers after a new compilation is created
- afterCompile
- BuildMoudle () is fired after execution
- emit
- EmitFile () is triggered when executed – generates resources before they are put into the output directory.
- done
- Triggered when compilation is complete
Plugin – Write a custom Plugin
-
The plugin is a class with (apply) methods, the apply method parameters are (compiler) calls, and the Compiler object is accessible throughout the compilation lifecycle
-
Process:
- (1) Install tapable in the project where the Compiler class resides
- (2) Write the Plugin class
- There must be an apply() method
- In the method, call the tap() registration method of the lifecycle hook corresponding to the hooks attribute of the compiler instance
- (3) New register the plugins in webpack.config.js
- You can loop over the plugins in the constructor of the Compiler class and execute the apply method
-
In plugin, tap() registers the listener, because it’s SyncHook, tap() registers, tapAsync(), tapPromise(), etc
class EntryOptionPlugin {
apply(compiler) {
compiler.hooks.entryOption.tap('EntryOptionPlugin', function() {
console.log('EntryOptionPlugin')
})
}
}
class AfterPlugin {
apply(compiler) {
compiler.hooks.afterPlugins.tap('AfterPlugin', function() {
console.log('AfterPlugin')
})
}
}
class RunPlugin {
apply(compiler) {
compiler.hooks.run.tap('RunPlugin', function() {
console.log('RunPlugin')
})
}
}
class CompilePlugin {
apply(compiler) {
compiler.hooks.compile.tap('CompilePlugin', function() {
console.log('CompilePlugin')
})
}
}
class AfterCompilePlugin {
apply(compiler) {
compiler.hooks.afterCompile.tap('AfterCompilePlugin', function() {
console.log('AfterCompilePlugin')
})
}
}
class EmitPlugin {
apply(compiler) {
compiler.hooks.emit.tap('emit', function() {
console.log('emit')
})
}
}
class DonePlugin {
apply(compiler) {
compiler.hooks.done.tap('DonePlugin', function() {
console.log('DonePlugin')
})
}
}
Copy the code
- Register the plug-in in webpack.config.js
plugins: [
new EntryOptionPlugin(),
new AfterPlugin(),
new RunPlugin(),
new CompilePlugin(),
new AfterCompilePlugin(),
new EmitPlugin(),
new DonePlugin()
]
Copy the code
- Introduce tapable into the Compiler class and new the different life cycles
- Perform the call() method in tapbale at different times when different functions are executing. In this case, it’s SyncHook so register with tap() and call with call()
class Compiler { constructor(config) { this.hooks = { entryOption: new SyncHook(), afterPlugins: new SyncHook(), run: new SyncHook(), compile: new SyncHook(), afterCompile: new SyncHook(), emit: new SyncHook(), done: New SyncHook(),}}} run() {// the run method does two things. // 2. Launch the package file this.hook.run.call () this.hooks.com compile.call() this.buildModule(path.resolve(this.root,) This.entry), true) // buildModule() is used to create dependencies for the module // arguments: // First argument: is the absolute path of the path specified by the entry // Second argument: Whether the main module enclosing hooks. AfterCompile. Call () the console. The log (enclosing modules, enclosing entryId) / / launch a file, This.hooks.dose.call () this.hooks.dose.call ()} this.hooks.dose.call ()}Copy the code
Analysis of webPack files
- To simplify the code, remove the exigent properties on webpack_require as follows
- Let’s simplify a little bit more
(function(modules){ var initialMoudles = {} function __webpack_require__(moduleId) return __webpack_require__('./ SRC /index.js')})() after execution is equivalent to calling __webpack_require__('./ SRC /index.js') and initialMoudles becomes the closure variable, Permanent memoryCopy the code
- Parameter object modules
{
"./src/a.js": function () { eval("") },
"./src/base/b.js": function () { eval("") },
"./src/base/c.js": function () { eval("") },
"./src/index.js": function () { eval("") },
}
Copy the code
- The first step:
- call
__webpack_require__('./src/index.js')
- perform
modules[moduleId].call()
Executes the eval() source in the modules parameter object ‘./ SRC /index’
- call
- The second step
- call
__webpack_require__('./src/a.js')
- perform
modules[moduleId].call()
Executes the eval() source in the modules parameter object ‘./ SRC /a.js’
- call
- The third step
- call
__webpack_require__('./src/b.js')
- perform
modules[moduleId].call()
Executes the eval() source in the modules parameter object ‘./ SRC /b.js’
- call
- The fourth step
- call
__webpack_require__('./src/c.js')
- perform
modules[moduleId].call()
That is, execute the eval() source in the modules parameter object ‘./ SRC /c.js’
- call
- Until all source code corresponding to moudleids in modules has been executed
Handwritten Webpack-compiler
process
- BuildModul () – the assignment process of modules objects
- (1) Pass webpack.config.js as a parameter to the Compiler class
- (2) Call the Compiler with the new command, generate the Compiler instance, and call the run method on compiler.prototype
- When the new command is executed, iterate over the apply() method on the plugin instance in the plugins array in webpack.config.js
- The tap => apply() method is called
Compiler.hooks. Hook function. tap()
Registering listener Events - Call => Call () executes events in different compiler functions to implement listening in different lifecycles
- (3) Call buildModule() and emitFile() in the run method
- (4) The buildModule() method takes webpack.config.js (the absolute path to the entry file) and (whether it is the main module) as parameters
- (5) Call getSource(‘absolutePath’) method in buildModule()
- Parameter is the absolute path of the module
- Read the source code through fs.readFilesync (path, options)
- Loop module->rules array ->test in webpack.config.js, and use the regular match between Test and absolutePath. If the match is successful, then recurse to the loader function to parse the file and return the file. Until the array member loader in moudle->rules->use is called
- (6) Call parse() in buildModule() to parse the source, modify the source, and return the source
- (7) the parse () method
- There are two parameters: the source code of the module and the path to the folder where the module file is located – that is, the folder where the file is located
- Return two values: the modified source code for the module and the array of dependencies for that module
- Note: Modify part (replace require name, key in moudules in ‘./ SRC/XXXXX ‘format, match loader and process source files)
- (8) The relative path of the module and the modified source code of the module correspond to each other as the key and value of the modules object
- (9) If parse() returns a non-empty array of dependencies for that module, iterate through that module’s array and recursively call the buildModule method until the last module has no dependencies
- EmitFile () – the process of transmitting the source code to the directory specified by webpack.config.js
- (1) Install ejS template engine and write template with entryId and modules
- (2) Obtain the path, filename of the output object in webpack.config.js
- (3) fs.readFilesync () Reads the EJS template source file
- (4) generate an executable file for esj.render()
- (5) fs.writeFileSync(file, data[, options]) writes the generated source file compiled by ESJ to outp.path with the name outpt.name
wpack.js #! /usr/bin/env node // one. Const path = require('path') const config = require(path.resolve('webpack.config.js')) // Webpack.config. js const Compiler = require('.. /lib/compiler.js') const compiler = new compiler (config) compiler.run()Copy the code
The compiler-run () method
-
The (run) method mainly does (two) things
- (1) call buildModul () – > modules = {} — — — — — — — — — — — — — dependencies key and vlue of collection of objects
- Key: the relative path of all modules
- Value: source code for all modules
- (2) Call the emitFile method -> send the packaged file to the specified folder
- (1) call buildModul () – > modules = {} — — — — — — — — — — — — — dependencies key and vlue of collection of objects
-
The specific process
- Call the (buildModule) method in run
- The (emitFile) method is called in run
-
buildMoudle(moduleAbsolutePath, isEntryModule)
-
BuildMoudle () parameters
- ModuleAbsolutePath: The absolute path of each module
- IsEntryModule: A Boolean value that indicates whether the entry module is the primary module. The entry module is usually index.js
-
Buildmoudle does a few things:
- through
fs.readFileSync(modulePath, { encoding: 'utf8' })
Read the source code corresponding to the passed module path- Note: This must be in UTF8 format, otherwise @babel/parse will report an error
- If so, mark the path of the main entry module with (this.entryid) (the path needs to be processed into the desired format).
- callparse()methods
- Pass :(unmodified source code) and where the entry file is (folder)
- Returns :(modified source code) and an array of dependencies for the current module, i.e., (files for the current module require)
- Modify the source code
- Convert source code to AST via @babel/ Parser
- Traverse the AST via @babel/traverse and modify, add, delete, and so on via @babel/types as you traverse
- Modify, add, and remove nodes of the AST using @babel/types
- The modified AST is converted to a source string using @babel/ Generator
- Modify the source code
- If (the current module still has dependencies), that is, the returned array of dependencies for the current module is not empty, then (recursively execute buildMoudle())
- Finally collect all the modules corresponding to the modules object
this.modules[moduleRelativePath] = sourceCode
- through
BuildModule (moduleAbsolutePath, isEntry) {// parameter // moduleAbsolutePath: Is the absolute path of the module, obtained through path.resolve(this.root, this.entry) // isEntry: Const Source = this.getSource(moduleAbsolutePath) // Reads the contents of the module's source file const moduleRelativePath = './' + path.relative(this.root, moduleAbsolutePath) // path.relative(from, to) // path.relative(from, // moduleRelativePath // indicates the relative path of the module file // moduleRelativePath = moduleAbsolutePath - Log (source, moduleRelativePath) if (isEntry) {this.entryId = moduleRelativePath // / SRC /index.js = entryId} const fatherPath = path.dirName (moduleRelativePath) // / SRC const {sourceCode, dependencies} = this.parse(source, fatherPath).replace(/\\/g, '/'); // Parse () main function // 1. Transform the source of the import file // 2. This.modules [moduleRelativePath] = sourceCode; this.modulerelativePath = sourceCode; this.modulerelativePath = sourceCode; // moduleRelativePath => moduleRelativePath // value => sourceCode dependencies. ForEach (dep => { This.buildmodule (path.join(this.root, dep), false)})Copy the code
The compiler-run () -buildMoudle () -getSource () method – adds the source code to be parsed by loader before being converted to parse()
- less-loader
const less = require('less')
const lessLoader = function(source) {
const that = this;
let res;
less.render(source, function(err, content) {
res = content.css.replace(/\n/g, '\\n').replace(/\r/g, '\\r')
// res = that.callback(null, content.css.replace(/\n/g, '\\n'))
})
return res;
}
Copy the code
- style-loader
const styleLoader = function(source) {
const style = `
const styleElement = document.createElement('style');
styleElement.innerHTML = ${JSON.stringify(source)};
document.head.appendChild(styleElement);
`
return style
}
module.exports = styleLoader
Copy the code
- The code for adding the Loader part to the getSource() method
Let content = fs.readFilesync (modulePath, {encoding: 'utf8' }) // { encoding: Const {rules} = this.config.module // Obtain the rule array for(let I = 0; i < rules.length; // Loop rules array const {test, use} = rules[I] // loop rules array const {test, use} = rules[I] // use is also an array, from back to front, If (test.test(modulePath)) {function runLoader() {const Loader = require(use[reverseIndex--]); // require('absolute path') import loader function content = Loader (content) // Execute loader function, Return the modified content of loader if (reverseIndex >= 0) {// runLoader()}} runLoader()}} // content // Fs. readFileSync(modulePath, {encoding: 'utf8'}) reads the source code of the module and returns the source code in UTF8 formatCopy the code
The compiler-run () -buildMoudle () -parse () method
parse(source, ParentPath (parentPath) {const dependencies = [] // parse.parse (source) // Traverse babelTraverse(AST, {CallExpression(p)} {// call the expression. And the node path conflict / / modify / / mainly do two things / / 1. The require () = > __webpack_require__ () / / 2. The require ('. / a. s') = > the require (". / SRC/a. s) Const node = p.ode if (node.callee.name === 'require') { Callee. name = '__webpack_require__' // Replace require's name let modulePath = node.arguments[0].value; modulePath = "./" + path.join(parentPath, modulePath).replace(/\\/g, '/') + (path.extname(modulePath) ? '' : '.js'); // If the suffix exists, add an empty string. // If the suffix does not exist, add. ModulePath = './' + '/ SRC '+ 'index' + '.js' // Obtain dependencies. Push (modulePath) // convert node.arguments = [babeltypes.stringLiteral (modulePath)] // Change Literal from ARGUMtns in AST => Change Literal to the latest modulePath}}}) // Generate const sourceCode = babelGenerator(AST).code; Return {sourceCode, dependencies}}Copy the code
Compiler – run() – emitFile()
EmitFile () {// launch the file console.log(111111111) const {path: p, filename} = this.config.output const main = path.join(p, Const templeteSourceStr = this.getSource(path.join(__dirName, Main.ejs const code = ejs.render(templeteSourceStr, {entryId: this.entryId, modules: This.modules}) // Render template // Template has two arguments: entryId and modules this.assets = {} this.assets[main] = code; // value: WriteFileSync (main, this.assets[main]) // Fs.writefilesync (file, data[, options])}Copy the code
------
main.ejs
(function (modules) {
var installedModules = {};
function __webpack_require__(moduleId) {
if (installedModules[moduleId]) {
return installedModules[moduleId].exports;
}
var module = installedModules[moduleId] = {
i: moduleId,
l: false,
exports: {}
};
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
module.l = true;
return module.exports;
}
return __webpack_require__(__webpack_require__.s = "<%-entryId%>");
})
({
<%for(let key in modules){%>
"<%-key%>":
(function (module, exports, __webpack_require__) {
eval(`<%-modules[key]%>`)
}),
<%}%>
});
Copy the code
The Compiler always file
const fs = require('fs') const path = require('path') const babelParser = require('@babel/parser') const babelTypes = require('@babel/types') const babelTraverse = require('@babel/traverse').default const babelGenerator = require('@babel/generator').default const ejs = require('ejs') const {SyncHook} = require('tapable') class Compiler { Constructor (config) {this.config = config // webapck.config.js Webpack configuration file module this.entryid = null // relative path of the entry file this.modules = {} // Used to store all module information // key: relative path of the module // value: This. Entry = config.entry.index; This.root = process.cwd(); This.hooks = {entryOption: new SyncHook(), afterPlugins: new SyncHook(), run: new SyncHook(), compile: new SyncHook(), afterCompile: new SyncHook(), emit: new SyncHook(), done: new SyncHook(), } // plugins get const plugins = this.config.plugins if (array.isarray (plugins)) {plugins.foreach (plugin => { Plugin. Apply (this) / /})} this. This is a compiler instance hooks. AfterPlugins. Call ()} getSource (modulePath) {let the content = Fs. readFileSync(modulePath, {encoding: 'utf8'}) // Remember to utF8 format const {rules} = this.config.module for(let I = 0; i < rules.length; i++) { const {test, use} = rules[i] let reverseIndex = use.length - 1; if (test.test(modulePath)) { function runLoader() { const loader = require(use[reverseIndex--]) content = loader(content) console.log(content, '6666666666'); if (reverseIndex >= 0) { runLoader() } } runLoader() } } // content // fs.readFileSync(modulePath, {encoding: 'utf8'}) read the module source, return to utF8 format source code Return content} Parse (source, ParentPath (parentPath) {const dependencies = [] // parse.parse (source) // Traverse babelTraverse(AST, {CallExpression(p)} {// call the expression. And the node path conflict / / modify / / mainly do two things / / 1. The require () = > __webpack_require__ () / / 2. The require ('. / a. s') = > the require (". / SRC/a. s) Const node = p.ode if (node.callee.name === 'require') { Callee. name = '__webpack_require__' // Replace require's name let modulePath = node.arguments[0].value; modulePath = "./" + path.join(parentPath, modulePath).replace(/\\/g, '/') + (path.extname(modulePath) ? '' : '.js'); // If the suffix exists, add an empty string. // If the suffix does not exist, add. ModulePath = './' + '/ SRC '+ 'index' + '.js' // Obtain dependencies. Push (modulePath) // convert node.arguments = [babeltypes.stringLiteral (modulePath)] // Change Literal from ARGUMtns in AST => Change Literal to the latest modulePath}}}) // Generate const sourceCode = babelGenerator(AST).code; // Return {sourceCode, dependencies}} buildModule(moduleAbsolutePath, isEntry) {// parameter // moduleAbsolutePath: Is the absolute path of the module, obtained through path.resolve(this.root, this.entry) // isEntry: Const Source = this.getSource(moduleAbsolutePath) // Read the contents of the module's source file let moduleRelativePath = './' + path.relative(this.root, moduleAbsolutePath).replace(/\\/g, '/'); console.log(path.relative(this.root, moduleAbsolutePath)) // path.relative(from, to) // path.relative(from, // moduleRelativePath // indicates the relative path of the module file // moduleRelativePath = moduleAbsolutePath - Log (source, moduleRelativePath) if (isEntry) {this.entryId = moduleRelativePath // / SRC /index.js = entryId} const fatherPath = path.dirName (moduleRelativePath) // / SRC const {sourceCode, dependencies} = this.parse(source, FatherPath) // Parse () main function // 1. // 2. Return the modified source code and dependency list // Parameter: This.modules [moduleRelativePath] = sourceCode; // Modules [moduleRelativePath] = sourceCode; // moduleRelativePath => moduleRelativePath // value => sourceCode dependencies. ForEach (dep => { This.buildmodule (path.join(this.root, dep), false)}) } emitFile() {// shoot the file const {path: p, filename} = this.config.output const main = path.join(p, Const templeteSourceStr = this.getSource(path.join(__dirName, Main.ejs const code = ejs.render(templeteSourceStr, {entryId: this.entryId, modules: This.modules}) // Render template // Template has two arguments: entryId and modules this.assets = {} this.assets[main] = code; // value: Fs.writefilesync (main, this.assets[main]) Options])} run() {// the run method does two things // 1. // 2. Launch the package file this.hook.run.call () this.hooks.com compile.call() this.buildModule(path.resolve(this.root,) This.entry), true) // buildModule() is used to create dependencies for the module // arguments: // First argument: is the absolute path of the path specified by the entry // Second argument: Whether the main module enclosing hooks. AfterCompile. Call () the console. The log (enclosing modules, enclosing entryId) / / launch a file, }} module. Exports = Compiler.} / / moduleCopy the code
data
Packaging principles: www.jianshu.com/p/89bd63d25… Packaging Principle 2: juejin.cn/post/684490… Webpack Loader: juejin. Cn/post / 684490…