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

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

相关推荐
前端互助会3 小时前
Live2D形象展示与文本语音播报:打造生动交互体验的完整实现
前端·vue.js·microsoft·交互
努力的小郑4 小时前
今晚Cloudflare一哆嗦,我的加班计划全泡汤
前端·后端·程序员
武昌库里写JAVA5 小时前
微擎服务器配置要求,微擎云主机多少钱一年?
java·vue.js·spring boot·后端·sql
dy17175 小时前
el-table表头上下显示内容
javascript·vue.js·elementui
q***64975 小时前
头歌答案--爬虫实战
java·前端·爬虫
凌波粒5 小时前
SpringMVC基础教程(4)--Ajax/拦截器/文件上传和下载
java·前端·spring·ajax
液态不合群6 小时前
DDD驱动低代码开发:从业务流程到领域模型的全链路设计
前端·低代码·架构·ddd
jonyleek6 小时前
JVS低代码开发中,如何创建自定义前端页面并接入到现有系统中,从创建到接入的全攻略
前端·低代码·前端框架·软件开发
谢尔登6 小时前
【React】React组件的渲染过程分为哪几个阶段?
前端·javascript·react.js
MediaTea7 小时前
Python 第三方库:Flask(轻量级 Web 框架)
开发语言·前端·后端·python·flask