当前位置:网站首页>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/就可以看到如下图的界面。
边栏推荐
- 遇到恶意退款不用怕,App 内购买项目的退款通知现已可用
- unix环境编程 第十五章 15.9 共享存储
- arcgis制图之天地图符号样式配置
- TensorFlow:NameError: name ‘input_data’ is not defined
- 性能测试(05)-表达式和业务关联-json关联
- Product Quantization (PQ)
- Shell script combat (2nd edition) / People's Posts and Telecommunications Press Script 2 Validate input: letters and numbers only
- MySQL外键在数据库中的作用
- unix环境编程 第十五章 15.3 函数popen和pclose
- 解决1.tensorflow运行使用CPU不使用GPU 2.tensorflow环境下的GPU版本号 3.tensorflow和cuda以及cudnn版本对应问题 4.查看cuda和cudnn版本
猜你喜欢

Solve the ali cloud oss - the original 】 【 exe double-click response can't open, to provide a solution

arcgis制图之天地图符号样式配置

相关系数计算,热力图绘制,代码实现

The complete grammar of CSDN's markdown editor

性能测试(01)-jmeter元件-线程组、调试取样器

【原创】VMware Workstation实现Openwrt软路由功能,非ESXI,内容非常详细!

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

pytorch widedeep文档

C语言统计不同单词数

机器学习--线性回归(Linear Regression)
随机推荐
MATLAB代码实现三次样条插值
从位图到布隆过滤器
Quartz的理解
一键完成物联网产品注册,快速体验在线调试设备
unix环境编程 第十五章 15.5FIFO
UNIX Environment Programming Chapter 15 15.5FIFO
1006 Sign In and Sign Out (25分)
彻底理解工厂模式
人物 | 从程序员到架构师,我是如何快速成长的?
Getting Started with MNIST Machine Learning
Netscope: Online visualization tool for neural network structures
性能测试(04)-表达式和业务关联-JDBC关联
乘积量化(PQ)
RPN principle in faster-rcnn
Cluster understanding
numpy库中的函数 bincount() where() diag() all()
备战金三银四:如何成功拿到阿里offer(经历+面试题+如何准备)
shell脚本实战(第2版)/人民邮电出版社 脚本1 在PATH中查找程序
Shell script combat (2nd edition) / People's Posts and Telecommunications Press Script 1 Find programs in the PATH
MySQL外键在数据库中的作用