[譯] Vue 最黑暗的一天

原文:Vue's Darkest Day 做者:Daniel Elkingtonjavascript

譯者注:原文寫於2019年6月21日vue

今天,我驚訝的發現,往常積極友好的 VueJS 社區陷入了一場激烈的戰爭。 兩週前,Vue 的建立者尤雨溪發佈了一個請求意見稿(RFC),用於在即將發佈的 Vue 3.0 中使用基於函數的方式編寫 Vue 組件。今天,一個 Reddit 上批評性的帖子Hacker News 上一些相似的批評性的評論,引發大批開發者涌向本來的 RFC 來表達他們的憤怒,其中一些有點侮辱性。 在不少地方都有人聲稱:java

  • 全部 Vue 代碼都必須以全新的方式重寫,由於現有的語法正在被移除,而且被其餘東西取代;
  • 人們花在學習 Vue 上的全部時間都被浪費了,由於一切都會改變;
  • 新語法比舊的更糟糕,由於它沒有強制的結構,而且會致使意大利麪條式代碼;
  • Vue 核心團隊在沒有任何諮詢的狀況下忽然施行一個巨大的破壞性的變化;
  • Vue 要變成 React 了!
  • 不,Vue 要變成 AngularJS/Angular 了!
  • 全部 HTML 都要寫在一個超長的字符串裏!

看過 Reddit 上成堆的負面評論,你可能會在 RFC 頁面上驚訝的發現尤雨溪的 RFC 收到的正面的表情迴應的比例比負面的高得多,並且許多早期評論都是至關正面的。 實際上,第一條評論就充滿了溢美之詞。git

我就是第一個寫評論的人。 我碰巧收到新 RFC 的通知,立刻讀了一下,發現這正是我想從 Vue 3.0 獲得的,並且它會給我極大的幫助,因而我在 RFC 發佈 15 分鐘後留下了第一條評論來表達個人謝意。 我但願在這裏進一步說明爲何我以爲新提案是一個如此好的主意,但首先,要回應一些批評。github

我懷疑不少人在閱讀了 Hacker News 或 Reddit 上有着不少誤導性評論的帖子以後有點激動,他們在沒有閱讀原始提案的狀況下就表達了本身的憤怒。 尤雨溪已經更新了這個提案,經過問答的方式迴應了人們的不少問題,總的來講:api

  • 若是你不想重寫任何代碼,那麼你就不須要重寫——新語法是附加的,並且只要舊語法仍然被普遍使用,它在 Vue 3.0 中依然有效。就算它最終被從核心代碼中移除了,也能夠很容易地經過插件來使舊語法 100% 有效
  • 學習 Vue 的時間並無浪費——新組件語法使用的概念與你以前花時間學習的同樣,其餘概念,例如單文件組件、模板、scoped style 的功用徹底同樣。
  • 沒有通過諮詢,就不會改變—— RFC 就是在 諮詢。新語法離發佈還有很長一段路要走。
  • 不,HTML 代碼不須要被寫進一個超長字符串。

一個更主觀的觀點是:新語法不如舊語法,而且會致使結構化程度較低的代碼。 我但願經過一個簡單的例子來講明爲何我在看到 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 中,這些選項是按照類型組織的。

固然,對於像這樣的小例子來講,這不過重要。可是想象一個更大的例子,它有不少功能,須要 datacomputedmethods、甚至是一兩個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
    };
  }
};
複製代碼

注意:

  • 很容易把相關的東西放到一塊兒;
  • 經過查看 setup 函數的返回值,咱們能夠很容易地知道模板中能夠獲取什麼變量;
  • 咱們甚至能夠避免暴露模板不須要獲取的內部狀態(touched)。

除此以外,新語法能夠有完整的 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日更新:

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

相關文章
相關標籤/搜索