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

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

相关推荐
烛阴1 分钟前
自动化测试、前后端mock数据量产利器:Chance.js深度教程
前端·javascript·后端
好好学习O(∩_∩)O6 分钟前
QT6引入QMediaPlaylist类
前端·c++·ffmpeg·前端框架
敲代码的小吉米6 分钟前
前端HTML contenteditable 属性使用指南
前端·html
testleaf18 分钟前
React知识点梳理
前端·react.js·typescript
站在风口的猪110818 分钟前
《前端面试题:HTML5、CSS3、ES6新特性》
前端·css3·html5
Xiao_die88818 分钟前
前端八股之CSS
前端·css
每天都有好果汁吃1 小时前
基于 react-use 的 useIdle:业务场景下的用户空闲检测解决方案
前端·javascript·react.js
穗余1 小时前
NodeJS全栈开发面试题讲解——P10微服务架构(Node.js + 多服务协作)
前端·面试·node.js
横冲直撞de1 小时前
前端下载文件,文件打不开的问题记录
前端
占星安啦1 小时前
一个html实现数据库自定义查询
java·前端·javascript·数据库·动态查询