Vue 3.0的又一亮点是带来了一个新的开发构建工具:Vite。根据官方文档的描述,它是一个在开发环境下利用现代浏览器原生ES模块进行代码文件的按需加载,并在生产环境下将其与Rollup进行绑定的一个web开发构建工具。
优势
1、更快的冷启动
2、即时热更新(Instant HMR)
3、按需编译
新功能
Bare Module Resolving
原生的ES加载模块不支持裸露模块加载(bare module import)。
import { createApp } from 'vue'
在Vue 2中,如果使用上面的代码,会直接抛出一个错误,因为这种写法一般意味着我们在引用node_modules
中的模块,在构建的过程中由 Webpack
等工具来帮我们找这个模块的具体路径进行打包。但如果直接使用浏览器打开,会抛出找不到模块的错误,这是因为浏览器并不知道项目里有node_modules
这个文件夹,它只能通过绝对路径或相对路径去寻找模块。
Vite在这方面进行了改进,它遍历所有提供服务的js文件,从中找到你所引用的那个模块,并将此模块的路径重写成/@modules/xxx
(如上述例子中的vue
将重写成/@modules/vue
)的路径重写成/@modules/vue
。通过这种方式,Vite实现了对资源的统一管理,并在此基础上提供了一种能够从项目已安装的所有依赖项中找到当前所引用的模块的解决方案。
Hot Module Replacement
create-vite-app所提供的vue,react,preact模版均支持热更新(HMR)。对于手动HMR,Vite提供一个API:import.meta.hot
。
要使模块实现自我接受,请使用import.meta.hot.accept
:
export const count = 1
// the conditional check is required so that HMR related code can be
// dropped in production
if (import.meta.hot) {
import.meta.hot.accept((newModule) => {
console.log('updated: count is now ', newModule.count)
})
}
同时,模块也可以使用import.meta.hot.acceptDeps
接受来自直接依赖项的更新,而无需重新加载自身:
import { foo } from './foo.js'
foo()
if (import.meta.hot) {
import.meta.hot.acceptDeps('./foo.js', (newFoo) => {
// the callback receives the updated './foo.js' module
newFoo.foo()
})
// Can also accept an array of dep modules:
import.meta.hot.acceptDeps(['./foo.js', './bar.js'], ([newFooModule, newBarModule]) => {
// the callback receives the updated modules in an Array
})
}
一个自我接受或允许被其他模块接受的模块,可以通过hot.dispose
清除由其更新副本创建的任何持久性副作用:
function setupSideEffect() {}
setupSideEffect()
if (import.meta.hot) {
import.meta.hot.dispose((data) => {
// cleanup side effect
})
}
请注意,Vite实现的HMR实际上并不会交换最初导入的模块,如果一个已经被接受的模块重定向了来自其它依赖项中的模块,Vite会负责更新这些被重定向的模块(这些模块必须被赋值给let
声明的变量)。与此同时,位于当前模块的依赖链上游的模块将不会接收到这些更新。
TypeScript
Vite支持直接引用.ts
文件或者在Vue SFC中通过<script lang="ts">
标签进行引入。值得注意的是,Vite只提供ts到js的转义,但不支持类型检查,它将这部分工作托付给了IDE和构建程序。Vite使用esbuild
对ts进行转译,其速度是tsc
的20至30倍,开发者所做的任何改动,通过热更新,可以在50ms内展示在浏览器上。
由于Vite不支持类型检查, ts有一些特性并不支持,比如const enum和隐式type-only的导入。Vite推荐在
tsconfig.json
中开启isolatedModules
以便及时得到相关告警。
CSS / JSON Importing
Vite支持直接通过JavaScript引入css
和json
文件,而且均支持热更新。
.json
文件将以object
的形式导出.css
文件将不会导出任何东西,除非该文件以.module.css
结尾
Asset URL Handling
Vite的静态资源管理功能与现有的vue-cli
以及webpack的file-loader
差不多。在此基础上,Vite添加了一项公共基础路径功能。该功能主要用于在嵌套的公共路径下部署项目,通过指定参数--base=/your/public/path/
即可重写项目中的所有静态资源路径。
该功能有两种调用方式:
- 您可以通过从JavaScript导入静态资产文件来获取解析后的公共路径。例如,
import path from './foo.png'
,它将为您提供其解析的公共路径作为字符串。 - 如果需要动态连接路径,则可以使用全局注入的
import.meta.env.BASE_URL
变量,该变量将成为公共基本路径。请注意,此变量在构建期间会被静态替换,因此它必须完全按原样显示(即import.meta.env['BASE_URL']
不起作用)。
PostCSS
Vite会自动将用户定义的PostCSS的相关设置应用到.vue
文件及其所引用的.css
文件中的所有样式。你只需要下载安装你所需要的插件,并在项目的根目录添加一个postcss.config.js
即可。
CSS Modules
在*.css
文件中你可以使用<style module>
标签进行引入,而对于纯css文件,则需要将css文件的后缀改为*.module.css
。同时,在使用CSS模块时,可以不配置PostCSS,开箱即用。
CSS Pre-Processors
由于Vite仅针对现代浏览器,因而其推荐将原生的CSS变量与实现了CSSWG草案并编写符合未来标准的CSS的PostCSS插件一起使用。如果用户坚持使用CSS预处理器,也可以安装相应的预处理器并像以往一样使用它。
举个例子,用户安装预处理器sass后,在代码中可以这样调用:
<style lang="scss">
/* use scss */
</style>
在Vite 1.0.0-beta.9+版本后,可以通过在配置文件中通过设置
cssPreprocessOptions
来给预处理器传递参数
// vite.config.js
export default {
cssPreprocessOptions: {
less: {
modifyVars: {
'preprocess-custom-color': 'green'
}
}
}
}
JSX
Vite支持.jsx
和.tsx
文件。JSX也可以通过esbuild
进行移植。Vue 3有开箱即用的默认JSX配置:
import { createApp } from 'vue'
function App() {
return <Child>{() => 'bar'}</Child>
}
function Child(_, { slots }) {
return <div onClick={() => console.log('hello')}>{slots.default()}</div>
}
createApp(App).mount('#app')
当前,这是自动导入一个jsx兼容函数,该函数将esbuild生成的JSX调用转换为次优的Vue 3兼容的vnode调用。Vue 3将最终提供可利用Vue 3的运行时快速路径的自定义JSX转换。
Web Assembly
在1.0.0-beta.3+版本,Vite支持直接引用预编译的.wasm
文件(有空介绍一下WASM)。WASM文件将作为一个初始化函数被引入,该函数将返回一个Promise对象,wasm实例作为Promise的返回结果被调用。
import init from './example.wasm'
init().then(exports => {
exports.test()
})
init
函数也可以使用imports
对象作为WebAssembly.instantiate
的第二个参数传入:
init({
imports: {
someFunc: () => { /* ... */ }
}
}).then(() => { /* ... */ })
Inline Web Workers
在1.0.0-beta.3+版本,Vite支持内联Web Worker。只需要在导入请求的路径中附加?worker
即可直接导入Web Worker脚本。其默认导出一个自定义的weoker构造器。
import MyWorker from './worker?worker'
const worker = new MyWorker()
当然,你也可以不使用内联Web Worker。在这种情况下,你需要将你的worker脚本放置在public
下,并通过new Worker('/worker.js')
初始化worker。
Custom Blocks
Vite支持Vue SFC中的自定义块。只需要在配置文件中给vueCustomBlockTransforms
为自定义块添加一系列的转换函数即可使用。
// vite.config.js
export default {
vueCustomBlockTransforms: {
i18n: ({ code }) => {
// i18n用于国际化翻译
// return transformed code
}
}
}
HTTPS/2
在启动服务时添加--https
参数,Vite将自动生成一个自签名证书,并启用TLS和HTTP/2。当然,你也可以通过修改配置文件中的httpsOptions
项来使用自定义的证书。证书文件格式支持key
,cert
,ca
,pfx
。
Dev Server Proxy
Vite支持代理服务器,即配置文件中的proxy
项。Vite使用koa-proxies
(使用http代理)作为其代理服务器。配置中的每个键,都可以是一个路径完整的选项。
// vite.config.js
export default {
proxy: {
// string shorthand
'/foo': 'http://localhost:4567/foo',
// with options
'/api': {
target: 'http://jsonplaceholder.typicode.com',
changeOrigin: true,
rewrite: path => path.replace(/^\/api/, '')
}
}
}
开发实战
Vue用户注意:Vite当前仅适用于Vue3.x。这也意味着不能使用尚未与Vue 3兼容的库。
npm init vite-app <项目名称>
cd <项目名称>
npm 安装
npm 运行开发
尽管Vite最初旨在与Vue 3配合使用,但它也可以支持其他框架。例如,尝试
npm init vite-app --template react
或--template preact
对比vue-cli及其他基于捆绑器的解决方案
Vite最大的区别在于,在开发中不会进行捆绑处理。源代码内置的ES Import语法将直接提供给浏览器,浏览器通过原生支持的<script module>
对它们进行解析,并为每个导入创建一个HTTP请求。开发服务器会拦截相关请求,并在必要时候进行代码转换。例如,当导入一个.vue
文件,开发服务器会先进行编译,然后再将编译后的结果返回给浏览器。这种方法有几大好处:
- 由于无需进行捆绑工作,因此服务器冷启动非常快
- 代码按需编译。仅编译当前页面所导入的代码,无需等待整个应用程序的代码进行捆绑操作
- 热更新(HMR)性能与程序中的模块总数量无关。无论应用程序有多大,HMR都能保持高效运行。