首页
/ Vite create-vite 的 Svelte + TypeScript 模板解析:svelte-ts 技术选型与配置实现

Vite create-vite 的 Svelte + TypeScript 模板解析:svelte-ts 技术选型与配置实现

2026-09-04 20:37:45作者:温艾琴Wonderful

本篇技术文章围绕 Vite 仓库中 packages/create-vite 提供的 Svelte + TypeScript 模板(svelte-ts)展开。读完你将理解该模板的定位与适用场景、相对 SvelteKit 的取舍原因,并掌握其 TypeScript 工程化配置(project references、typesallowJs/checkJs)、Vite 构建链路、Svelte 5 入口挂载方式,以及 HMR 状态保持的已知限制与外部 store 规避方案。

模板定位:create-vite 中的 svelte-ts 变体

create-vite 是 Vite 官方的项目脚手架工具,在 Svelte 技术栈下提供三个变体。从 create-vite 入口源码 中可以看到其定义:

{
  name: 'svelte',
  display: 'Svelte',
  color: red,
  variants: [
    { name: 'svelte-ts', display: 'TypeScript', color: blue },
    { name: 'svelte', display: 'JavaScript', color: yellow },
    {
      name: 'custom-svelte-kit',
      display: 'SvelteKit ↗',
      customCommand: 'npm exec sv create TARGET_DIR',
    },
  ],
},

即选择 Svelte + TypeScript 时会使用本模板(模板目录),而 SvelteKit 变体实际上是把命令委托给 sv create,并不走 create-vite 的本地模板复制流程。

本地模板目录的完整结构如下,内容刻意保持最小化:

template-svelte-ts/
├── .vscode/
│   └── extensions.json        # VS Code 扩展推荐
├── public/
│   ├── favicon.svg
│   └── icons.svg
├── src/
│   ├── assets/                # hero.png / svelte.svg / vite.svg
│   ├── lib/
│   │   └── Counter.svelte     # 演示 $state 的计数组件
│   ├── App.svelte
│   ├── app.css
│   └── main.ts                # Svelte 5 应用入口
├── index.html
├── package.json
├── svelte.config.js
├── tsconfig.json               # project references 根配置
├── tsconfig.app.json           # 应用代码(src)
├── tsconfig.node.json          # Node 侧(vite.config.ts)
├── vite.config.ts
└── _gitignore                 # 脚手架生成时重命名为 .gitignore

使用方式是在目标目录执行(模板的 README 即面向该使用场景编写):

npm create vite@latest my-app -- --template svelte-ts
cd my-app
npm install
npm run dev

推荐 IDE 配置:VS Code + Svelte 扩展

模板 README 给出的推荐 IDE 组合是 VS Code + Svelte 官方 VS Code 扩展(扩展 ID 为 svelte.svelte-vscode)。

更值得注意的机制是:README 提到,其他模板往往只在 README 里"间接推荐"扩展,而本模板额外提供了 .vscode/extensions.json 文件。从 _gitignore 可以看到 .vscode/* 目录默认被忽略、仅保留 extensions.json 进入版本控制:

# Editor directories and files
.vscode/*
!.vscode/extensions.json

而该文件的内容只有一条推荐:

{
  "recommendations": ["svelte.svelte-vscode"]
}

其作用是:用户用 VS Code 打开由模板生成的项目时,VS Code 会主动弹出提示,建议安装 Svelte 扩展,从而保证 .svelte 文件的语法高亮、补全等 IntelliSense 能力开箱即用。

技术选型:为什么用这个模板而不是 SvelteKit

模板 README 的 "Technical considerations" 一节直接回答了两个高频问题,以下完整继承其结论并结合仓库现状说明。

为什么用这个模板而不是 SvelteKit?

  • SvelteKit 自带一套路由方案,这对部分用户而言并非首选;
  • SvelteKit 本质是一个"恰好底层使用 Vite 的框架",而不是一个纯粹的 Vite 应用。

本模板的设计目标是包含尽可能少的内容,让人能以最简形态启动 Vite + TypeScript + Svelte 项目,同时在开发体验上兼顾 HMR 与 IntelliSense。它的功能定位与 create-vite 的其他模板对齐,适合初学者入门 Vite + Svelte 项目。README 同时指出:该模板的结构刻意与 SvelteKit 保持相似,以便日后需要 SvelteKit 的扩展能力与可扩展性时能够平滑迁移。

TypeScript 配置体系:project references 与 types

模板的 TypeScript 采用 project references 拆分为应用侧与 Node 侧两个子项目。根 tsconfig.json 只负责引用,不直接编译任何文件:

{
  "files": [],
  "references": [
    { "path": "./tsconfig.app.json" },
    { "path": "./tsconfig.node.json" }
  ]
}

应用侧:tsconfig.app.json

tsconfig.app.json 继承 @tsconfig/svelte 的官方共享配置,并在此基础上针对 Svelte + Vite 场景做了关键定制:

{
  "extends": "@tsconfig/svelte/tsconfig.json",
  "compilerOptions": {
    "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
    "target": "es2023",
    "module": "esnext",
    "types": ["svelte", "vite/client"],
    "allowArbitraryExtensions": true,
    "noEmit": true,
    "allowJs": true,
    "checkJs": true,
    "moduleDetection": "force"
  },
  "include": ["src/**/*.ts", "src/**/*.js", "src/**/*.svelte"]
}

逐项解读:

  • types: ["svelte", "vite/client"]:显式引入 svelte(Svelte 编译相关类型)与 vite/clientimport.meta.env?.svg/?.png 等资源导入声明)两类全局类型,使 import logo from './assets/svelte.svg' 这类资源导入在类型检查下合法。这与 App.svelte 中直接 import svelteLogo from './assets/svelte.svg' 的写法相配套。
  • target: es2023 / module: esnext / noEmit: true:产物交给 Vite/Rolldown 处理,TypeScript 只负责类型检查(noEmit),esnext 模块形态匹配 ESM 打包器。
  • allowArbitraryExtensions:允许导入带任意扩展名的模块,配合 Svelte 文件的模块解析需要。
  • include 覆盖 src 下的 .ts.js.svelte 文件——svelte-check 会基于这份配置对 .svelte 文件做完整类型检查(见后文 check 脚本)。
  • 注释中特别提示:allowJs 设为 false 并不能阻止在 .svelte 文件里写 JavaScript,这直接引出了下一节的设计取舍。

关于 global.d.ts 与 compilerOptions.types 的取舍

README 专门回答了"为什么用 global.d.ts 而不是 jsconfig.json/tsconfig.json 里的 compilerOptions.types":设置 compilerOptions.types 会屏蔽掉所有未显式列出的类型声明包;而使用三斜杠引用(/// <reference types="..." />)的 global.d.ts 方式,既能保留 TypeScript"接受整个工作区类型信息"的默认行为,又能叠加 sveltevite/client 的类型。

需要说明的是,从当前仓库的模板实现看,tsconfig.app.json 已经改为通过 "types": ["svelte", "vite/client"] 显式声明这两类类型(同时 include 限定了 src 范围),README 中的讨论可视为对这一设计问题的原始论证。两种方案的核心权衡一致:types 白名单更严格、可预测,三斜杠引用则保留工作区默认类型发现。

Node 侧:tsconfig.node.json

tsconfig.node.json 单独覆盖 vite.config.ts,采用 "Bundler mode" 的一整套现代 lint 型编译选项:

{
  "compilerOptions": {
    "target": "es2023",
    "lib": ["ES2023"],
    "types": ["node"],
    "module": "nodenext",
    "allowImportingTsExtensions": true,
    "verbatimModuleSyntax": true,
    "moduleDetection": "force",
    "noEmit": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "erasableSyntaxOnly": true,
    "noFallthroughCasesInSwitch": true
  },
  "include": ["vite.config.ts"]
}

要点:types: ["node"] 让配置文件内可以使用 Node 全局;module: nodenext + verbatimModuleSyntax 保证 import 语法与 Node 模块解析语义一致;erasableSyntaxOnly 要求只使用可被纯文本擦除的 TS 语法(不允许需要编译期转换的 enum 等),这也是 Svelte/Vite 生态对轻量 TS 的常见约束。

为什么在 TS 模板中启用 allowJs

这是 README 里论证最充分的一个决策,其结论值得原样继承:

  • allowJs: false 确实能阻止项目中出现 .js 文件,但无法阻止在 .svelte 文件内部使用 JavaScript 语法——所以对 .svelte 单文件组件来说,禁用 JS 文件并不彻底;
  • 更糟的是,allowJs: false 会连带强制 checkJs: false,落入"两全不得"的局面:既不能保证整个代码库都是 TypeScript,又让现存 JavaScript 丧失了更好的类型检查;
  • 此外,混合代码库本身存在合理的使用场景(例如渐进式迁移中的项目)。

当前模板的落地方式是在 tsconfig.app.json 中同时打开 allowJs: truecheckJs: true,并附注释说明"默认对 .svelte.js 文件中的 JS 做类型检查;若希望 JS 使用动态类型,可关闭 checkJs"。即:允许 JS 存在,但对它保持严格检查。

构建链路:vite.config.ts 与 package.json 脚本

Vite 配置只有三行核心内容——注册 Svelte 官方 Vite 插件:

// vite.config.ts
import { svelte } from '@sveltejs/vite-plugin-svelte'
import { defineConfig } from 'vite'

// https://vite.dev/config/
export default defineConfig({
  plugins: [svelte()],
})

配套的 svelte.config.js 同样是空配置占位,仅标注了 @sveltejs/vite-plugin-svelteSvelteConfig 类型,留待用户按需扩展(预处理器、编译选项等)。

package.json 定义了四个脚本与完整依赖清单(版本以当前仓库为准):

{
  "name": "vite-svelte-ts-starter",
  "private": true,
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview",
    "check": "svelte-check --tsconfig ./tsconfig.app.json && tsc -p tsconfig.node.json"
  },
  "devDependencies": {
    "@sveltejs/vite-plugin-svelte": "^7.3.0",
    "@tsconfig/svelte": "^5.0.8",
    "@types/node": "^24.13.3",
    "svelte": "^5.57.0",
    "svelte-check": "^4.7.6",
    "typescript": "~6.0.2",
    "vite": "^8.2.2"
  }
}

各脚本职责:

脚本 命令 作用
dev vite 启动开发服务器,含 .svelte 文件 HMR
build vite build 产物构建(ESM 打包 + 资源处理)
preview vite preview 本地预览 build 产物
check svelte-check --tsconfig ./tsconfig.app.json && tsc -p tsconfig.node.json 双阶段类型检查:先由 svelte-check 按应用侧配置检查 src(含 .svelte),再由 tsc 检查 Node 侧配置

注意 check 脚本显式传入了 --tsconfig ./tsconfig.app.json,这正是 project references 拆分的直接收益:svelte-check 只看应用侧语义,tsc 只看 Node 侧,互不干扰。

应用代码:Svelte 5 的 mount 入口与 $state

入口 src/main.ts 使用 Svelte 5 的 mount API(替代旧版 new App({ target })):

import { mount } from 'svelte'
import './app.css'
import App from './App.svelte'

const app = mount(App, {
  target: document.getElementById('app')!,
})

export default app

Counter.svelte 则演示了 Svelte 5 的响应式 rune:

<script lang="ts">
  let count: number = $state(0)
  const increment = () => {
    count += 1
  }
</script>

<button type="button" class="counter" onclick={increment}>
  Count is {count}
</button>

index.html 中以 <div id="app"></div> 作为挂载点,并用 <script type="module" src="/src/main.ts"> 引入入口;App.svelte 组合了 Counter 组件、logo 资源导入与静态部署图标(<use href="/icons.svg#...">),并提示"编辑 src/App.svelte 保存以测试 HMR"。

HMR 为什么不保留组件局部状态

README 的最后一个 FAQ 涉及一个高频困惑:HMR 更新后,为什么组件里的局部状态(如 Countercount)被重置?

结论是:HMR 的状态保持存在一系列"坑"。由于其行为经常出人意料,svelte-hmr@sveltejs/vite-plugin-svelte默认禁用了状态保持。README 给出的工程化对策是:把需要跨 HMR 更新存活的状态提升到外部 store——外部 store 模块不会被组件自身的 HMR 替换掉,因此状态得以保留。README 给出的最小示例:

// store.ts
// 一个极简的外部 store
import { writable } from 'svelte/store'
export default writable(0)

需要指出:该示例沿用了 svelte/storewritable 经典写法;在 Svelte 5 中同样可以直接用模块级 $state(模块只会被 HMR 失效一次,组件内局部状态则会被重置)达到类似目的,但 README 给出的 writable 外部 store 方案在 4/5 版本下都通用,是文档钦定的稳妥路径。

总结

svelte-ts 模板展示了 Vite 生态"最小可用脚手架"的完整设计思路:

  1. 结构最小:仅入口、根组件、演示组件与全局样式,无路由、无状态库;
  2. 工程严格:project references 拆分应用/Node 两侧 TS 配置,svelte-check + tsc 双检查脚本,allowJs/checkJs 兼顾混合代码库;
  3. DX 保障.vscode/extensions.json 自动推荐 Svelte 扩展,vite/client 类型让资源导入合法化;
  4. 迁移友好:目录结构刻意对齐 SvelteKit,为日后升级预留通道。

结合 README 与上述源码文件(tsconfig.app.jsonpackage.jsonsrc/main.ts),即可完整复现该模板从脚手架生成、本地开发到类型检查、构建预览的全流程。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
590
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
889
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
982
503
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384