Vuex 自动化生成工具

发布于:2025-06-11 ⋅ 阅读:(20) ⋅ 点赞:(0)

Vuex 自动化生成工具需求文档


1. 需求背景

为提升前端开发效率,减少重复代码编写,需开发一个自动化工具,根据输入参数自动生成完整的 Vuex 存储模块(包括api.js,mutations.js,actions.js,getters.js,mutation-types。js)。
在这里插入图片描述


2. 功能需求
2.1 输入参数
参数名 类型 必填 描述 示例值
apiName string API 名称(驼峰命名) getUserInfo
apiUrl string API 地址 /api/user/info
remark string 接口注释 获取用户详细信息
requestType string HTTP 方法(默认 GET) post
apiType string API 分类 get create update download

2.2 生成文件及规则
2.2.1 api.js 文件
  • 功能:存储 API 地址常量
  • 生成规则
    /**
     * 获取用户详细信息
     */
    export const getUserInfo = '/api/user/info';
    
  • 校验
    • 文件不存在时自动创建
    • 接口名称或地址重复时抛出错误
2.2.2 mutation-types.js 文件
  • 功能:定义 mutation 类型常量
  • 生成规则
    /**
     * 获取用户详细信息
     */
    export const GETUSERINFO_REQUEST = 'GETUSERINFO_REQUEST';
    export const GETUSERINFO_RECEIVE = 'GETUSERINFO_RECEIVE';
    
2.2.3 mutations.js 文件
  • 功能:生成状态变更逻辑
  • 生成规则
    [type.GETUSERINFO_REQUEST](state) {
      state.userInfoStatus = 'loading';
      state.userInfo = {};
    },
    [type.GETUSERINFO_RECEIVE](state, res) {
      state.userInfoStatus = 'loaded';
      state.userInfo = res?.content || {};
    }
    
2.2.4 getters.js
  • 功能:生成状态获取方法
  • 生成规则
    export const userInfo = state => state.userInfo || {};
    export const userInfoStatus = state => state.userInfoStatus || '';
    
2.2.5 actions.js
  • 功能:生成 API 调用逻辑
  • 生成规则
    export const getUserInfo = ({ commit }, data) => {
      commit(type.GETUSERINFO_REQUEST, data);
      return ajax.post(
        API.getUserInfo,
        { commit },
        data,
        type.GETUSERINFO_RECEIVE
      );
    };
    

3. 非功能性需求
3.1 错误处理
  • 接口名称/地址重复时中止操作并提示
  • 文件权限不足时抛出异常
3.2 目录管理
  • 默认生成到 ./store/modules/ 目录
  • 自动创建缺失的目录层级
3.3 代码风格
  • 统一使用 2 空格缩进
  • 自动生成 JSDoc 注释

4. 使用示例
// 调用示例
addStoreToFile('getUser', '/api/user', '获取用户数据', 'get', 'user');

5. 输出验证

执行成功后需生成以下文件结构:

store/
  modules/
    api.js
    mutations.js
    getters.js
    actions.js
  mutation-types.js

基础代码

const fs = require('fs');
const path = require('path');
const BASE_DIR = './store/user'; // 所有文件生成到store/modules下
const apiFilePath = `${BASE_DIR}/api.js`
const mutationsFilePath = `${BASE_DIR}/mutations.js`
const actionsFilePath = `${BASE_DIR}/actions.js`
const mutationTypesFilePath = `${BASE_DIR}/mutation-types.js`
const gettersFilePath = `${BASE_DIR}/getters.js`
// 校验文件内是否已经存在
function _checkCorrect(filePath, regStr, apiUrl) {
    // 1. 检查文件是否存在
    if (!fs.existsSync(filePath)) {
        fs.writeFileSync(filePath, '', 'utf8');
    }
    // 2. 读取文件内容
    const content = fs.readFileSync(filePath, 'utf8');

    // 3. 检查接口是否已存在
    const apiRegex = new RegExp(regStr, 'g');
    if (regStr && apiRegex.test(content)) {
        throw new Error(`已存在`);
        return false
    }

    // 4. 检查URL是否已存在
    if (apiUrl && content.includes(apiUrl)) {
        throw new Error(`接口地址 ${apiUrl} 已存在`);
        return false
    }
    return true
}
// 匹配是否满足条件的函数
function _checkFunctionExists(content, apiName) {
    // 匹配两种函数定义方式:
    // 1. export const fnName = ({ commit }) => {...}
    // 2. export function fnName({ commit }) {...}
    const funcRegex = new RegExp(
        `export\\s+(?:const\\s+${apiName}\\s*=\\\s*\\(|function\\s+${apiName}\\s*\\\()`
    );
    return funcRegex.test(content);
}
// 重新设置名称
function _getNewApiName(apiName, requestType, apiType) {
    if (apiType) {
        // 将apiName首字母大写
        apiName = apiName.replace(apiName[0], apiName[0].toUpperCase())
        apiName = requestType == 'get' ? (requestType + apiName) : (apiType + apiName)
        return apiName
    }
    return apiName
}
// 在文件顶部添加创建目录的函数
function _ensureDirectoryExists() {
    if (!fs.existsSync(BASE_DIR)) {
        fs.mkdirSync(BASE_DIR, { recursive: true });
    }
}
// 1. api写入
function addApiToFile(apiName, apiUrl, remark = '', requestType, apiType) {
    let filePath = `${BASE_DIR}/api.js`
    let isCorrect = _checkCorrect(apiFilePath, `export const ${apiName} *= *['"].+['"]`, apiUrl)
    if (isCorrect) {

        const newApi = `
/**
 * ${remark} 
 */
export const ${apiName} = '${apiUrl}';\n`;
        fs.appendFileSync(apiFilePath, newApi, 'utf8');

        return `接口 ${apiName} 添加成功`;
    }

}
// 2. mutation-types写入
function addMutationTypes(apiName, remark) {
    apiName = apiName.toUpperCase()

    let isCorrect1 = _checkCorrect(mutationTypesFilePath, `export const ${apiName + '_REQUEST'} *= *['"].+['"]`)
    let isCorrect2 = _checkCorrect(mutationTypesFilePath, `export const ${apiName + '_RECEIVE'} *= *['"].+['"]`)
    if (isCorrect1 && isCorrect2) {
        let newApi = `
/**
 * ${remark} 
 */
export const ${apiName + '_REQUEST'} = '${apiName + '_REQUEST'}';\n
export const ${apiName + '_RECEIVE'} = '${apiName + '_RECEIVE'}';\n`;
        fs.appendFileSync(mutationTypesFilePath, newApi, 'utf8');
        return `mutation-types ${apiName} 添加成功`;

    }
}
//3. getters 写入
function addGettersToFile(apiName, remark) {
    let isCorrect1 = _checkCorrect(gettersFilePath, `export const ${apiName + 'Info'} *= *['"].+['"]`)
    let isCorrect2 = _checkCorrect(gettersFilePath, `export const ${apiName + 'Status'} *= *['"].+['"]`)
    if (isCorrect1 && isCorrect2) {
        let newApi = `
/**
 * ${remark} 
 */
export const ${apiName + 'Info'} = state =>state[${apiName + 'Info'}]||{};\n
export const ${apiName + 'Status'} = state =>state[${apiName + 'Status'}]||'';\n`;
        fs.appendFileSync(gettersFilePath, newApi, 'utf8');
        return `getters ${apiName} 添加成功`;
    }
}
// 4. mutations 写入
function addMutationsToFile(apiName, remark) {
    // 1. 初始化文件(如果需要)
    if (!fs.existsSync(mutationsFilePath)) {
        fs.writeFileSync(mutationsFilePath, `
import * as type from './mutation-types'
export const state = {}
export const mutations = {}
        `.trim(), 'utf8');
    }

    // 2. 读取最新内容(每次重新读取)
    let content = fs.readFileSync(mutationsFilePath, 'utf8');

    // 3. 处理state
    const stateRegex = /export\s+const\s+state\s*=\s*{([^{}]*?(?:\{[^{}]*\}[^{}]*?)*)}/ ///export\s+const\s+state\s*=//\s*{([\s\S]*?)}/;
    if (stateRegex.test(content)) {
        content = content.replace(stateRegex, (match, stateContent) => {
            if (new RegExp(`${apiName}Info\\s*:|${apiName}Status\\s*:`).test(stateContent)) {
                throw new Error(`State中已存在${apiName}相关属性`);
            }
            return `export const state = {
    ${stateContent.trim()}
    ${remark ? `// ${remark}` : ''}
    ${apiName}Info: {},
    ${apiName}Status: '',
}`;
        });
    }

    // 4. 处理mutations(关键修正)
    const mutationRegex = /export\s+const\s+mutations\s*=\s*{([\s\S]*?)}\s*$/;
    if (mutationRegex.test(content)) {
        content = content.replace(mutationRegex, (match, mutationContent) => {
            if (new RegExp(`\\[type\\.${apiName.toUpperCase()}_(REQUEST|RECEIVE)\\]`).test(mutationContent)) {
                throw new Error(`Mutations中已存在${apiName}相关操作`);
            }
            return `export const mutations = {
    ${mutationContent.trim()}
    ${remark ? `// ${remark}` : ''}
    [type.${apiName.toUpperCase()}_REQUEST](state) {
        state.${apiName}Status = 'loading';
        state.${apiName}Info = {};
    },
    [type.${apiName.toUpperCase()}_RECEIVE](state, res) {
        state.${apiName}Status = 'loaded';
        state.${apiName}Info = res?.content || {};
    },
}`;
        });
    }

    // 5. 写入最终结果
    fs.writeFileSync(mutationsFilePath, content, 'utf8');
    return `${apiName}相关mutations配置已添加`;
}
//5. actions.js
function addActionsToFile(apiName, remark, requestType) {
    // 1. 初始化文件(如果需要)
    if (!fs.existsSync(actionsFilePath)) {
        fs.writeFileSync(actionsFilePath, `
import * as type from './mutation-types'
import * as API from './api'
import { ajax } from '@/utils/fetch'
        `.trim(), 'utf8');
    }

    // 2. 读取最新内容(每次重新读取)
    let content = fs.readFileSync(actionsFilePath, 'utf8');
    // 3. 判断文件中是否存在函数名称为apiName的函数
    if (_checkFunctionExists(content, apiName)) {
        throw new Error(`函数 ${apiName} 已存在`);
    }

    // 4. 追加新函数
    const newAction = `
${remark ? `// ${remark}` : ''}  
 export const ${apiName} = ({ commit }, data) => {
     commit(type.${apiName.toUpperCase()}_REQUEST, data);
     return ajax.${requestType}(
         API.${apiName},
         { commit },
         data,
         type.${apiName.toUpperCase()}_RECEIVE
     );
 };`;

    fs.appendFileSync(actionsFilePath, newAction, 'utf8');
    return `${apiName}相关actions配置已添加`;

}

// apiType:['get', 'create', 'download', 'update', 'delete']
function addStoreToFile(apiName, apiUrl, remark = '', requestType = 'get', apiType) {
    _ensureDirectoryExists()
    let newApiName = _getNewApiName(apiName, apiType)
    // 1. 添加 API
    addApiToFile(newApiName, apiUrl, remark,)
    // 2. 添加 addMutationTypes
    addMutationTypes(apiName, remark)
    // 3. 添加 getters
    addGettersToFile(apiName, remark)
    // 4. 添加 mutations
    addMutationsToFile(apiName, remark)
    // 5. 添加 actions
    addActionsToFile(newApiName, remark, requestType)
    return 'success'

}


try {
    // console.log(addStoreToFile('getName', 'api/user/name', '获取名称'));
    // console.log(addStoreToFile('getUser', 'api/user/User', '获取用户信息'));
    console.log(addStoreToFile('getStore1', 'api/user/store1', '获取用户信息', 'post'));

} catch (error) {
    console.error(error.message);
}