Vue + Vite + TS(Vitesse) 脚手架使用记录
文章目录
该脚手架详情请查看官方文档: gcclll/vitesse: 🏕 Opinionated Vite Starter Template
本文是使用和学习过程中的一些笔记!
项目目录结构:
|
|
特性(来自官方),下面会对每个特性使用到的插件进行分析:
⚡️
Vue 3, Vite 2, pnpm, ESBuild - born with fastness🗂 File based routing
基于文件结构的路由系统,使用插件 hannoeru/vite-plugin-pages: File system based route generator for ⚡️Vite
代码入口: vite-plugin-pages/index.ts at main · hannoeru/vite-plugin-pages
1 2 3 4 5 6
async load(id) { if (id !== MODULE_ID_VIRTUAL) return return ctx.resolveRoutes() },
resolveRoutes()
解析src/pages
路由:vite-plugin-pages/context.ts at 771e956fd41589d8c9012c05016503e227d15b21 · hannoeru/vite-plugin-pages1 2 3 4 5 6
async resolveRoutes() { if (this.options.resolver === 'vue') return await resolveVueRoutes(this) if (this.options.resolver === 'react') return await resolveReactRoutes(this) }
->
resolveVueRoutes(this)
vite-plugin-pages/vue.ts at 771e956fd41589d8c9012c05016503e227d15b21 · hannoeru/vite-plugin-pages1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
export async function resolveVueRoutes(ctx: PageContext) { const { nuxtStyle, caseSensitive } = ctx.options // ctx.pageRouteMap 这个应该就是 src/pages/* 下所有文件解析出来的结构 const pageRoutes = [...ctx.pageRouteMap.values()] // sort routes for HMR .sort((a, b) => countSlash(a.route) - countSlash(b.route)) const routes: Route[] = [] pageRoutes.forEach((page) => { const pathNodes = page.route.split('/') // add leading slash to component path if not already there const component = page.path.replace(ctx.root, '') const customBlock = ctx.customBlockMap.get(page.path) const route: Route = { name: '', path: '', component, customBlock, rawRoute: page.route, } let parentRoutes = routes for (let i = 0; i < pathNodes.length; i++) { // ..., 将 pathNode 解析成 Route 结构 } parentRoutes.push(route) }) let finalRoutes = prepareRoutes(ctx, routes) finalRoutes = (await ctx.options.onRoutesGenerated?.(finalRoutes)) || finalRoutes let client = generateClientCode(finalRoutes, ctx.options) client = (await ctx.options.onClientGenerated?.(client)) || client return client }
PageContext
通过setupViteServer
借用 vite 起的服务来监听(setupWatcher
)目录文 件的变化(unlink,add,change
),执行对应的操作(removePage, addPage, checkCustomBlockChange
) 目的都是为了更新pageRouteMap
这个结构。searchGlob
会扫描options.dirs
指定的目录(默认是src/pages
),找出符合条件的页 面解析成路由。简单来说 vite-plugin-pages 就是通过借用
vite
起的服务去监听src/pages
目录的变 化,如果有文件变化就将其根据其路径和文件名解析成对应的路由对象。📦 Components auto importing
使用的插件:antfu/unplugin-vue-components: 📲 On-demand components auto importing for Vue
src/index.ts -> src/core/unplugin.ts
给 vite 服务增加 watcher :
ctx.setupWatcher(chokidar.watch(ctx.options.globs))
transform 转换函数:
1 2 3 4 5 6 7
async transform(code, id) { // ... const result = await ctx.transform(code, id) ctx.generateDeclaration() return result // ... },
可以转换的时候进行转换,然后生成声明,即
src/components.d.ts
中的内容。Context
中同样也是通过 vite server 监听文件 unlink,add 变化,执行removeComponents
或addCompnents
, 以addComponents
为例:1 2 3 4 5 6 7 8 9 10 11
addComponents(paths: string | string[]) { debug.components('add', paths) const size = this._componentPaths.size toArray(paths).forEach(p => this._componentPaths.add(p)) if (this._componentPaths.size !== size) { this.updateComponentNameMap() return true } return false }
->
updateComponentNameMap()
更新的是componentNameMap
这个对象,它会被中的
generateDeclaration
函数解析最后生成对应的代码写入(await fs.writeFile(filepath, code, 'utf-8')
)到src/components.d.ts
中🍍 State Management via Pinia
📑 Layout system
📲 PWA
🎨 Windi CSS - next generation utility-first CSS framework
😃 Use icons from any icon sets, with no compromise
antfu/unplugin-icons: 🤹 Access thousands of icons as components on-demand universally.
代码入口
src/index.ts
loader 函数 ->generateComponentFromPath()
:unplugin-icons/loader.ts at 4fde686174e0d054eac180c179dbae820afefba1 · antfu/unplugin-icons
1 2 3 4 5 6
export async function generateComponentFromPath(path: string, options: ResolvedOptions) { const resolved = resolveIconsPath(path) if (!resolved) return null return generateComponent(resolved, options) }
loadCollection ->
`@iconify-json/${name}/icons.json`
-> installawait tryInstallPkg(`@iconify-json/${name}`)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
export async function tryInstallPkg(name: string) { if (pending) await pending if (!tasks[name]) { // eslint-disable-next-line no-console console.log(cyan(`Installing ${name}...`)) tasks[name] = pending = installPackage(name, { dev: true, preferOffline: true }) .then(() => sleep(300)) .catch((e) => { warnOnce(`Failed to install ${name}`) console.error(e) }) .finally(() => { pending = undefined }) } return tasks[name]! }
使用 antfu/install-pkg: Install package programmatically. 下载安装 icon, 执行:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
execa( agent, [ agent === 'yarn' ? 'add' : 'install', options.dev ? '-D' : '', ...args, ...names, ].filter(Boolean), { stdio: options.silent ? 'ignore' : 'inherit', cwd: options.cwd, }, )
等于是
yarn add -D @iconify-json/icon-name
安装。根据解析出来的路径
resolved
去下载 iconlet svg = await getIcon(collection, icon, query, options)
下载成功后转成 svg1 2 3 4 5 6 7 8
return await mergeIconProps( `<svg>${body}</svg>`, collection, id, query, () => attributes, options, )
🌍 I18n ready
🗒 Markdown Support
🔥
Use the new <script setup> syntax📥 APIs auto importing - use Composition API and others directly
antfu/unplugin-auto-import: Auto import APIs on-demand for Vite, Webpack and Rollup
引入 unplugin-auto-import 然后在 vite.config.ts 中增加:
1 2 3 4 5 6 7 8 9 10 11 12
plugins: ] AutoImport({ imports: [ 'vue', 'vue-router', 'vue-i18n', '@vueuse/head', '@vueuse/core', ], dts: 'src/auto-imports.d.ts', }), ]
其实和 🔗 components auto import 原理差不多, 差异在这个是直接通过 vite.config.ts 中的配置中获取需要自动导入的插件。
代码:
src/index.ts
-> transform ->generateConfigFiles()
1 2 3 4 5 6 7 8 9
const generateConfigFiles = throttle(500, false, () => { if (resolved.dts) fs.writeFile(resolved.dts, generateDeclaration(resolved.imports, resolved.resolvedImports), 'utf-8') const { eslintrc } = resolved if (eslintrc.enabled && eslintrc.filepath) fs.writeFile(eslintrc.filepath, generateESLintConfigs(resolved.imports, resolved.resolvedImports, eslintrc), 'utf-8') })
根据
resolved.imports
配置,生成声明代码写入到resolved.dts
指定的文件。后面是 将自动引入的文件加入 eslint 的配置文件。最后生成类似下面的代码:
1 2 3 4
declare global { const asyncComputed: typeof import('@vueuse/core')['asyncComputed'] // ... }
🖨 Static-site generation (SSG) via vite-ssg
🦔 Critical CSS via critters
🦾 TypeScript, of course
⚙️ Unit Testing with Vitest, E2E Testing with Cypress on GitHub Actions
☁️ Deploy on Netlify, zero-config