美文网首页
vite 学习笔记

vite 学习笔记

作者: strong9527 | 来源:发表于2021-04-08 09:33 被阅读0次

参考文章

背景

Vite是一个构建工具,旨在为现代web项目提供更快、更精简的开发体验。

vite主要分两个模块:

  • 通过native ES module实现的本地开发服务,可以提供极快的热更新服务。
  • 通过rollup对生产环境打包,可以极大地优化打包生产环境的代码。

vite 和 webpack 开发环境最大的区别就是vite 在开发环境抛弃了打包这一个理念,直接在开发环境使用Javascript module,减少打包带来的时间损耗,极大地方便了本地开发。

对于vite的学习,我主要总结了以下四个模块进行总结。

  • 模块路径解析
  • 不同格式文件处理
  • 热更新
  • 预打包

模块路径解析

对于一个native es module服务系统而言,不同模块的路径解析非常重要,这里面有以下几个问题:

  • node_modules等特殊路径内容如何处理
  • 相对路径不便于记录唯一文件路径

vite的解决方式:

  • 将裸模块(node_modules)做转换 "vue.js" --> "/@modules/vue.js"
  • 将相对路径转为绝对路径,便于vite统一文件路径识别。 import '../../a.js' --> '/src/a.js'

不同格式文件处理

通过不同格式的处理,我们可以理解类似于webpack loader对于不同文件是如何处理的, 了解vite工作机制。

  • vue
  • css
  • json
  • html

对于不同格式的文件,vite统一都处理成javascript格式,在返回的response 中添加
Content-Type: application/javascript; charset=utf-8

vue:

vue 的组件是一个单文件组件的机制。一个vue组件的定义基本分三个部分:

<template></template>
<script></script>
<style></style>

编译器会将一个vue组件的三部分分别处理。在vite中,请求一个组件的资源:

截屏2021-03-18 下午7.53.28.png

Helloworld.vue script 逻辑部分编译:

// 此文件可以理解为一个组件的script逻辑部分
import string from '/src/string.js'
const __script = {
    name: 'HelloWorld',
    props: {
        msg: String
    },
    data() {
        return {
                age: 123
        }
    }
}
// 这里引入组件的template部分
import "/src/components/HelloWorld.vue?type=style&index=0"
// 这里引入组件的style部分
import {render as __render} from "/src/components/HelloWorld.vue?type=template"
__script.render = __render
__script.__hmrId = "/src/components/HelloWorld.vue"
__script.__file = "/Users/lizhuang/gitcode/vite-test/src/components/HelloWorld.vue"
export default __script

HelloWorld.vue?type=style 样式部分编译:

import { updateStyle } from "/vite/client"
const css = "\nh1 {\n  background: red;\n}\n"
updateStyle("62a9ebed-0", css)
export default css

HelloWorld.vue?type=template 结构部分编译:

import {
  toDisplayString as _toDisplayString,
  createVNode as _createVNode,
  createTextVNode as _createTextVNode,
  Fragment as _Fragment,
  openBlock as _openBlock,
  createBlock as _createBlock
} from "/@modules/vue.js"

const _hoisted_1 = /*#__PURE__*/
_createVNode("p", null, "string1", -1 /* HOISTED */
)
const _hoisted_2 = /*#__PURE__*/
_createVNode("p", null, [/*#__PURE__*/
_createTextVNode("Edit "), /*#__PURE__*/
_createVNode("code", null, "components/HelloWorld.vue"), /*#__PURE__*/
_createTextVNode(" to test hot module replacement.")], -1 /* HOISTED */
)

export function render(_ctx, _cache, $props, $setup, $data, $options) {
    return (_openBlock(),
    _createBlock(_Fragment, null, [_createVNode("h1", null, _toDisplayString($props.msg), 1 /* TEXT */
    ), _createVNode("button", {
        onClick: _cache[1] || (_cache[1] = $event=>($data.count++))
    }, "count is: " + _toDisplayString($data.count), 1 /* TEXT */
    ), _hoisted_1, _createVNode("p", null, _toDisplayString($data.string), 1 /* TEXT */
    ), _hoisted_2], 64 /* STABLE_FRAGMENT */
    ))
}

JSON 文件格式处理

通过 rollup-pluginutils 的dataToEsm方法

{
  custom: 'data',
  to: ['treeshake']
}

转变为:

export const custom = 'data';
export const to = ['treeshake'];
export default { custom, to };

CSS 文件格式处理

其实在vue的样式部分已经有所涉及。

import { updateStyle } from "/vite/client"
const css = "\nh1 {\n  background: red;\n}\n"
updateStyle("62a9ebed-0", css)
export default css

其中 updateStyle 是更新样式的关键函数,我们进行分析:


/**
* content: css文件内容
*/
function updateStyle(id, content) {
  ...
        if (!style) {
      style = new CSSStyleSheet()
      style.replaceSync(content)
      document.adoptedStyleSheets = [...document.adoptedStyleSheets, style]
    } else {
      style.replaceSync(content)
    }
  ...
}    
        

vite利用** CSSStyleSheet **代表一个样式表,利用javascript的接口编辑或者添加相关的样式。

当然还有一个特殊的情况就是css文件中有@import 等操作, 这种特殊的情况,vite直接使用style标签进行样式插入。

cosnt style = document.createElement('style')
style.setAttribute('type', 'text/css')
style.innerHTML = content
document.head.appendChild(style)

热更新

vite1 代码较少,这可以让我们低成本的学习一个开发环境热更新的具体细节

image.png

其中第四部处理不同文件的方式,列在了下方:

async function handleMessage(payload: HMRPayload) {
  const { path, changeSrcPath, timestamp } = payload as UpdatePayload
  switch (payload.type) {
    case 'connected':
      console.log(`[vite] connected.`)
      break
    case 'vue-reload':
      queueUpdate(
        import(`${path}?t=${timestamp}`)
          .catch((err) => warnFailedFetch(err, path))
          .then((m) => () => {
            __VUE_HMR_RUNTIME__.reload(path, m.default)
            console.log(`[vite] ${path} reloaded.`)
          })
      )
      break
    case 'vue-rerender':
      const templatePath = `${path}?type=template`
      import(`${templatePath}&t=${timestamp}`).then((m) => {
        __VUE_HMR_RUNTIME__.rerender(path, m.render)
        console.log(`[vite] ${path} template updated.`)
      })
      break
    case 'style-update':
      // check if this is referenced in html via <link>
      const el = document.querySelector(`link[href*='${path}']`)
      if (el) {
        el.setAttribute(
          'href',
          `${path}${path.includes('?') ? '&' : '?'}t=${timestamp}`
        )
        break
      }
      // imported CSS
      const importQuery = path.includes('?') ? '&import' : '?import'
      await import(`${path}${importQuery}&t=${timestamp}`)
      break
      .... 还有很多,就不一一列举了
  }
}

预打包

vite的预打包优化手段其实和小程序页面预加载技术,以及网页的prefetch,preload等的原理是基本一致的,当我们尽量少的打包过后,那么预打包那些没有处理的文件就是优化的手段之一。

vite 会去分析package.json 当中的依赖项,会将依赖进行打包并缓存:

截屏2021-03-21 下午3.40.26.png

其中lodash较为特殊,因为其文件众多,如果不进行预打包的话,开发项目将会请求很多相关文件,造成网页reload时性能衰减。所以vite预打包的另外一个重要的功能就是通过rollup或者esbuild(vite 不同版本实现不同),将过于零散的文件打包,减少网络请求,提高页面reload性能

截屏2021-03-21 下午3.42.57.png

相关文章

网友评论

      本文标题:vite 学习笔记

      本文链接:https://www.haomeiwen.com/subject/cvemkltx.html