使 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 分支,相信很快就能在下个版本和大家见面。

相关推荐
我爱甜妹3 天前
vite项目保存代码后不刷新页面 vite热更新
vite
jason_yang4 天前
vue3+element-plus按需自动导入-正确姿势
vue.js·vite·element
WujieLi6 天前
初识 Vite+:一文了解 Rust 驱动的新一代前端工具链
javascript·rust·vite
却尘6 天前
Vite 炸裂快,Webpack 稳如山,Turbopack 想两头要:谁才是下一个王?
前端·面试·vite
萌萌哒草头将军7 天前
尤雨溪宣布 oxfmt 即将发布!比 Prettier 快45倍 🚀🚀🚀
前端·webpack·vite
晓得迷路了7 天前
栗子前端技术周刊第 102 期 - Vite+ 正式发布、React Native 0.82、Nitro v3 alpha 版...
前端·javascript·vite
jason_yang10 天前
vue3中使用auto-import与cdn插件冲突问题
vue.js·vite·cdn
进阶的鱼10 天前
React+ts+vite脚手架搭建(五)【规范篇】
前端·react.js·vite
xiaohe060113 天前
👋 一起写一个基于虚拟模块的密钥管理 Rollup 插件吧(四)
vite·rollup.js
parade岁月13 天前
nuxt和vite使用环境比变量对比
前端·vite·nuxt.js