@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 组件代码
  • 操作组件的各个部分
  • 生成新的组件代码
  • 转换组件代码风格
  • 分析组件结构

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

相关推荐
阿成学长_Cain22 分钟前
Linux telinit 命令详解:运行级别切换|关机重启|系统维护一站式掌握
linux·运维·前端·网络
Patrick_Wilson35 分钟前
最佳实践是有保质期的:从一次 CDN external 白屏事故说起
前端·性能优化·前端工程化
YHHLAI1 小时前
[特殊字符] Agent 智能体开发实战 · 第一课:Tool Use —— 让大模型自动干活
前端·人工智能
nuIl1 小时前
我把 5 个编码 Agent 塞进了一个 npm 包
前端·agent·claude
L-影1 小时前
FastAPI 静态文件:Web 页面的“固定展柜”与“加速引擎”
前端·fastapi
陪我去看海1 小时前
受够了 AI 写完代码留下一堆端口?我做了一个端口管理App!
前端·macos·vibecoding
Xuepoo1 小时前
我抛弃了 DOM,但保留了无障碍访问
前端
李明卫杭州1 小时前
Vue2 vs Vue3 的 h 函数:渲染函数完整迁移指南
前端
星栈1 小时前
LiveView 的认证系统:从登录到权限,我一开始以为有 `current_user` 就算完事了
前端·前端框架·elixir
在因斯坦1 小时前
摸摸鱼之前端自己研究 Jenkins 自动化部署
前端