Рука на Vite, раскройте тайну Vite

внешний интерфейс Vite
Рука на Vite, раскройте тайну Vite

Другие статьи министра из серии Vite, приглашаем друзей стрелять кирпичами:

Подготовка к 2021 году: инженерная практика Vite, рекомендуемая коллекция

Подготовка к 2021 году: лучшие практики проекта Vite2

Знание руководства по разработке плагинов Vite2

предисловие

Многие друзья спрашивали меня, почемуviteМожет ли запуск и разработка быть такими быстрыми?

На самом деле, в документе есть довольно четкие инструкции для этой части, текст будет обсуждать эти знания и приведет вас к тому, чтобы углубиться в нижний слой и реализовать свои собственные от руки.Vite, эти проблемы будут решены легко! Мало того, мы также можем полностью понять детали компиляции и разбора vue3 SFC, которые, можно сказать, полны урожая!

Аннотация

  • Проблемы с вебпаком
  • Vite использует другой подход
  • Как работает Вайт
  • Внедрите свой собственный Vite вручную
  • Загрузка исходного кода
  • видеоурок
  • Последующий творческий план

Проблемы с вебпаком

Знакомый веб-пакет должен запускать предварительный просмотр локального сервера разработки в реальном времени во время разработки. потому чтоВесь файл проекта должен быть упакован, скорость запуска во время разработки будет становиться все медленнее и медленнее по мере расширения масштаба проекта. Та же проблема существует для горячих обновлений после изменения файлов во время разработки.

img

Смотрите, неважно, нужно вам это или нет, вас будут насильно кормить тортом!

image-20210413111741115

Vite использует другой подход

ViteОн очень хорошо решает две вышеуказанные проблемы. Запустите сервер разработки без упаковки кода файла, загрузите необходимую обработку модуля в соответствии с запросом клиента,Реализуйте настоящую загрузку по требованию. Для обновления файлов,ViteHMR выполняется на собственном ESM. Нужно только точно аннулировать цепочку между редактируемым модулем и его ближайшей границей HMR (чаще всего только сам модуль), чтобы обновления HMR всегда были быстрыми, независимо от размера приложения. Дайте то, что вы действительно хотите съесть!

img

Как работает Вайт

Как работает магия? Секрет в том, что Vite использует преимущества браузераnative ES module importsОсобенности, используя метод ES для организации кода, браузер автоматически запрашивает необходимые файлы, а также компилирует и возвращает по мере необходимости на стороне сервера, полностью пропуская процесс упаковки. Ключевое изменениеindex.htmlМетод импорта входного файла в

image-20201102112021740

Таким образом, код может быть организован в виде модуля ES6 в main.js:

vite должен выполнять различную работу по синтаксическому анализу в соответствии с запрошенным типом ресурса, напримерApp.vue, содержимое, возвращаемое пользователю, выглядит следующим образом:

// 原先的script部分内容
import HelloWorld from '/src/components/HelloWorld.vue'

const __script = {
    name: 'App',
    components: {
        HelloWorld
    }
}

// 可见`template`部分转换为了一个模板请求,解析结果是一个渲染函数
import {render as __render} from "/src/App.vue?type=template"

__script.render = __render
__script.__hmrId = "/src/App.vue"
__script.__file = "/Users/yt/projects/vite-study/src/App.vue"
export default __script

Ниже приведено содержимое проанализированной функции рендеринга:

Внедрите свой собственный Vite вручную

Создайте сервер разработки

Сервер разработки можетindex.htmlВернитесь в браузер:

const Koa = require('koa')
const app = new Koa()

app.use(async (ctx) => {
  const url = ctx.request.url
  if (url === '/') {
    ctx.type = 'text/html'
    ctx.body = fs.readFileSync('./index.html', 'utf-8')  
  }
})

app.listen(3000, () => {
  console.log('kvite start');
})

Ошибка просмотра, нужно исправитьmain.jsнагрузка

image-20210318165748277

загрузить js

Сервер добавляет поддержку запросов файлов js:

// 导入path
const path = require('path')

app.use(async (ctx) => {
  if (url === '/') {} 
  else if (url.endsWith('.js')) {
    // 获取js文件绝对路径,读取并返回
    const p = path.join(__dirname, url)
    ctx.type = 'text/javascript'
    ctx.body = fs.readFileSync(p, 'utf-8')
  }
})

Загружать сторонние библиотеки

Если пользователь импортирует стороннюю зависимость, такую ​​какvue

import {createApp, h} from 'vue'

createApp({
  render: () => h('div', 'hello, kvite!')
}).mount('#app')

main.jsЗапрос успешно вернул содержимое:

image-20210318174802304

Но мы обнаружили, что браузеры поддерживают загрузку файлов только по относительному пути:

image-20210318174907653

Здесь главное заменить裸模块路径для相对路径, например, мы будемfrom 'vue'заменитьfrom '/@modules/vue'

function rewriteImport(content) {
  return content.replace(/ from ['|"]([^'"]+)['|"]/g, function(s0, s1){
    if (s1.startsWith('./') || s1.startsWith('/') || s1.startsWith('../')) {
      return s0
    } else {
      return ` from '/@modules/${s1}'`
    }
  })
}

app.use(async (ctx) => {
  const url = ctx.request.url
  if (url === '/') {
    // ...
  } else if (url.endsWith('.js')) {
    // ...
    const ret = fs.readFileSync(p, 'utf-8')
    // 重写裸模块导入部分
    ctx.body = rewriteImport(ret)
  }
})

Просмотр результатов преобразования

image-20210319100854747

Вы можете видеть, что браузер пытается загрузить/@modules/vue, что означает, что замена прошла успешно

image-20210319101005916

Наконец, обработайте загрузку модуля зависимостей: объектный файл находится в папке модуля.package.jsonЕсть описания:

image-20210319101321723

Получите этот путь и прочитайте целевой файл, конкретная реализация выглядит следующим образом:

else if (url.startsWith('/@modules')) {
    const moduleName = url.replace("/@modules/", "");
    const prefix = path.join(__dirname, "../node_modules", moduleName);
    const module = require(prefix + "/package.json").module;
    const filePath = path.join(prefix, module);
    const ret = fs.readFileSync(filePath, "utf8");
    ctx.type = "text/javascript";
    ctx.body = rewriteImport(ret);
}

моделирование процесса

некоторые библиотеки будут иметь доступprocess, поэтому он сообщитprocessНеопределенная ошибка, добавьте ее на главную страницуmockПросто избегайте

if (url === '/') {
    ctx.type = 'text/html'
    const content = fs.readFileSync('./index.html', 'utf-8').replace(
      '<script type="module" src="/src/main.js"></script>',
      `
        <script>
          window.process = {env:{NODE_ENV:'dev'}}
        </script>
        <script type="module" src="/src/main.js"></script>
      `,
    )
    ctx.body = content
}

Контент отрендерен успешно!

image-20210325152502815

Обработка запроса SFC

окончательная обработкаSFCразбор, напримерApp.vue,

<template>
  <div>{{ title }}</div>
</template>

<script>
import { ref } from "vue";
export default {
  setup() {
    const title = ref("hello, kvite!");
    return { title };
  },
};
</script>
import { createApp, h } from "vue";
import App from './App.vue'

createApp(App).mount("#app");

использоватьcompiler-sfcа такжеcompiler-domСкомпилировать SFC

const compilerSfc = require("@vue/compiler-sfc");
const compilerDom = require("@vue/compiler-dom");
else if (url.indexOf('.vue') > -1) {
   // SFC路径
		const p = path.join(__dirname, url.split("?")[0]);
    const ret = compilerSfc.parse(fs.readFileSync(p, 'utf-8'))
		// SFC文件请求
    if (!query.type) {
      const scriptContent = ret.descriptor.script.content
      const script = scriptContent.replace('export default ', 'const __script = ')
      // 返回App.vue解析结果
      ctx.type = 'text/javascript'
      ctx.body = `
        ${rewriteImport(script)}
        import { render as __render } from '${url}?type=template'
        __script.render = __render
        export default __script
      `
    } else if (query.type === 'template') {
      // 模板内容
      const template = ret.descriptor.template.content
      // 编译为render
      const render = compilerDom.compile(template, { mode: 'module' }).code
      ctx.type = 'text/javascript'
      ctx.body = rewriteImport(render)
    }
}

Вы закончили~

image-20210325161038448

видеоурок

Сельский староста специально записал поддерживающее видео и повелел всем показать своеVite:

Принцип работы Vite и реализация рукописного ввода «завершены»

добро пожаловать друзьяТройной + подписка, ваша поддержка - самая большая мотивация для меня продолжать ❤️

Поддержка исходного кода

Добро пожаловать в публичный аккаунт村长学前端самовывоз