使 vite-plugin-uni-pages 支持类型安全

支持类型安全 · Issue #105 · uni-helper/vite-plugin-uni-pages (github.com)

原理

正如 issue 中所说,生成 .d.ts,覆写 uni-app 提供的类型接口,我们所熟知的 unplugin-vue-component、unplugin-auto-import 等插件均采用这种方式实现。

实现

vite-plugin-uni-pages 目前的 Context 类中已经有 pages 和 subPages 的所有数据,所以只要实现配置、生成、和在合适的时机写 dts 文件即可。

配置

新增一个配置项 dts,使其接受 string 或者 boolean,默认为 true,当其为 string 时,作为相对路径生成文件,为 flase 时不生成文件,为 true 时生成到 ./uni-pages.d.ts(与其他 vite 插件统一位置)

ts 复制代码
// src/types.ts
export interface Options {
...
dts?: boolean | string
...
}

生成

生成也很简单,关键是要生成什么,首先当然是老三套注释,禁用 eslint、prettier、tsc

ts 复制代码
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// Generated by vite-plugin-uni-pages

然后重载全局接口 Uni 的四个路由跳转函数,当然这意味着你必须在 tsconfig.json 中配置 "types": ["@dcloudio/types"]

其中比较关键的是我们都知道的 tabBar 页面必须使用 switchTab 函数去跳转。其他页面可以用任意跳转函数。

ts 复制代码
interface NavigateToOptions {
  url: "/pages/login" | '...'
}
interface RedirectToOptions extends NavigateToOptions {}
interface SwitchTabOptions {
  url: "/pages/index" | '...'
}
type ReLaunchOptions = NavigateToOptions | SwitchTabOptions;

declare interface Uni {
  navigateTo(options: UniNamespace.NavigateToOptions & NavigateToOptions): void;
  redirectTo(options: UniNamespace.RedirectToOptions & RedirectToOptions): void;
  switchTab(options: UniNamespace.SwitchTabOptions & SwitchTabOptions): void;
  reLaunch(options: UniNamespace.ReLaunchOptions & ReLaunchOptions): void;
}

有了目标就简单多了,我们只要获取到所有普通页面和所有 tabBar 页面,使用模板生成即可

ts 复制代码
export function getDeclaration(ctx: PageContext) {
  const subPagesPath = ctx.subPageMetaData.map((sub) => {
    return sub.pages.map(v => (`"${normalizePath(join(sub.root, v.path))}"`))
  }).flat()
  const tabsPagesPath = ctx.pagesGlobConfig?.tabBar?.list?.map((v) => {
    return `"${v.pagePath}"`
  }) ?? []
  const allPagesPath = [...ctx.pageMetaData.filter(page => !tabsPagesPath.includes(page.path)).map(v => `"${v.path}"`), ...subPagesPath]
  const code = `/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// Generated by vite-plugin-uni-pages

interface NavigateToOptions {
  url: ${allPagesPath.join(' |\n       ')};
}
interface RedirectToOptions extends NavigateToOptions {}

interface SwitchTabOptions {
  ${tabsPagesPath.length ? `url: ${tabsPagesPath.join(' | ')}` : ''}
}

type ReLaunchOptions = NavigateToOptions | SwitchTabOptions;

declare interface Uni {
  navigateTo(options: UniNamespace.NavigateToOptions & NavigateToOptions): void;
  redirectTo(options: UniNamespace.RedirectToOptions & RedirectToOptions): void;
  switchTab(options: UniNamespace.SwitchTabOptions & SwitchTabOptions): void;
  reLaunch(options: UniNamespace.ReLaunchOptions & ReLaunchOptions): void;
}
`
  return code
}

最后是写入函数

ts 复制代码
async function writeFile(filePath: string, content: string) {
  await mkdir(dirname(filePath), { recursive: true })
  return await writeFile_(filePath, content, 'utf-8')
}

export async function writeDeclaration(ctx: PageContext, filepath: string) {
  const originalContent = existsSync(filepath) ? await readFile(filepath, 'utf-8') : ''

  const code = getDeclaration(ctx)
  if (!code)
    return

  if (code !== originalContent)
    await writeFile(filepath, code)
}

合适的时机

最后只需在 updatePagesJSON 函数最后获取到最新的pages后,调用即可。

总结

从插件本身出发,这个功能还是很好实现的,进一步增强了 VS Code 下开发 uni-app 的体验。

目前代码已经合并到 main 分支,相信很快就能在下个版本和大家见面。

相关推荐
FogLetter17 小时前
Vite vs Webpack:前端构建工具的双雄对决
前端·面试·vite
前端赵哈哈3 天前
Vite 图片压缩的 4 种有效方法
前端·vue.js·vite
前端赵哈哈3 天前
解决 Vue+TS 项目打包(vue-tsc -b && vite build)未使用变量提示 / 报错问题
前端·vue.js·vite
萌萌哒草头将军4 天前
🚀🚀🚀 告别复制粘贴,这个高效的 Vite 插件让我摸鱼🐟时间更充足了!
前端·vite·trae
Noxi_lumors4 天前
VITE BALABALA require balabla not supported
前端·vite
可爱生存报告4 天前
vue3 vite quill-image-resize-module打包报错 Cannot set properties of undefined
前端·vite
xiaohe06014 天前
👋 一起写一个基于虚拟模块的密钥管理 Rollup 插件吧(二)
vite·rollup.js
Olrookie4 天前
RuoYi-Vue3项目中Swagger接口测试404,端口问题解析排查
前端·webpack·vite
米欧4 天前
使用luckysheet在线处理复杂表格
前端·excel·vite
国家不保护废物4 天前
Webpack 与 Vite:现代前端构建工具的双子星
前端·webpack·vite