Импорт как строки через ?raw

Vite поддерживает специальный механизм импорта файлов в виде обычной строки. Для этого к пути файла добавляется суффикс ?raw.

Пример:

import text fr om './file.txt?raw'

console.log(text)

В этом случае содержимое файла не обрабатывается как JavaScript-модуль, а загружается как строка целиком.

Если файл file.txt содержит:

Hello Vite

то переменная text будет содержать:

"Hello Vite"

Как работает ?raw

Во время обработки модулей Vite анализирует импортируемый путь. Если путь содержит ?raw, Vite:

  1. Читает файл с диска.
  2. Преобразует содержимое в строку.
  3. Генерирует JavaScript-модуль.
  4. Экспортирует строку как default export.

Фактически:

import content from './example.txt?raw'

превращается примерно в:

const content = `
содержимое файла
`

export default content

Поддерживаемые типы файлов

?raw работает практически с любыми текстовыми файлами:

import html from './template.html?raw'
import css from './styles.css?raw'
import md from './article.md?raw'
import svg from './icon.svg?raw'
import jsonText from './data.json?raw'
import shader from './shader.glsl?raw'

Главное условие — файл должен быть текстовым.


Импорт HTML как строки

Один из самых распространённых сценариев — загрузка HTML-шаблонов.

Пример

template.html

<div class="card">
  <h2>Title</h2>
</div>

Импорт:

import template from './template.html?raw'

document.body.innerHTML = template

Импорт SVG как строки

Без ?raw SVG обычно обрабатывается как URL ресурса:

import iconUrl from './icon.svg'

С ?raw содержимое SVG становится строкой:

import iconSvg from './icon.svg?raw'

console.log(iconSvg)

Результат:

<svg width="100" height="100">
  ...
</svg>

Это особенно полезно при:

  • динамической модификации SVG;
  • инлайновой вставке;
  • генерации иконок;
  • изменении цвета через строковые операции.

Работа с Markdown

Markdown-файлы часто импортируются через ?raw.

Структура

content/
  article.md

Импорт

import markdown from './content/article.md?raw'

После этого строку можно передать в markdown-парсер:

import { marked } from 'marked'
import markdown from './article.md?raw'

const html = marked(markdown)

Импорт исходного кода

Иногда необходимо отображать код прямо в интерфейсе.

Пример

import source from './example.js?raw'

console.log(source)

Это используется в:

  • playground-средах;
  • системах документации;
  • редакторах кода;
  • демонстрационных примерах;
  • генераторах документации.

Использование с WebGL и шейдерами

Очень распространённый сценарий — импорт GLSL-шейдеров.

Vertex shader

vertex.glsl

attribute vec3 position;

void main() {
  gl_Position = vec4(position, 1.0);
}

Импорт:

import vertexShader from './vertex.glsl?raw'

Разница между обычным импортом и ?raw

Обычный импорт ресурса

import file from './example.txt'

Чаще всего возвращается URL:

'/assets/example.a1b2c3.txt'

Импорт через ?raw

import file from './example.txt?raw'

Возвращается содержимое:

'Текст внутри файла'

Использование с fetch

Иногда возникает вопрос: что лучше — ?raw или fetch().

Вариант с ?raw

import text from './text.txt?raw'

Особенности:

  • содержимое попадает в bundle;
  • файл доступен сразу;
  • отсутствует сетевой запрос;
  • работает синхронно после загрузки модуля.

Вариант с fetch

const response = await fetch('/text.txt')
const text = await response.text()

Особенности:

  • выполняется HTTP-запрос;
  • файл может кэшироваться отдельно;
  • ресурс можно менять независимо от bundle.

Когда лучше использовать ?raw

?raw хорошо подходит для:

  • шаблонов;
  • markdown;
  • SVG;
  • конфигураций;
  • примеров кода;
  • встроенных текстовых ресурсов;
  • шейдеров;
  • небольших HTML-фрагментов.

Когда ?raw использовать нежелательно

Не рекомендуется импортировать через ?raw:

  • очень большие текстовые файлы;
  • массивные JSON-данные;
  • крупные статьи;
  • лог-файлы;
  • большие SQL-дампы.

Причина — содержимое встраивается в bundle и увеличивает размер JavaScript.


Импорт JSON как строки

Обычный JSON-импорт:

import data from './data.json'

Возвращает объект:

console.log(data.user.name)

Импорт через ?raw:

import jsonText from './data.json?raw'

Возвращает строку:

console.log(jsonText)

При необходимости строку можно распарсить вручную:

const data = JSON.parse(jsonText)

Использование с динамическим импортом

?raw работает и с import().

Пример

const module = await import('./template.html?raw')

console.log(module.default)

Это удобно при ленивой загрузке шаблонов.


Обработка в production-сборке

Во время production build:

vite build

Vite:

  1. читает файл;
  2. встраивает строку в итоговый модуль;
  3. минимизирует JavaScript;
  4. включает строку в bundle.

Дополнительные файлы при этом не создаются.


Влияние на размер bundle

Каждый импорт через ?raw увеличивает итоговый JavaScript-бандл.

Например:

import hugeText from './huge.txt?raw'

Если файл содержит 2 МБ текста, то bundle тоже увеличится примерно на 2 МБ.


Экранирование символов

Vite корректно экранирует:

  • переводы строк;
  • кавычки;
  • обратные слэши;
  • unicode-символы.

Например:

Line 1
Line "2"

превращается в безопасную JavaScript-строку.


Использование с TypeScript

TypeScript может не понимать импорт нестандартных расширений.

Решение

Создание файла деклараций:

// vite-env.d.ts

declare module '*?raw' {
  const content: string
  export default content
}

После этого TypeScript корректно распознаёт импорт:

import shader from './shader.glsl?raw'

Комбинирование с другими query-параметрами

Vite поддерживает разные query-модификаторы:

?raw
?url
?worker
?inline

Пример:

import workerUrl from './worker.js?url'

или:

import svgText from './icon.svg?raw'

Каждый модификатор изменяет способ обработки файла.


Ограничения ?raw

Отсутствие обработки содержимого

Файл импортируется как есть.

Например:

import css from './style.css?raw'

CSS не будет:

  • минифицироваться;
  • преобразовываться PostCSS;
  • обрабатываться autoprefixer;
  • внедряться в DOM.

Отсутствие tree shaking

Так как импортируется одна большая строка, tree shaking неприменим.


Отсутствие частичной загрузки

Vite загружает весь файл полностью.

Невозможно:

  • импортировать только часть файла;
  • стримить содержимое;
  • читать фрагменты.

Использование в Vue

Пример компонента

<script setup>
import template from './template.html?raw'
</script>

<template>
  <div v-html="template"></div>
</template>

Использование в React

import markdown from './article.md?raw'

export function App() {
  return (
    <pre>{markdown}</pre>
  )
}

Использование в Svelte

<script>
  import text from './text.txt?raw'
</script>

<pre>{text}</pre>

Использование в собственных Vite-плагинах

Плагины могут самостоятельно обрабатывать ?raw.

Пример проверки query:

export default function myPlugin() {
  return {
    name: 'my-plugin',

    load(id) {
      if (id.endsWith('?raw')) {
        console.log('Raw import:', id)
      }
    }
  }
}

Сравнение ?raw и ?url

?raw

import text from './file.txt?raw'

Результат:

'Содержимое файла'

?url

import url from './file.txt?url'

Результат:

'/assets/file.a1b2c3.txt'

Практический пример: встроенный шаблон

import cardTemplate from './card.html?raw'

function renderCard(title) {
  return cardTemplate.replace('{{title}}', title)
}

card.html

<div class="card">
  <h2>{{title}}</h2>
</div>

Практический пример: система документации

import exampleCode from './demo.js?raw'

const codeBlock = document.querySelector('#code')

codeBlock.textContent = exampleCode

Практический пример: SQL-запросы

import query from './query.sql?raw'

console.log(query)

query.sql

SEL ECT *
FROM users
WH ERE active = 1;

Практический пример: генерация inline SVG

import icon fr om './icon.svg?raw'

const button = document.querySelector('button')

button.innerHTML = icon

Практический пример: хранение email-шаблонов

import emailTemplate from './email.html?raw'

function buildEmail(name) {
  return emailTemplate.replace('{{name}}', name)
}

Особенности HMR

При изменении raw-файла Vite автоматически обновляет модуль через HMR.

Например:

import template from './template.html?raw'

После изменения template.html модуль обновится без полной перезагрузки страницы.


Поведение при SSR

В SSR-режиме ?raw также возвращает строку:

import html from './template.html?raw'

Это одинаково работает:

  • в браузере;
  • на сервере;
  • в Node.js-рендеринге через Vite SSR.

Совместимость с Rollup

Так как production-сборка Vite основана на Rollup, механизм ?raw интегрирован и в процесс финального бандлинга.

Во время сборки Rollup получает уже подготовленный виртуальный модуль со строковым экспортом.


Внутренний принцип работы

Упрощённая схема:

Файл → Vite plugin pipeline → raw loader → JS module

Пример преобразования:

hello.txt

export default "hello"

Типичные ошибки

Отсутствие default

Неправильно:

import { text } from './file.txt?raw'

Правильно:

import text from './file.txt?raw'

Импорт бинарных файлов

Плохо подходит для:

  • PNG;
  • JPEG;
  • MP4;
  • ZIP;
  • PDF.

Для них лучше использовать:

?url

или обычный импорт ресурсов.


Слишком большие raw-файлы

import massiveData from './huge.log?raw'

Это может:

  • резко увеличить bundle;
  • ухудшить startup performance;
  • увеличить расход памяти.

Поддержка Unicode

?raw корректно работает с:

  • кириллицей;
  • emoji;
  • китайскими символами;
  • UTF-8 контентом;
  • многострочным текстом.

Пример:

Привет мир
こんにちは
مرحبا

Импорт:

import text from './unicode.txt?raw'

Роль ?raw в экосистеме Vite

Механизм ?raw является частью философии Vite:

  • прямой работы с ESM;
  • модульного импорта ресурсов;
  • минимальной конфигурации;
  • удобной интеграции текстовых файлов;
  • прозрачной обработки ассетов.

Он позволяет использовать файловую систему как источник данных без дополнительных загрузчиков и сложной конфигурации webpack-style экосистем.