当前位置:网站首页>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/就可以看到如下图的界面。
边栏推荐
- AQS同步组件-FutureTask解析和用例
- Solve 1. tensorflow runs using CPU but not GPU 2. GPU version number in tensorflow environment 3. Correspondence between tensorflow and cuda and cudnn versions 4. Check cuda and cudnn versions
- 【 original 】 VMware Workstation implementation Openwrt soft routing, the ESXI, content is very detailed!
- 深度学习--循环神经网络(Recurrent Neural Network)
- String类型的字符串对象转实体类和String类型的Array转List
- [Original] Usage of @PrePersist and @PreUpdate in JPA
- 基于STM32设计的环境检测设备
- Create a table in a MySQL database through Doc
- 1006 Sign In and Sign Out (25分)
- centos7.5 设置Mysql开机自启动
猜你喜欢
随机推荐
性能测试(01)-jmeter元件-线程组、调试取样器
Mysql多表查询
实测办公场景下,国产远程控制软件的表现力如何?(技术解析)
微信小程序——天气查询
多商户商城系统功能拆解26讲-平台端分销设置
VBA实战(11) - 工作表(Sheet) 操作汇总
1007 Maximum Subsequence Sum (25分)
json库的dumps()方法和loads()方法
Dialogue with the DPO of a multinational consumer brand: How to start with data security compliance?See you on 8.11 Live!
编解码(seq2seq)+注意机制(attention) 详细讲解
unix环境编程 第十五章 15.9 共享存储
Multi-merchant mall system function disassembly 26 lectures - platform-side distribution settings
torch.cat()函数的官方解释,详解以及例子
MySQL外键在数据库中的作用
The complete grammar of CSDN's markdown editor
[华为云在线课程][SQL语法分类][数据操作][学习笔记]
985毕业,工作3年,分享从阿里辞职到了国企的一路辛酸和经验
Received your first five-figure salary
山东招远通报星童幼儿园食品安全问题最新调查情况
RPN principle in faster-rcnn