Vite总结

概要

Vite是一个基于浏览器原生ES imports的开发服务器。开发环境利用了浏览器原生支持模块化特性,浏览器解析imports,服务端按需编译返回内容,跳过打包过程。生产环境内置rollup实现打包构建。其特点如下:

  • 快速冷启动
  • 瞬间热更新
  • 真正的按需加载和按需编译

使用

Vite默认搭配vue使用,但是也可以搭配react等其他框架。注意:开发环境下需要使用支持 native ES module imports的浏览器。

// 默认使用Vue框架
npm init vite-app <project-name>
// react中使用
npm init vite-app --template react <project-name>
cd <project-name>
npm install
// 开发环境
npm run dev
// 生产环境
npm run build

项目根目录下新增配置文件

// vite.config.js  
const path = require("path");
    
module.exports = {
  alias: {
    "/@/": path.resolve(__dirname, "./src"), // 键必须以斜线开始和结束
  },
  hostname: "localhost",
  port: 5000,
  open: true, // 自动在浏览器打开
  https: true, // 是否开启 https
  ssr: false, // 服务端渲染
  outDir: "dist", // 输出路径
  proxy: {
    '/api': {
      target: 'https://api.xxx.com',
      changeOrigin: true,
      rewrite: path => path.replace(/^\/api/, '')
    }
  },
  plugins: [] // 自定义插件
  build: {    // 生产环境构建选项,https://cn.vitejs.dev/guide/build.html#public-base-path
    target: 'es2015', // 目标语言
    rollupOptions: {  // 透传给rollup, https://rollupjs.org/guide/en/#big-list-of-options
      input: {
      },
      output: {
      }
    }
      
  }
};

原理

esbuild

esbuild是一个「JavaScript」 代码转译和压缩工具,它可以将「JSX」和「TypeScript」等代码编译成目标版本的JavaScript代码。功能上类似babel和tsc。

  • 加载器
  • 压缩
  • 打包
  • Tree shaking
  • Source map 生成
  • 将 JSX 和较新的 JS 语法移植到 ES6

esbuild 基于go语言编写,构建速度远远超过传统打包工具参考。下图是使用不同工具打包three.js的时间。
在这里插入图片描述

Vite 使用 Esbuild 来进行代码的编译,其中jsx和tsx都是通过esbuild解析生成AST,再生成最终的es module的代码,commonjs到ES module也使用它来进行转换

Javascript模块

当给script 标签添加type="module"的属性时如:

<script type="module" src="./main.js"></script>

浏览器就会像服务器发起一个GET http://localhost:3000/main.js请求main.js文件:

// main.js:
import { add } from './add.js'
console.log(add(1,2))

浏览器请求到了main.js文件,检测到内部含有import引入的包,又会对其内部的 import 引用发起 HTTP 请求获取模块的内容文件,如: GET http://localhost:3000/add.js

注意:这里需要使用http协议打开html。如果使用file协议打开会报跨域,原因是file协议不支持跨域,但是type=module的js会默认产生跨域请求。

Vite通过Koa启动服务器并劫持浏览器请求,并在后端进行相应的处理将项目中使用的文件通过不同插件处理后返回给浏览器渲染页面,并能实现真正的按需加载
在这里插入图片描述

plugin

Vite插件机制基于KOA洋葱模型,对于一些浏览器不能直接处理的文件类型如vue文件、jsx等分发不同的插件处理成js模块返回给浏览器,plugin大致分类如下

  • 用户注入的 plugins —— 自定义 plugin
  • hmrPlugin —— 处理 hmr
  • htmlRewritePlugin —— 重写 html 内的 script 内容
  • moduleRewritePlugin —— 重写模块中的 import 导入
  • moduleResolvePlugin ——获取模块内容
  • vuePlugin —— 处理 vue 单文件组件
  • esbuildPlugin —— 使用 esbuild 处理资源
  • assetPathPlugin —— 处理静态资源
  • serveStaticPlugin —— 托管静态资源
  • cssPlugin —— 处理 css/less/sass 等引用

接下来看看 plugin 的实现方式,以处理css为例。

// node/server/serverPluginCss.ts
export const cssPlugin: ServerPlugin = ({ root, app, watcher, resolver }) => {
 app.use(async (ctx, next) => {
   await next()
   if (isCSSRequest(ctx.path)) {
     const id = JSON.stringify(hash_sum(ctx.path))
     if (isImportRequest(ctx)) {
       const { css, modules } = await processCss(root, ctx)
       ctx.type = 'js'
       // 返回编译后的css代码
       ctx.body = codegenCss(id, css, modules)
     }
   }
 })
}

export function codegenCss(
 id: string,
 css: string,
 modules?: Record<string, string>
): string {
 // updateStyle是客户端定义的更行样式的方法
 let code =
   `import { updateStyle } from "${clientPublicPath}"\n` +
   `const css = ${JSON.stringify(css)}\n` +
   `updateStyle(${JSON.stringify(id)}, css)\n`
 if (modules) {
   code += dataToEsm(modules, { namedExports: true })
 } else {
   code += `export default css`
 }
 return code
}
//client/client.ts   
export function updateStyle(id: string, content: string) {
  let style = sheetsMap.get(id)
  if (supportsConstructedSheet && !content.includes('@import')) {
    if (style && !(style instanceof CSSStyleSheet)) {
      removeStyle(id)
      style = undefined
    }

    if (!style) {
      style = new CSSStyleSheet()
      style.replaceSync(content)
      // 使用动态注入css对象方式
      document.adoptedStyleSheets = [...document.adoptedStyleSheets, style]
    } else {
      style.replaceSync(content)
    }
  } else {
    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)
}

线上打包

Vite内置rollup用于线上打包,源码位于 node/build/index.ts 中,build目录导出一个 build 方法,同样也有许多 plugin,不过这些 plugin 与 server 中的用途不一样,因为 build 使用了 rollup ,所以这些 plugin 也是为 rollup 打包的 plugin。可以通过vite.config.js build.rollupOptions直接添加rollup配置。

不足

  • Esbuild 现在还不够稳定,无法用于生产环境的打包,项目打包的时候需要使用Rollup,这就造成开发环境和生产环境的代码不一致,有可能一些bug会无法定位
  • 浏览器的支持度还有待提升
  • ssr还在试验阶段

其他资源

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值