此文已同步到公众号【因卓诶】以及因卓诶博客:

从0开始理解Vite的主要新特性(一) - 因卓诶-爱分享爱原创的技术博客 ~ 个人博客​www.yinzhuoei.com

vite这个工具确实尤大在微博上造势很猛,又以各种骚操作着实火了一把,那我们今天就一起了解一下vite吧~

我们可以从vite仓库的readme可以看到安装vite非常的方便

// https://github.com/vitejs/vite
npm init vite-app <project-name>
cd <project-name>
npm install
npm run dev

我们在本地环境运行之后可以看到这样的页面,就说明我们可以开始使用了。


我们首先要理解vite的工作原理,它为什么这么快?

当我们打开工程的index.html的时候,我们可以发现script的type是module

<body><div id="app"></div><script type="module" src="/src/main.js"></script>
</body>

如果你不了解script标签中的module是什么意思,那么MDN解释说如果标示了module的话会把代码当作js模块来执行,一篇关于es6的文章也很好的介绍了:

https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/script

如果你熟悉es6的模块概念,模块仅仅就是普通的含有js代码的文件而已,我们可以用importexport关键字对变量,对象的导出和导入,而这个机制在高级浏览器已经完全实现了。

不得不说,尤大不仅是技术上的大神,而且富有创造和想象力。让vite变成了一个0捆绑的开发服务器,利用浏览器高级特性让开发体验变得更好

当浏览器解析保留了模块关键字的代码,从而会导致HTTP请求,vite通过koa拦截了这些请求:

.vue => 拦截请求 => 编译 => 返回给客户端

那么没有打包的vite和老东家的基于打包的vue-cli就有了一些明显的优势了:

1. vite利用了客户端能力不用打包其他服务,原生的ES Import直接输出提高了冷启动速度。

2. vite按需编译当前页面需要的组件,而不需要打包整个APP的组件,这样的提升对比cli无疑是项目越大速度差距越大。

3. HMR更新速度不会和模块数量牵扯,vite会让HMR一如既往的保持快速。

使用vite开发应用可能在前期除了启动速度,其他功能是要等到应用慢慢变大才能真实的感受vite的强大。


TS支持

vite内置TS的支持,开箱即用:

<script lang="ts">
import { ref } from "vue";export default {name: 'App',setup(){let hello = ref<string>("1");console.log(hello.value);}
}
</script>

值得一提的是,内置的TS不是TS官方出的tsc cli,而是之前就听说过的ESBuild,现在vite的TS支持是ESBuild也不奇怪,毕竟是要一快到底么。

——ESBuild的ReadMe说了一句振奋人心的话

实际上,我们目前的网站构建工具比实际速度慢10-100倍

为什么ESbuild这么快?

  • 因为Esbuild是用GO语言直接编译成原生代码
  • 由于GO语言特点,它的解析和映射并发非常快
  • 避免了不必要的配置
  • 数据转换很简单很快速

虽然ESbuild是一个非常快速的打包器,但是不支持热模块更新和没有开箱即用的工具,而且要像webpack一样想做一款基于ESbuild的插件,我认为目前是非常难的。所以Vite将它的长处用在了处理ts编译上,大型项目中编译TS文件,Vite几乎是一瞬间的事情。


热模块更新

我们要知道热模块更新和我们传统的刷新页面的区别,以webpack的dev-server服务器举例,通过启动开发服务器,页面与服务器建立了websocket,我们修改了代码之后给页面发送消息,页面才会执行刷新命令,本质上这种live-reload机制已经对开发非常友好了,但是在带有状态的页面上,reload不会有更好的开发体验:

当页面存在弹窗或者编辑框等,代码修改之后,liveReload会重载页面,如果刷新代码的同时不会重载页面而是重新加载修改过的文件就完美,所以这个机制就是webpack提出的热替换技术,也就是我们说的热更新

webpack.config.js

module.exports = {mode: "development", // 开发环境devtool: "cheap-module-eval-source-map",devServer: {contentBase: "./bundle",open: true,hot: true, // 开启热模块更新hotOnly: true  // 更新失败不会刷新页面配置},module: {rules: [{test: /.css$/,use: ["style-loader", "css-loader"]}]}
}

css的loader中的实现已经做了热更新的处理,通过HMR这个插件中的API

// main.js
if (module.hot) {module.hot.accept(function() {// 监听变化,则修改});module.hot.dispose(function() {// 移除});
}

我们了解了HMR基本操作之后就可以看看Vite是如何做HMR的:

vite和webpack的HMR实现机制是一样的,都是通过客户端和服务端建立socket连接,服务端有变化则通知客户端做出改变:

 // server/serverPlugin.tswatcher.on('change', (file) => {if (!(file.endsWith('.vue') || isCSSRequest(file))) {handleJSReload(file);}})

在handleJSReload函数中递归调用了walkImportChain这个函数,这个函数的作用就是查看当前变化的文件是谁引入了它(JS/Vue),那么在递归中没有找到谁引入它,就Full-reload

 send({type: 'full-reload',path: publicPath})

如果找到了引用这个JS的文件了就热更新:

send({type: 'multi',updates: boundaries.map((boundary) => {return {type: boundary.endsWith('vue') ? 'vue-reload' : 'js-update',path: boundary,changeSrcPath: publicPath,timestamp}})})

在客户端中,vite则在核心处理函数handleMessage中定义了消息的类型:

  • connected: websocket连接
  • vue-reload: vue文件的script更新
  • vue-rerender: vue文件的template更新
  • style-update:css样式表变更
  • style-remove: css样式表移除
  • js-update: js文件更新
  • full-reload:刷新页面

客户端接受了不同的消息类型去做不同处理,根据timestamp时间戳去请求新文件,而vue文件则通过HMRRuntime更新。


裸模块导入

vite同样也支持其他家打包器的日常功能,浏览器不允许我们直接引入裸模块,例如:

import { add } from "lodash"

vite在裸模块处理上有着对vue得天独厚的优势,vite不仅仅的可以改写普通模块的路径然后正确的解析,还对vue这个依赖有特殊的处理:

如果项目本地没有安装vue依赖,那么引入vue模块会按照vite依赖的vue版本去执行,这就说明了如果你全局安装了vue,那么在vite项目中能更方便的找到它

vite重写了模块加载路径:

 // src/node/server/serverPluginModuleRewrite.tsctx.body = rewriteImports(root,content!,importer,resolver,ctx.query.t)

引入的模块上下文在经过rewriteImports方法处理body以后,就会造成这样的效果:

import vue from "vue" => import vue from "@/modules/vue.js"

rewriteImports这个方法中使用到了Esbuild中的es-module-lexer进行词法分析,对esbuild本来就不熟悉的我去看了这个插件发现这个词法分析器又小,又可以快速对JS进行分析,这里就简单看一下官网的demo吧。

// 伪代码
// 和vite源码中一样
import {   init as initLexer,   parse as parseImports,   ImportSpecifier
} from 'es-module-lexer' // 需要初始化
await initLexer;
const [import, export] = parseImports(`import {a} from "a"export const add = 1;
`
console.log(import[0].s);
// 解析结果的返回有这样几种
// "s" is shorthand for "start"
// "e" is shorthand for "end"
// "ss" is shorthand for "statement start"
// "se" is shorthand for "statement end"

经过这样的词法处理,我们的模块引入路径就被这样替换了,尽管这个插件这么强,对于词法分析这种东西我们开发者平时也用不到,所以大家只需要知道vite的模块路径替换是借助es-module-lexer进行词法分析的就可以啦~


总结

vite和webpack对比,我认为webpack是一个纯正的打包工具,它的生态非常丰富,可以基于插件做各种事情,但是像尤大说的一样,很少有基于webpack上层封装的工具出现,也就是具有很大学习和配置成本,而vite提供了更丝滑的开发体验,以及内置的强大的HMR,TS支持,WebAssembly支持等等,所以使用哪款产品要看业务需要。


由于vite目前还在不断的更新中,但是主要特性的原理应该是不会变的,因为vite有非常多优秀的其他特性,还有我们这篇文章提到的3个特性还有很多值得细细研究的地方,所以这个vite系列会继续做,谢谢大家支持哦

accept 返回0_从0开始理解Vite的主要新特性(一)相关推荐

  1. Swift 2.0初探:值得注意的新特性

    转眼间,Swift已经一岁多了,这门新鲜.语法时尚.类型安全.执行速度更快的语言已经渐渐的深入广大开发者的心.我同样也是非常喜爱这门新的编程语言. 今年6月,一年一度的WWDC大会如期而至,在大会上A ...

  2. Kafka 2.8.0 正式发布,增加了哪些新特性?

    导读:目前 Kafka 已经定位为一个分布式流式处理平台,它以高吞吐.可持久化.可水平扩展.支持流数据处理等多种特性而被广泛使用.目前越来越多的开源分布式处理系统如 Cloudera.Storm.Sp ...

  3. MySQL 8.0 在关系数据库方面有这些新特性

    作者 | 捏造的信仰 原文 | https://segmentfault.com/a/1190000013803247 本文介绍几个 8.0 在关系数据库方面的主要新特性. 你可能已经知道 MySQL ...

  4. php7.0 yield,PHP7中生成器的新特性 yield-from amp;amp; return-values

    生成器委托 简单地翻译官方文档的描述: PHP7中,通过生成器委托(yield from),可以将其他生成器.可迭代的对象.数组委托给外层生成器.外层的生成器会先顺序 yield 委托出来的值,然后继 ...

  5. C#8.0的两个有趣的新特性以及gRPC

    最近每天忙着跑很多地方,回家就不想动了,没什么心情写东西.今天有空,稍微写一点. 下文中: 关于C#语法特性的部分需要Visual Studio 2019支持. 关于.NET Core的部分需要安装. ...

  6. c# 路径下的最近文件夹_C#8.0的两个有趣的新特性以及gRPC

    最近每天忙着跑很多地方,回家就不想动了,没什么心情写东西.今天有空,稍微写一点. 下文中: 关于C#语法特性的部分需要Visual Studio 2019支持. 关于.NET Core的部分需要安装. ...

  7. CentOS 7.6 MySQL 8.0 RPM包方式安装及新特性介绍

    一.MySQL 8.0新特性: 1.默认字符集由latin1变为utf8mb4 2.MyISAM系统表全部换成InnoDB表 3.自增主键AUTO_INCREMENT的值支持持久化 4.InnoDB表 ...

  8. 重磅!阿里云MongoDB 5.0发布,速来围观新特性

    简介:2021年9月29日下午,阿里云数据库与MongoDB共同发布了阿里云MongoDB 5.0.MongoDB于2021年7月中发布最新5.0版本,阿里云MongoDB率先跟进官方最新版本能力,快 ...

  9. mysql rpm 安装6_CentOS 7.6 MySQL 8.0 RPM包方式安装及新特性介绍

    一.MySQL 8.0新特性: 1.默认字符集由latin1变为utf8mb4 2.MyISAM系统表全部换成InnoDB表 3.自增主键AUTO_INCREMENT的值支持持久化 4.InnoDB表 ...

最新文章

  1. 项目中用到的三个绿色自动备份方法
  2. activity堆栈式管理
  3. LeetCode Minimum Genetic Mutation(dfs,bfs)
  4. Google ARCore SDK
  5. [JavaWeb-MySQL]DML_操作表
  6. AT4995-[AGC034E] Complete Compress【树形dp】
  7. springboot配置shiro多项目实现session共享的详细步骤
  8. 阿里年薪80w数据总监分享:一张图了解数据分析完整流程
  9. 自己动手写一个业务实体生成器(1)
  10. 应坚刚《概率论》第一章重点习题解答
  11. Adobe Reader 已停止工作
  12. XtuningTheBert
  13. html5 图形 标签,HTML5 canvas 标签介绍:定义图形
  14. ERROR: pip‘s dependency resolver does not currently take into account all the packages that are inst
  15. 关于临时HY学长被安排拉二分题不想翻译找到DYM学长这件事
  16. 秒杀竞拍屡创网络神话:馅饼还是美丽陷阱
  17. 2022TikTok行业发展现状
  18. QQ文件及文件夹的一些知识
  19. 机器人自动驾驶中的时间同步
  20. 联想拯救者Y7000如何重装系统

热门文章

  1. Java中的访问修饰符详解
  2. 封装性的基本使用练习2
  3. 开源数据同步神器——canal
  4. php 反射类 解析注释,php反射获取类和方法中的注释
  5. java 连接solrcloud_Solr 14 - SolrJ操作SolrCloud集群 (Solr的Java API)
  6. deepin终端启动自安装程序
  7. pytorch clamp 与clamp_区别
  8. BZOJ:4820: [Sdoi2017]硬币游戏BZOJ:1444: [Jsoi2009]有趣的游戏(高斯消元求概率)
  9. 我的编程之路(二十五) 上海的老同学
  10. LeetCode - Remove Nth Node From End of List