为开发提供极速响应的vite工作原理是怎样的呢?前言 webpack的构建原理 webpack先通过分析JavaScri
前言
webpack的构建原理
vite的构建原理
那么vite
的构建原理又是什么呢?大家在日常使用vite
创建一个vue项目时,有没有注意到,初次创建的这个vue项目会发生很多的请求
这是为什么呢?原来啊对于vite
而言,它会先读取到该项目的代码,然后将唯一的html文件输出给浏览器,让浏览器加载,然后加载引入JavaScript文件,此时浏览器就会向vite
要JavaScript文件,vite
也就会去项目中找JavaScript文件.使用vite
构建工具,那么该项目的文件会被浏览器当做资源请求,将整个vue文件都做成接口请求.
手搓实现vite的工作流程
这是该项目的目录结构,我们使用npm init -y
初始化该项目,并将vue
的源码下载过来了方便使用.
我们现在目的是打造一个能实现vite
工作流程一样的项目出来.创建了一个叫作simple-vite.js
文件.
这里我们使用的Node.js
原生所具有的模块.
const http = require('http')
const fs = require('fs')
const path = require('path')
http
用于创建一个服务,fs
和path
则是用于读取文件内容或者写文件内容以及获取文件相对应的路由.
const server = http.createServer((req, res) => {
})
server.listen(5173, () => {
console.log('项目运行在 5173');
})
第一步我们在req
中解构出我们发生请求的url
地址,我们拿到这url
,去判断是请求一个什么文件,假设请求的是根目录文件,也就是说url === '/'
我们就返回唯一的html文件给浏览器.如果请求的是一个js文件,那么我们就通过path.resolve(__dirname, url.slice(1))
拿到该文件的路径,然后返回给浏览器,这里我们打造了一个函数用于重构一下import
引入的写法.
function rewriteImport(content) {
return content.replace(/ from ['|"]([^'"]+)['|"]/g, function(s0, s1) {
if (s1[0] !== '.' && s1[1] !== '/') {
return ` from '/@modules/${s1}'`
} else {
return s0
}
})
}
变成了这样,而不是import {createApp} from "vue"
,这样子方便我们后续去找vue
的源码使用.
如果url
读取到的是url.startsWith('/@modules/')
这样我们就需要在node_modules
文件夹下找.假设这个路径是/@modules/vue
为了方便我们在node_modules
中查找我们就需要将/@modules/vue
使用replace
方法变成/vue
这样我们就能准确找到该文件夹了,那么源码会在哪里呢?会在vue
文件夹下的package.json
中的module
,将其返回给浏览器即可.
如果读取到的是一份vue
文件的话,首先我们先获取到该文件的完整路径.我们都是找一个Vue文件是包括template
,script
,style
三部分的,所以我们就要借助Vue.js 生态系统中的两个重要模块@vue/compiler-sfc
和 @vue/compiler-dom
,通过const { descriptor } = compilerSfc.parse(fs.readFileSync(p, 'utf8'))
解析出vue文件的各个部分。然后动态生成一个JavaScript文件.并且将<template>
部分的编译结果作为渲染函数导入进来。
const content = `
${rewriteImport(descriptor.script.content.replace('export default', 'const __script = '))}
import { render as __render } from "${url}?type=template"
__script.render = __render
export default __script
`
当客户端请求带有 type=template
查询参数的 .vue
文件时,服务器会返回该文件的模板部分经过编译后的 JavaScript 代码
else if (query.get('type') === 'template') { // 问我要 App.vue 的 template 部分
const template = descriptor.template
const render = compilerDom.compile(template.content, {mode: 'module'}).code
res.writeHead(200, {'Content-Type': 'application/javascript'})
res.end(rewriteImport(render))
}
当url.endsWith('.css')
判断为一个css文件,我们需要处理css文件的请求,并将css文件的转化成为一个JavaScript文件,该文件会帮助我们再客户端动态生成一份style
.
然后我们运行一下该项目看看是否和vite
一样发送接口请求获取资源文件
该项目的主要功能的完整代码:
const http = require('http')
const fs = require('fs')
const path = require('path')
const compilerSfc = require('@vue/compiler-sfc')
const compilerDom = require('@vue/compiler-dom')
function rewriteImport(content) {
return content.replace(
if (s1[0] !== '.' && s1[1] !== '/') {
return ` from '/@modules/${s1}'`
} else {
return s0
}
})
}
const server = http.createServer((req, res) => {
const { url } = req
const query = new URL(req.url, `http://${req.headers.host}`).searchParams;
if (url === '/') {
res.writeHead(200, {
'content-type': 'text/html'
})
let content = fs.readFileSync('./index.html', 'utf-8')
res.end(content)
} else if (url.endsWith('.js')) { // '/src/main.js'
const p = path.resolve(__dirname, url.slice(1))
res.writeHead(200, {
'content-type': 'application/javascript'
})
let content = fs.readFileSync(p, 'utf-8')
res.end(rewriteImport(content))
} else if (url.startsWith('/@modules/')) { // '/@modules/vue'
const prefix = path.resolve(__dirname, 'node_modules', url.replace('/@modules/', ''))
const module = require(prefix + '/package.json').module
const p = path.resolve(prefix, module)
const content = fs.readFileSync(p, 'utf-8')
res.writeHead(200, {
'content-type': 'application/javascript'
})
res.end(rewriteImport(content))
} else if (url.indexOf('.vue') !== -1) {
const p = path.resolve(__dirname, url.split('?')[0].slice(1))
const { descriptor } = compilerSfc.parse(fs.readFileSync(p, 'utf8'))
if (!query.get('type')) {
res.writeHead(200, {'Content-Type': 'application/javascript'})
const content = `
${rewriteImport(descriptor.script.content.replace('export default', 'const __script = '))}
import { render as __render } from "${url}?type=template"
__script.render = __render
export default __script
`
res.end(content)
} else if (query.get('type') === 'template') {
const template = descriptor.template
const render = compilerDom.compile(template.content, {mode: 'module'}).code
res.writeHead(200, {'Content-Type': 'application/javascript'})
res.end(rewriteImport(render))
}
} else if (url.endsWith('.css')) {
const p = path.resolve(__dirname, url.slice(1))
const file = fs.readFileSync(p, 'utf8')
const content = `
const css = "${file.replace(/\n/g,'')}"
let link = document.createElement('style')
link.setAttribute('type','text/css')
document.head.appendChild(link)
link.innerHTML = css
export default css
`
res.writeHead(200, {'Content-Type': 'application/javascript'})
res.end(content)
}
})
server.listen(5173, () => {
console.log('项目运行在 5173');
})
本文到此就结束了,希望对大家有所帮助,如有不足,恳请各位大侠指点一二,让我功力
精进一二!!!
转载自:https://juejin.cn/post/7418797990304858175