vue3如何批量设置每个vue页面的defineOptions的name;

本文一共有三个脚本
初始脚本:不成熟,不推荐
优化版脚本: 直接将所有的vue页面使用对应路径命名,推荐
定制版脚本: 将vue路由对应的vue页面,使用路由的name;对于非vue路由的页面,直接使用对应地址路径命名,推荐

1.思路

通过遍历src/views下的文件,找到.vue文件,截取文件路径的最后两位或全部路径,使用驼峰命名

2.初始脚本

可以通过脚本批量修改 .vue 文件
‌创建脚本 auto-set-component-name.mjs

针对没有添加defineOptions的.vue文件添加defineOptions和name(但是未处理有defineOptions的情况

import fs from "fs"
import path from "path"
import { fileURLToPath } from "url"

const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)

// 🔧 配置区 ============================================
const targetDir = path.join(__dirname, "src/views")
const PATH_DEPTH = Infinity  // 自由修改数字:2→最后两级,3→最后三级,Infinity→全部路径
// =====================================================

const toPascalCase = (str) => {
  return str
    .replace(/[-_](.)/g, (_, c) => c.toUpperCase())
    .replace(/(^\w)/, m => m.toUpperCase())
    .replace(/\.vue$/, '')
}

const processDirectory = (dir) => {
  const files = fs.readdirSync(dir, { withFileTypes: true })

  files.forEach(file => {
    const fullPath = path.join(dir, file.name)
    file.isDirectory() ? processDirectory(fullPath) : processVueFile(fullPath)
  })
}

const processVueFile = (filePath) => {
  if (path.extname(filePath) !== '.vue') return

  const relativePath = path.relative(targetDir, filePath)
  const pathSegments = relativePath
    .split(path.sep)
    .slice(-PATH_DEPTH) // 🔥 核心修改点:根据配置截取路径段
    .map(segment => toPascalCase(segment))

  const componentName = pathSegments.join('')
  let content = fs.readFileSync(filePath, 'utf8')


  // 修复正则表达式:支持任意顺序的 setup 属性
  const scriptSetupRegex = /<script\s+((?:.(?!\/script>))*?\bsetup\b[^>]*)>/gmi

  if (!content.includes('defineOptions')) {

    content = content.replace(scriptSetupRegex, (match, attrs) => {
      return `<script ${attrs}>
defineOptions({
  name: '${componentName}'
})`
    })
    fs.writeFileSync(filePath, content)
    console.log(`✅ 成功注入 name: ${componentName}${filePath}`)
  }
}

processDirectory(targetDir)
console.log('🎉 所有 Vue 组件 name 注入完成!')


3.‌运行脚本‌

node auto-set-component-name.mjs

4.优化版本

思路:

4.1通过遍历sr/view下的.vue文件

找到每个.vue的路径,使用路径去生成name

4.2判断是否有defineOptions

有defineOptions,在判断是否有name:

  • 有name:替换name(可注释此段逻辑)
  • 无name:添加name

无defineOptions:

  • 直接添加defineOptions和name

4.3优化版脚本

直接将所有的vue页面使用对应路径命名

import fs from "fs"; // 文件系统模块,用于读写文件
import path from "path"; // 路径处理模块
import { fileURLToPath } from "url"; // 用于转换URL路径

const __filename = fileURLToPath(import.meta.url); // 当前文件绝对路径
const __dirname = path.dirname(__filename); // 当前文件所在目录

// 🔧 配置区 ============================================
const targetDir = path.join(__dirname, "src/views"); // 目标目录:当前目录下的src/views
const PATH_DEPTH = Infinity;  // 路径深度设置 自由修改数字:2→最后两级,3→最后三级,Infinity→全部路径
// =====================================================

const toPascalCase = (str) => {
  return str
    .replace(/[-_](.)/g, (_, c) => c.toUpperCase()) // 转换连字符/下划线后的字母为大写
    .replace(/(^\w)/, (m) => m.toUpperCase()) // 首字母大写
    .replace(/\.vue$/, ""); // 移除.vue后缀
};

const processDirectory = (dir) => {
  const files = fs.readdirSync(dir, { withFileTypes: true }); // 读取目录内容
  files.forEach((file) => {
    const fullPath = path.join(dir, file.name); // 获取完整路径
    file.isDirectory() ? processDirectory(fullPath) : processVueFile(fullPath); // 递归处理目录,直接处理文件
  });
};

const processVueFile = (filePath) => {
  if (path.extname(filePath) !== ".vue") return; // 过滤非Vue文件

  // 生成组件名逻辑
  const relativePath = path.relative(targetDir, filePath);
  const pathSegments = relativePath
    .split(path.sep)  // 按路径分隔符拆分
    .slice(-PATH_DEPTH)  // 根据配置截取路径段
    .map((segment) => toPascalCase(segment)); // 转换为PascalCase

  const componentName = pathSegments.join(""); // 拼接成最终组件名
  let content = fs.readFileSync(filePath, "utf8"); // 文件内容处理
  const oldContent = content; // 保存原始内容用于后续对比

  const scriptSetupRegex = /<script\s+((?:.(?!\/script>))*?\bsetup\b[^>]*)>/gim; // 灵活匹配找到script
  let hasDefineOptions = false; // 标识是否找到defineOptions

  // 处理已存在的 defineOptions
  const defineOptionsRegex = /defineOptions\(\s*{([\s\S]*?)}\s*\)/g;
  content = content.replace(defineOptionsRegex, (match, inner) => {
    hasDefineOptions = true; // 标记已存在defineOptions

    // 替换或添加 name 属性
    const nameRegex = /(name\s*:\s*['"])([^'"]*)(['"])/;
    let newInner = inner;

    if (nameRegex.test(newInner)) { // 存在name属性时替换
      newInner = newInner.replace(nameRegex, `$1${componentName}$3`);
      console.log(`✅ 成功替换【name】: ${componentName}${filePath}`);
    } else { // 不存在时添加
      newInner = newInner.trim() === ""
        ? `name: '${componentName}'`
        : `name: '${componentName}',\n${newInner}`;
      console.log(`✅ 成功添加【name】: ${componentName}${filePath}`);
    }

    return `defineOptions({${newInner}})`; // 重组defineOptions
  });

  // 新增 defineOptions(如果不存在)
  if (!hasDefineOptions) {
    content = content.replace(scriptSetupRegex, (match, attrs) => {
      return `<script ${attrs}>
defineOptions({
  name: '${componentName}'
})`;
    });
    console.log(`✅ 成功添加【defineOptions和name】: ${componentName}${filePath}`);
  }

  // 仅在内容变化时写入文件
  if (content !== oldContent) {
    fs.writeFileSync(filePath, content);
    // console.log(`✅ 成功更新 name: ${componentName} → ${filePath}`);
  }
};

processDirectory(targetDir);
console.log("🎉 所有 Vue 组件 name 处理完成!");

4.4定制版脚本

该定制版:将vue路由对应的vue页面,使用路由的name;对于非vue路由的页面,直接使用对应地址路径命名

import constantRoutes from "./src/router/constant_routes.js"

// 动态添加路由添加
const dynamicRoutes = constantRoutes||[] // 获取vue路由配置
// console.log('%c【' + 'dynamicRoutes' + '】打印', 'color:#fff;background:#0f0', dynamicRoutes)

// 递归找对象
const findItem = (pathUrl, array) => {
  for (const item of array) {
    let componentPath
    // 使用示例
    componentPath = getComponentPath(item.component) ? getComponentPath(item.component).replace(/^@|\.vue$/g, '') : undefined
    // console.log('%c【' + 'componentPath' + '】打印', 'color:#fff;background:#0f0', pathUrl, componentPath)
    // 检查当前项的id是否匹配
    if (componentPath === pathUrl) return item;

    // 如果有子节点则递归查找
    if (item.children?.length) {
      const result = findItem(pathUrl, item.children);
      if (result) return result; // 找到则立即返回
    }
  }
  return undefined; // 未找到返回undefined
}

// 提取组件路径的正则表达式
const IMPORT_PATH_REGEX = /import\(["'](.*?)["']\)/;

// 获取路径字符串
const getComponentPath = (component) => {
  if (!component?.toString) return null;

  const funcString = component.toString();
  const match = funcString.match(IMPORT_PATH_REGEX);

  return match ? match[1] : null;
};



// console.log('%c【' + 'componentPath' + '】打印', 'color:#fff;background:red', componentPath)





import fs from "fs"; // 文件系统模块,用于读写文件
import path from "path"; // 路径处理模块
import { fileURLToPath } from "url"; // 用于转换URL路径

const __filename = fileURLToPath(import.meta.url); // 当前文件绝对路径
const __dirname = path.dirname(__filename); // 当前文件所在目录

// 🔧 配置区 ============================================
const targetDir = path.join(__dirname, "src/views"); // 目标目录:当前目录下的src/views
const PATH_DEPTH = Infinity;  // 路径深度设置 自由修改数字:2→最后两级,3→最后三级,Infinity→全部路径
// =====================================================

const toPascalCase = (str) => {
  return str
    // .replace(/[-_](.)/g, (_, c) => c.toUpperCase()) // 转换连字符/下划线后的字母为大写
    // .replace(/(^\w)/, (m) => m.toUpperCase()) // 首字母大写
    .replace(/\.vue$/, ""); // 移除.vue后缀
};

const processDirectory = (dir) => {
  const files = fs.readdirSync(dir, { withFileTypes: true }); // 读取目录内容
  console.log('%c【' + 'dir' + '】打印', 'color:#fff;background:#0f0', dir)
  files.forEach((file) => {
    const fullPath = path.join(dir, file.name); // 获取完整路径
    file.isDirectory() ? processDirectory(fullPath) : processVueFile(fullPath); // 递归处理目录,直接处理文件
  });
};

const processVueFile = (filePath) => {
  if (path.extname(filePath) !== ".vue") return; // 过滤非Vue文件

  // 生成组件名逻辑
  const relativePath = path.relative(targetDir, filePath);
  console.log('%c【' + 'targetDir' + '】打印', 'color:#fff;background:#0f0', targetDir)
  console.log('%c【' + 'filePath' + '】打印', 'color:#fff;background:#0f0', filePath)
  console.log('%c【' + 'relativePath' + '】打印', 'color:#fff;background:#0f0', relativePath)
  const pathSegments = relativePath
    .split(path.sep)  // 按路径分隔符拆分
    .slice(-PATH_DEPTH)  // 根据配置截取路径段
    .map((segment) => toPascalCase(segment)); // 转换为PascalCase

  const vuePath = '/views/' + pathSegments.join("/"); // 拼接成最终组件名

  let componentName = findItem(vuePath, dynamicRoutes)?.name ? findItem(vuePath, dynamicRoutes)?.name : vuePath
  console.log(filePath, componentName);

  let content = fs.readFileSync(filePath, "utf8"); // 文件内容处理
  const oldContent = content; // 保存原始内容用于后续对比

  const scriptSetupRegex = /<script\s+((?:.(?!\/script>))*?\bsetup\b[^>]*)>/gim; // 灵活匹配找到script
  let hasDefineOptions = false; // 标识是否找到defineOptions

  // 处理已存在的 defineOptions
  const defineOptionsRegex = /defineOptions\(\s*{([\s\S]*?)}\s*\)/g;
  content = content.replace(defineOptionsRegex, (match, inner) => {
    hasDefineOptions = true; // 标记已存在defineOptions

    // 替换或添加 name 属性
    const nameRegex = /(name\s*:\s*['"])([^'"]*)(['"])/;
    let newInner = inner;

    if (nameRegex.test(newInner)) { // 存在name属性时替换
      newInner = newInner.replace(nameRegex, `$1${componentName}$3`);
      console.log(`✅ 成功替换【name】: ${componentName}${filePath}`);
    } else { // 不存在时添加
      newInner = newInner.trim() === ""
        ? `name: '${componentName}'`
        : `name: '${componentName}',\n${newInner}`;
      console.log(`✅ 成功添加【name】: ${componentName}${filePath}`);
    }

    return `defineOptions({${newInner}})`; // 重组defineOptions
  });

  // 新增 defineOptions(如果不存在)
  if (!hasDefineOptions) {
    content = content.replace(scriptSetupRegex, (match, attrs) => {
      return `<script ${attrs}>
defineOptions({
  name: '${componentName}'
})`;
    });
    console.log(`✅ 成功添加【defineOptions和name】: ${componentName}${filePath}`);
  }

  // 仅在内容变化时写入文件
  if (content !== oldContent) {
    fs.writeFileSync(filePath, content);
    // console.log(`✅ 成功更新 name: ${componentName} → ${filePath}`);
  }
};

processDirectory(targetDir);
console.log("🎉 所有 Vue 组件 name 处理完成!");

5.package.json添加命令行

上述的脚本虽然可以通过node auto-set-component-name.mjs执行,但是考虑到每次新增页面时候都要执行下。
可以考虑将在package.json里加入命令行

    "dev": "pnpm setName && vite --mode beta --host",
    "setName": "node auto-set-component-name.mjs",

使用pnpm setName即使运行node auto-set-component-name.mjs,或者在启动项目时候,pnpm dev 也会触发pnpm setName的执行,以此确保所有.vue的name都有

在这里插入图片描述

6.vue3获取组件实例,获取defineOptions的name

vue3获取组件实例,获取defineOptions的name

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值