当前位置:网站首页>通过模拟Vite一起深入其工作原理
通过模拟Vite一起深入其工作原理
2022-08-05 02:47:00 【懒得跟猪打架】
Vite实现原理(静态web服务器)
vite的核心功能
- 静态web服务器
- 启动Vite的时候,会将当前目录作文静态文件服务器的根目录。
- 静态文件服务器会拦截部分请求,例如的单文件组件,拦截请求进行即时编译并返回结果。以及样式预编译模块代码。
- 内部使用的是
koa框架来开启静态文件服务器
- 编译单文件组件
- 拦截浏览器不识别的模块并处理
- HMR
- 通过web socket实现模块热替换。
模拟vite实现一个开启静态文件服务器的CLI
vite-cli 模拟项目
首先我们开一下vite项目的前端请求资源的路径及内容和项目目录文件的差异:
浏览器请求:
第2个是client是HMR所用到的web socket 的client,这里我们仅模拟实现基本的核心功能,此处忽略HMR相关的内容。
来看第三个main.js
// main.js
import {
createApp } from '/node_modules/.vite/deps/vue.js?v=b039f9b3'
import App from '/src/App.vue'
createApp(App).mount('#app')
可以看到其中的路径跟我们的源代码是有区别的。我们手写源代码的内容如下:
import {
createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
import后面导入的资源路径差异:
如果是第三方模块:请求路径会转化为/node_modules/开头真正的模块文件在当前项目的第三方资源相对路径。
自编写的模块:请求路径./会转化为/src/开头
再看后续请求到的.vue后缀的单文件组件资源:
// App.vue
import {
createHotContext as __vite__createHotContext } from "/@vite/client";import.meta.hot = __vite__createHotContext("/src/App.vue");import HelloWorld from '/src/components/HelloWorld.vue'
// This starter template is using Vue 3 experimental <script setup> SFCs
// Check out https://github.com/vuejs/rfcs/blob/master/active-rfcs/0040-script-setup.md
const _sfc_main = {
__name: 'App',
setup(__props, {
expose }) {
expose();
const __returned__ = {
HelloWorld }
Object.defineProperty(__returned__, '__isScriptSetup', {
enumerable: false, value: true })
return __returned__
}
}
import {
createElementVNode as _createElementVNode, createVNode as _createVNode, Fragment as _Fragment, openBlock as _openBlock, createElementBlock as _createElementBlock } from "/node_modules/.vite/deps/vue.js?v=b039f9b3"
const _hoisted_1 = /*#__PURE__*/_createElementVNode("img", {
alt: "Vue logo",
src: "/src/assets/logo.png"
}, null, -1 /* HOISTED */)
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createElementBlock(_Fragment, null, [
_hoisted_1,
_createVNode($setup["HelloWorld"], {
msg: "Hello Vue 3 + Vite" })
], 64 /* STABLE_FRAGMENT */))
}
import "/src/App.vue?vue&type=style&index=0&lang.css"
_sfc_main.__hmrId = "7ba5bd90"
typeof __VUE_HMR_RUNTIME__ !== 'undefined' && __VUE_HMR_RUNTIME__.createRecord(_sfc_main.__hmrId, _sfc_main)
import.meta.hot.accept(({
default: updated, _rerender_only }) => {
if (_rerender_only) {
__VUE_HMR_RUNTIME__.rerender(updated.__hmrId, updated.render)
} else {
__VUE_HMR_RUNTIME__.reload(updated.__hmrId, updated)
}
})
import _export_sfc from '/@id/plugin-vue:export-helper'
export default /*#__PURE__*/_export_sfc(_sfc_main, [['render',_sfc_render],['__file',"/Users/huiquandeng/projects/lg-fed-lp/my-module/vite-cli-demo/vite-demo/src/App.vue"]])
//# sourceMappingURL=data:application/json;base64,eyJ2...
我们自编写的App.vue,内容如下:
<template>
<img alt="Vue logo" src="./assets/logo.png" />
<HelloWorld msg="Hello Vue 3 + Vite" />
</template>
<script setup> import HelloWorld from './components/HelloWorld.vue' // This starter template is using Vue 3 experimental <script setup> SFCs // Check out https://github.com/vuejs/rfcs/blob/master/active-rfcs/0040-script-setup.md </script>
<style> #app {
font-family: Avenir, Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 60px; } </style>
差别在于服务端返回了@vue/compiler-sfc编译的内容,内部的自定义组件访问路径被修改为了/src/App.vue?vue&type=style&index=0&lang.css,并且返回的内容最后添加了//# sourceMappingURL=data:application/json;base64,eyJ2Z...的sourceMap项目内容。
后面的HelloWorld.vue也是同样情况。
请求的HelloWorld.vue内容:
import {
createHotContext as __vite__createHotContext } from "/@vite/client";import.meta.hot = __vite__createHotContext("/src/components/HelloWorld.vue");import {
reactive } from '/node_modules/.vite/deps/vue.js?v=b039f9b3'
const _sfc_main = {
__name: 'HelloWorld',
props: {
msg: String
},
setup(__props, {
expose }) {
expose();
const state = reactive({
count: 0 })
const __returned__ = {
state, reactive }
Object.defineProperty(__returned__, '__isScriptSetup', {
enumerable: false, value: true })
return __returned__
}
}
import {
toDisplayString as _toDisplayString, createElementVNode as _createElementVNode, createTextVNode as _createTextVNode, Fragment as _Fragment, openBlock as _openBlock, createElementBlock as _createElementBlock, pushScopeId as _pushScopeId, popScopeId as _popScopeId } from "/node_modules/.vite/deps/vue.js?v=b039f9b3"
const _withScopeId = n => (_pushScopeId("data-v-469af010"),n=n(),_popScopeId(),n)
const _hoisted_1 = /*#__PURE__*/ _withScopeId(() => /*#__PURE__*/_createElementVNode("p", null, [
/*#__PURE__*/_createElementVNode("a", {
href: "https://vitejs.dev/guide/features.html",
target: "_blank"
}, " Vite Documentation "),
/*#__PURE__*/_createTextVNode(" | "),
/*#__PURE__*/_createElementVNode("a", {
href: "https://v3.vuejs.org/",
target: "_blank"
}, "Vue 3 Documentation")
], -1 /* HOISTED */))
const _hoisted_2 = /*#__PURE__*/ _withScopeId(() => /*#__PURE__*/_createElementVNode("p", null, [
/*#__PURE__*/_createTextVNode(" Edit "),
/*#__PURE__*/_createElementVNode("code", null, "components/HelloWorld.vue"),
/*#__PURE__*/_createTextVNode(" to test hot module replacement. ")
], -1 /* HOISTED */))
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createElementBlock(_Fragment, null, [
_createElementVNode("h1", null, _toDisplayString($props.msg), 1 /* TEXT */),
_hoisted_1,
_createElementVNode("button", {
type: "button",
onClick: _cache[0] || (_cache[0] = $event => ($setup.state.count++))
}, " count is: " + _toDisplayString($setup.state.count), 1 /* TEXT */),
_hoisted_2
], 64 /* STABLE_FRAGMENT */))
}
import "/src/components/HelloWorld.vue?vue&type=style&index=0&scoped=true&lang.css"
_sfc_main.__hmrId = "469af010"
typeof __VUE_HMR_RUNTIME__ !== 'undefined' && __VUE_HMR_RUNTIME__.createRecord(_sfc_main.__hmrId, _sfc_main)
import.meta.hot.accept(({
default: updated, _rerender_only }) => {
if (_rerender_only) {
__VUE_HMR_RUNTIME__.rerender(updated.__hmrId, updated.render)
} else {
__VUE_HMR_RUNTIME__.reload(updated.__hmrId, updated)
}
})
import _export_sfc from '/@id/plugin-vue:export-helper'
export default /*#__PURE__*/_export_sfc(_sfc_main, [['render',_sfc_render],['__scopeId',"data-v-469af010"],['__file',"/Users/huiquandeng/projects/lg-fed-lp/my-module/vite-cli-demo/vite-demo/src/components/HelloWorld.vue"]])
//# sourceMappingURL=data:application/json;base64,eyJ2Z...
我们手编的HelloWorld.vue,内容如下:
<template>
<h1>{
{ msg }}</h1>
<p>
<a href="https://vitejs.dev/guide/features.html" target="_blank">
Vite Documentation
</a>
|
<a href="https://v3.vuejs.org/" target="_blank">Vue 3 Documentation</a>
</p>
<button type="button" @click="state.count++">
count is: {
{ state.count }}
</button>
<p>
Edit
<code>components/HelloWorld.vue</code> to test hot module replacement.
</p>
</template>
<script setup> import {
defineProps, reactive } from 'vue' defineProps({
msg: String }) const state = reactive({
count: 0 }) </script>
<style scoped> a {
color: #42b983; } </style>
其中的路径同样是被转换成了/src/components/HelloWorld.vue?vue&type=style&index=0&scoped=true&lang.css
所以紧接着上面两个.vue后缀的当文件组件会再次请求,并且带着一些参数如下:vue&type=style&index=0&scoped=true&lang.css,解析Query string parameters值如下:
vue:
type: style
index: 0
scoped: true
lang.css:
vue 指示当前单文件组件使用的模版的框架类型type 指示当前气球模版中的资源类型,处理该单文件组件时需要使用对应vue的compiler进行编译,获得的类型有template/style/script等。index 这个是不同不同类型资源可能存在多部分,index是数组的索引scoped 指示的是当前组件文件中的样式文件的是局部作用域还是全局的lang.css 指示当前的样式所使用的样式预编译语言类型。如果是scss/less/stylus/postcss等则需要相应的预编译处理。
模拟实现vite-cli
根据以上分析,我们模拟vite-cli的步骤大概可以分为以下几步:
- 使用koa框架创建一个静态文件服务器。
- 利用中间件出修改第三方模块的路径
- 加载第三方模块
- 编译单文件组件
- 处理其他类型的静态资源文件的的请求路径
- 要想速度优化得更快,还需要为各个转化过程添加相对应的缓存,以避免相同文件进行相同步骤的转换操作。
实现代码
首先创建一个目录,使用npm init -y进行初始化后,package.json中配置CLI项目的必须选项bin, 如下:{“bin”: "index.js}"
所以我们项目的入口文件就是index.js,内容如下:
// index.js
// 这个家伙太懒了,看完源码都懒得实现了
// 源码vite中还实现了plugin形式配置使用的框架
全文终…
边栏推荐
猜你喜欢

DAY23: Command Execution & Code Execution Vulnerability

The 20th day of the special assault version of the sword offer
![[ROS] (10) ROS Communication - Service Communication](/img/4d/4657f24bd7809abb4bdc4b418076f7.png)
[ROS] (10) ROS Communication - Service Communication

01 【前言 基础使用 核心概念】

2022-08-04: Input: deduplicated array arr, the numbers in it only contain 0~9.limit, a number.Return: The maximum number that can be spelled out with arr if the requirement is smaller than limit.from

倒计时 2 天|云原生 Meetup 广州站,等你来!

【LeetCode刷题】-数之和专题(待补充更多题目)

【OpenCV 图像处理2】:OpenCV 基础知识

What should I do if the self-incrementing id of online MySQL is exhausted?

dmp(dump)转储文件
随机推荐
QT语言文件制作
Industry case | insurance companies of the world's top 500 construction standards can be used to drive the business analysis system
In 2022, you still can't "low code"?Data science can also play with Low-Code!
Physical backup issues caused by soft links
采用redis缓存的linux主从同步服务器图片硬盘满了移到新目录要修改哪些指向
C student management system Insert the student node at the specified location
Multithreading (2)
HDU 1114: Piggy-Bank ← The Complete Knapsack Problem
继承关系下构造方法的访问特点
How OpenGL works
Simple description of linked list and simple implementation of code
解决端口占用问题 Port xxxx was already in use
Apache DolphinScheduler, a new generation of distributed workflow task scheduling platform in practice - Medium
mysql tree structure query problem
View handler 踩坑记录
语法基础(变量、输入输出、表达式与顺序语句)
private package
The 22-07-31 weeks summary
Everyone in China said data, you need to focus on core characteristic is what?
常见的硬件延迟