原文:Vue's Darkest Day 做者:Daniel Elkingtonjavascript
译者注:原文写于2019年6月21日vue
今天,我惊讶的发现,往常积极友好的 VueJS 社区陷入了一场激烈的战争。 两周前,Vue 的建立者尤雨溪发布了一个请求意见稿(RFC),用于在即将发布的 Vue 3.0 中使用基于函数的方式编写 Vue 组件。今天,一个 Reddit 上批评性的帖子和 Hacker News 上一些相似的批评性的评论,引发大批开发者涌向本来的 RFC 来表达他们的愤怒,其中一些有点侮辱性。 在不少地方都有人声称:java
看过 Reddit 上成堆的负面评论,你可能会在 RFC 页面上惊讶的发现尤雨溪的 RFC 收到的正面的表情回应的比例比负面的高得多,并且许多早期评论都是至关正面的。 实际上,第一条评论就充满了溢美之词。git
我就是第一个写评论的人。 我碰巧收到新 RFC 的通知,立刻读了一下,发现这正是我想从 Vue 3.0 获得的,并且它会给我极大的帮助,因而我在 RFC 发布 15 分钟后留下了第一条评论来表达个人谢意。 我但愿在这里进一步说明为何我以为新提案是一个如此好的主意,但首先,要回应一些批评。github
我怀疑不少人在阅读了 Hacker News 或 Reddit 上有着不少误导性评论的帖子以后有点激动,他们在没有阅读原始提案的状况下就表达了本身的愤怒。 尤雨溪已经更新了这个提案,经过问答的方式回应了人们的不少问题,总的来讲:api
一个更主观的观点是:新语法不如旧语法,而且会致使结构化程度较低的代码。 我但愿经过一个简单的例子来讲明为何我在看到 RFC 时如此兴奋,以及为何我以为它更优秀,将会致使结构化 更好 的代码。函数
考虑一下下面的有趣组件,用户能够输入宠物的详细信息。请注意:学习
你能够在这里尝试组件的demo,也能够在这里查看使用 Vue 2.x 编写的代码(在 components/Vue2.vue)this
考虑一下这个组件的 JavaScript:spa
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;
}
}
};
复制代码
实质上,咱们有一些数据、从这些数据计算出的属性、以及 操做这些数据的方法。 注意,在 Vue 2.x 中咱们 没有办法把相关的东西放在一块儿。 咱们不能把 petName
数据声明放在 petNameComment
计算属性或者 onPetNameBlur
方法旁边,由于在 Vue 2.x 中,这些选项是按照类型组织的。
固然,对于像这样的小例子来讲,这不过重要。可是想象一个更大的例子,它有不少功能,须要 data
、computed
、methods
、甚至是一两个watcher
。 目前还 没有好方法 来把相关的东西放一块儿!有人可能会使用诸如 Mixin 或高阶组件之类的办法,可是它们都有问题——很难辨别一个属性来自哪里,还有命名空间的冲突。 (是的,在这种状况下,拆分为多个组件是可能的,可是这个相似的例子就不行)
新提案不是按照选项的类型来组织组件,而是容许咱们按照实际功能来组织组件。 这相似于你在电脑上整理我的文件的方式——你一般没有“表格”文件夹和“Word 文档”文件夹,相反,你可能有一个”工做“文件夹和一个”假期计划“文件夹。想象一下使用提案里的语法来编写组件(尽我所能,若是你看到了什么 bug 请告诉我):
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
};
}
};
复制代码
注意:
除此以外,新语法能够有完整的 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
};
}
};
复制代码
在 Vue 2.x 中,我常常发现本身写了个“怪兽组件”,它很难分解成更小的部分——它不能分解成其余的组件,由于不少事务基于少许状态。 然而,使用提案中的语法,很容易看出大型组件的逻辑能够被分解为更小的可重用部分,在必要时移动到独立的文件里,留给你小的、易于理解的函数和组件。
这是目前为止 Vue 最黑暗的一天吗?看起来是的。一直团结追随这个项目方向的社区已经分裂了。 但我但愿人们可以从新审视这个提案,它没有破坏任何东西,只要他们想,仍然能够按照选项的类型来组织它们,可是能够作到更多——更清晰的代码、更简洁的代码、更有意思的库、还有完善的 Typescript 支持。
最后,在使用开源软件时,最好记住,全靠维护者投入的大量精力,你才能够无偿使用它。 今天的一些过度批评是他们不该该承受的。好在这些无礼的批评只是少数(尽管数量至关多),大多数人能以更礼貌的方式表达本身。
2019年6月23日更新:
我很快就写好了原文,并无指望它能获得这样的关注。而后我意识到这个代码示例对于我想要表达的观点来讲过于复杂,因此我把它简化了不少。本来的代码示例在这里。