How does Vite work?

Declare a script tag of type module as follows: < script type = “module” SRC = “/ SRC/main. Js” > < / script > browser like server initiating a GET request to http://localhost:3000/src/main.js. The main js file:

// /src/main.js:
import { createApp } from 'vue'
import App from './App.vue'

createApp(App).mount('#app')
Copy the code

When the browser requests the main.js file and detects that it contains the package imported by import, it sends an HTTP request to its import reference to obtain the content file of the module, for example: GET http://localhost:3000/@modules/vue.js For example: GET http://localhost:3000/src/App.vue, its main function is through the browser hijack Vite these requests, and corresponding processing in the back-end will be used in the project file with a simple decomposition and integration, and then returned to the browser to render the page, Vite doesn’t package files, so it runs much faster than the original WebPack compiler!

What did Vite do?

1. Overwrite the import module path with /@modules/ in front of it, the browser will send the request again after overwriting

Original main.js file:



The main.js file requested after building with Vite:

2. Intercept requests containing /@modules/, import the corresponding module from node_modules and return

3. Parse the. Vue file

For example, the app.vue file is as follows:

<template>
  <HelloWorld msg=Vue 3.0 + Vite />
</template>

<script>
import HelloWorld from './components/HelloWorld.vue'

export default {
  name: 'App'.components: {
    HelloWorld
  }
}
</script>
Copy the code

Parsed into the render function and returned to the browser to render the page:

Request:http://localhost:3000/src/App.vue

When a VUE file is requested, the KOA middleware detects that it is a VUE template file and adds one after the requesttype=templateparameter

Such as:http://localhost:3000/src/App.vue?type=template

Koa uses this parameter to determine whether to request a Vue template file, compile it into a JS file and return it to the browser

4. The static service plug-in implements the function of returning static files

app.use(static(root))
app.use(static(path.resolve(root, 'public')))
Copy the code

Handwritten Vite code to achieve the above four functions:

Create a new Vite project:

NPM instal-g create-viet-app // Create a project named myvitevue3 CD my-viet-vue3 Yarn install // The installation project depends on yarn dev // to start the projectCopy the code

Let’s create a new one in the root directoryvite/index.jsfile

By running thenode vite/index.jsInstead ofyarn devStart the project

Use self-implemented Vite to simulateviteThe four functions of the

Here is a page rendered using a self-written Vite:

//vite/index.js
const fs = require('fs').promises
const Koa = require('koa')
const path = require('path')
const chalk = require('chalk')
const static = require('koa-static')
const { parse } = require('es-module-lexer')
const MagicString = require('magic-string')
const { Readable } = require('stream')

// Read the body method
async function readBody(stream) {
	if (stream instanceof Readable) {
		return new Promise((resolve) = > {
			let res = ' '
			stream.on('data'.function (chunk) {
				res += chunk
			});
			stream.on('end'.function () {
				resolve(res)
			})
		})
	} else {
		returnstream; }}/ / koa middleware
const resolvePlugin = [
	// 1. Add /@modules/vue in front of the import module path, and then the browser will send the request again
	({ app, root }) = > {
		function rewriteImports(source) {
			let imports = parse(source)[0];
			let ms = new MagicString(source);
			if (imports.length > 0) {
				for (let i = 0; i < imports.length; i++) {
					let { s, e } = imports[i];
					let id = source.slice(s, e); Vue./ app.vue
					// 不是./ ζˆ–θ€… /
					if (/ / ^ ^ \ / \].test(id)) {
						id = `/@modules/${id}`;
						ms.overwrite(s, e, id)
					}
				}
			}
			return ms.toString();
		}
		app.use(async (ctx, next) => {
			await next(); // Static service
			// By default the static service is executed first and the middleware puts the result into ctx.body
			// To convert the stream to a string, you only need to deal with references in JS
			if (ctx.body && ctx.response.is('js')) {
				let r = await readBody(ctx.body); // vue => /@modules
				constresult = rewriteImports(r); ctx.body = result; }})},// 2. Intercept requests containing /@modules/vue, go to node_modules and import the corresponding module and return
	({ app, root }) = > {
		const reg = /^\/@modules\//
		app.use(async (ctx, next) => {
			// If there is no match for /@modules/vue, proceed
			if(! reg.test(ctx.path)) {return next();
			}
			const id = ctx.path.replace(reg, ' ');

			let mapping = {
				vue: path.resolve(root, 'node_modules'.'@vue/runtime-dom/dist/runtime-dom.esm-browser.js'),}const content = await fs.readFile(mapping[id], 'utf8');
			ctx.type = 'js'; // The file returned is jsctx.body = content; })},// 3. Parse the.vue file
	({ app, root }) = > {
		app.use(async (ctx, next) => {
			if(! ctx.path.endsWith('.vue')) {
				return next();
			}
			const filePath = path.join(root, ctx.path);
			const content = await fs.readFile(filePath, 'utf8');
			// Import the.vue file parsing template
			const { compileTemplate, parse } = require(path.resolve(root, 'node_modules'.'@vue/compiler-sfc/dist/compiler-sfc.cjs'))
			let { descriptor } = parse(content);
			if(! ctx.query.type) {//App.vue
				let code = ' '
				if (descriptor.script) {
					let content = descriptor.script.content;
					code += content.replace(/ ((? :^|\n|;) \s*)export default/.'$1const __script=');
				}
				if (descriptor.template) {
					const requestPath = ctx.path + `? type=template`;
					code += `\nimport { render as __render } from "${requestPath}"`;
					code += `\n__script.render = __render`
				}
				code += `\nexport default __script`
				ctx.type = 'js';
				ctx.body = code
			}
			if (ctx.query.type == 'template') {
				ctx.type = 'js';
				let content = descriptor.template.content
				const { code } = compileTemplate({ source: content }); // Convert the template in app.vue to the render functionctx.body = code; }})},// 4. Static service plug-in can return file function
	({ app, root }) = > {
		app.use(static(root))
		app.use(static(path.resolve(root, 'public')))}]function createServer() {
	let app = new Koa()
	const context = {     // Create a context directly to share functionality between different plug-ins
		app,
		root: process.cwd() // C:\Users\... \my-vite-vue3
	}

	// Run middleware
	resolvePlugin.forEach(plugin= > plugin(context))

	return app
}

createServer().listen(4000.() = > {
	console.log(' Dev server running at:')
	console.log(` > Local: ${chalk.cyan('http://localhost:4000/')}`)})Copy the code

Images and CSS files have not been processed yet, so the corresponding functions can be achieved by removing the images introduced by app.vue and the CSS introduced by main.js