当前位置:网站首页>vite的原理,手写vite
vite的原理,手写vite
2022-08-09 10:51:00 【榴莲不好吃】
什么是vite
一个基于浏览器原生ES模块的开发服务器。利用浏览器去解析模块,在服务器端按需编译返回,完全跳过了打包这个概念,服务器随起随用。同时另有有Vue文件支持,还搞定定了热更新,而且热更新的速度不会随着模块增加而变慢
vite的实现原理
Vite在浏览器端使用的是 export import 方式导入和导出的模块;
vite同时实现了按需加载;
Vite高度依赖module script特性。
Vite由两个主要部分组成:
1.dev server:利用浏览器的ESM能力来提供源文件,具有丰富的内置功能并具有高效的HMR
2.生产构建:生产环境利用Rollup来构建代码,提供指令用来优化构建过程
Vite的特点
Instant Server Start —— 即时服务启动
Lightning Fast HMR —— 闪电般快速的热更新
Rich Features —— 丰富的功能
Optimized Build —— 经过优化的构建
Universal Plugin Interface —— 通用的Plugin接口
Fully Typed APIs —— 类型齐全的API
为了实现上述特点,Vite要求项目完全由ES模块模块组成,common.js模块不能直接在Vite上使用。因此不能直接在生产环境中使用。在打包上依旧还是使用rollup等传统打包工具。因此Vite目前更像是一个webpack-dev-server的开发工具。
手动实现简易版Vite
需要用到的模块有
vue,koa
分别安装他们
npm i vue koa
需要使用koa来搭建一个服务器
const Koa = require("koa");
const app = new Koa()
app.use(async ctx => {
...代码实现
})
// 设置监听的端口号
app.listen(3000, () => {
console.log("vite start up....");
})
项目目录如下,index.html引入入口文件main.js
,main.js以及App.vue
// mian.js
import {
createApp, h} from "vue";
import App from "./App.vue"
createApp(App).mount("#app")
// app.vue
<template>
<div class="title">{
{
title}}</div>
</template>
<script>
import {
reactive, toRefs } from 'vue'
export default {
setup() {
const state = reactive({
title:"标题" })
return {
...toRefs(state) }
},
}
</script>
<style scoped> .title{
font-size: 20px;background:red;} </style>
从ctx中解构出当前请求的url对url进行分析
app.use(async ctx => {
const {
url } = ctx.request
// 首页请求,加载index.html,
if (url == "/") {
ctx.type = "text/html"
ctx.body = fs.readFileSync(path.join(__dirname, "./index.html"), "utf8")
}
})
访问index.html的时候会接着访问里面的mian.js,main.js里面引用了vue最终的都会被替换成,
import {createApp, h} from ‘/@modules/vue’;
// 对js文件进行处理
else if (url.endsWith(".js")) {
const p = path.join(__dirname, url);
ctx.type = "application/javascript";
// main.js里面引用了node_module的模块,此处需要将地址重写,打个标记,例如
// inport xx from 'vue' 替换成 inport xx from '/@modules/vue'
ctx.body = modulerewriteImport(fs.readFileSync(p, "utf8"));
}
/@modules/的路径处理,在node_module里面查到对应的js文件(es5文件)
else if (url.startsWith('/@modules/')) {
// 截取模块名称,例如 /@modules/vue 中的vue,对应的就是node_module文件夹下的vue文件夹
const moduleName = url.replace("/@modules/", ""); //
// 去node_modules目录中找,最终找到当前模块的完整路径
const prefix = path.join(__dirname, "../node_modules", moduleName);
// prefix 路径下,找到package.json文件,然后获取module字段,
const module = require(prefix + "/package.json").module;
// 找到对应的es5文件地址
const filePath = path.join(prefix, module);
ctx.type = "application/javascript"
// 读取改该文件
ctx.body = modulerewriteImport(fs.readFileSync(filePath, "utf8"))
}
接下来是对vue文件的处理,此处需要用到vue内置的插件@vue/compiler-sfc和@vue/compiler-dom
@vue/compiler-sfc:会解析三大要素,template,script,styles
@vue/compiler-dom: 将template编译成下面这种render函数,
import {… } from “vue”
export function render(_ctx, _cache) {
return (_openBlock(), _createElementBlock(“div”, { class: “title” }, _toDisplayString(_ctx.title), 1 /* TEXT */))
}
// 查看当前是不是vue文件
else if (url.indexOf('.vue') > -1) {
// 获取加载文件路径
const p = path.join(__dirname, url.split("?")[0])
const fileData = await fs.readFileSync(p, 'utf8')
const ret = compilerSFC.parse(fileData)
// console.log(ret); // 可以看下ret,结果都在content中
if (!query.type) {
// 第一次访问页面,提取script部分,然后,将template部分重新拼接成一个请求,并加上type=template标识
// SFC请求
// 读取vue文件,解析为js,将script里面的代码单独拿出来解析
// 获取脚本部分的内容
const scriptContent = ret.descriptor.script.content
// 替换默认到处为一个常量,方便后续修改
const script = scriptContent.replace('export default', 'const __script = ');
ctx.type = "application/javascript"
ctx.body = ` ${
modulerewriteImport(script)} // template 部分 import {render as __render} from '${
url}?type=template' __script.render = __render export default __script; `;
} else if (query.type == 'template') {
// 如果有template标识,就处理template里面的代码
// 这里面的处理其实就是处理上面import {render as __render} from '${url}?type=template'的请求
const tpl = ret.descriptor.template.content;
// 编译为render
const render = compilerDOM.compile(tpl, {
mode: 'module' }).code
ctx.type = "application/javascript"
// css处理
let css = ''
ret.descriptor.styles.forEach((item, index) => {
css += ` const style${
index} = document.createElement('style') style${
index}.setAttribute('type', 'text/css') style${
index}.innerHTML = \"${
item.content.replace(/\r\n/g, "")}\" document.head.appendChild(style${
index}) `
})
//body里得到的其实就是 __script.render = __render的render函数
ctx.body = ` ${
modulerewriteImport(render)} ${
modulerewriteImport(css)} `;
}
}
重写模块路径方法
// 模块地址重写 inport xx from 'vue' -> inport xx from '/@modules/vue'
function modulerewriteImport(content) {
return content.replace(/ from ['"](.*)['"]/g, function (s1, s2) {
if (s2.startsWith('./') || s2.startsWith('/') || s2.startsWith('../')) {
return s1
} else {
return ` from '/@modules/${
s2}'`
}
})
}
到这里我们在package.json里面添加命令
"scripts": {
"lanvite": "node ./lanvite.js" // 开发调试的话建议安装nodemon
},
启动 npm run lanvite打开http://localhost:3000/就可以看到如下图的界面。
边栏推荐
- [Error record] Solve the problem that ASRock J3455-ITX cannot be turned on without a monitor plugged in
- arcgis制图之天地图符号样式配置
- faster-rcnn中的RPN原理
- 实测办公场景下,国产远程控制软件的表现力如何?(技术解析)
- 机器学习--线性回归(Linear Regression)
- 深度学习--生成对抗网络(Generative Adversarial Nets)
- 相关系数计算,热力图绘制,代码实现
- 深度学习--循环神经网络(Recurrent Neural Network)
- unix环境编程 第十五章 15.7消息队列
- 强化学习 (Reinforcement Learning)
猜你喜欢

Shell script combat (2nd edition) / People's Posts and Telecommunications Press Script 1 Find programs in the PATH

985毕业,工作3年,分享从阿里辞职到了国企的一路辛酸和经验

prometheus接入mysqld_exporter

类与对象 (下)

OneNote 教程,如何在 OneNote 中搜索和查找笔记?

非科班毕业生,五面阿里:四轮技术面+HR一面已拿offer

深度学习--循环神经网络(Recurrent Neural Network)

CSDN的markdown编辑器语法完整大全

OpenSSF's open source software risk assessment tool: Scorecards

真香!肝完Alibaba这份面试通关宝典,我成功拿下今年第15个Offer
随机推荐
2022强网杯WP
信息系统项目的十大管理
jmeter BeanShell 后置处理器
cnn的输入输出
Oracle数据库常用函数总结
在webgis中显示矢量化后的风险防控信息
性能测试(06)-逻辑控制器
OpenSSF's open source software risk assessment tool: Scorecards
TensorFlow—计算梯度与控制梯度 : tf.gradients和compute_gradients和apply_gradients和clip_by_global_norm控制梯度
[Error record] Solve the problem that ASRock J3455-ITX cannot be turned on without a monitor plugged in
Netscope: Online visualization tool for neural network structures
【报错记录】解决华擎J3455-ITX不插显示器无法开机的问题
Official explanation, detailed explanation and example of torch.cat() function
Tensorflow realize parameter adjustment of linear equations
UNIX Environment Programming Chapter 15 15.6 XSI IPC
对话跨国消费品牌DPO:数据安全合规从何做起?8.11直播见!
Mysql多表查询
tensorflow和numpy对应的版本,报FutureWarning: Passing (type, 1) or ‘1type‘ as a synonym of type is deprecate
深度学习--自编码器(AutoEncoder)
C语言数组题_校门外的树_标记法