zoukankan      html  css  js  c++  java
  • vite 为什么快

    一、ES module 减少服务启动时间

    import { foo } from './other-module'
    

    由于大多数现代浏览器都支持上面的 ES module 语法,所以在开发阶段,我们就不必对其进行打包,这节省了大量的服务启动时间。另外,vite 按需加载当前页面所需文件,一个文件一个http请求,进一步减少启动时间。

    二、缓存减少页面更新时间

    每个文件通过 http 头缓存在浏览器端,当编辑完一个文件,只需让此文件缓存失效。当基于 ES module 进行热更新时,仅需更新失效的模块,这使得更新时间不随包的增大而增大。

    vite 怎么做到的?

    当我们执行yarn dev时,vite-cli 会在本地启动一个 koa 服务:

    export function createServer(config) {
      ...
      const app = new Koa()
      const server = resolveServer(config, app.callback())
      ...
      // 加载插件
      ...
    
      const listen = server.listen.bind(server)
      server.listen = (async (port, ...args) => {
        if (optimizeDeps.auto !== false) {
          await require('../optimizer').optimizeDeps(config)
        }
        return listen(port, ...args)
      })
      ...
    
      return server;
    }
    
    

    监听端口,执行其他服务之前,会执行optimizeDeps方法,即优化依赖。vite 文档将这部分优化叫做依赖预打包Dependency Pre-Bundling,这么做的理由有两个:一是将非 ES module转化为可被浏览器导入的 ESM;二是将 ESM 依赖的多个内部模块转化为一个模块,以减少浏览器请求从而提升页面加载速度。

    按需加载

    上面我们提到,vite通过按需加载减少等待时间,这是如何做到的?所谓一生二,二生三,三生万物。一切的根源就是服务启动后,我们访问的初始地址http://localhost:3000/。首页,浏览器根据这个 url 发出第一个请求,通过serverPluginServeStatic.ts插件获取到位于项目根目录的index.html文件,再通过serverPluginHtml.ts向其插入<script type="module">import "/vite/client"</script>,最终我们看到第一个请求返回的内容如下:

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <script type="module">import "/vite/client"</script>
    
      <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" src="/src/main.js"></script>,浏览器向我们的本地服务器请求这个文件,koa 插件serverPluginModuleRewrite.ts会重写main.js源文件的 import 的解析方式:

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

    上面的内容经过插件处理后,将以下内容返回给浏览器:

    import { createApp } from '/@modules/vue.js'
    import App from '/src/App.vue'
    import '/src/index.css?import'
    

    这里文档npm-dependency-resolving也有部分提及。浏览器解析此文件,依次发送三个请求:

    当浏览器发送第二个请求,koa 插件serverPluginVue.ts会编译单文件组件 App.vue,并打上 etag(用于缓存)后再返回给浏览器(A流程)。浏览器再解析此文件并更新页面,重复这个过程(发送请求、服务端响应、浏览器解析)直到不再发送请求。

    组件热更新

    vue 组件热更新,由serverPluginVue.tsserverPluginHmr.tsclient.ts三个部分共同完成。其中client.ts会被发送到浏览器端,位于服务端的 serverPluginHmr.ts 通过 websocket 和 client.ts 进行通信。

    我们以vite-cli默认模板项目举例,假设更改了App.vue文件内容,此时:

    • 1)serverPluginVue.ts 使用 serverPluginHmr.ts 提供的 send 方法向 client.ts 所在的浏览器端发送数据,即数据通过 websockt 从服务端推送到浏览器,数据内容如下:

      {
        "type": "vue-reload",
        "path": "/src/App.vue",
        "changeSrcPath": "/src/App.vue",
        "timestamp": 1609815863830
      }
      
    • 2) client.ts 收到数据后,执行import('/src/App.vue?t=1609815863830')

    • 3)接着浏览器发起请求:http://localhost:3000/src/App.vue?t=1609815863830,时间戳的作用是使url缓存失效

    • 4)最后,执行重复上面的A流程。
      经过上述步骤,页面得以更新。当然,更新的类型还有full-reloadjs-update以及样式更新等,但总体流程都框定在以上步骤,这里就不展开了。

    p.s.以上内容搭配vite源码再看更易理解

  • 相关阅读:
    c++ 小记
    扁平化设计
    json 学习
    c#多线程下载
    php 序列化储存和转化 json_encode() json_decode($q,true)
    支付接口
    JSON.stringify 语法实例讲解 字符串
    jquery 单选框整个选中
    js戳和php戳时间换算
    windows mysql提示:1045 access denied for user 'root'@'localhost' using password yes 解决方案
  • 原文地址:https://www.cnblogs.com/fayin/p/14234872.html
Copyright © 2011-2022 走看看