首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >记一次重构三个月前的 Vue 项目

记一次重构三个月前的 Vue 项目

作者头像
kifuan
发布2022-11-12 10:55:53
2940
发布2022-11-12 10:55:53
举报

简介

是我之前写过的一个图片切割器,这是 GitHub 链接,其主要功能是通过 canvas 对图片进行一个切割,从而达到发朋友圈、个人资料大图片的一个效果。

用到的库有:Vue, Pinia, Tailwind CSS

这是我三个月前一晚上加工出来的粗糙产物,所以说有比较多的槽点,也算是新人容易犯的一些错误。

重构点

这里列出几个大的点。注意,尽管有些地方我用的是 HTML 代码块(右上角或左上角显示 HTML),但它们都是 Vue 代码,只是我不想再配置 markdown 引擎罢了。

策略对象的 Store

  • 原写法
/* 这里也给出接口,不想看可以略过,没什么影响 */
export interface StrategyStep {
  label: string
  size: number
  offset: [number, number]
}

export interface Strategy {
  label: string
  unit: number
  scale: number
  steps: StrategyStep[]
}

export const strategies: Record<string, Strategy> = {
  qq3x3: {
    label: 'QQ个人资料图片3x3',
    unit: 0.333333,
    scale: 0.75,
    steps: [ /* 略 */ ]
  },
}

export const useStrategyStore = defineStore('strategy', () => {
  const strategy = ref('qq3x3')
  return { strategy }
})

这导致用的时候是非常丑,所以说我们需要改进。

  • 改进
export const useStrategyStore = defineStore('strategy', () => {
  const strategy = ref(strategies.qq3x3)
  const name = ref('qq3x3')

  const strategyName = computed({
    get: () => name.value,
    set: (value) => {
      if (!Reflect.has(strategies, value))
        throw new TypeError(`unknown strategy: ${value}`)
      name.value = value
      strategy.value = strategies[value]
    },
  })

  return { strategy: readonly(strategy), strategyName }
})

这样的话,通过提供一个计算属性 strategyName 和只读的 strategy 策略对象,来避免一些操作失误导致 bug 的出现。

循环渲染

  • 原写法

大概是这个意思,具体的

<script setup lang="ts">
const canvases = [] as HTMLCanvasElement[]
// 此处省略...
</script>

<template>
  <div v-for="(step, index) in strategy.steps" :key="index">
    <p>{{ step.label }}</p>
    <canvas
      :ref="el => { canvases[index] = el as HTMLCanvasElement }"
    />
    <button @click="handleDownload(index)">
      下载
    </button>
  </div>
</template>

这里把 for 循环渲染里面的内容直接存到数组里了。

  • 改进

我们应该把内部单独抽象成一个组件,这样才合理,而不是用数组来处理一批相同的事物。鉴于每个 canvas 是一个图片片段,这里抽象出一个PhotoFragment.vue(省略除了 props 的其它部分):

<script setup lang="ts">
const { index } = defineProps<{ index: number }>()
</script>

<template>
  <div v-show="image !== undefined" class="flex flex-col space-y-4">
    <p>{{ step.label }}</p>
    <canvas ref="canvas" />
    <Button @click="handleDownload">
      下载
    </Button>
  </div>
</template>

我们既然用 store 传递信息了,就不要在 for 循环上通过 props 传了,这样会造成很多麻烦。

那么再用一个 PhotoResult.vue 渲染 PhotoFragments.vue(省略 ts 部分):

<template>
  <div class="space-y-4">
    <h1 class="text-3xl">
      处理结果
    </h1>
    <PhotoFragment
      v-for="(step, index) in strategy.steps"
      :key="step.label + index"
      :index="index"
    />
    <div v-show="image === undefined" class="text-slate-500 font-medium">
      暂无数据
    </div>
  </div>
</template>

这里的 key 我只是觉得,这个组件应该在 step 被更换时也更新一下,所以就把 stepindex 配合起来组成了一个 key,至于是不是非得这么写不可,我还是不太清楚的。

总结

主要的点其实就是这两个,具体的代码大家可以到 GitHub 仓库去查看,链接已经放在文章开头。就这样,拜拜。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 简介
  • 重构点
    • 策略对象的 Store
      • 循环渲染
      • 总结
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档