Vite 特性和部分源码解析

2022-12-05 0 416

点选上方“后端控制技术混合型”,优先选择“标为隆哥蒙第三天数高度关注控制技术蔬果!Vite 特性和部分源码解析

Vite 的优点

Vite 的主要就优点就是 Bundleless。如前所述应用领域程序开始原生植物的全力支持 JavaScript 组件功能 (https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Modules?fileGuid=DDr3GGh6QRvQgWQC),JavaScript 组件倚赖 import 和 export 的优点,现阶段非主流应用领域程序基本上都全力支持;

想查阅具体内容全力支持的版能 点选这儿 (https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Modules?fileGuid=DDr3GGh6QRvQgWQC);

那这有什么竞争优势呢?

拿掉装箱关键步骤

装箱是合作开发人员借助装箱辅助工具将应用领域各组件子集在一同逐步形成 bundle,以很大准则加载组件的标识符,以期在不全力支持组件化的应用领域程序里采用,因此能减少 http 允诺的数目。但只不过在邻近地区合作开发操作过程中装箱反倒减少了我们摸查难题的技术难度,减少了积极响应时数,Vite 在邻近地区开发指示中去除装箱关键步骤,进而延长构筑时数。

按需加载

为的是减少 bundle 大小不一,通常会想按需加载,主要就有三种形式:

采用静态导入 import() 的形式触发器的加载组件,被导入组件仍然须要提早校对装箱;采用 tree shaking 等形式尽全力的拿掉未提及的组件;

而 Vite 的形式更加间接,它只在某一组件被 import 的这时候静态的加载它,同时实现了或者说的按需加载,减少了加载文档的表面积,延长了时数;

Vite 合作开发自然环境市场主体业务流程

右图是 Vite 在合作开发自然环境运转时加载文档的市场主体业务流程。

Vite 特性和部分源码解析

Vite 部份源标识符导出

总体目录结构

|-CHANGELOG.md

|-LICENSE.md

|-README.md

|-bin

|  |-openChrome.applescript

|  |-vite.js

|-client.d.ts

|-package.json

|-rollup.config.js #装箱配置文档

|-scripts

|  |-patchTypes.js

|-src

|  |-client #客户端

|  |  |-client.ts

|  |  |-env.ts

|  |  |-overlay.ts

|  |  |-tsconfig.json

|  |-node #服务端

|  |  |-build.ts

|  |  |-cli.ts #指示入口文档

|  |  |-config.ts

|  |  |-constants.ts #常量

|  |  |-importGlob.ts

|  |  |-index.ts

|  |  |-logger.ts

|  |  |-optimizer

|  |  |  |-esbuildDepPlugin.ts

|  |  |  |-index.ts

|  |  |  |-registerMissing.ts

|  |  |  |-scan.ts

|  |  |-plugin.ts #rollup 插件

|  |  |-plugins   #插件相关文档

|  |  |  |-asset.ts

|  |  |  |-clientInjections.ts

|  |  |  |-css.ts

|  |  |  |-esbuild.ts

|  |  |  |-html.ts

|  |  |  |-index.ts 

|  |  |  |-…

|  |  |-preview.ts

|  |  |-server

|  |  |  |-hmr.ts #热更新

|  |  |  |-http.ts

|  |  |  |-index.ts

|  |  |  |-middlewares #中间件

|  |  |  |  |-…

|  |  |  |-moduleGraph.ts #组件间关系组装(树形)

|  |  |  |-openBrowser.ts #打开应用领域程序

|  |  |  |-pluginContainer.ts

|  |  |  |-send.ts

|  |  |  |-sourcemap.ts

|  |  |  |-transformRequest.ts

|  |  |  |-ws.ts

|  |  |-ssr

|  |  |  |-__tests__

|  |  |  |  |-ssrTransform.spec.ts

|  |  |  |-ssrExternal.ts

|  |  |  |-ssrManifestPlugin.ts

|  |  |  |-ssrModuleLoader.ts

|  |  |  |-ssrStacktrace.ts

|  |  |  |-ssrTransform.ts

|  |  |-tsconfig.json

|  |  |-utils.ts

|-tsconfig.base.json

|-types

|  |-…                  

server 核心方法

从入口文档 cli.ts,能看到三个指示对应了 3 个核心的文件&方法;

dev 指示

文档路径:./server/index.ts;

主要就方法:createServer;

主要就功能:项目的邻近地区合作开发指示,如前所述 httpServer 启动服务,Vite 通过对允诺路径的劫

项目源标识符如下:

import { createApp } from vue

;

import App from ./index.vue

;

经服务端重写后,node_modules 文档夹下的三方包标识符路径也会被拼接完整。

import __vite__cjsImport0_vue from “/node_modules/.vite/vue.js?v=ed69bae0”

const createApp = __vite__cjsImport0_vue[“createApp”

];

import App from /src/pages/back-sky/index.vue

;

2. build 指示 文档路径:./build.ts;

主要就方法:build;

主要就功能:采用 rollup 装箱校对

3. optimize 指示

文档路径:./optimizer/index.ts;

主要就方法:optimizeDeps;

主要就功能:主要就针对第三方包,Vite 在执行 runOptimize 的这时候中会采用 rollup 对三方包重新校对,将校对成符合 esm 组件规范的新的包放入 node_modules 下的 .vite 中,然后配合 resolver 对三方包的导入进行处理:采用校对后的包内容代替原来包的内容,这样就解决了 Vite 中不能采用 cjs 包的难题。

下面是 .vite 文档夹中的 _metadata.json 文档,它在预校对的操作过程中生成,罗列了所有被预校对完成的文档及其路径。例如:

{

  “hash”“31d458ff”

,

  “browserHash”“ed69bae0”

,

  “optimized”

: {

    “element-plus/lib/utils/dom”

: {

      “file”“/Users/zcy/Documents/workspace/back-sky-front/node_modules/.vite/element-plus_lib_utils_dom.js”

,

      “src”“/Users/zcy/Documents/workspace/back-sky-front/node_modules/element-plus/lib/utils/dom.js”

,

      “needsInterop”true

    },

    “element-plus”

: {

      “file”“/Users/zcy/Documents/workspace/back-sky-front/node_modules/.vite/element-plus.js”

,

      “src”“/Users/zcy/Documents/workspace/back-sky-front/node_modules/element-plus/lib/index.esm.js”

,

      “needsInterop”false

    },

    “vue”

: {

      “file”“/Users/zcy/Documents/workspace/back-sky-front/node_modules/.vite/vue.js”

,

      “src”“/Users/zcy/Documents/workspace/back-sky-front/node_modules/vue/dist/vue.runtime.esm-bundler.js”

,

      “needsInterop”true

    },

    ……

    }

  }

}

组件导出

预构筑 (https://cn.vitejs.dev/guide/dep-pre-bundling.html?fileGuid=DDr3GGh6QRvQgWQC) 是用来提升页面重载速度,它将 CommonJS、UMD 等转换为 ESM 格式。预构筑这一步由 esbuild (http://esbuild.github.io/?fileGuid=DDr3GGh6QRvQgWQC) 执行,这使得 Vite 的冷启动天数比任何如前所述 JavaScript 的装箱程序都要快得多。

Vite 特性和部分源码解析

为什么 ESbuild 会更快?

采用 Go 语言重度并行,采用 CPU高效采用内存Scratch 编写,减少采用三方库,避免导致性能不可控

重写导入为合法的 URL,例如 /node_modules/.vite/my-dep.js?v=f3sf2ebd 以期应用领域程序能够正确导入它们

热更新

热更新市场主体业务流程如下:

服务端如前所述 watcher 监听文档改动,根据类型判断更新形式,并校对资源客户端通过 WebSocket 监听到一些更新的消息类型客户端收到资源信息,根据消息类型执行热更新逻辑Vite 特性和部分源码解析

下面是服务端热更新的核心 hmr.ts 中的部份判断逻辑;

如果配置文档或者自然环境文档发生修改时,会触发服务重启,才能让配置生效。

if(file === config.configFile || file.endsWith(.env

)) {

  // auto restart server 配置&自然环境文档修改则自动重启服务  debugHmr(`[config change] ${chalk.dim(shortFile)}`

)

  config.logger.info(

    chalk.green(config or .env file changed, restarting server…

),

    { cleartruetimestamptrue

 }

  )

  await

 restartServer(server)

  return

}

html 文档更新时,将会触发页面的重新加载。

if (file.endsWith(.html)) {// html 文档更新  config.logger.info(chalk.green(`page reload `

) +         chalk.dim(shortFile), {

    cleartrue

,

    timestamptrue

  })

  ws.send({

    typefull-reload

,

    path

: config.server.middlewareMode

    ? *    : /

+ normalizePath(path.relative(config.root, file))

  })

else

 {

  // loaded but not in the module graph, probably not jsdebugHmr(`[no modules matched] ${chalk.dim(shortFile)}`

)

}

Vue 等文档更新时,都会进入 updateModules 方法,正常情况下只会触发 update,同时实现热更新,热替换;

function updateModules(

  file: string,

  modules: ModuleNode[],

  timestamp: number,

{ config, ws }: ViteDevServer

{

  const

 updates: Update[] = []

  const invalidatedModules = new Set

()

 // 遍历插件数组,关联下面的片段  for (const mod of

 modules) {

    const boundaries = new Set

<{

      boundary

: ModuleNode

      acceptedVia

: ModuleNode

    }>()

    // 设置天数戳

invalidate(mod, timestamp, invalidatedModules)

    // 查找提及组件,判断是否须要重载页面    const

hasDeadEnd = propagateUpdate(mod, timestamp, boundaries)

    // 找不到提及者则会发起刷新    if

 (hasDeadEnd) {

      config.logger.info(chalk.green(`page reload `

) + chalk.dim(file), {

        cleartrue

,

        timestamptrue

      })

      ws.send({

        typefull-reload

      })

      return

    }

    updates.push(

…[…boundaries].map(({ boundary, acceptedVia }) =>

 ({

        type`${boundary.type}-update` as Update[type

],

timestamp,

        path

: boundary.url,

        acceptedPath

: acceptedVia.url

      }))

    )

  }

  // 日志输出

  config.logger.info(

    updates

      .map(({ path }) => chalk.green(`hmr update `

) + chalk.dim(path))

      .join(\n

),

    { cleartruetimestamptrue

 }

  )

  // 向客户端发送消息,进行热更新操作

  ws.send({

    typeupdate

,

    updates

  })

}

上面标识符中的 modules 是热更新时须要执行的各插件

for (const plugin of

config.plugins) {

  if

 (plugin.handleHotUpdate) {

    const filteredModules = await

 plugin.handleHotUpdate(hmrContext)

    if

(filteredModules) {

      hmrContext.modules = filteredModules

    }

  }

}

Vite 会把组件的依赖关系组合成 moduleGraph,它的结构类似树形,热更新中判断哪些文档须要更新也会依赖 moduleGraph;它的文档内容大致如下:

// moduleGraph 返回的 ModuleNode 大致结构

 ModuleNode {

  id/Users/zcy/Documents/workspace/back-sky-front/src/pages/back-sky/index.js

,

  file/Users/zcy/Documents/workspace/back-sky-front/src/pages/back-sky/index.js

,

  importersSet

 {},

  importedModulesSet

 {

    ModuleNode {

      id/Users/zcy/Documents/workspace/back-sky-front/node_modules/.vite/vue.js?v=32cfd30c

,

      file/Users/zcy/Documents/workspace/back-sky-front/node_modules/.vite/vue.js

,

      ……

      lastHMRTimestamp: 0

,

      url/node_modules/.vite/vue.js?v=32cfd30c

,

      typejs

    },

    ModuleNode {

      id/Users/zcy/Documents/workspace/back-sky-front/src/pages/back-sky/index.vue

,

      file/Users/zcy/Documents/workspace/back-sky-front/src/pages/back-sky/index.vue

,

……

      url: /src/pages/back-sky/index.vue

,

      typejs

    },

    ModuleNode {

      id/Users/zcy/Documents/workspace/back-sky-front/node_modules/element-plus/lib/theme-chalk/index.css

,

      file/Users/zcy/Documents/workspace/back-sky-front/node_modules/element-plus/lib/theme-chalk/index.css

,

      importers: [Set

],

      importedModulesSet

 {},

      acceptedHmrDepsSet

 {},

      isSelfAcceptingtrue

,

      transformResult: [Object

],

      ssrTransformResultnull

,

      ssrModulenull

,

      lastHMRTimestamp0

,

      url/node_modules/element-plus/lib/theme-chalk/index.css

,

      typejs

    },

    ……

},

  acceptedHmrDepsSet

 {},

  isSelfAcceptingfalse

,

  transformResult

: {

    codeimport __vite__cjsImport0_vue from

 +

      “/node_modules/.vite/vue.js?v=32cfd30c”; const createApp = 

 +

      __vite__cjsImport0_vue[“createApp”];\nimport App from

 +

      “/src/pages/back-sky/index.vue;\nimport “

 + …,

    mapnull

,

    etagW/”846-Qa424gJKl3YCqHDWXXsM1mFHRqg”

  },

  ssrTransformResultnull

,

  ssrModulenull

,

  lastHMRTimestamp0

,

  url/src/pages/back-sky/index.js

,

  typejs

}

原有项目切换

最后我们来看下如何采用 Vite 去装箱一个旧的 Vue 项目;

首先我们须要升级 Vue3

npm install vue@next

并为项目添加 Vite 配置文档,在根目录下创建 vite.config.js,并为它添加一些基础的配置。

// vite.config.js

// vite2.1.5

const path = require(path

);

import vue from@vitejs/plugin-vue

;

export

 default {

  // 配置选项

  resolve: {

    alias

: {

      @utils: path.resolve(__dirname, ./src/utils

)

    },

  },

  plugins: [vue()],

};

提及的第三方组件库可能也会须要升级,例如:升 element-ui 至 element-plus

npm install element-plus

Vue3 在 import 时,需采用 createApp 方法进行初始化

import { createApp } from vue

;

import App from ./index.vue

;

const

app = createApp(App);

import

 {

  ElInput,

  ElLoading,

from element-plus

;

app.use(ElButton);

app.use(ElLoading);

……

到这儿就能将项目运转起来了。注意:Vite 官方不允许省略 .vue 后缀,否则就会报错;

[plugin:vite:import-analysis] Failed to resolve import “./todoList” from “src/pages/back-sky/components/header/index.vue”

. Does the file exist?

/components/header/index.vue:2:231

  |  

2  |  import todoList from ./todoList

;

import todoList from ./todoList.vue

;

最后我们来对比一下该项目三种构筑形式天数的对比;

Webpack 冷启动,耗时 7513ms:

⚠ 「wdm」: Hash: 1ad1dd54289cfad8ecbe

Version: webpack 4.46.0

Time: 7513ms

Built at: 2021-05-24 13:59:35

相同项目 Vite 冷启动,耗时 924ms:

> vite

Pre-bundling dependencies:

  vue

element-plus

  @zcy/zcy-request

  element-plus/lib/utils/dom

(this will be run only when your dependencies or config have changed)

  vite v2.3.3 dev server running at:

  > Local: http://localhost:3000/

> Network: use `–host` to expose

  ready in

 924ms.

二次启动(预校对的依赖已存在),耗时 407ms;

> vite

  vite v2.3.3 dev server running at:

> Local: http://localhost:3000/

  > Network: use `–host` to expose

  ready in

 407ms.

总结

采用 Vite 进行邻近地区服务启动和热更新都会有明显的提效,至于校对装箱环节的差异点有哪些?效果如何?你们还踩过哪些坑?留言告诉我吧。

推荐阅读

What are CJS, AMD, UMD, and ESM in Javascript?(https://dev.to/iggredible/what-the-heck-are-cjs-amd-umd-and-esm-ikm?fileGuid=DDr3GGh6QRvQgWQC)

在看点这儿

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务