1. Webpack principle brief introduction
1.1 Core Concepts
JavaScript module Bundler. By analyzing the dependencies between modules, all modules are finally packaged into one or more bundlers, which can be directly referenced by HTML. In essence, Webpack simply provides packaging capabilities and a file-handling mechanism, and then precompiles and packages the code through the various Loaders and plugins in the ecosystem. Therefore, Webpack has a high degree of extensibility and can better play the power of community ecology.
- Entry: Entry file from which Webpack will start analysis and compilation.
- Output: export path. The file path and file name of the bundler file created after packaging.
- Module: any file can be used as a Module in Webpack, and will be loaded and packaged according to different loaders configured;
- Chunk: code blocks. You can combine all module codes into one or more code blocks based on configuration for loading on demand to improve performance.
- Loader: module Loader for loading and converting various file types;
- Plugin: Extension plug-in, through Webpack corresponding event hooks, can intervene in any link in the packaging process, so as to modify the code as needed;
1.2 Workflow (load – Compile – Output)
- Read configuration files, initialize configuration parameters according to commands, and create Compiler objects;
- Call the plug-in’s Apply method to mount the plug-in listener, and then compile from the entry file;
- According to the file type, the corresponding Loader is called to compile the module, and the corresponding event is triggered at the appropriate time point, and Plugin is called for execution. Finally, the dependent module is found according to the module dependency, and the third step is performed recursively.
- Wrap all compiled code into code blocks (Chuck) and determine the output by dependency and configuration. This step, the file can still be modified by Plugin;
- Finally, according to Output, the contents of the file are written to the specified folder one by one to complete the whole process;
1.3 Module Packaging
(function(modules) {// simulate require function, load module from memory; Function __webpack_require__(moduleId) {// Cache module if (installedModules[moduleId]) {return installedModules[moduleId].exports; } var module = installedModules[moduleId] = { i: moduleId, l: false, exports: {} }; // Execute code; modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); // Flag: indicates whether the loading is complete. module.l = true; return module.exports; } / /... // Start loading entry file; return __webpack_require__(__webpack_require__.s = "./src/index.js"); })({"./ SRC /index.js": function (module, __webpack_exports__, __webpack_require__) {// Execute compiled code using eval; // Continue recursively referencing module internal dependencies; // The template string is not used for readability purposes; eval(` __webpack_require__.r(__webpack_exports__); // var _test__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("test", ./src/test.js"); `); }, "./src/test.js": function (module, __webpack_exports__, __webpack_require__) { // ... }})Copy the code
Conclusion:
- Module mechanisms: WebPack itself implements a mechanism for simulating modules by wrapping them around business code to provide a module mechanism;
- File compilation: WebPack provides a set of compilation rules that pipe file strings through loaders and plugins.
1.4 Packaging principle of WebPack
Initialization parameter
: Reads and merges parameters from configuration files and Shell statements to arrive at the final parametersBegin to compile
Initialize the Compiler object with the parameters obtained in the previous step, load all the configured plug-ins, and execute the object’s run method to start compilingDetermine the entrance
: Finds all entry files according to the entry in the configurationCompile the module
: Starting from the import file, call all configured Loader to translate the module, find the module that the module depends on, and then recurse this step until all import dependent files are processed in this stepComplete module compilation
: After using Loader to translate all modules in Step 4, the final content of each module after translation and the dependencies between them are obtainedOutput resources
: Assemble chunks containing multiple modules one by one according to the dependency between the entry and modules, and convert each Chunk into a separate file and add it to the output list. This step is the last chance to modify the output contentOutput complete
: After determining the output content, determine the output path and file name based on the configuration, and write the file content to the file system
1.5 summarize
- Initialization parameters: Read and merge parameters from configuration files and Shell statements to arrive at the final configuration parameters.
- Start compiling: Initializes the parameters obtained from the previous step
Compiler
Object, loads all configured plug-ins, executes the object’srun
Method starts compiling. - Define entry: root scope according to configuration
entry
Find all the entry files. - Compile module: from the entry file, call all configured
loader
Translate the module and find the module that the module depends on. This step is performed recursively until all the module files of the entry dependency have been processed by this step. - Complete module compilation: After using Loader to translate all modules in step 4, the final content of each module after translation and the dependencies between them are obtained.
- Output resources: Assembled into modules based on the dependencies between entry and module
chunk
And then put eachchunk
Add it to the output list as a separate file. This step is the last chance to modify the output. - Output complete: After determining the output content, determine the output path and file name based on the configuration, and write the file content to the file system.
2. Webpack hot update principle
- When one or more files are modified;
- The file system receives changes and notifies them
webpack
; webpack
Recompile to build one or more modules and notify the HMR server for updates;HMR Server
usewebSocket
noticeHMR runtime
Need to update,HMR
Run time passHTTP
Request updatejsonp
HMR
The runtime replaces the modules in the update and, if it is determined that they cannot be updated, triggers a full page refresh
3 webpack Loader
Since Webpack is based on Node, Webpack can only recognize JS modules, such as CSS/HTML/image files, and can’t load, so you need a converter for different formats. What Loader does is not hard to understand: it modifiers the strings passed in by Webpack as needed. For example, the simplest Loader:
Html-loader /index.js module.exports = function(htmlSource) {// return a string of processed code // remove all comments in the HTML file return htmlSource.replace(/<! --[\w\W]*? -->/g, '') }Copy the code
Of course, the actual Loader is not so simple. It usually needs to analyze the code, build an AST (abstract syntax tree), iterate for targeted modifications, and then regenerate the new code string. For example, babel-loader performs the following steps:
babylon
将ES6/ES7
Code parsing intoAST
babel-traverse
对AST
Ergodic translation is performed to obtain a new AST- new
AST
throughbabel-generator
Converted toES5
Loader features:
- Chain delivery, chain execution in reverse order when configured;
- Based on the Node environment, it has higher permissions, such as adding, deleting, checking and modifying files;
- Can be synchronous or asynchronous;
Commonly used Loader:
file-loader
: Load file resources, such as fonts/pictures, with the function of moving/copying/naming.url-loader
: usually used to load images, you can convert small images directly to Date URLS, reducing requests;babel-loader
: Load JS/JSX files, convert ES6 / ES7 codes to ES5, and smooth out compatibility problems;ts-loader
Load ts/TSX files and compile TypeScript.style-loader
: Sets the CSS code to<style>
Tags are inserted into the HTML;css-loader
: parse @import and URL (), referencing CSS files and corresponding resources;postcss-loader
: Used for CSS compatibility processing, with many functions, such as adding prefixes, unit conversion, etc.less-loader / sass-loader
: CSS preprocessor, in the CSS added a lot of syntax, improve the development efficiency;
Writing principles:
- Single rule: Each Loader does only one thing;
- Chain calls: Webpack calls each Loader in a chain order;
- Unified principle: Follow Webpack design rules and structure, input and output are strings, each Loader is completely independent, plug and play;
4. webpack Plugin
The plug-in system is a key factor in the success of Webpack. Webpack triggers a number of event hooks throughout the life of the build, and the Plugin can listen for these events to make targeted changes to the packaged content at the appropriate point in time as required.
The simplest plugin looks like this:
Class Plugin{// The apply method is called when the plug-in is registered. // The apply method receives the Compiler object. // The Api provided by the Compiler can listen for events. // compilation is used to listen for each compilation loop // each file change, Compiler. Plugin ('compilation',function(compilation) {})}}Copy the code
Register plug-ins:
// webpack.config.js
module.export = {
plugins:[
new Plugin(options),
]
}
Copy the code
Event flow mechanism:
Webpack is like an assembly line in a factory. Raw materials are processed one by one by Loader and Plugin, and finally output results.
- Through the chain call, string up one Loader in sequence;
- Plugins can be inserted at every step of the production process through an event flow mechanism;
At the heart of the Webpack event stream programming paradigm is the base class Tapable, which implements the subscription and broadcast of events in observer mode:
Const {SyncHook} = require("tapable") const hook = new SyncHook(['arg']) // subscribe to hook. Tap ('event', (arg) = > {/ / 'event - hook' console. The log (arg)}) / / radio hook. Call (' event - hook)Copy the code
The two most important classes in Webpack — Compiler and Compilation — inherit from Tapable and also have this event flow mechanism.
-
Compiler: It can be simply understood as the instance of Webpack, which contains all configuration information in the current Webpack, such as options, loaders, plugins and other information. It is globally unique and can only be initialized at startup and passed one by one with the life cycle.
-
Compilation: Can be called a Compilation instance. When listening for file changes, Webpack creates a new Compilation object and starts a new Compilation. It contains the current input resources, output resources, changing files, and so on. At the same time, through its API, it can listen to the event hooks triggered during the compilation process.
-
The difference between:
Compiler
Globally unique and exists from start to end;Compilation
Each compilation cycle is recreated for each compilation;
-
Commonly used the Plugin:
- UglifyJsPlugin: Compress and obfuscate code;
- CommonsChunkPlugin: Code split;
- ProvidePlugin: automatic loading module;
- Html-webpack-plugin: loads HTML files and introduces CSS/JS files;
- Extract-text-webpack-plugin/mini-css-extract-plugin: Extract styles, generate CSS files; DefinePlugin: Define global variables;
- Optimize – CSS -assets-webpack-plugin: OPTIMIZE – CSS -webpack-plugin;
- Webpack-bundle-analyzer: Code analysis;
- Compression -webpack-plugin: use gzip to compress JS and CSS
- Happypack: Use multiple processes to speed up code building;
- EnvironmentPlugin: Defines environment variables;
-
Call the plug-in apply function to pass in the Compiler object
-
The Compiler object listens for events
What is the difference between loader and plugin?
By default, WebAPck can only package JS and JOSN modules. To package other modules, loader needs to be used. Loader can convert the contents of the module into content that webPack or other LaoDer can recognize.
loader
Module conversion, or loader. Different files, different needsloader
To deal with.plugin
Plugins can participate in the entire WebPack packaging process, and different plugins can do different events at the right time.
What plug-ins are in WebPack, and what do they do?
html-webpack-plugin
Automatically creates an HTML file and inserts the packaged JS into the HTML fileclean-webpack-plugin
Before each package, delete all contents in the entire output foldermini-css-extrcat-plugin
Pull out the CSS code and put it in a separate fileoptimize-css-assets-plugin
Compress CSS
Implement a plug-in that compiles the exit command
apply (compiler) {
const afterEmit = (compilation, cb) => {
cb()
setTimeout(function () {
process.exit(0)
}, 1000)
}
compiler.plugin('after-emit', afterEmit)
}
}
module.exports = BuildEndPlugin
Copy the code
<script> export default { mounted () { var isGithub = location.href.indexOf('FE-Interview-Questions')! ==-1 var sId = isGithub ? '59154049' : '66575297' var script = document.createElement("script"); script.type = "text/javascript" script.charset="UTF-8" script.src = `http://tajs.qq.com/stats?sId=${sId}` document.body.appendChild(script); } } </script>Copy the code
5. Webpack compilation optimization
Code optimization:
Dead code elimination is a process adopted by many programming languages. This process is called dead Code elimination (DCE).
For example, our UglifyJs will help us remove code that cannot be executed in production, such as:
var fn = function() { return 1; // The following code is impossible to execute; // Use UglifyJs (Webpack4+ built-in) to do DCE; var a = 1; return a; }Copy the code
Tree-shaking is a metaphor. We compare the packaged code to a tree, which actually means that the tool “shakes” our packaged JS code, and “shakes” the unused code down (delete). That is, eliminate module code that is referenced but not used.
-
How it works: Because it is optimized at compile time, the basic premise is static analysis of the syntax, and ES6’s modular mechanism provides this possibility. Without a runtime, the code can be literally statically analyzed to determine the corresponding dependencies.
-
Problem: Functions with side effects cannot be shaken by tree-shaking
- In reference to some third-party libraries, it is necessary to observe whether the amount of code introduced is as expected;
- Write pure function as far as possible to reduce the side effects of function;
- You can use
webpack-deep-scope-plugin
, scope analysis can be carried out to reduce the occurrence of such cases, but attention still needs to be paid;
Code-spliting: code splitting technology, which is used to divide code into multiple copies for lazy loading or asynchronous loading to avoid the large size of the first screen loading caused by packing the code into one copy.
-
SplitChunksPlugin is used in Webpack for splitting;
-
Split by page: package different pages into different files;
-
Split by function:
- Large modules such as player and computing library are split and then lazily loaded and introduced;
- Extracting reusable business code and reducing redundant code;
-
Split by file modification frequency: Package infrequently modified code, such as third-party libraries, separately without changing the hash value of the file to maximize the use of the browser cache.
In the case of scope promotion, the scattered modules are divided into the same scope, avoiding the repeated introduction of codes, and effectively reducing the packaged code volume and memory loss at runtime.
Compilation performance optimization:
-
Upgrading to the latest version of Webpack can effectively improve compilation performance;
-
Use dev-server/module hot replacement (HMR) to improve the development experience;
- Ignoring node_modules can improve compilation efficiency when listening.
-
Narrow the compilation
modules
: Specify module path to reduce recursive search;mainFields
: Specify entry file description field to reduce search;noParse
: Avoid loading of modular files;includes/exclude
: Specify search scope/exclude unnecessary search scope;alias
: Cache directories to avoid repeated addressing;
-
babel-loader
- ignore
node_moudles
To avoid compiling code that has already been compiled in third-party libraries - use
cacheDirectory
, can cache compilation results, avoid repeated compilation
- ignore
-
Multiprocess concurrency
webpack-parallel-uglify-plugin
: Multi-process concurrent compression of JS files, improve compression speed;HappyPack
: multi-process concurrent filesLoader
Parsing;
-
Third-party library module cache:
DLLPlugin
和DLLReferencePlugin
It can be packaged and cached ahead of time to avoid recompiling every time;
-
Using the analysis
Webpack Analyse / webpack-bundle-analyzer
Analyze the packaged files to find areas to optimize- Configure profile: true to monitor the time spent in each compilation phase and find the most time spent
-
source-map
:- Development:
cheap-module-eval-source-map
- Production:
hidden-source-map
;
- Development:
Optimize webPack packaging speed
-
Reduce file search scope
- Like through an alias
loader
的test
.include & exclude
-
Webpack4 compresses parallelism by default
-
Happypack is called concurrently
-
Babel can also be compiled by cache
6. Webpack import() principle
Principle of dynamic import
Import () method for dynamic loading
- This feature allows us to load our code on demand and use it
promise
To retrieve the loaded package - All are in the code
import()
The modules will all be grouped into a separate package and placed inchunk
Store in the directory. When the browser runs to this line of code, it automatically requests the resource, enabling asynchronous loading
// Here is a simple demo. // As you can see, the syntax of import() is very simple. Import ('lodash'). Then (_ => {// Do something with lodash (A.K.A '_')... })Copy the code
How to implement dynamic import in Webpack?
- use
import(/** webpackChunkName: "lodash" **/ 'lodash').then(_ => {})
At the same timewebpack.config.js
To configureThe output of chunkFilename
for[name].bunld.js
The module to be imported is pulled separately into onebundle
To achieve code separation. - use
async
Because ofimport()
It returns onepromise
So we can useasync
Delta function to simplify it, but you need tobabel
Such preprocessor and processing conversionasync
The plug-in.const _ = await import(/* webpackChunkName: "lodash" */ 'lodash');
7. What kinds of file fingerprints does Webpack have?
hash
Is related to the construction of the entire project, as long as there are file changes in the project, the entire project is builthash
Values change, and all files share the samehash
Value. (Granularity of the entire project)chunkhash
Is based on the different entry for dependency file parsing, build the correspondingchunk
(module), generate the correspondinghash
Value. Only the modified oneschunk
New ones are generated only after being rebuilthash
Value, doesn’t affect anything elsechunk
. (particle sizeentry
For each entry file)contenthash
It has to do with each generated file, each file has a uniquehash
Value. When the contents of the file to be built change, a new one is generatedhash
Value, and changes to this file do not affect other files in the same module. (Granularity the contents of each file)
If webpack uses a hash name, does it overwrite the generated hash every time
There are three cases:
- If it is
hash
If it is related to the whole project, if one file is changed, all files arehash
They all change and they all share onehash
Value; - If it is
chunkhash
If so, only withentry
Each entry file is associated with, that is, the samechunk
The following files have been modifiedchunk
Under the filehash
The value will change - If it is
contenthash
For each generated file, a new file will be generated only if the content of the file to be built changeshash
Value does not affect other files.
8 How to deal with pictures in Webpack?
There are two image-handling loaders in Webpack:
file-loader
: solveCSS
And so on to introduce the picture path problem; (Settled throughurl
.import/require()
Etc.)url-loader
: When the picture is smaller than the setlimit
Parameter value,url-loader
Take the picturebase64
Encoding (when the project has a lot of pictures, passurl-loader
forbase64
It’s going to decrease after codinghttp
Number of requests to improve performance), is greater than the limit valuefile-loader
Copy the image and export it to the compile directory.
Abstract syntax tree AST
Abstract Syntax Tree, which parses code letter by letter into Tree object form. This is the basis for translation between languages, code syntax checking, code style checking, code formatting, code highlighting, code error prompts, code auto-completion, and so on
Function square(n) {return n * n} const element = {type: "FunctionDeclaration", start: 0, end: 35, id: Identifier, expression: false, generator: false, params: [1, element], body: BlockStatement }Copy the code
10. What are the problems with using babel-loader? How can you optimize it?
- It makes compiling slow. The solution can be found in
webpack
thebabel-loader
Use in configurationexclude
This option is used to remove folders that do not need to be compiled (e.gnode_modules
andbower_components
), the other can be setcacheDirectory
Options fortrue
If caching is enabled, the result of translation will be cached in the file systembabel-loader
At least twice as fast (the more code you have, the more noticeable it is). babel-loader
The size of the packaged file is too large. Babel uses very small helper code for some public methods, such as_extend
. By default, it is added to every file that needs it, resulting in a large package file. Solution: Introducebabel runtime
As a separate module to avoid duplication. In other words, it can be used@babel/plugin-transform-runtime
andbabel-runtime
.
11. The Babel principle
The compilation process of Babel is divided into three stages: parsing, transforming, and generating. Take ES6 compilation to ES5 as an example:
ES6
Code input;babylon
The AST is obtained by analysis.plugin
用babel-traverse
对AST
Tree traversal compilation, get newAST
Tree;- with
babel-generator
throughAST
Tree generatedES5
The code.
12. How does Babel compile Class?
For the class below:
class Person {
constructor ({ name }) {
this.name = name
this.getSex = function () {
return 'boy'
}
}
getName () {
return this.name
}
static getLook () {
return 'sunshine'
}
}
Copy the code
When we are using these Plugins of Babel or preset, there is a configuration property loose which defaults to false in such conditions:
Class compiled:
- On the whole
Class
It’s going to be encapsulated into oneIIFE
Execute function immediately - The immediately executed function returns a constructor with the same name as the class
- Instance properties and methods are defined in constructors (e.g
name
andgetSex()
) - Property methods declared inside a class (
getName
) and static property methods (getLook
) will beObject.defineProperty
Sets its enumerable property tofalse
Compiled code:
"use strict"; function _classCallCheck(instance, Constructor) { if (! (instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } var Person = /*#__PURE__*/ (function () { function Person(_ref) { var name = _ref.name; _classCallCheck(this, Person); this.name = name; this.getSex = function () { return "boy"; }; } _createClass( Person, [ { key: "getName", value: function getName() { return this.name; }, }, ], [ { key: "getLook", value: function getLook() { return "sunshine"; }, }, ] ); return Person; }) ();Copy the code
Why does Babel use object.defineProperty for class processing? Is it any different than using the prototype chain directly?
- Properties and methods declared through stereotype chains are enumerable, that is, can be
for... of...
To search for - Methods declared inside a class are not enumerable
So, in order to conform to the true semantics of ES6, Babel compiles classes using Object.defineProperty to define prototype methods.
However, by setting Babel loose mode to true, it does not strictly follow ES6 semantics and compiles code in a way that is more consistent with how we normally write code. You can set babelrc as follows:
{
"presets": [["env", { "loose": true }]]
}
Copy the code
For example, the property methods of the Person class above will be compiled to declare methods directly on the prototype chain:
"use strict"; var Person = /*#__PURE__*/function () { function Person(_ref) { var name = _ref.name; this.name = name; this.getSex = function () { return 'boy'; }; } var _proto = Person.prototype; _proto.getName = function getName() { return this.name; }; Person.getLook = function getLook() { return 'sunshine'; }; return Person; } ();Copy the code
conclusion
-
Loose is false by default when compiling with Babel, i.e., non-loose mode
-
In either case, the transformed property methods defined inside the class are defined on the constructor’s prototype object; Static properties are defined on constructors
-
Except in non-looser mode, these property methods are handled by the _createClass function, which sets the property’s enumerable value to False via Object.defineProperty()
-
Since Object is used in the _createClass function, there are side effects in non-loose mode, but not in loose mode.
-
UglifyJS in Webpack will still consider loose mode as having side effects, while Rollup has the function of program flow analysis to better judge whether the code really has side effects, so it will consider loose mode as having no side effects.
(A side effect is roughly defined as the behavior of a function that affects, or may affect, variables outside the function.)
13. What is Tree Shaking
Understanding tree-shaking
Function:
It means that some useless code is removed during packaging
Principle:
ES6
Module introduction is statically analyzed, so you can correctly determine which modules are loaded at compile time- Analyze the program flow to determine which variables are not used or referenced, and then delete the code
Features:
- It is turned on by default in production mode, but due to the
babel
Compile all modules are encapsulated intoIIFE
It has side effects that cannot betree-shaking
off - Can be found in
package.json
In the configurationsideEffects
To specify which files have side effects. It has two values, one is of Boolean type, if yesfalse
All files have no side effects. If it is an array, the file path in the array indicates that changing the file has side effects rollup
andwebpack
In thetree-shaking
Different levels of, e.gbabel
After the translationclass
If thebabel
The translation of is in loose mode (i.eloose
fortrue
),webpack
They still think it has side effects notree-shaking
Off,rollup
Will be. This is becauserollup
There is flow analysis to better determine whether the code is actually causing side effects.
The principle of
ES6 Module
Introduce static analysis, so compile time to determine exactly which modules are loaded- Statically analyze the program flow to determine which modules and variables are not being used or referenced, and then remove the corresponding code
Rely on the import/export
Conditional retrieval is performed by importing all packages. As follows:
import foo from "foo";
import bar from "bar";
if(condition) {
// foo.xxxx
} else {
// bar.xxx
}
Copy the code
ES6’s import syntax is perfect to use tree shaking, because you can analyze unnecessary code when it’s not running
The dynamic feature modules of CommonJS mean that Tree Shaking does not apply. Because it is impossible to determine which modules are needed or not before they actually run. In ES6, a completely static import syntax is introduced: import. This also means that the following imports are not available:
If (condition) {myDynamicModule = require("foo"); } else { myDynamicModule = require("bar"); }Copy the code
14. Vite
Is a browser based on the native ES module import development server, in the development environment, the use of the browser to parse import, on the server side according to the need to compile and return, completely skipped the concept of packaging, the server is ready to use. At the same time, Vue files are not only supported, but also support hot update, and the speed of hot update does not slow down with the increase of modules. Use Rollup packaging in a production environment
Vite characteristics
- Dev Server starts immediately without waiting;
- Module hot update in near real time;
- Compile required files as needed to avoid compiling files that are not needed;
- Out of the box, avoid various Loader and Plugin configuration;
Out of the box
TypeScript
– Built-in supportless/sass/stylus/postcss
– Built-in support (separate compiler installation required)
Does the production environment need to be packaged
You don’t need to pack, you need to start the server, you need browser support
Handwritten implementation
Vite core functions: Static Server + Compile + HMR
Core ideas:
-
Use the current project directory as the root of the static file server
-
Intercept partial file requests
- Processing code
import node_modules
In the module - To deal with
vue
Single file component (SFC
) compilation
- Processing code
-
Implement HMR through WebSocket
#! /usr/bin/env node const path = require('path') const { Readable } = require('stream') const Koa = require('koa') const send = require('koa-send') const compilerSfc = require('@vue/compiler-sfc') const cwd = process.cwd() const streamToString = stream => new Promise((resolve, reject) => { const chunks = [] stream.on('data', chunk => chunks.push(chunk)) stream.on('end', () => resolve(Buffer.concat(chunks).toString('utf8'))) stream.on('error', Reject)}) const app = new Koa() // override request path, /@modules/xxx => /node_modules/ app.use(async (ctx, next) => { if (ctx.path.startsWith('/@modules/')) { const moduleName = ctx.path.substr(10) // => vue const modulePkg = require(path.join(cwd, 'node_modules', moduleName, 'package.json')) ctx.path = path.join('/node_modules', moduleName, Modulepkg.module)} await next()}) // Index.html app.use(async (CTX, next) => { // ctx.path // http://localhost:3080/ // ctx.body = 'my-vite' await send(ctx, ctx.path, { root: cwd, index: 'index.html'}) // There may be additional processing of the corresponding result await next()}) //.vue file request processing, Use (async (CTX, next) => { if (ctx.path.endsWith('.vue')) { const contents = await streamToString(ctx.body) const { descriptor } = compilerSfc.parse(contents) let code if (ctx.query.type === undefined) { code = descriptor.script.content code = code.replace(/export\s+default\s+/, 'const __script = ') code += ` import { render as __render } from "${ctx.path}? type=template" __script.render = __render export default __script` // console.log(code) ctx.type = 'application/javascript' ctx.body = Readable.from(Buffer.from(code)) } else if (ctx.query.type === 'template') { const templateRender = compilerSfc.compileTemplate({ source: descriptor.template.content }) code = templateRender.code } ctx.type = 'application/javascript' ctx.body = Readable.from(buffer. from(code))} await next()}) // Replace special positions in code app.use(async (CTX, next) => { if (ctx.type === 'application/javascript') { const contents = await streamToString(ctx.body) ctx.body = contents .replace(/(from\s+['"])(?! [./])/g, '$1/@modules/') .replace(/process.env.NODE_ENV/g, '"production"') } }) app.listen(3080) console.log('Server running @ http://localhost:3080')Copy the code