原文: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日更新:
我很快就寫好了原文,並無指望它能獲得這樣的關注。而後我意識到這個代碼示例對於我想要表達的觀點來講過於複雜,因此我把它簡化了不少。本來的代碼示例在這裏。