@asiimov/sfc-generator: 强大的 Vue 单文件组件生成工具之 parseSfcScript 详解

前言

上一篇介绍了如何操作 templateAst 来实现生成 template 代码,那么今天我们就来讲一下如何操作单文件组件中的 script 代码。

功能特点

  • 支持 Options API 和 Composition API 两种风格
  • 支持 TypeScript 和 JSX 语法
  • 提供统一的 API 来操作组件的各个部分
  • 返回标准的 AST,方便进一步处理

基本用法

typescript 复制代码
import { parseSfcScript } from 'sfc-generator'

// Options API 模式
const optionsApiCode = `
export default {
  name: 'AInput',
  data() {
    return {
      value: '1'
    }
  }
}
`

const { api: optionsApi, ast } = parseSfcScript(optionsApiCode)

// Composition API 模式
const setupApiCode = `
const checked = ref('')
const str = ref('')
`

const { api: setupApi, ast } = parseSfcScript(setupApiCode, { setup: true })

API 详解

1. Options API 模式

Data 操作

typescript 复制代码
// 添加数据
api.data().add('number', t.numericLiteral(1))
// 结果:
// export default {
//   name: 'AInput',
//   data() {
//     return {
//       value: '1',
//       number: 1
//     }
//   }
// }

// 更新数据
api.data().update('value', t.booleanLiteral(true))
// 结果:
// export default {
//   name: 'AInput',
//   data() {
//     return {
//       value: true,
//       number: 1
//     }
//   }
// }

// 删除数据
api.data().remove('value')
// 结果:
// export default {
//   name: 'AInput',
//   data() {
//     return {
//       number: 1
//     }
//   }
// }

Methods 操作

typescript 复制代码
// 添加方法
api.methods().add(
  t.objectMethod(
    'method',
    t.identifier('handleInput'),
    [t.identifier('val')],
    t.blockStatement([
      template.statement('this.value = val')(),
    ]),
  ),
)
// 结果:
// export default {
//   name: 'AInput',
//   methods: {
//     handleChange(val) {
//       this.value = val
//     },
//     handleInput(val) {
//       this.value = val
//     }
//   }
// }

// 更新方法
api.methods().update(
  t.objectMethod(
    'method',
    t.identifier('handleChange'),
    [t.identifier('val')],
    t.blockStatement([
      template.statement('this.data = val')(),
    ]),
  ),
)

// 删除方法
api.methods().remove('handleChange')

Props 操作

typescript 复制代码
// 添加 props
api.props().add(
  t.objectProperty(
    t.identifier('checked'),
    t.identifier('Boolean'),
  ),
)
// 结果:
// export default {
//   name: 'Switch',
//   props: {
//     checked: Boolean
//   }
// }

// 更新 props
api.props().update(
  t.objectProperty(
    t.identifier('checked'),
    t.objectExpression([
      t.objectProperty(
        t.identifier('type'),
        t.identifier('String'),
      ),
      t.objectProperty(
        t.identifier('default'),
        t.booleanLiteral(false),
      ),
    ]),
  ),
)

// 删除 props
api.props().remove('checked')

2. Composition API 模式

Ref 操作

typescript 复制代码
// 添加 ref
api.data().add(
  'value',
  t.stringLiteral(''),
)
// 结果:
// const checked = ref('');
// const value = ref("");

// 更新 ref
api.data().update('checked', t.booleanLiteral(false))
// 结果:
// const checked = ref(false);
// const value = ref("");

// 删除 ref
api.data().remove('checked')
// 结果:
// const value = ref("");

Computed 操作

typescript 复制代码
// 添加 computed
api.computed().add(
  'reverseName',
  template.expression('() => lastName.value + \' \' + firstName.value')(),
)
// 结果:
// const firstName = ref('Davide');
// const lastName = ref('Li')
// const fullName = computed(() => firstName.value + lastName.value)
// const reverseName = () => lastName.value + ' ' + firstName.value

// 更新 computed
api.computed().update(
  'fullName',
  template.expression('() => firstName.value + \' \' + lastName.value')(),
)

// 删除 computed
api.computed().remove('fullName')

Methods 操作

typescript 复制代码
// 添加方法
api.methods().add(
  template.statement(`function handleChange(val){
    checked.value = val
  }`)() as t.FunctionDeclaration,
)
// 结果:
// const inputValue = ref('')
// const checked = ref(false)
// function handleInput(val) {
//   inputValue.value = val
// }
// function handleChange(val){
//   checked.value = val
// }

// 更新方法
api.methods().update(
  template.statement(`function handleInput(value) {
    inputValue.value = value
  }`)() as t.FunctionDeclaration,
)

// 删除方法
api.methods().remove('handleInput')

Watch 操作

typescript 复制代码
// 添加 watch
api.watch().add(
  template.statement('watch(x, (newX) => {\nconsole.log(`x is ${newX}`)\n})')() as t.ExpressionStatement
)
// 结果:
// const x = ref(0)
// watch(x, (newX) => {
//   console.log(`x is ${newX}`)
// })

实际应用场景

1. 组件代码生成器

typescript 复制代码
function generateComponent(options) {
  const { api } = parseSfcScript('')
  
  // 添加数据
  api.data().add('formData', t.objectExpression([]))
  
  // 添加方法
  api.methods().add(
    t.objectMethod(
      'method',
      t.identifier('submit'),
      [],
      t.blockStatement([]),
    ),
  )
  
  return generate(api.ast)
}

注意事项

  1. 在使用 TypeScript 时,需要设置 lang: 'ts' 选项
  2. 使用 JSX 语法时,需要设置 jsx: true 选项
  3. 操作 API 时注意返回值的类型检查
  4. 修改 AST 后需要使用 @babel/generator 生成代码

总结

parseSfcScript 是一个功能强大的工具函数,它可以帮助我们:

  • 解析 Vue 组件代码
  • 操作组件的各个部分
  • 生成新的组件代码
  • 转换组件代码风格
  • 分析组件结构

通过合理使用这个工具,我们可以大大提高组件开发效率,实现更多自动化的工作。

相关推荐
糕冷小美n1 小时前
elementuivue2表格不覆盖整个表格添加固定属性
前端·javascript·elementui
小哥不太逍遥1 小时前
Technical Report 2024
java·服务器·前端
沐墨染2 小时前
黑词分析与可疑对话挖掘组件的设计与实现
前端·elementui·数据挖掘·数据分析·vue·visual studio code
anOnion2 小时前
构建无障碍组件之Disclosure Pattern
前端·html·交互设计
threerocks2 小时前
前端将死,Agent 永生
前端·人工智能·ai编程
问道飞鱼3 小时前
【前端知识】Vite用法从入门到实战
前端·vite·项目构建
爱上妖精的尾巴3 小时前
8-10 WPS JSA 正则表达式:贪婪匹配
服务器·前端·javascript·正则表达式·wps·jsa
Zhencode3 小时前
Vue3响应式原理之ref篇
vue.js
小疙瘩4 小时前
element-ui 中 el-upload 多文件一次性上传的实现
javascript·vue.js·ui
Aliex_git4 小时前
浏览器 API 兼容性解决方案
前端·笔记·学习