当前位置:网站首页>通过模拟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形式配置使用的框架
全文终…
边栏推荐
- Semi-Decentralized Federated Learning for Cooperative D2D Local Model Aggregation
- C student management system Find student nodes based on student ID
- DAY22: sqli-labs shooting range clearance wp (Less01~~Less20)
- 线上MySQL的自增id用尽怎么办?
- matlab绘制用颜色表示模值大小的箭头图
- C language diary 9 3 kinds of statements of if
- 01 【前言 基础使用 核心概念】
- Data storage practice based on left-order traversal
- [Fortune-telling-60]: "The Soldier, the Tricky Way"-2-Interpretation of Sun Tzu's Art of War
- Introduction to SDC
猜你喜欢

Unleashing the engine of technological innovation, Intel joins hands with ecological partners to promote the vigorous development of smart retail

LeetCode uses the minimum cost to climb the stairs----dp problem

C语言实现简单猜数字游戏

Go 微服务开发框架 DMicro 的设计思路

select 标签自定义样式

nodeJs--encapsulate routing

甘特图来啦,项目管理神器,模板直接用

继承关系下构造方法的访问特点

Everyone in China said data, you need to focus on core characteristic is what?

leetcode 15
随机推荐
word分栏小记
undo problem
[C language] Detailed explanation of stacks and queues (define, destroy, and data operations)
mysql tree structure query problem
2022-08-04:输入:去重数组arr,里面的数只包含0~9。limit,一个数字。 返回:要求比limit小的情况下,能够用arr拼出来的最大数字。 来自字节。
继承关系下构造方法的访问特点
继承关系下构造方法的访问特点
leetcode - a subtree of another tree
Go 微服务开发框架 DMicro 的设计思路
Matlab drawing 3
How to solve the error cannot update secondary snapshot during a parallel operation when the PostgreSQL database uses navicat to open the table structure?
The linear table lookup
select 标签自定义样式
Ant Sword Advanced Module Development
【LeetCode刷题】-数之和专题(待补充更多题目)
The Tanabata copywriting you want has been sorted out for you!
【解密】OpenSea免费创造的NFT都没上链竟能出现在我的钱包里?
后期学习计划
Study Notes-----Left-biased Tree
正则表达式,匹配中间的某一段字符串