We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Vite 主要是用了 js modules,在认识 vite 之前,我们先简单学习一下 javascript modules。 MDN 的 javascript modules 文档 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules#aside_%E2%80%94_.mjs_versus_.js
Vite
js modules
vite
javascript modules
简单的说,支持javascript modules的浏览器,可以通过script标签引入一个 esm 模块,并且支持其中的import和 export语法,在解析到对应的语法后会在浏览器中动态加载对应的js文件。
script
esm
import
export
我们写一个简单的测试应用来理解什么是 javascript modules,代码都在这里 https://github.com/lihongxun945/javascript-modules-test
入口文件 index.html,通过script标签加载一个模块:
index.html
<html> <body> <script type="module" src="./index.js"></script> </body> </html>
index.js引用了另一个模块,并初始化:
index.js
import Person from './person.js'; const person = new Person();
person.js文件直接导出一个 Person类:
person.js
export default function Person () { console.log('person'); }
启动一个静态服务器,直接访问 index.html,可以发现上述代码无需任何编译,在浏览器中可以直接运行。且浏览器会通过网络请求分别加载 index.js 和 person.js
浏览器已经原生支持了 javascript modules,为什么还要vite呢?有以下几个原因:
bundle
vite 默认把index.html放在了项目的根目录,这和我们的webpack项目放在public中有挺大区别。vite 官网上对这个设计做了解释,总结一下主要原因是对于使用 javascript modules的项目来说,index.html 本来就是编译入口文件也是Server的根路径,也就是说既应该放在 src也应该放在 public 中,所以干脆直接放在根目录,这样也不用写 PUBLIC_URL 之类的代码,既符合已有规范还方便,所以就这么写了。
webpack
public
src
PUBLIC_URL
那webpack 为啥不这样做? 因为 webpack 的编译入口文件其实是 index.js而不是index.html,而Server的根路径其实是编译后的 index.html,所以就没这么设计。
示例中的 index.html通过如下代码加载 main.tsx:
main.tsx
<script type="module" src="/src/main.tsx"></script>
这直接用了 javascript modules 能力 加载的 main.tsx源码是这样的:
import React from 'react' import ReactDOM from 'react-dom' import './index.css' import App from './App' ReactDOM.render( <React.StrictMode> <App /> </React.StrictMode>, document.getElementById('root') )
TS显然无法被浏览器识别,而在浏览器中加载的文件已经经过了 vite的编译,编译后的代码如下:
var _jsxFileName = "/Users/hongxun.lhx/github/my-vue-app/src/main.tsx"; import __vite__cjsImport0_react from "/node_modules/.vite/react.js?v=8ca9e3e0"; const React = __vite__cjsImport0_react.__esModule ? __vite__cjsImport0_react.default : __vite__cjsImport0_react; import __vite__cjsImport1_reactDom from "/node_modules/.vite/react-dom.js?v=8ca9e3e0"; const ReactDOM = __vite__cjsImport1_reactDom.__esModule ? __vite__cjsImport1_reactDom.default : __vite__cjsImport1_reactDom; import "/src/index.css"; import App from "/src/App.tsx"; import __vite__cjsImport4_react_jsxDevRuntime from "/node_modules/.vite/react_jsx-dev-runtime.js?v=8ca9e3e0"; const _jsxDEV = __vite__cjsImport4_react_jsxDevRuntime["jsxDEV"]; ReactDOM.render(/* @__PURE__ */ _jsxDEV(React.StrictMode, { children: /* @__PURE__ */ _jsxDEV(App, {}, void 0, false, { fileName: _jsxFileName, lineNumber: 8, columnNumber: 5 }, this) }, void 0, false, { fileName: _jsxFileName, lineNumber: 7, columnNumber: 3 }, this), document.getElementById("root"));
我们来看一看代码是怎么编译的。 main.tsx依赖的 React 和 ReactDOM 经过了 vite 的编译,且缓存在了 node_modules/.vite 目录中。这样做好处是只需要加载两个文件,如果不处理那么浏览器需要加载很多React的依赖。根据官网的说明,vite启动时会自动分析node_modules依赖并把他们都打包,所以并不会因为这些依赖太多导致浏览器加载大量js。 由于打包是在本地进行的,冷启动显然会受到影响,经过自己本地实际测试,冷启动有打包 React相关依赖,热启动无需打包,启动速度分别是:
React
ReactDOM
node_modules/.vite
node_modules
435ms
236ms
esbuild
100ms
index.css 显然也会被编译成 JS,否则 import 会报错,我们看看编译后的 index.css:
index.css
import { createHotContext as __vite__createHotContext } from "/@vite/client"; import.meta.hot = __vite__createHotContext("/src/index.css"); import { updateStyle, removeStyle } from "/@vite/client" const id = "/Users/hongxun.lhx/github/my-vue-app/src/index.css" const css = "body {\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',\n 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',\n sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\ncode {\n font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',\n monospace;\n}\n" updateStyle(id, css) import.meta.hot.accept() export default css import.meta.hot.prune(() => removeStyle(id))
编译的结果和我们想象的差不多,这里有一个 updateStyle方法,起作用就是通过style标签把CSS插入到文档中,方法的实现如下:
updateStyle
style
function updateStyle(id, content) { let style = sheetsMap.get(id); { if (style && !(style instanceof HTMLStyleElement)) { removeStyle(id); style = undefined; } if (!style) { style = document.createElement('style'); style.setAttribute('type', 'text/css'); style.innerHTML = content; document.head.appendChild(style); } else { style.innerHTML = content; } } sheetsMap.set(id, style); }
因为我们是 import index.css 的写法,所以export default css实际 并没啥用,如果是用CSS Module写法就有用了。
import index.css
export default css
回到 main.tsx,JSX被编译成了JS是常规操作,对 App.tsx的处理也和上面的逻辑类似。
JSX
App.tsx
App.tsx中加载了一张图片:
import logo from './logo.svg'
根据用Webpack的经验,显然 logo.svg也会被编译成JS,并返回文件地址,实际也确实是这样:
Webpack
logo.svg
export default "/src/logo.svg"
认识了Vite的基本原理之后,就可以明白vite为什么在本地开发那么快了。主要是基于以下几点:
esmodule
那么esbuild 为什么这么快呢? 官方是有解释的,可以看这里:https://esbuild.github.io/faq/#why-is-esbuild-fast 总结一下几个主要原因:
esbuild有这么多优点,那么有没有缺点呢? 当然有的,esbuild的快其实来源于两部分:一部分是 GO语言和多线程带来的优势,另一个部分其实是舍弃了一些特性换来的速度提升。比如 esbuild 省略了语法检查,官方文档中明确说明了esbuild没有做TS类型检查,实际我测试发现JS语法检查也没做;没有生产环境需要用到的代码分割等特性(但有计划做)。因为这些不完善的地方,在打包生产环境代码的时候,vite依然用的是 rollup。
The text was updated successfully, but these errors were encountered:
No branches or pull requests
认识javascript modules
Vite
主要是用了js modules
,在认识vite
之前,我们先简单学习一下javascript modules
。 MDN 的javascript modules
文档 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules#aside_%E2%80%94_.mjs_versus_.js简单的说,支持
javascript modules
的浏览器,可以通过script
标签引入一个esm
模块,并且支持其中的import
和export
语法,在解析到对应的语法后会在浏览器中动态加载对应的js文件。我们写一个简单的测试应用来理解什么是
javascript modules
,代码都在这里 https://github.com/lihongxun945/javascript-modules-test入口文件
index.html
,通过script
标签加载一个模块:index.js
引用了另一个模块,并初始化:person.js
文件直接导出一个 Person类:启动一个静态服务器,直接访问
index.html
,可以发现上述代码无需任何编译,在浏览器中可以直接运行。且浏览器会通过网络请求分别加载index.js
和person.js
认识vite
为什么需要vite
浏览器已经原生支持了
javascript modules
,为什么还要vite
呢?有以下几个原因:esm
语法的支持bundle
优化性能,可以把多个小文件合并成大文件避免浏览器加载成百上千个文件当然还有一些其他原因,比如生产环境打包、HMR、chunks等
vite 目录结构
vite
默认把index.html
放在了项目的根目录,这和我们的webpack
项目放在public
中有挺大区别。vite
官网上对这个设计做了解释,总结一下主要原因是对于使用javascript modules
的项目来说,index.html
本来就是编译入口文件也是Server的根路径,也就是说既应该放在src
也应该放在public
中,所以干脆直接放在根目录,这样也不用写PUBLIC_URL
之类的代码,既符合已有规范还方便,所以就这么写了。那
webpack
为啥不这样做? 因为webpack
的编译入口文件其实是index.js
而不是index.html
,而Server的根路径其实是编译后的index.html
,所以就没这么设计。加载js和CSS
示例中的
index.html
通过如下代码加载main.tsx
:这直接用了
javascript modules
能力加载的
main.tsx
源码是这样的:TS显然无法被浏览器识别,而在浏览器中加载的文件已经经过了 vite的编译,编译后的代码如下:
我们来看一看代码是怎么编译的。
main.tsx
依赖的React
和ReactDOM
经过了vite
的编译,且缓存在了node_modules/.vite
目录中。这样做好处是只需要加载两个文件,如果不处理那么浏览器需要加载很多React
的依赖。根据官网的说明,vite启动时会自动分析node_modules
依赖并把他们都打包,所以并不会因为这些依赖太多导致浏览器加载大量js。由于打包是在本地进行的,冷启动显然会受到影响,经过自己本地实际测试,冷启动有打包
React
相关依赖,热启动无需打包,启动速度分别是:435ms
236ms
虽然冷启动确实慢了一些,但是不得不说
esbuild
打包React
只多用了100ms
,相比webpack
依然有大幅提升。index.css
显然也会被编译成 JS,否则import
会报错,我们看看编译后的index.css
:编译的结果和我们想象的差不多,这里有一个
updateStyle
方法,起作用就是通过style
标签把CSS插入到文档中,方法的实现如下:因为我们是
import index.css
的写法,所以export default css
实际 并没啥用,如果是用CSS Module写法就有用了。回到
main.tsx
,JSX
被编译成了JS是常规操作,对App.tsx
的处理也和上面的逻辑类似。静态资源
App.tsx
中加载了一张图片:根据用
Webpack
的经验,显然logo.svg
也会被编译成JS,并返回文件地址,实际也确实是这样:Vite快在哪里?
认识了
Vite
的基本原理之后,就可以明白vite为什么在本地开发那么快了。主要是基于以下几点:esmodule
加载src文件,按需编译单个文件。node_modules
依赖被打包成大文件,避免了浏览器加载多个js。esbuild
打包速度无敌快。那么
esbuild
为什么这么快呢?官方是有解释的,可以看这里:https://esbuild.github.io/faq/#why-is-esbuild-fast
总结一下几个主要原因:
esbuild
有这么多优点,那么有没有缺点呢?当然有的,
esbuild
的快其实来源于两部分:一部分是 GO语言和多线程带来的优势,另一个部分其实是舍弃了一些特性换来的速度提升。比如esbuild
省略了语法检查,官方文档中明确说明了esbuild
没有做TS类型检查,实际我测试发现JS语法检查也没做;没有生产环境需要用到的代码分割等特性(但有计划做)。因为这些不完善的地方,在打包生产环境代码的时候,vite依然用的是 rollup。The text was updated successfully, but these errors were encountered: