实用的VUE系列——vite 真的跟 vue 没关系,他只是这个团队的人写的构建工具
点击关注公众号,“技术干货”及时达!vite 到底是什么说起vite 很多jym可能非常熟悉,并且脱口而出,「不就是vue脚手架吗」?
我说,说的对,但是不全对,他除了可以是vue的脚手架,还可以是react脚手架,还可以是svelte的脚手架
目前支持的模板预设如下:
其实这种东西,他有一个特殊的名字,叫「构建工具」
接下来,我们开始第二个问题,构建工具和脚手架有什么区别呢?
脚手架所谓脚手架是创建前端项目的命令行工具,集成了常用的功能和配置,方便我们快速搭建项目
说人话
脚手架建立的是前端的工程
他有以下特点
可以帮助我们快速生成项目的基础代码脚手架工具的项目模板经过了开发者的提炼和检验,一定程度上代表了某类项目的最佳实践脚手架工具支持使用自定义模板,我们可以根据不同的项目进行“定制”构建工具所谓构建工具,我的理解其实就是解放我们双手的自动化的兼容不同的代码的转换工具
es语法现在已经更新到了ECMAScript2024,我们在写代码的时候就可以用最新的语法
「可浏览器做不到啊,因为他不认识,于是这时候就需要有一种工具能自动化的将代码打包成浏览器认识的代码」
这时候就需要构建工具
所谓前端构建工具 「简单的说就是将我们开发环境的代码,转化成生产环境可用来部署的代码。」
那么脚手架跟构建工具有什么区别呢?
区别我们知道脚手架是为了创建项目的,而构建工具是为了打包项目的
也就是说,脚手架中包含构建工具,并且传统意义上的构建工具(注意:这里不一定是前端构建工具)可构建脚手架
所以他们是可能一个交叉关系,
很绕是吧,总而言之,言而总之,我们只需要理解,他们一个是打包的,一个是制作打包的就可以了,这也不是咱们本次的重点
当然,只拿前端构建工具来说应该是个包含关系(脚手架包含构建工具)
在vite 出现之前,我们还是要礼貌性的介绍一下他的前辈们
其他的构建工具们在vite 出现之前,
有远古时代的browserify、grunt,glup ,有传统的Webpack、Rollup、Parcel,也有现代的Esbuild、Vite
他们的出现分别是为了解决不同的痛点承载不同的使命。有的已经退出历史舞台,有的即将退出历史舞台
有人深入群众,地位不可撼动,总之,感谢他们,让我们看到到了这辉煌而又繁荣的前端江湖
在这个江湖中,如果说地位最牢固的,当属webpack
那么他到底和vite 有什么区别呢?
vite 和 webpack 的区别开始之前我们来说一下 vite 的优点
1.光速启动2.热模块替换3.按需编译看了以上这三个优点,我们就可以用一句话概括,「vite 和 webpack 在生产环境没有区别」
vite 主要解决的问题是开发环境的体验问题
他对比webpack用一句很中肯的话可以评价「生态落后,技术领先,开发体验好」
我们一个个来解释
生态落后这个就不需要多说了,老牌打包工具webpack 插件生态琳琅满目,只要你业务能用到的,基本上都能找到插件使用,这一点,我对后来的后起之秀们说,「革命尚未成功,同志仍需努力」
而 vite虽然继承了 rolup 的一些插件机制,可与老大哥还是有一定差距
技术领先这个就更不用多说了, 使用esbuild 插件机制 常用业务中常用功能都做到了顶尖,
而反观 webpack就是,单拎出来哪一项都不行,总体来看条件还行
开发体验好这个但凡用过 webpack 和 vite 的 对比下就知道,高下立判
所以,大家在选用构建工具的时候,请根据自身项目的需求,酌情选择,就好像择偶一样
「你是想要个好看的,还是想要个条件好的」
请想清楚!!!!
vite原理说起原理,其实这是一个老生常谈的问题,因为前面有无说的人讲过了
——vite 在开发环境就是利用 script type="module" 现代浏览器支持模块化语法的能力,动态按需加载代码
但我认为其实不是这么简单
所以这一次,结合一个项目来简单研究下原理
div id="app"/div
script type="module"
import { createApp } from 'vue'
import Main from './Main.vue'
createApp(Main).mount('#app')
/script
上方就是我们的 html 文件,当我们去访问这个 html 文件的时候他 虽然他支持module 但也是执行不了的,因为vue路径找不到
所以虽然 vite 号称是利用module,但其实还是要对源文件做处理,他支持的 esm 模块,要是浏览器认识的模块才行,于是他也要对代码进行编译,只不过是即时编译
以上代码就是编译之后的 html文件
接下来执行执行其中代码 请求 vue.js文件 请求.vue文件
然后问题来了vue.js 浏览器好歹认识 可这个.vue怎么办呢?
这时候就是 vite的服务就起作用了,继续处理,本质上来讲, 当我访问到 .vue 文件在浏览器层面就要发请求了 要不就是文件系统请求,要不就是 http 请求
于是 vite 就可以上手段了,他对这个请求做处理不就好了嘛!返回浏览器能看得懂的
源代码
template
h1Vueversion{{version}}/h1
divclass="comments"!--hello--/div
pre{{timeasstring}}/pre
divclass="hmr-block"
Hmr/
/div
/template
scriptsetuplang="ts"
import{version,defineAsyncComponent}from'vue'
importHmrfrom'./Hmr.vue'
import{ref}from'vue'
constTsGeneric=defineAsyncComponent(()=import('./TsGeneric.vue'))
consttime=ref('loading...')
window.addEventListener('load',()={
setTimeout(()={
const[entry]=performance.getEntriesByType('navigation')
time.value=`loadedin${entry.duration.toFixed(2)}ms.`
},0)
})
/script
stylescoped
.comments{
color:
}
/style
摇身一变就会成为这样
//浏览器热更新内容
import{createHotContextas__vite__createHotContext}from"/@vite/client";import.meta.hot=__vite__createHotContext("/Main.vue");import{defineComponentas_defineComponent}from"/node_modules/.vite/deps/vue.js?v=e88b16ae";
import{version,defineAsyncComponent}from"/node_modules/.vite/deps/vue.js?v=e88b16ae";
importHmrfrom"/Hmr.vue";
import{ref}from"/node_modules/.vite/deps/vue.js?v=e88b16ae";
//组件js编译后的信息
const_sfc_main=/*@__PURE__*/_defineComponent({
__name:"Main",
setup(__props,{expose:__expose}){
__expose();
constTsGeneric=defineAsyncComponent(()=import("/TsGeneric.vue"));
consttime=ref("loading...");
window.addEventListener("load",()={
setTimeout(()={
const[entry]=performance.getEntriesByType("navigation");
time.value=`loadedin${entry.duration.toFixed(2)}ms.`;
},0);
const__returned__={TsGeneric,time,version,Hmr
Object.defineProperty(__returned__,"__isScriptSetup",{enumerable:false,value:true
return__returned__;
}
});
import{toDisplayStringas_toDisplayString,createElementVNodeas_createElementVNode,createCommentVNodeas_createCommentVNode,createVNodeas_createVNode,Fragmentas_Fragment,openBlockas_openBlock,createElementBlockas_createElementBlock}from"/node_modules/.vite/deps/vue.js?v=e88b16ae";
const_hoisted_1={class:"hmr-block"
//render函数
function_sfc_render(_ctx,_cache,$props,$setup,$data,$options){
return_openBlock(),_createElementBlock(
_Fragment,
null,
[
_createElementVNode("h1",null,"Vueversion"+_toDisplayString($setup.version)),
_cache[0]||(_cache[0]=_createElementVNode(
"div",
{class:"comments"},
[
_createCommentVNode("hello")
],
-1
/*HOISTED*/
)),
_createElementVNode(
"pre",
null,
_toDisplayString($setup.time),
1
/*TEXT*/
),
_createElementVNode("div",_hoisted_1,[
_createVNode($setup["Hmr"])
])
],
64
/*STABLE_FRAGMENT*/
}
import"/Main.vue?t=1723640466717&vue&type=style&index=0&scoped=4902c357&lang.css";
_sfc_main.__hmrId="4902c357";
typeof__VUE_HMR_RUNTIME__!=="undefined"__VUE_HMR_RUNTIME__.createRecord(_sfc_main.__hmrId,_sfc_main);
import.meta.hot.accept((mod)={
if(!mod)return;
const{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_sfcfrom"/@id/__x00__plugin-vue:export-helper";
exportdefault/*@__PURE__*/_export_sfc(_sfc_main,[["render",_sfc_render],["__scopeId","data-v-4902c357"],["__file","/Users/a58/open_source_project/vite-plugin-vue/playground/vue/Main.vue"]]);
然后我们有惊奇的发现css没有处理,别急,我们发现,有这样一行代码
import"/Main.vue?t=1723640466717&vue&type=style&index=0&scoped=4902c357&lang.css";
他其实本质上将 当做一个请求重新处理了,结果如下
import{createHotContextas__vite__createHotContext}from"/@vite/client";
import.meta.hot=__vite__createHotContext("/Main.vue?vue&type=style&index=0&scoped=4902c357&lang.css");
import{updateStyleas__vite__updateStyle,removeStyleas__vite__removeStyle}from"/@vite/client"
const__vite__id="/Users/a58/open_source_project/vite-plugin-vue/playground/vue/Main.vue?vue&type=style&index=0&scoped=4902c357&lang.css"
const__vite__css="\n.comments[data-v-4902c357]{\ncolor:\n}\n"
__vite__updateStyle(__vite__id,__vite__css)
import.meta.hot.accept()
import.meta.hot.prune(()=__vite__removeStyle(__vite__id))
他将 css 内容变成 js,通过执行js动态的将css 挂在dom中
所以 vite 的原理就是将编译好的各种文件变成模块化的 js执行,js 的执行过程中,引用了vue、react 等框架 从而绘制出页面
如此一来,相信大家对vite 原理应该有了初步的理解,其实在 vite 的背后并没有大家想的只是利用type="module的能力去做资源的读取,是一个资源服务器
本质上,他就是一个强大的web服务 对于所有的请求做拦截,读取本地资源做对应的处理,变成浏览器能看懂的 esm代码,他背后承载着,打包、收发请求、编译、热更新 等各种操作
?vite 的本质: 利用浏览器type="module能力和 http 能力的web打包服务器
?源码解读说完了原理,我们知道他是讲源码中的文件,处理成 esm 的js 文件顺序执行,最终绘制出页面
那么他是怎么实现的呢?
这里我实现了一个 mini-vite 供大家参考 mini-vite
接下来我们就简单的解读一下源码
少说废话,先看图
上图中,我们简单介绍了,vite 初始化以及运行图 流程图
这里由于篇幅关系,我们就用简写的方式研究一下他的内部构造
我们主要需要研究一下几个方向
1、怎样建立 web 服务器的2、怎样处理编译的3、hmr 热更新怎么处理的4、为什么这么快5、为什么能按需处理1、怎样建立 web 服务器的建立web 服务器,其实很简单了,其实我们常用的 web服务框架都可koa、express、connect这种小而美的都可以,源码中选择的就是connect
服务器选好了,我们就要开始启动了,但是我们发现,在日常的开发中,是这一堆命令
"scripts":{
"dev":"vite",
"build":"vitebuild",
"debug":"node--inspect-brkvite",
"preview":"vitepreview"
},
于是我们就又需要有命令行工具,这样当我们启动 vite 命令的时候 才能启动命令行工具来启动服务
那么怎么启动命令行工具呢?
我们只需要在 node_modules的包中,对应下载的项目的package.json中加入以下命令
"bin": {
"mini-vite": "bin/mini-vite"
},
如果有不明白 bin是个什么东西请,去温习下npm相关内容
这样就可以正式开始了
constcli=cac()
//命令行工具跟commander类似,比如输入viteservevitedev执行对应的方法
cli
.command('[root]','Runthedevelopmentserver')
.alias('serve')
.alias('dev')
.action(async(option)={
//核心server启动方法
awaitstartDevServer(option)
})
以上代码中就可以拿到命令行的参数,执行不同的逻辑,我们本次就是 dev
接下来就是启动服务的时刻了,我们简化了源码中的流程,只保留核心逻辑
exportasyncfunctionstartDevServer(type){
//Connect是一个可扩展的HTTP服务器框架,类似于koa
constapp=connect()
//process.cwd()是一个方法,用于获取Node.js进程的当前工作目录
constroot=process.cwd()
//时间戳
conststartTime=Date.now()
//得到插件数组
constplugins=resolvePlugins()
//初始化插件
constpluginContainer=createPluginContainer(plugins)
//初始化模块映射实例,主要后期用来快速存取用的
constmoduleGraph=newModuleGraph((url)=pluginContainer.resolveId(url))
//监听文件变动,后期用来做热跟新
constwatcher=chokidar.watch(root,{
ignored:['**/node_modules/**','**/.git/**'],
ignoreInitial:true,
})
//WebSocket对象后期热更新就靠它
constws=createWebSocketServer(app)
//开发服务器上下文
constserverContext:ServerContext={
root:normalizePath(process.cwd()),
app,
pluginContainer,
plugins,
moduleGraph,
ws,
type:type=='react'?'react':'vue',
watcher,
}
//绑定热更新
bindingHMREvents(serverContext)
for(constpluginofplugins){
//调用插件的配置方法,主要就是在插件中保存当前上下文实例
//后续插件中可能用的到
if(plugin.configureServer){
awaitplugin.configureServer(serverContext)
}
}
//添加洋葱圈中间件,每次请求都会过一遍中间件
//根据不同类型,返回不同的资源
//核心编译逻辑
app.use(transformMiddleware(serverContext))
//入口HTML资源
app.use(indexHtmlMiddware(serverContext))
//静态资源
app.use(staticMiddleware(serverContext.root))
//启动服务器
app.listen(3000,async()={
awaitoptimize(root,type=='react'?'src/main.tsx':'src/main.ts')
console.log(
green('??No-Bundle服务已经成功启动!'),
`耗时:${Date.now()-startTime}ms`,
)
console.log(`本地访问路径:${blue('http://localhost:3000')}`)
})
}
以上代码中,就是服务启动的流程,启动过程中,通过中间件将编译 热更新 等流程全部初始化完毕,接下来,当浏览器请求的时候服务就开始加载对应资源返回给客户端
2、怎样处理编译处理编译其实就是利用vite 最优秀的插件设计,其实写过 rollup插件 的jym 就会发现vite 跟他插件设计几乎相同
我们先来简单的找个 rollup 插件举例
//@filename:rollup-plugin-my-example.js
exportdefaultfunctionmyExample(){
return{
name:'my-example',//此名称将出现在警告和错误中
resolveId(source){
if(source==='virtual-module'){
//这表示rollup不应询问其他插件或
//从文件系统检查以找到此ID
returnsource;
}
returnnull;//其他ID应按通常方式处理
},
load(id){
if(id==='virtual-module'){
//"virtual-module"的源代码
return'exportdefault"Thisisvirtual!"';
}
returnnull;//其他ID应按通常方式处理
}
}
//@filename:rollup.config.js
importmyExamplefrom'./rollup-plugin-my-example.js';
exportdefault({
input:'virtual-module',//由我们的插件解析
plugins:[myExample()],
output:[{
file:'bundle.js',
format:'es'
}]
});
接下来我们用 vite 如法炮制一个简单的插件,就用源码中最简单的json 插件举例,
/**
*https://github.com/rollup/plugins/blob/master/packages/json/src/index.js
*
*ThissourcecodeislicensedundertheMITlicensefoundinthe
*LICENSEfileat
*https://github.com/rollup/plugins/blob/master/LICENSE
*/
import{dataToEsm}from'@rollup/pluginutils'
import{SPECIAL_QUERY_RE}from'../constants'
importtype{Plugin}from'../plugin'
import{stripBomTag}from'../utils'
exportinterfaceJsonOptions{
/**
*GenerateanamedexportforeverypropertyoftheJSONobject
*@defaulttrue
*/
namedExports?:boolean
/**
*GenerateperformantoutputasJSON.parse("stringified").
*EnablingthiswilldisablenamedExports.
*@defaultfalse
*/
stringify?:boolean
}
//Customjsonfilterforvite
constjsonExtRE=/\.json(?:$|\?)(?!commonjs-(?:proxy|external))/
constjsonLangs=`\\.(?:json|json5)(?:$|\\?)`
constjsonLangRE=newRegExp(jsonLangs)
exportconstisJSONRequest=(request:string):boolean=
jsonLangRE.test(request)
exportfunctionjsonPlugin(
options:JsonOptions={},
isBuild:boolean,
):Plugin{
return{
name:'vite:json',
//只有编译方法
transform(json,id){
//判断当前文件是不是json类型如果不是就不处理直接交给中间件插件处理
if(!jsonExtRE.test(id))returnnull
//含有特殊的json也不处理
if(SPECIAL_QUERY_RE.test(id))returnnull
//去除文件的bom头
json=stripBomTag(json)
try{
//vite传入配置能提升性能
if(options.stringify){
//生产环境
if(isBuild){
return{
//duringbuild,parsethendouble-stringifytoremoveall
//unnecessarywhitespacestoreducebundlesize.
code:`exportdefaultJSON.parse(${JSON.stringify(
JSON.stringify(JSON.parse(json)),
)})`,
map:{mappings:''},
}
}else{
//开发环境直接返回
return`exportdefaultJSON.parse(${JSON.stringify(json)})`
}
}
//默认逻辑
//转为对象
constparsed=JSON.parse(json)
return{
//将给定的数据转换为ES模块导出语法的字符串。
code:dataToEsm(parsed,{
preferConst:true,
namedExports:options.namedExports,
}),
map:{mappings:''},
}
}catch(e){
//错误兜底处理暂时不看
constposition=extractJsonErrorPosition(e.message,json.length)
constmsg=position
?`,invalidJSONsyntaxfoundatposition${position}`
:`.`
this.error(`FailedtoparseJSONfile`+msg,position)
}
},
}
}
exportfunctionextractJsonErrorPosition(
errorMessage:string,
inputLength:number,
):number|undefined{
if(errorMessage.startsWith('UnexpectedendofJSONinput')){
returninputLength-1
}
consterrorMessageList=/atposition(\d+)/.exec(errorMessage)
returnerrorMessageList
?Math.max(parseInt(errorMessageList[1],10)-1,0)
:undefined
}
上述代码中,就是一个插件的简单的实现过程 他的返回值主要包含resolveId、load、transform 三个方法,当然,都不是必须的
那么接下来问题来了,插件有了,他是怎么执行实现编译的呢?
很简单,还记得服务的中间件机制吗?
插件系统 和中间件机制类似,当我们注册插件的中间件以后,请求过来以后,注册的插件就会遍历启动相关插件 顺序的对代码进行处理, 最终返回目标结果
上图就是 json文件的请求结果
那么接下来问题又来了,他怎么实现流水线顺序执行的呢?
很简单,利用 for 循环,当请求来了以后,我们利用注册的中间件 处理得到请求 url
然后对 url 进行处理,for 循环所有插件,插件流水线处理内容,这里我们简单的用简版代码举个例子
//核心编译逻辑中间件,当请求的时候每次执行执行中间件逻辑
app.use(transformMiddleware(serverContext))
//中间件逻辑
exportfunctiontransformMiddleware(
serverContext:ServerContext,
):NextHandleFunction{
returnasync(req,res,next)={
//如果请求方法不是GET或者请求URL为空,则跳过当前中间件,交给下一个中间件或者路由处理函数
//相当于兜底处理
if(req.method!=='GET'||!req.url){
returnnext()
}
consturl=req.url
debug('transformMiddleware:%s',url)
//transformJSandCSSrequest
//判断是否是jscss,vue或者静态资源类型
if(
isJSRequest(url)||
isVue(url)||
isCSSRequest(url)||
//静态资源的import请求,如importlogofrom'./logo.svg?import';
isImportRequest(url)
){
//核心处理函数传入url和上下文对象
letresult=awaittransformRequest(url,serverContext)
if(!result){
//如果没有结果,那么就交给下一个中间件处理
returnnext()
}
//如果有,并且不是一个字符串,我们拿到编译结果就可以了
//因为有的返回的可能是一个对象
if(resulttypeofresult!=='string'){
result=result.code
}
res.statusCode=200
res.setHeader('Content-Type','application/javascript')
//返回给客户端,下一个中间件就不走了
returnres.end(result)
}
//如果什么都不命中,给加一个中间件处理
returnnext()
}
}
上述代码中,就是请求来了以后的处理逻辑,这里我们且不看其他内人,我们关注pluginContainer.transform方法,就是核心的编译逻辑,他是怎么做的呢?
asynctransform(code,id){
constctx=newContext()asany
//遍历所有插件
for(constpluginofplugins){
//找到有编译的方法
if(plugin.transform){
//执行编译完成后交给下一个插件
constresult=awaitplugin.transform.call(ctx,code,id)
if(!result)continue
if(typeofresult==='string'){
code=result
}elseif(result.code){
code=result.code
}
}
}
return{code}
},
上述代码中你就会发现,他是利用 for 循环遍历插件,处理code代码,最后得到我们想要的内容,至于插件的形式,就不再赘述,开文中已经展示过!
如此一来,vite 就通过插件的形式,高明的可拓展的实现我们的需求
3、hmr 热更新怎么处理的hmr 是目前前端开发最重要的东西,因为谁都不想改动一个代码就得重启一下服务,用户体验很重要
那么他到底是怎么实现的呢?
?其实本质很简单:利用 ws 的能力,主动推送变动内容通知客户端更新代码
?当然,别看这么简单的事情,也是要严格的执行三步走战略
1、 监听文件变化2、建立 ws通信3、通知客户端更改代码1、 监听文件变化第一步就很简单了,我们只需要一个工具chokidar
//监听文件变动,后期用来做热跟新
constwatcher=chokidar.watch(root,{
ignored:['**/node_modules/**','**/.git/**'],
ignoreInitial:true,
})
2、建立 ws通信这一步 我们同样的也是用 node 的ws这个库
//服务端启动ws服务
importconnectfrom'connect'
import{red}from'picocolors'
import{WebSocketServer,WebSocket}from'ws'
import{HMR_PORT}from'./constants'
exportfunctioncreateWebSocketServer(server:connect.Server):{
send:(msg:string)=void
close:()=void
}{
letwss:WebSocketServer
//启动服务端创建ws注意这里port要和客户端保持一致
wss=newWebSocketServer({port:HMR_PORT})
wss.on('connection',(socket)={
socket.send(JSON.stringify({type:'connected'}))
})
wss.on('error',(e:Error{code:string})={
if(e.code!=='EADDRINUSE'){
console.error(red(`WebSocketservererror:\n${e.stack||e.message}`))
}
})
return{
send(payload:Object){
conststringified=JSON.stringify(payload)
wss.clients.forEach((client)={
if(client.readyState===WebSocket.OPEN){
//发送ws消息
client.send(stringified)
}
})
},
close(){
wss.close()
},
}
}
//客户端连接
//客户端本身支持ws直接连接就可以了
constsocket=newWebSocket(`ws://localhost:__HMR_PORT__`,'vite-hmr')
socket.addEventListener('message',async({data})={
handleMessage(JSON.parse(data)).catch(console.error)
})
3、通知客户端更改代码这是最重要的一步,当服务端监听到文件变化以后,发送通知
代码如下
exportfunctionbindingHMREvents(serverContext:ServerContext){
const{watcher,ws,root}=serverContext
//监听文件变化
watcher.on('change',async(file)={
console.log(`?${blue('[hmr]')}${green(file)}changed`)
const{moduleGraph}=serverContext
//确定改动文件
awaitmoduleGraph.invalidateModule(file)
//发送更新内容
ws.send({
type:'update',
updates:[
{
type:'js-update',
timestamp:Date.now(),
path:'/'+getShortName(file,root),
acceptedPath:'/'+getShortName(file,root),
},
],
})
})
}
这里我们为了能让大家看明白,模拟一下简单的 js 变动,这里发送的内容其实很简单,只是变动的文件名,时间戳等信息
接下来就是客户端的更新问题了,也很简单重新请求一下这个js 文件即可
//热更新逻辑
asyncfunctionfetchUpdate({path,timestamp}:Update){
constmod=hotModulesMap.get(path)
if(!mod)return
constmoduleMap=newMap()
constmodulesToUpdate=newSetstring()
modulesToUpdate.add(path)
awaitPromise.all(
Array.from(modulesToUpdate).map(async(dep)={
const[path,query]=dep.split(`?`)
try{
//这里会去请求新的文件,导入之后直接执行
constnewMod=awaitimport(
path+`?t=${timestamp}${query?`&${query}`:''}`
)
moduleMap.set(dep,newMod)
}catch(e){}
}),
)
}
当然,其实有些热更新细节,比如 css请求、如何保存原始数据 等等,可能要费劲一点,但主要流程也是大致相同,我们理解主要原理即可
4、为什么这么快以及为什么能按需处理这个其实在文章开头已经埋过伏笔,这两个问题,其实就是一个相辅相成问题,之所以快就是因为能按需处理,而按需处理就导致他非常快
好像还是很绕,我们来详细解释一下
在传统的 webpack 时代,我们的 serve 服务在启动之前是需要编译全部文件的,所以在启动之初是非常耗时的
而到了 vite 时代,我们由于利用 浏览器对于 esm,我们只需要请求的时候服务处理成 esm 模块即可,所以,理论上来说启动服务只需要一瞬间,因为他省略了打包的逻辑,但当然也是有代价的,你请求页面的时候,就会即时编译,会有些许的损耗
但这也比启动的全量编译消耗小很多
「这时候我们就解释了第一个问题 之所以快是因为启动的时候不用编译」
我们在来解释第二个问题,为什么能按需处理
同样的在 webpack 时代,我们启动的时候要全量打包,之所以这么做是因为,「我不知道你最开始要打开什么页面,进入哪个路由,因为是编译是前置的」,所以要一股脑全给你
而由于 vite 是即时编译,于是我就能知道你要什么,你要什么就给什么, 当然就做到了按需加载
最后终于写完了希望各位 jym 给个三连,老骥下台鞠躬!!!!
点击关注公众号,“技术干货”及时达!
阅读原文
网站开发网络凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设、网站改版、域名注册、主机空间、手机网站建设、网站备案等方面的需求...
请立即点击咨询我们或拨打咨询热线:13245491521 13245491521 ,我们会详细为你一一解答你心中的疑难。 项目经理在线