💡
极速的服务启动
使用原生 ESM 文件,无需打包!
⚡️
轻量快速的热重载
无论应用程序大小如何,都始终极快的模块热替换(HMR)
🛠️
丰富的功能
对 TypeScript、JSX、CSS 等支持开箱即用。
📦
优化的构建
可选 “多页应用” 或 “库” 模式的预配置 Rollup 构建
🔩
通用的插件
在开发和构建之间共享 Rollup-superset 插件接口。
🔑
完全类型化的API
灵活的 API 和完整的 TypeScript 类型。
Vite(法语意为 “快速的”,发音 /vit/,发音同 “veet”)是一种新型前端构建工具,能够显著提升前端开发体验。它主要由两部分组成:
一个开发服务器,它基于 原生 ES 模块 提供了 丰富的内建功能,如速度快到惊人的 模块热更新(HMR)。
一套构建指令,它使用 Rollup 打包你的代码,并且它是预配置的,可输出用于生产环境的高度优化过的静态资源。
Vite 是一种具有明确建议的工具,具备合理的默认设置。您可以在 功能指南 中了解 Vite 的各种可能性。通过 插件,Vite 支持与其他框架或工具的集成。如有需要,您可以通过 配置部分 自定义适应你的项目。
Vite 还提供了强大的扩展性,可通过其 插件 API 和 JavaScript API 进行扩展,并提供完整的类型支持。
在开发阶段,Vite 将 esnext 作为转换目标,因为我们假设使用的是现代浏览器,它支持所有最新的 JavaScript 和 CSS 特性。这样可以防止语法降级,让 Vite 尽可能地接近原始源代码。
对于生产构建,默认情况下 Vite 的目标浏览器支持 原生 ES 模块、原生 ESM 动态导入 和 import.meta。旧版浏览器可以通过官方的 @vitejs/plugin-legacy。查看 构建生产环境 了解更多细节。
Vite是一个由原生ES module驱动的web开发构建工具,在开发环境下基于浏览器原生Es import开发,在生产环境下基于Rollup打包。
vite的特点有:
- 闪电般的冷启动速度
- 即使热更新
- 真正的按需编译
要求:
vite 要求项目完全由esmodule模块组成
common.js模块不能在vite上使用
打包上依旧还是rollup等传统打包工具
vite的底层实现:
- 已知目录结构
- App.vue内容
<template>
<div>
<h1>大家好,我是阿亮</h1>
<h2>
<span>count is {{ count }}</span>
<button @click="count++">+1</button>
<button @click="add">add+1</button>
</h2>
</div>
</template>
<script>
import { ref } from "vue";
export default {
setup() {
const count = ref(6);
function add() {
count.value++;
}
return { add, count };
},
};
</script>
- index.css
h1 {color: red;}
- main.js
import { str } from './moduleA.js'
console.log('vite ...', str);
import { createApp, h } from 'vue';
import App from './App.vue'
import './index.css'
const App1 = {
render() {
// <div><div>hello vite</div></div>
return h('div', null, [
h('div', null, String('hello vite'))
])
}
}
createApp(App).mount('#app')
- moduleA.js
export const str = 'zanlan 321'
- index.html
<h1>hello vite</h1>
<div id="app"></div>
<script src="./src/main.js" type="module"></script>
- index.js
const Koa = require('Koa')
const fs = require('fs')
const path = require('path')
const app = new Koa()
const compilerSfc = require('@vue/compiler-sfc')
const compilerDom = require('@vue/compiler-dom')
app.use(async (ctx) => {
const { url, query } = ctx.request
console.log('url ------------' + url);
if (url === '/') {
ctx.type = 'text/html'
let content = fs.readFileSync('./index.html', 'utf-8')
content = content.replace('<script', `
<script>
window.process = {env:{NODE_ENV:'dev'}}
</script>
<script`)
ctx.body = content
} else if (url.endsWith('.js')) {
// /src/main.js => 代码文件所在位置/src/main.js
const p = path.resolve(__dirname, url.slice(1))
const content = fs.readFileSync(p, 'utf-8')
ctx.type = 'application/javascript'
ctx.body = rewriteImport(content)
} else if (url.startsWith('/@modules')) {
// 第三方库的支持
// /@modules/vue => node_modules
// /@modules/vue => 代码的位置/node_modules/vue/ 的es模块入口
const prefix = path.resolve(
__dirname,
"node_modules",
url.replace("/@modules/", "")
)
// 读取package.json的module属性
const module = require(prefix + '/package.json').module
const p = path.resolve(prefix, module)
const ret = fs.readFileSync(p, 'utf-8')
ctx.type = 'application/javascript'
ctx.body = rewriteImport(ret)
} else if (url.indexOf('.vue') > -1) {
// /*.vue?type = template
const p = path.resolve(__dirname, url.split('?')[0].slice(1))
const { descriptor } = compilerSfc.parse(fs.readFileSync(p, 'utf-8'))
if (!query.type) {
// 第一步vue文件 => template script (compiler-sfc)
// descriptor => js + template生成render部分
ctx.type = "application/javascript"
// 借用vue自导的compile框架,解析单文件组件,其实相当于vue-loader做的事情
ctx.body = `
${rewriteImport(
descriptor.script.content.replace("export default", "const __script = ")
)}
import { render as __render } from "${url}?type=template"
__script.render = __render
export default __script
`
} else {
// 第二步 template模板 => render函数(compiler-dom)
const template = descriptor.template
const render = compilerDom.compile(template.content, { mode: 'module' })
ctx.type = "application/javascript"
console.log('render', render);
ctx.body = rewriteImport(render.code)
}
} else if (url.endsWith('.css')) {
console.log('9999999999999999999999999');
const p = path.resolve(__dirname, url.slice(1))
const file = fs.readFileSync(p, 'utf-8')
// css转换为js代码
// 利用js添加一个style标签
const content = `
const css = "${file.replace(/\n/g, "")}"
let link = document.createElement('style')
link.setAttribute('type','text/css')
document.head.appendChild(link)
link.innerHTML = css
export default css
`
ctx.type = 'application/javascript'
ctx.body = content
}
// 第三方库的支持
// vue => node_modules/***
// 改写函数
// 需要改写 欺骗一下浏览器 'vue' => '/@modules/vue' => 别名
// from 'xxx'
function rewriteImport(content) {
return content.replace(/ from ['|"]([^'"]+)['|""]/g, (...args) => {
if (args[1][0] !== '.' && args[1][1] !== '/') {
return ` from '/@modules/${args[1]}'`
} else {
return args[0]
}
})
}
/**
rewriteImport('import { createApp, h } from \'vue\'')
"import { createApp, h } from '/@modules/vue'"
*/
})
app.listen(3000, () => {
console.log('vite start at 3000');
})
- pakage.json
{
"name": "vite",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"koa": "^2.13.4",
"vue": "^3.2.36"
}
}
- 执行node index.js
- 在页面上输入 localhost:3000,展示页面如下: