The preface
This morning, I wrote a simple version of Webpack-flow. I wanted to record it and share it with you. So next, let’s take a look at the webpack-flow process and code implementation part
1. Webpack execution process
- The above WebPack implementation process is roughly divided into these steps, and our implementation process is also based on the general direction, we will start from the entry to the method
2. Execute entry and use function
- Webpack function: We export the function that collects parameters and instantiates the compiler, which is also the execution entry
- Compiler class: Used to start compiling, exposes a method run that is called to formally start the compilation process
- ResolveLoader: execute loader to parse es6 syntax. Here we simulate babel-loader to parse ES6 syntax
- Compose function: implements bottom-up execution of the loader
3. Specific code implementation
3.1 First of all, let’s start from the entrance to see what the entrance does ??????
// Webpack compiler entry
function webpack(options) {
// 1 -- Parameter merge, merge options passed by webpack call with options in shell
const argv = process.argv.slice(2)
const shellOptions = argv.reduce((shellOptions, curr) = > {
const {key, value} = curr.split('=')
shellOptions[key.slice(2)] = value
return shellOptions
}, {})
constmergeOptions = {... options, ... shellOptions}// 2 -- Start compiling to get the Compiler object
const compiler = new Compiler(mergeOptions)
// 2 -- loads all configured plug-ins
if (mergeOptions.plugins && Array.isArray(mergeOptions.plugins)) {
mergeOptions.plugins.forEach(plugin= > plugin.apply(compiler))
}
return compiler
}
Copy the code
- From the above code we can see that the Webpack entry function is no more than the following two functions?
- Combine the webpack.config parameter with the parameters in the shell command
- Instantiate the Compiler class to get the instance
- Loading plugins, which are expected to say that loading is more than subscribing, shows the custom plugin code below
class DonPlugin {
apply(compiler) {
// The tap function is used to publish and subscribe via the tapable library
compiler.hooks.done.tap('done'.() = > {
console.log('Execution completed.............. ')}}}module.exports = DonPlugin
Copy the code
3.2 Compiler implementation
class Compiler {
constructor(options) {
this.hooks = {
run: new SyncHook(),
done: new SyncHook()
}
this.options = options
}
// Start compiling entry
run() {
const chunks = {}
// 2 -- Start determining the entry for compilation
const {context = process.cwd(), entry} = this.options
if (typeof entry === 'string') {
this.options.entry = {main: entry}
}
// -- call the hook here
this.hooks.run.call('run')
// 3 -- Start traversal loader starts parsing files
resolveLoader(chunks, this)
this.hooks.done.call('done')
// 5 -- Writes files
const {path: dirPath, filename} = this.options.output
if(! fs.existsSync(dirPath)) { fs.mkdirSync(dirPath) }Object.keys(chunks).forEach(fileName= > {
const newPath = path.join(dirPath, `${fileName}.js`)
chunks[fileName].forEach(content= > {
fs.writeFileSync(newPath, content, {encoding: 'utf-8'})})})}}Copy the code
- We’ll start with the constructor, which instantiates the two publish subscribe hooks, Run and done, via tapable. The plugin is executed by subscribing first, and the events triggered are described below.
- Start by determining the build entry, which in turn resolves dependencies
- At this point we’ll see the code
this.hooks.run.call('run')
This is the time to trigger, in fact our plugin is called at runtime - The compiled class will parse the files one by one through the Loader along the entry, which is this code
resolveLoader('done')
This code will be explained in the next step - This is where the code runs
this.hooks.done.call('done')
Loader parsing is complete, and the file will be written soon. So we call the done hook.
At this point, we are reminded of the sentence in the screenshot above, “Webpack will broadcast a specific event at a specific time, and the plugin will execute a specific logic after listening for the event of interest. “The so-called specific time here is different stages to trigger the subscription, and the execution of a specific event is the hook function of the subscription in the plug-in
3.3 Implementation of resolveLoader
function resolveLoader(chunks, compiler) {
// Import file and loader module
const {entry, module = {}} = compiler.options
// Parse the rule
const {rules} = module
// Whether to configure loader
if (rules && Array.isArray(rules)) {
Object.keys(entry).forEach(keyName= > {
const modules = []
// File path
const filePath = entry[keyName]
// Read the contents of the file
const fileContent = fs.readFileSync(filePath, 'utf-8')
rules.forEach(rule= > {
// If the loader.test matching rule is met, invoke the corresponding loader to resolve the problem
if (rule.test.test(filePath)) {
const methods = rule.use.map(methodPath= > require(methodPath))
modules.push(compose(methods)(fileContent))
}
})
// 4 -- Add chunks to the server after compiling
chunks[keyName] = modules
})
}
}
Copy the code
- The above code is not special description, in fact, loadr parsing file, file source
3.4 Implementation of compose function
function compose(resolveLoaders) {
if (resolveLoaders.length === 0) return x= > x
if (resolveLoaders.length === 1) return x= > resolveLoaders[0](x)
return resolveLoaders.reduce((a, b) = > (. args) = >a(b(... args))) }Copy the code
The above code, without much elaboration, is essentially an implementation of compose, which we often write, using the source code for Redux
4. Conclusion
The above is the simple implementation of Webpack-flow, if not welcome correction
5. Source address and extension
- The address of the above code is GitBub
- In fact, xiaobian also wrote an article, you can refer to personal blog