jQuery === 麪條式代碼?

自從React/Vue等框架流行以後,jQuery被打上了麪條式代碼的標籤,甚至成了「過街老鼠」,好像誰還在用jQuery,誰就還活在舊時代,不少人都爭先恐後地擁抱新框架,各大博客網站有很大一部分的博客都在介紹新的框架,爭當時代的「弄潮兒」。新框架帶來的新的理念,新的開發方式不能否認帶來了生產效率,可是jQuery等就應該被打上「舊時代」麪條式代碼的標籤麼?javascript

咱們從一篇文章提及:《React.js 的介紹 - 針對瞭解 jQuery 的工程師(譯)》,英文原文是這個《React.js Introduction For People Who Know Just Enough jQuery To Get By》, 這篇文章我很久前就看過,如今再把它翻出來,裏面對比了下jQuery和React分別實現一個發推的功能,做者用jQuery寫着寫着代碼就亂套了,而用React無論需求多複雜,代碼條理依舊很清晰。css

咱們一步步按照原文做者的思路來拆解。html

(1)輸入個數爲0時,發送按鈕不可點擊

以下圖所示,當輸入框沒有內容時,發推按鈕置灰不可點,有內容點才能點。前端

做者寫的代碼是這樣的:java

// 初始化狀態
$("button").prop("disabled", true);

// 文本框的值發生變化時
$("textarea").on("input", function() {
  // 只要超過一個字符,就
  if ($(this).val().length > 0) {
    // 按鈕能夠點擊
    $("button").prop("disabled", false);
  } else {
    //不然,按鈕不能點擊
    $("button").prop("disabled", true);
  }
});複製代碼

這個代碼自己寫得很累贅,首先,既然一開始那個button是disabled的,那就直接在html上寫個disabled屬性就好了:react

<form class="tweet-box"> <textarea name="textMsg"></textarea> <input disabled type="submit" name="tweet" value="Tweet"> </form>複製代碼

第二個要控制按鈕的狀態,其實核心只要一行代碼就好了,不須要寫那麼長:jquery

let form = $(".tweet-box")[0];
$(form.textMsg).on("input", function() {
    form.tweet.disabled = this.value.length <= 0;
}).trigger("input");複製代碼

這個代碼應該夠簡潔了吧,並且代碼在jQuery和原生之間來回切換,遊刃有餘。ajax

(2)實現剩餘字數功能

以下圖所示:segmentfault

這個也好實現:框架

let form = $(".tweet-box")[0],
    $leftWordCount = $("#left-word-count");

$(form.textMsg).on("input", function() {
    // 已有字數
    let wordsCount = this.value.length;
    $leftWordCount.text(140 - wordsCount);
    form.tweet.disabled = wordsCount <= 0; 
});複製代碼

(3)添加圖片按鈕

以下圖所示,左下角多了一個選擇照片的按鈕:

若是用戶選擇了照片,那麼可輸入字數將會減小23個字符,而且Add Photo文案要變成Photo Added。咱們先來看下做者是怎麼實現的,以下代碼:

if ($(this).hasClass("is-on")) {
  $(this)
    .removeClass("is-on")
    .text("Add Photo");
  $("span").text(140 - $("textarea").val().length);
} else {
  $(this)
    .addClass("is-on")
    .text("✓ Photo Added");
  $("span").text(140 - 23 - $("textarea").val().length);
}複製代碼

若是代碼像做者這樣寫的話確實是比較亂,並且比較麪條式。可是咱們能夠優雅地實現。首先,選擇照片通常會寫一個input[type=file]的隱藏輸入框蓋在上傳圖標下面:

<div class="upload-container"> <img src="upload-icon.png" alt> <span id="add-photo">Add Photo</span> <input type="file" name="photoUpload"> </div>複製代碼

而後監聽它的change事件,在change事件裏面給form套一個類:

$(form.photoUpload).on("change", function() {
    // 若是選擇了照片則添加一個photo-added的類
    this.value.length ? $(form).addClass("photo-added")
                // 不然去掉
                : $(form).removeClass("photo-added");
            
});複製代碼

而後就能夠來實現文案改變的需求了,把上面#add-photo的span標籤添加兩個data屬性,分別是照片添加和未添加的文案,以下代碼所示:

<span id="add-photo" data-added-text="Photo Added" data-notadded-text="Add Photo"></span>複製代碼

經過form的類結合before/after僞類控制html上的文案,以下代碼所示:

#add-photo:before {
    content: attr(data-empty-text);
}

form.photo-added #add-photo:before {
    content: attr("data-added-text);
}複製代碼

這樣就能夠了,咱們算是用了一個比較優雅的方式實現了一個文案變化的功能,其中CSS的attr能夠兼容到IE9,而且這裏html/css/js相配合,共同完成這個變化的功能,這應該也挺好玩的。

剩下一個要減掉23字符的需求,只須要在減掉的時候判斷一下:

$(form.textMsg).on("input", function() {
    // 已有字數
    let wordsCount = this.value.length;
    form.tweet.disabled = wordsCount <= 0;
    $leftWordCount.text(140 - wordsCount - 
            //若是已經添加了圖片再減掉23個字符
            ($(form).hasClass("photo-added") ? 23 : 0));
});複製代碼

而後在選擇圖片以後trigger一下,讓文字發生變化,以下代碼倒數第二行:

/* * @trigger 會觸發文字輸入框的input事件以更新剩餘字數 */
$(form.photoUpload).on("change", function() {
    // 若是選擇了照片則添加一個photo-added的類
    this.value.length ? $(form).addClass("photo-added") : 
                // 不然去掉
                $(form).removeClass("photo-added");
    $(form.textMsg).trigger("input");
});複製代碼

這裏又使用了事件的機制,用reac應該基本上都是用狀態state控制了。

再來看最後一個功能。

(4)沒有文字可是有照片發推按鈕要可點

上面是隻要沒有文字,那麼發推按鈕不可點,如今要求有圖片就可點。這個也好辦,由於若是有圖片的話,form已經有了一個類,因此只要再加一個判斷就能夠了:

$(form.textMsg).on("input", function() {
    // 已有字數
    let wordsCount = this.value.length;
    form.tweet.disabled = wordsCount <= 0 
            //disabled再添加一個與判斷
            && !$(form).hasClass("photo-added");
    $leftWordCount.text(140 - wordsCount - 
            //若是已經添加了圖片再減掉23個字符
            ($(form).hasClass("photo-added") ? 23 : 0));
});複製代碼


最後看一下,彙總的JS代碼,加上空行和註釋總共只有23行:

let form = $(".tweet-box")[0],
    $leftWordCount = $("#left-word-count");

$(form.textMsg).on("input", function() {
    // 已有字數
    let wordsCount = this.value.length;
    form.tweet.disabled = wordsCount <= 0 
            //disabled再添加一個與判斷
            && !$(form).hasClass("photo-added");
    $leftWordCount.text(140 - wordsCount - 
            //若是已經添加了圖片再減掉23個字符
            ($(form).hasClass("photo-added") ? 23 : 0));
});

/* * @trigger 會觸發文字輸入框的input事件以更新剩餘字數 */
$(form.photoUpload).on("change", function() {
    // 若是選擇了照片則添加一個photo-added的類
    this.value.length ? $(form).addClass("photo-added") : 
            // 不然去掉
            $(form).removeClass("photo-added");
    $(form.textMsg).trigger("input");
});複製代碼

html大概有10行,還有6行核心CSS,不過這兩個比較易讀。再來看一下React的完整版本,做者的實現:

var TweetBox = React.createClass({
  getInitialState: function() {
    return {
      text: "",
      photoAdded: false
    };
  },
  handleChange: function(event) {
    this.setState({ text: event.target.value });
  },
  togglePhoto: function(event) {
    this.setState({ photoAdded: !this.state.photoAdded });
  },
  remainingCharacters: function() {
    if (this.state.photoAdded) {
      return 140 - 23 - this.state.text.length;
    } else {
      return 140 - this.state.text.length;
    }
  },
  render: function() {
    return (
      <div className="well clearfix"> <textarea className="form-control" onChange={this.handleChange}></textarea> <br/> <span>{ this.remainingCharacters() }</span> <button className="btn btn-primary pull-right" disabled={this.state.text.length === 0 && !this.state.photoAdded}>Tweet</button> <button className="btn btn-default pull-right" onClick={this.togglePhoto}> {this.state.photoAdded ? "✓ Photo Added" : "Add Photo" } </button> </div>
    );
  }
});

React.render(
  <TweetBox />, document.body );複製代碼

React的套路是監聽事件而後改變state,在jsx的模板裏,使用這些state展現,而jQuery的套路是監聽事件,而後本身去控制DOM展現。React幫你操做DOM,jQuery要本身去操做DOM,前者提供了便利但同時也失去了靈活性,後者增長了靈活性但同時增長了複雜度。

使用jQuery很多人容易寫出麪條式的代碼,可是寫代碼的風格我以爲和框架不要緊,關鍵還在於你的編碼素質,就像你用了React寫class,你就能夠說你就是面向對象了?不見得,我在《JS與面向對象》這篇文章提到,寫class並不表明你就是面向對象,面向對象是一種思想而不是你代碼的組織形式。一旦你離開了React的框架,是否是又要回到麪條式代碼的風格了?若是是的話那就說明你並無沒有掌握面向對象的思想。不過,React等框架可以方便地組件化,這點是不能否認的。

還有一個須要注意的是,框架會幫你屏蔽掉不少原生的細節,讓你專心於業務邏輯,但每每也讓你喪失了原生的能力不論是html仍是js,而這纔是最重要的功底。例如說對於事件,因爲全部的事件都是直接綁在目標元素,而後經過state或者其它第三方的框架進行傳遞,這樣其實就沒什麼事件的概念了。因此須要警戒使用了框架可是喪失了基本的前端能力,再如ajax分頁改變url,或者說單頁面路由的實現方式,還有先後退的控制,基本上可以完整回答地比較少。不少人都會用框架作頁面,可是不懂JS.

相關文章
相關標籤/搜索