Vue 最黑暗的一天 LongLiveSvelte

2025-05-25

Vue 最黑暗的一天

LongLiveSvelte

今天,我惊讶地看到一向积极友好的 VueJS 社区陷入了一场激烈的争斗。两周前,Vue 的创始人尤雨溪发布了一份征求意见稿(RFC),要求在即将发布的 Vue 3.0 中引入一种基于函数式编写 Vue 组件的新方式。今天,Reddit 上出现了一条批评性的帖子,随后Hacker News上也出现了类似的批评性评论,这导致大量开发者涌向最初的 RFC 页面表达他们的愤怒,其中一些评论甚至近乎辱骂。有人在多个地方声称:

  • 所有 Vue 代码都必须以全新的方式重写,因为现有的语法已被删除并替换为其他语法;
  • 由于一切都即将改变,人们学习 Vue 所花的所有时间都被浪费了;
  • 新的语法比旧的语法更差,没有强制结构,并且会导致意大利面条式代码;
  • Vue Core 团队在没有任何协商的情况下突然实施了一项重大变更;
  • Vue 正在转变为 React!
  • 不,Vue 正在变成 AngularJS/Angular!
  • 现在所有 HTML 都需要写成一个巨大的字符串!

Reddit 帖子里充斥着大量负面评论,当你访问RFC 页面时,你可能会惊讶地发现,尤先生的 RFC 中关于表情符号的正面和负面评论比例极高,而且很多初始评论都相当正面。事实上,第一条评论就充满了赞扬。

我是第一个评论的作者。我碰巧收到了新的 RFC 通知,立即阅读了它,发现它正是我想要的 Vue 3.0 版本,而且会非常有帮助,于是在 RFC 发布后的 15 分钟内就留下了第一条评论,表达我的感激之情。我希望在这里详细解释一下为什么我认为这个新提案是一个好主意,但首先,我想先回应一些批评。

我猜很多人在读了 Hacker News 或 Reddit 上一些带有误导性的评论后,会有些激动,甚至没看原始提案就表达了愤怒。Evan You 现在已经更新了提案,并附上了一份问答,解答了大家提出的许多问题。总结一下:

  • 如果您不想重写任何代码,则无需重写——新语法是附加的,而旧语法在整个 Vue 3.0 版本中以及在广泛使用的情况下都将保持有效。即使旧语法最终从核心代码中移除,插件也可以轻松地使旧语法仍然 100% 有效
  • 学习 Vue 所花的时间并没有浪费——新的组件语法使用了您花时间学习的相同概念,其他概念(如单文件组件、模板和范围样式)的工作方式完全相同。
  • 任何变更都是未经协商而做出的——RFC就是协商结果。新语法距离正式发布还有很长的路要走。
  • 不,HTML 代码不需要写成一个巨大的字符串。

稍微主观一点的观点是,新语法不如旧语法,会导致代码结构性较差。我希望用一个简单的例子来解释为什么我看到 RFC 时如此兴奋,以及为什么我认为它更胜一筹,并且会带来更好的结构化代码。

考虑以下有趣的组件,它允许用户输入宠物的详细信息。请注意

  • 当他们输入完宠物的名字时会显示一条消息;
  • 在他们选择宠物的尺寸后,会显示另一条消息。在输入任何数据之前填写表格 - 不显示消息 输入数据后的表格 - 显示消息

您可以在此处尝试该组件的演示,并可以在此处查看使用 Vue 2.x 的完整代码(参见 components/Vue2.vue)。

考虑这个组件的 JavaScript:

export default {
  data() {
    return {
      petName: "",
      petNameTouched: false,
      petSize: "",
      petSizeTouched: false
    };
  },
  computed: {
    petNameComment: function() {
      if (this.petNameTouched) {
        return "Hello " + this.petName;
      }
      return null;
    },
    petSizeComment: function() {
      if (this.petSizeTouched) {
        switch (this.petSize) {
          case "Small":
            return "I can barely see your pet!";
          case "Medium":
            return "Your pet is pretty average.";
          case "Large":
            return "Wow, your pet is huge!";
          default:
            return null;
        }
      }
      return null;
    }
  },
  methods: {
    onPetNameBlur: function() {
      this.petNameTouched = true;
    },
    onPetSizeChange: function() {
      this.petSizeTouched = true;
    }
  }
};
Enter fullscreen mode Exit fullscreen mode

本质上,我们有一些数据、基于这些数据计算出的属性,以及操作这些数据的方法。需要注意的是,在 Vue 2.x 中,没有办法将相关的内容放在一起。我们不能将数据声明放在计算属性或方法petName旁边,因为在 Vue 2.x 中,内容是按类型分组的。petNameCommentonPetNameBlur

当然,对于像这样的小例子来说,这没什么大不了的。但想象一下一个更大的例子,它包含多个功能,需要datacomputedmethods,甚至一watcher两个。目前还没有好的方法将相关的内容放在一起!人们或许可以使用 Mixins 或高阶组件之类的东西,但它们存在问题——很难看到属性的来源,并且存在命名空间冲突的问题。(是的,在这种情况下,可以将内容拆分成多个组件,但请考虑一下这个类似的例子,它不能拆分。)

新提案不再按选项类型组织组件,而是允许我们按实际功能组织组件。这类似于您在计算机上组织个人文件的方式——通常不会有“电子表格”文件夹和“Word 文档”文件夹,而是可能有“工作”文件夹和“假期计划”文件夹。请考虑使用提案语法编写的上述组件(我尽可能在不查看输出的情况下尽力做到最好——如果您发现任何错误,请告诉我!):

import { state, computed } from "vue";
export default {
  setup() {
    // Pet name
    const petNameState = state({ name: "", touched: false });
    const petNameComment = computed(() => {
      if (petNameState.touched) {
        return "Hello " + petNameState.name;
      }
      return null;
    });
    const onPetNameBlur = () => {
      petNameState.touched = true;
    };

    // Pet size
    const petSizeState = state({ size: "", touched: false });
    const petSizeComment = computed(() => {
      if (petSizeState.touched) {
        switch (this.petSize) {
          case "Small":
            return "I can barely see your pet!";
          case "Medium":
            return "Your pet is pretty average.";
          case "Large":
            return "Wow, your pet is huge!";
          default:
            return null;
        }
      }
      return null;
    });
    const onPetSizeChange = () => {
      petSizeState.touched = true;
    };

    // All properties we can bind to in our template
    return {
      petName: petNameState.name,
      petNameComment,
      onPetNameBlur,
      petSize: petSizeState.size,
      petSizeComment,
      onPetSizeChange
    };
  }
};
Enter fullscreen mode Exit fullscreen mode

注意

  • 将相关事物归为一类是极其容易的事情;
  • 通过查看设置函数返回的内容,我们可以轻松地看到我们在模板中可以访问的内容;
  • 我们甚至可以避免暴露模板不需要访问的内部状态(“接触”)。

最重要的是,新语法可以轻松实现完整的 TypeScript 支持,这在 Vue 2.x 基于对象的语法中很难实现。而且我们可以轻松地将可重用的逻辑提取到可重用的函数中。例如

import { state, computed } from "vue";

function usePetName() {
  const petNameState = state({ name: "", touched: false });
  const petNameComment = computed(() => {
    if (petNameState.touched) {
      return "Hello " + petNameState.name;
    }
    return null;
  });
  const onPetNameBlur = () => {
    petNameState.touched = true;
  };
  return {
    petName: petNameState.name,
    petNameComment,
    onPetNameBlur
  };
}

function usePetSize() {
  const petSizeState = state({ size: "", touched: false });
  const petSizeComment = computed(() => {
    if (petSizeState.touched) {
      switch (this.petSize) {
        case "Small":
          return "I can barely see your pet!";
        case "Medium":
          return "Your pet is pretty average.";
        case "Large":
          return "Wow, your pet is huge!";
        default:
          return null;
      }
    }
    return null;
  });
  const onPetSizeChange = () => {
    petSizeState.touched = true;
  };
  return {
    petSize: petSizeState.size,
    petSizeComment,
    onPetSizeChange
  };
}

export default {
  setup() {
    const { petName, petNameComment, onPetNameBlur } = usePetName();
    const { petSize, petSizeComment, onPetSizeChange } = usePetSize();
    return {
      petName,
      petNameComment,
      onPetNameBlur,
      petSize,
      petSizeComment,
      onPetSizeChange
    };
  }
};
Enter fullscreen mode Exit fullscreen mode

在 Vue 2.x 中,我经常发现自己编写的“庞然大物”组件很难拆分成更小的部分——它无法分解成其他组件,因为基于少量状态发生了太多事情。然而,使用建议的语法,很容易看出如何将大型组件的逻辑分解成更小的可复用部分,并在必要时将其移动到单独的文件中,从而获得小巧、易于理解的函数和组件。

这是 Vue 迄今为止最黑暗的一天吗?看起来是的。此前一直团结在项目方向上的社区如今已经分裂了。但我希望大家能重新审视这个提案,它不会破坏任何东西,仍然允许他们根据选项类型进行分组(如果他们愿意的话),但允许更多——更清晰的代码、更干净的代码、更多有趣的库可能性,以及完整的 TypeScript 支持。

最后,在使用开源软件时,务必记住,维护人员为免费使用的产品付出了大量心血。如今看到的一些近乎辱骂的批评,他们实在不应该容忍。值得庆幸的是,这些不尊重的评论只是少数(尽管数量不小),而且许多人都能够以更尊重的方式表达自己的想法。

2019年6月23日更新:
我写原帖的速度非常快,没想到它能引起如此大的关注。之后我意识到代码示例对于我想要阐述的观点来说太复杂了,所以我对其进行了极大的简化。原始代码示例可以在这里找到

文章来源:https://dev.to/daniellkington/vue-s-darkest-day-3fgh
PREV
我如何学习任何类型的新技术(作为高级开发人员)
NEXT
宣布推出 TechSchool:一个学习编程的免费开源平台