摘要:知乎原地址編寫優雅的前端業務代碼前言當我們在寫業務代碼的時候,我們到底在寫什么其實是對交互的一些處理。遍歷,通過事件委派,將事件綁定在上。事件綁定濫用使用進行統一管理。寫代碼要說人話。
知乎 live 原地址:編寫優雅的前端業務代碼
前言當我們在寫業務代碼的時候,我們到底在寫什么?
其實是對交互的一些處理。所有的交互都是基于用戶或者瀏覽器的一些行為來觸發的,比如渲染頁面,在頁面onload方法觸發之后,我們的js代碼才會執行,比如說懶加載,根據用戶滾動或者可視區域的變化來觸發,比如按鈕的點擊之后頁面的局部刷新,比如input框輸入、表單提交、上傳文件等等,以上所說的這些行為組成起來的就是我們前端要寫的業務邏輯。
我們所寫的業務都是基于事件來對產品功能和場景進行描述。
頁面的執行順序是怎樣的?
頁面在初始化的時候,先加載css,然后加載html的節點,最后將js放在頁尾按順序執行,然后css加載,模板輸出之后css生效,js再加載,js再對頁面的模板進行交互處理,比如編譯模板,最后再把交互功能比如事件進行綁定。無論寫什么業務,都會是這個生命周期。
一個頁面的輸出是有它的生命周期的,同時,一個js程序,比如vue或者react也是有自己的生命周期的,所以每一個類,每一個業務邏輯都是有自己的生命周期的。
下面是react的組件的生命周期:最開始的時候先拿到默認的參數屬性,然后初始化狀態,在render之前有一個事件的廣播,在render之后有一個事件的廣播,這時候這個組件就render到頁面上了。組件在運行的時候有兩個方式,一個是狀態的改變,它會觸發update,再去觸發state的改變,然后再對應地重新render,在render之前會先觸發事件廣播,render之后也會觸發一個事件廣播。另一個方式是卸載,卸載之前會觸發一個廣播。
vue的生命周期和react的其實是很像的,它只不過比react多了一步對template和el進行一個判斷的掃描,對應的也是render渲染,在渲染之后會有一個屬性的update,有一個銷毀的過程。
總結一下,vue和react的生命周期其實就是完成它這個前端框架的業務邏輯。
實例下面這段代碼是將一個面向過程的js程序改為面向對象方式之后的js程序。
(function (global, $, _, doc) { "use strict"; // 定義一個構造器,是這個文件的入口 var app = function (options) { options = options || {}; this.a = options.a; // ... this.eventMap = { "click .title": "titleClick", "dbclick .input": "inputDbclick", }; // 初始化所有節點屬性 this.initEles(); this.init(); }; // 定義構造函數靜態屬性,掛載所有選擇器的屬性 app.Eles = { pap: $(".paper"), centryY: $(".centry-y") }; // 工具方法 var utils = { has: function(arr, name) { return arr.indexOf(name) > -1; }, // ... }; app.prototype = { constructor: app, initEles: function () { var eles = app.Eles; for (var name in eles) { if (eles.hasOwnProperty(name)) { this[name] = $(eles[name]); } } }, init: function () { this.bindEvent(this.eventMap); }, initDrag: function () { // ... }, uninitDrag: function () { // ... }, bindEvent: function (maps) { this.initDrag(); this.initOrdinaryEvents(maps); }, unbindEvent: function (maps) { this.uninitDrag(); this.unInitOrdinaryEvents(maps); }, initOrdinaryEvents: function (maps) { this._scanEventsMap(maps, true); }, unInitOrdinaryEvents: function (maps) { this._scanEventsMap(maps, false); }, _scanEventsMap: function (maps, isOn) { var delegateEventSplitter = /^(S+)s*(.*)$/, bind = isOn ? this._delegate : this._undelegate; for (var keys in maps) { if (maps.hasOwnProperty(keys)) { var matchs = keys.match(delegateEventSplitter); bind(matchs[1], matchs[2], this[maps[keys]].bind(this)); } } }, _delegate: function (name, selector, func) { // 事件委派,將事件綁定在$(documet)上 doc.on(name, selector, func); }, _undelegate: function (name, selector, func) { doc.off(name, selector, func); }, destroy: function() { this.unbindEvent(); } }; // 將構造函數掛在window上,那么在外部也能訪問閉包內的屬性,相當于對外暴露了一個接口 global.app = app; $(function () { // 實例化構造函數,相當于這個構造函數就開始執行了 new app(); }); })(this, this.jQuery, this._, this.jQuery(document));
上述代碼做的事情:
進行了生命周期的定義,把文件的入口移到了一個構造器里面,通過new這個構造器來進行這個js程序的入口的初始化。
定義eventMap,定義bindEvent、initOrdinaryEvents、_scanEventsMap、_delegate方法。遍歷eventMap,通過事件委派,將事件綁定在$(documet)上。之前的使用的是onclick或者on方法來進行事件的綁定,維護的時候要到各個地方去找,現在只需要關注eventMap就很方便,能夠清楚知道事件、選擇器和事件處理函數名。
給構造器綁定了一個靜態屬性Eles,那就不需要用$(".paper"),而是用this.pap(initEles方法實現的)即可,好處是在壓縮的時候,字符串是不能被壓縮的,而this.pap會被壓縮,壓縮率會更高。
代碼優化的技巧if (e.target.id === "titleDrag" || e.target.id == "subtitleDrag") {} // 改為 if (["titleDrag", "subtitleDrag"].indexOf(e.target.id) > -1) {}
$(".center-box").removeClass("hidden").css({ width: ui.helper.width(), left: parseInt(centerY.css("left")) - Math.floor(ui.helper.width() / 2) }); // 改為 var width = ui.helper.width(); var left = parseInt(centerY.css("left"), 10); this.centerBox.removeClass("hidden").css({ width: width, left: left - Math.floor(width / 2) });
posObj[event.target.id] = { id: event.target.id, outerHTML: this.delStyle(event.target.outerHTML), style: "#" + event.target.id + "{position: absolute;left:" + (ui.offset.left - 260) / mmToPx + "mm;top:" + (ui.offset.top - 40) / mmToPx + "mm;}" }; // 改為 posObj[id] = { id: id, outerHTML: this.delStyle(target.outerHTML), style: "#" + id + "{" + this._getPositionLT(top, left, mmToPx) + "}" };
$(target).next().removeClass("hidden"); $(target).next().find(".line-left").css({ top: 40, left: ui.offset.left, height: pap.css("height"), width: parseInt(pap.css("width")) - ui.offset.left + 260 + "px" }); $(target).next().find(".line-top").css({ left: 260, top: ui.offset.top, width: pap.css("width"), height: parseInt(pap.css("height")) - ui.offset.top + 40 + "px" }); // 改為 this._drawNextLine(nextEle, left, top);
this.centerBox.addClass("hidden"); // 改為 utils.hide(centerBox);
var ul = this.widgetUl; var ht; switch (e.target.id) { case "thin": ht = "
var txt = this.txt; var cus = this.cus; var mmToPx = this.mmToPx; var pap = this.pap; var target = $(e.target); txt.text(target.text()); switch (e.target.id) { case "a4": pap.css({ width: 210 * mmToPx + "px", height: 297 * mmToPx + "px" }); cus.addClass("hidden"); break; case "b5": pap.css({ width: 176 * mmToPx + "px", height: 250 * mmToPx + "px" }); cus.addClass("hidden"); break; case "16k": pap.css({ width: 184 * mmToPx + "px", height: 260 * mmToPx + "px" }); cus.addClass("hidden"); break; case "cus": cus.removeClass("hidden"); break; } // 改為 var txt = this.txt; var cus = this.cus; var id = e.target.id; var target = $(e.target); var setpapCss = { "a4": [210, 297], "b5": [176, 250], "16k": [184, 260] }; txt.text(target.text()); if (setpapCss[id]) { var wh = setpapCss[id]; this.setPapWH(wh[0], wh[1]); utils.hide(cus); } if (id === "cur") { utils.show(cus); }總結
常見的 js 業務場景分析以及解決思路:
選擇器濫用
把所有的選擇器的屬性掛載構造函數靜態屬性上,統一進行管理,并通過initEles方法將其掛載在this上。
事件綁定濫用
使用eventMap進行統一管理。
生命周期混亂,沒概念
app在實例化的時候會往頁面里加這個組件,調用this.destory時會解綁所有事件(還可以補充把html刪掉)。
復用性和沙盒安全
模板渲染技巧
參考artTemplate.js。
解耦你的業務 js 代碼,如何在業務中使用設計模式?
模塊化和繼承到底怎么用。
模塊化就是把文件拆分成小文件。
拆分維度問題和作用域傳遞。
找到utils,拒絕ctrl+c/v。
常見的一些業務優化方法總結:
判斷太多怎么辦。
參照上述優化代碼中的switch...case例子。
dom操作到底怎么做才是最好的,找到最優解。
建議用沒有樣式意義的自定義屬性data-*來作為選擇器,而不是用具有樣式意義的class和id,因為如果有一天css類名變了,那么js也得改變。
樣式該怎么加。
jQuery的css方法,或者對jQuery的css方法的進一步封裝。
增加你的項目可維護性和代碼可讀性:
注釋真的好嗎?
先寫偽代碼,所有的方法不考慮它的實現,把它拆成粒度比較細的顆粒,用方法名來描述業務邏輯,把方法名都寫好了之后,互相調用,最后再添加最細粒度的方法的實現。用這種方法來寫的話,不寫注釋也可以,因為方法名就把業務邏輯解釋了。
格式化的問題。
單詞難拼,句子好讀,代碼50行好讀,300行難讀。
寫代碼要說人話。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/95117.html
摘要:大家好,我叫,江湖人稱吃土小叉,目前擔任公司的前端負責人半年多了,一路上摸爬滾打,歷經團隊人員變動,近日頗有感觸,于是結合自己近半年的前端負責人實踐經驗,權當作一個學習記錄,整理歸納一下小作坊團隊前端負責人的修煉要點大部分只是記錄了關鍵詞, 大家好,我叫XX,江湖人稱吃土小2叉,目前擔任公司的前端負責人半年多了,一路上摸爬滾打,歷經團隊人員變動,近日頗有感觸,于是結合自己近半年的前端負...
摘要:官網地址聊天機器人插件開發實例教程一創建插件在系統技巧使你的更加專業前端掘金一個幫你提升技巧的收藏集。我會簡單基于的簡潔視頻播放器組件前端掘金使用和實現購物車場景前端掘金本文是上篇文章的序章,一直想有機會再次實踐下。 2道面試題:輸入URL按回車&HTTP2 - 掘金通過幾輪面試,我發現真正那種問答的技術面,寫一堆項目真不如去刷技術文章作用大,因此刷了一段時間的博客和掘金,整理下曾經被...
摘要:函數是一等公民。其實閉包本身也是函數式編程的一個應用。劣勢不能算是嚴格意義上的函數式語言,很多函數式編程的特性并沒有。 隨著大前端時代的到來,在產品開發過程中,前端所占業務比重越來越大、交互越來越重。傳統的老夫拿起JQuery就是一把梭應付當下重交互頁面已經十分乏力。于是乎有了Angular,React,Vue這些現代框架。 但隨之而來的還有大量的新知識新名詞,如MVC,MVVM,Fl...
閱讀 3400·2021-11-24 10:30
閱讀 3269·2021-11-22 15:29
閱讀 3706·2021-10-28 09:32
閱讀 1255·2021-09-07 10:22
閱讀 3336·2019-08-30 15:55
閱讀 3619·2019-08-30 15:54
閱讀 3494·2019-08-30 15:54
閱讀 2833·2019-08-30 15:44