vite的作用,vite实现原理
本文主要介绍了Vite的原理。Vite是一个更轻更快的web应用开发工具,面向现代浏览。Vite创建的项目是一个普通的Vue3应用,比基于Vue-cli创建的应用少了很多配置文件和依赖项。基于Vite相关资料的内容,有需要的朋友可以参考一下。
:
目录
1.概述2。静态测试服务器3的实现。第三方模块的处理。单个文件组件的处理
1. 概述
Vite是一个更轻更快的web应用开发工具,面向现代浏览器。基于底层ECMAScript标准原生模块系统的ES模块实现。他的出现解决了webpack冷启动时间长,Webpack HMR热更新响应速度慢的问题。
默认情况下,Vite创建的项目是一个普通的Vue3应用,比基于Vue-cli创建的应用有更少的配置文件和依赖项。
Vite创建的项目需要的开发依赖非常少,只需要Vite和@ vue/compiler-sfc,这里Vite是一个运行工具,compiler-sfc是用来编译末尾的单文件组件。vue。您还可以通过在创建项目时指定不同的模板来支持其他框架的使用,比如React。项目创建后,可以通过两个命令启动和打包。
#打开服务器
邀请发球
#包装
vite构建
因为Vite启动的web服务不需要编译打包,所以启动速度极快。调试阶段运行的大部分代码都是你在编辑器里写的代码,真的比编译后的webpack渲染快很多。当然,生产环境还是要打包的。毕竟大多数时候,我们使用的最新ES规范在浏览器中并没有得到支持。Vite的打包过程和webpack类似,所有文件都会被编译打包在一起。代码裁剪的需求是通过Vite的原生动态导入实现的,所以打包的结果只能支持现代的浏览器。如果需要兼容旧版浏览器,可以引入Polyfill。
除了浏览器环境不支持模块化和新语法,模块文件还会产生大量的http请求。如果用模块化的方式开发,一个页面会有几十个甚至几十个模块,很多时候会出现几kb的文件。加载几十个js资源打开一个页面显然是不合理的。
Vite创建的项目几乎不需要额外的配置。默认情况下已经支持TS、Less、Sass、Stylus和postcss,但是需要单独安装相应的编译器。同时,默认也支持jsx和Web Assembly。Vite的好处是提高开发者在开发过程中的体验。web开发服务器无需等待即可立即启动,模块热更新近乎实时。所需文件将按需编译,避免编译未使用的文件。并且避免开箱即用的加载程序和插件的配置。Vite的核心功能包括启动静态web服务器、编译单个文件组件和提供HMR功能。启动vite时,首先会把当前项目目录作为静态服务器的根目录,静态服务器会拦截一些请求,实时编译请求文档,处理其他浏览器无法识别的模块,通过websocket实现hmr。
2. 实现静态测试服务器
首先,实现一个可以启动静态web服务器的命令行工具。Vite1.x内部使用Koa实现静态服务器。(PS:节点命令行工具可以查看我之前的文章,这里就不介绍了,直接粘贴代码就可以了)。
npm初始化
npm安装koa koa-send -D
bin的入口文件设置为本地index.js
#!/usr/bin/env节点
const Koa=require(koa )
const send=require(koa-send )
const app=新Koa()
//打开静态文件服务器
app.use(async (ctx,next)={
//加载静态文件
await send(ctx,ctx.path,{ root: process.cwd(),index: index.html})
等待下一个()
})
app.listen(5000)
Console.log(服务器已启动http://localhost:5000 )
这样就编写了一个节点静态服务器的工具。
3. 处理第三方模块
我们的做法是,当代码中使用第三方模块(node_modules中的文件)时,可以通过修改第三方模块的路径给它一个ID,然后在服务器中获取这个ID来处理。
首先需要修改第三方模块的路径,这里需要一个新的中间件来实现。要判断当前返回给浏览器的文件是否是javascript,只需查看响应头中的content-type即可。如果javascript需要找到这个文件中引入的模块路径。Ctx.body是返回给浏览器的内容文件。这里的数据是一个流,需要转换成字符串进行处理。
const stream2string=(stream)={
返回新承诺((解决,拒绝)={
const chunks=[];
stream.on(data ,chunk={chunks.push(chunk)})
stream.on(end ,()={ resolve(Buffer.concat(chunks))。toString( utf-8 )})
stream.on(错误,拒绝)
})
}
//修改第三方模块路径
app.use(async (ctx,next)={
if(CTX . type=== application/JavaScript ){
const contents=await stream 2 string(CTX . body);
//修改主体中导入的路径,重新赋给主体,返回给浏览器。
//从“vue”导入vue,匹配到from“修改为from”@ modules/
CTX . body=contents . replace(/(from \ s[ ])(?[\.\/])/g, $ 1/@ modules/);
}
})
然后开始加载第三方模块。这里还需要一个中间件来判断请求路径是否是修改后的@模块的开头。如果是,转到node_modules并加载相应的模块,然后将它们返回到浏览器。这个中间件应该放在静态服务器之前。
//加载第三方模块
app.use(async (ctx,next)={
if(CTX . path . starts with(/@ modules/){
//拦截模块名称
const moduleName=CTX . path . substr(10);
}
})
获得模块名之后,需要获得模块的导入文件。在这里,你需要获取ES模块模块的导入文件。您需要找到模块的package.json,然后获取这个package.json中模块字段的值,这是导入文件。
//找到模块路径
const pkg path=path . join(process . pwd(), node_modules ,moduleName, package . JSON );
const pkg=require(pkg path);
//重新赋值ctx.path,需要重置一个已有的路径,因为之前的路径不存在。
CTX . path=path . join(/node _ modules ,moduleName,pkg . module);
//执行下一个中间件
a wat next();
这样,虽然浏览器请求是以@modules path的形式进来的,但是在加载之前,path path被改为node_modules中的path,这样在加载的时候,它就会去node_modules获取文件,并将加载的内容响应给浏览器。
加载第三方模块:
app.use(async (ctx,next)={
if(CTX . path . starts with(/@ modules/){
//拦截模块名称
const moduleName=CTX . path . substr(10);
//找到模块路径
const pkg path=path . join(process . pwd(), node_modules ,moduleName, package . JSON );
const pkg=require(pkg path);
//重新赋值ctx.path,需要重置一个已有的路径,因为之前的路径不存在。
CTX . path=path . join(/node _ modules ,moduleName,pkg . module);
//执行下一个中间件
a wat next();
}
})
4. 单文件组件处理
之前有人说浏览器不能处理。vue资源。浏览器只能识别js、css等常用资源,所以其他类型的资源需要在服务器端处理。当请求单个文件组件时,服务器需要将单个文件组件编译成js模块并返回给浏览器。
所以在这里,当浏览器第一次请求App.vue时,服务器会把单文件组件编译成一个对象,先加载这个组件,然后创建一个对象。
从导入Hello。/src/components/Hello.vue
const __script={
名称:“应用程序”,
组件:{
你好
}
}
然后加载导入文件,这次告诉服务器编译这个单文件组件的模板并返回一个渲染函数。然后在新创建的组件选项对象上挂载渲染函数,最后导出选项对象。
从“/src/App.vue”导入{ render as __render }?类型=模板
__script.render=__render
_ _脚本。__hmrId=/src/App.vue
导出默认_ _脚本
也就是说,vite会发送两个请求。第一个请求将编译单个文件,第二个请求将编译单个文件模板并返回一个呈现函数。
编译单文件选项:
首先,我们来认识一下第一个征用文件的情况。需要将单个文件组件编译成一个选项,这个选项也是通过中间件实现的。这个函数应该在处理静态服务器之后,处理第三方模块的路径之前执行。
首先,需要借助编译器sfc来编译单文件组件
//处理单个文件组件
app.use(async (ctx,next)={
if (ctx.path.endsWith(。vue)) {
//获取响应文件的内容,并将其转换为字符串。
const contents=await stream tostring(CTX . body);
//编译文件的内容
const { descriptor }=compiler sfc . parse(contents);
//定义状态代码
让代码;
//没有类型是第一个请求。
如果(!ctx.query.type) {
代码=descriptor . script . content;
//这里的代码格式是需要修改成我们之前贴的vite的样子。
//从导入Hello。/components/Hello.vue
//导出默认值{
//名称:应用程序,
//组件:{
//你好
//}
//}
//转换代码的格式,用const __script=替换导出默认值
code=code . relace(/export \ s default \ s/g, const __script=)
代码=`
从“${ctx.path}”导入{ render as __render }?类型=模板
__script.rener=__render
导出默认_ _脚本
`
}
//将浏览器响应头设置为js
ctx.type=应用程序/javascript
//将字符串转换成数据流,传递给下一个中间件。
ctx.body=stringToStream(代码);
}
等待下一个()
})
const stringToStream=text={
const stream=new Readable();
stream.push(文本);
stream.push(空);
回流;
}
NPM install @ vue/编译器-sfc -D
然后我们将处理单文件组件的第二个请求。第二个请求的url会取type=template参数,单文件组件的模板需要编译到render函数中。
首先,您需要确定当前请求是否具有type=template。
如果(!ctx.query.type) {
.
} else if(CTX . query . type=== template ){
//获取编译后的目标代码是render函数
const template render=compiler sfc . compile template({ source:descriptor . template . content })
//将渲染函数赋给代码并返回给浏览器
code=templateRender.code
}
这里,我们还需要处理工具中的process.env,因为这些代码会返回到浏览器中运行。如果我们不处理它们,它们就会默认为node,导致运行失败。可以在修改第三方模块路径的中间件中进行修改,修改路径后再添加一个修改后的process.env。
//修改第三方模块路径
app.use(async (ctx,next)={
if(CTX . type=== application/JavaScript ){
const contents=await stream 2 string(CTX . body);
//修改主体中导入的路径,重新赋给主体,返回给浏览器。
//从“vue”导入vue,匹配到from“修改为from”@ modules/
CTX . body=contents . replace(/(from \ s[ ])(?[\.\/])/g, $1/@modules/)。替换(/process\环境\。NODE_ENV/g," development );
}
})
到目前为止,已经实现了一个简短版本的vite。当然,这里我们只演示了。vue文件,但是我们没有处理css等其他资源更少。不过方法都差不多,有兴趣的同学可以自己实现。
#!/usr/bin/env节点
const path=require(path )
const { Readable }=require( stream )
const Koa=require(koa )
const send=require(koa-send )
const compiler sfc=require( @ vue/compiler-sfc )
const app=新Koa()
const stream2string=(stream)={
返回新承诺((解决,拒绝)={
const chunks=[];
stream.on(data ,chunk={chunks.push(chunk)})
stream.on(end ,()={ resolve(Buffer.concat(chunks))。toString( utf-8 )})
stream.on(错误,拒绝)
})
}
const stringToStream=text={
const stream=new Readable();
stream.push(文本);
stream.push(空);
回流;
}
//加载第三方模块
app.use(async (ctx,next)={
if(CTX . path . starts with(/@ modules/){
//截取模块名称
常量moduleName=CTX。路径。substr(10);
//找到模块路径
const pkg path=path。加入(流程。pwd(), node_modules ,moduleName, package。JSON’);
const pkg=require(pkg路径);
//重新给ctx.path赋值,需要重新设置一个存在的路径,因为之前的路径是不存在的
CTX。路径=路径。join(/node _ modules ,moduleName,pkg。模块);
//执行下一个中间件
一个wat next();
}
})
//开启静态文件服务器
app.use(async (ctx,next)={
//加载静态文件
await send(ctx,ctx.path,{ root: process.cwd(),index: index.html})
等待下一个()
})
//处理单文件组件
app.use(async (ctx,next)={
if (ctx.path.endsWith(.vue)) {
//获取响应文件内容,转换成字符串
const contents=await stream to string(CTX。体);
//编译文件内容
const { descriptor }=编译器sfc . parse(内容);
//定义状态码
让代码;
//不存在类型就是第一次请求
如果(!ctx.query.type) {
代码=描述符。剧本。内容;
//这里的密码格式是,需要改造成我们前面贴出来的轻快地中的样子
//从导入你好./components/Hello.vue
//导出默认值{
//名称:应用程序,
//组件:{
//你好
//}
//}
//改造密码的格式,将导出默认值替换为const __script=
代码=代码。relace(/export \ s default \ s/g, const __script=)
代码=`
从" ${ctx.path} "导入{渲染为__render }?类型=模板
__script.rener=__render
导出默认_ _脚本
`
} else if(CTX。查询。type=== template ){
//获取编译后的对象密码就是提出函数
const template render=编译器sfc . compile模板({ source:descriptor。模板。内容})
//将提出函数赋值给密码返回给浏览器
code=templateRender.code
}
//设置浏览器响应头为射流研究…
ctx.type=应用程序/javascript
//将字符串转换成数据流传给下一个中间件。
ctx.body=stringToStream(代码);
}
等待下一个()
})
//修改第三方模块路径
app.use(async (ctx,next)={
如果(CTX。type===应用程序/JavaScript ){
const contents=await流2字符串(CTX。体);
//将身体中导入的路径修改一下,重新赋值给身体返回给浏览器
//从“vue”导入vue,匹配到"从"修改为来自@模块/
CTX。正文=内容。替换(/(from \ s[ ])(?[\.\/])/g, $1/@modules/).替换(/process\环境\。NODE_ENV/g," development );
}
})
app.listen(5000)
console.log(服务器已经启动http://本地主机:5000)
到此这篇关于学习轻快地的原理的文章就介绍到这了,更多相关轻快地原理内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。