实现一个迷你vite

644 阅读3分钟

四月份的时候,尤大在 vue 3.0 beta 直播中提到了vite工具,还在微博上表示再也回不去 webpack 了, 引来了 webpack 核心开发人员肖恩的搞笑回复, 上一张尤大微博截图

1、什么是 Vite

Vite 是一个由原生 ES-Module 驱动的 Web 开发构建工具,vue3配套的脚手架工具。在开发环境下基于浏览器es6的import开发,生产环境下使用Rollup 打包。

它主要具有以下特点:

  • 快速的冷启动
  • 即时的模块热更新
  • 真正的按需编译

2、代码地址

github.com/vitejs/vite

3、怎么使用vite

npm init vite-app <project-name>
cd <project-name>
npm install
npm run dev

保持了跟vue-cli相同的结构,是不是还是熟悉的味道

├── index.html
├── package.json
├── public
│   └── favicon.ico
└── src
    ├── App.vue
    ├── assets
    │   └── logo.png
    ├── components
    │   └── HelloWorld.vue
    ├── index.css
    └── main.js

main.js

import { createApp } from 'vue'
import App from './App.vue'
import './index.css'

createApp(App).mount('#app')

index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <link rel="icon" href="/favicon.ico" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Vite App</title>
</head>
<body>
  <div id="app"></div>
  <script type="module" src="/src/main.js"></script>
</body>
</html>

<script>标签中使用type="module",浏览器就会知道这是一个ES6模块, 目前主流的浏览器都已经支持。 在浏览器端使用import、export导入导出模块,浏览器识别模块内部的import,发起http网络请求,获取文件内容。 我们可以试着理解 vite 的几个特性了。

  • webpack 之类的打包工具为了在浏览器里加载各模块,会借助胶水代码用来组装各模块,比如 webpack 使用 map 存放模块 id 和路径,使用 webpack_require 方法获取模块导出,vite 利用浏览器原生支持模块化导入这一特性,省略了对模块的组装,也就不需要生成 bundle,所以 冷启动是非常快的。
  • 打包工具会将各模块提前打包进 bundle 里,但打包的过程是静态的——不管某个模块的代码是否执行到,这个模块都要打包到 bundle 里,这样的坏处就是随着项目越来越大打包后的 bundle 也越来越大。而 ESM 天生就是按需加载的,只有 import 的时候才会去按需加载。

既然浏览器能直接识别ES-Module,为什么还要用vite呢?我们直接用浏览器打开index.html看一下,会发现报错了 其实vite是起了一个koa的http server,拦截import请求,根据请求内容的不同返回响应的结果。 比如,main.js的返回结果会变成这样 可见,vite对import的内容做了一些修改,下面我们实现一个迷你的vite。

4、实现一个迷你vite

代码地址: tiny-vite 引入koa-route来匹配路由,利用中间件来代替冗长的if-else。

支持第三方库

  • import文件的路径以"/", "./", or "../"开头才会合法的,所以第三方库,比如vue会被修改为/@modules/vue.js的形式;
  • 然后遇到/@modules/开头的请求,就去node_modules中找到package.json模块,加载module字段对应的文件。

支持.vue文件

  • 利用@vue-compiler-sfc解析vue单文件组件,变成import加type=template的形式
  • 文件内容拼成_script对象, 添加render方法,并将_script导出
  • 然后遇到?type=template结尾的请求,利用@vue-compiler-domtemplate解析成render函数,也就是第二步中的render

最后的结果

const __script = {
    name: 'App',
    setup() {
    	//...
    }
}

import {render as __render} from "/src/App.vue?type=template"
__script.render = __render
export default __script

支持.css文件

结果

import { updateStyle } from "/vite/client"
const css = "#app {\n  text-align: center;\n  color: #2c3e50;\n  margin-top: 60px;\n}\n"
updateStyle("\"2418ba23\"", css)
export default css

我们直接用一种简单的方式,添加style标签,innerHTML为css文件的内容来实现

其他

还可以支持ts、less、sass等等,感兴趣的话可以自己实现~

参考

有了 vite,还需要 webpack 么?