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

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

相关推荐
一个游离的指针24 分钟前
ES6基础特性
前端·javascript·es6
layman052825 分钟前
ES6/ES11知识点
前端·ecmascript·es6
2501_915373885 小时前
Vue 3零基础入门:从环境搭建到第一个组件
前端·javascript·vue.js
沙振宇8 小时前
【Web】使用Vue3开发鸿蒙的HelloWorld!
前端·华为·harmonyos
运维@小兵8 小时前
vue开发用户注册功能
前端·javascript·vue.js
蓝婷儿8 小时前
前端面试每日三题 - Day 30
前端·面试·职场和发展
oMMh8 小时前
使用C# ASP.NET创建一个可以由服务端推送信息至客户端的WEB应用(2)
前端·c#·asp.net
一口一个橘子9 小时前
[ctfshow web入门] web69
前端·web安全·网络安全
读心悦10 小时前
CSS:盒子阴影与渐变完全解析:从基础语法到创意应用
前端·css
香蕉可乐荷包蛋10 小时前
vue数据可视化开发echarts等组件、插件的使用及建议-浅看一下就行
vue.js·信息可视化·echarts