Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

vite 学习笔记 #8

Open
strongcode9527 opened this issue Mar 8, 2021 · 0 comments
Open

vite 学习笔记 #8

strongcode9527 opened this issue Mar 8, 2021 · 0 comments
Labels
工程 工程打包相关

Comments

@strongcode9527
Copy link
Owner

strongcode9527 commented Mar 8, 2021

参考文章

背景

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

@strongcode9527 strongcode9527 added the 工程 工程打包相关 label Feb 10, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
工程 工程打包相关
Projects
None yet
Development

No branches or pull requests

1 participant