序
隨著web專案越來越復雜,排除掉外部引入的代碼,開發的代碼動輒上千過萬行,甚至更多,按照最初的開發經驗,我們把代碼全部放在一個檔案上,除了代碼多以外,還有命名容易被覆寫,沖突等問題, 從管理上我們會選擇把它分開若干個js,分成各種部分,每個js管理某些部分或功能,不同檔案中的代碼可能由不同的多個人開發,如果把變數都放在全域上,可能會造成變數被篡改,函式名沖突等問題,
通常我們把每個分出來的部分,叫做模塊,模塊的撰寫每個團隊都有自己的規范,為了更好的管理,進而引申出模塊化開發,即把整個專案分成若干個模塊,分發給不同的人,每個模塊處理某些功能,最后通過協定把它們湊合在一起,最終形成了一個專案,
需求
然而web的模塊化并不那么容易,如果單純的使用js來處理邏輯,我們可以利用靈活的js特性,通過閉包或者面對物件方式解耦來處理,如果涉及到了html和css,我們需要考慮更多問題,
-
html和css作為字串的方式引入到js中,可能會導致除錯的不便;
-
把html單獨抽出來作為一個片段,需要部署到服務器中,檔案直接打開無法獲取;
-
將html放入頁面中,注冊事件以及參考其它物件,一不小心就會造成記憶體泄漏,記憶體的合理使用在webapp中是非常重要的;
-
css和html檔案一樣,是通過片段方式的引入,還是通過全域引入,如果以片段方式的引入,在切換頁面的時候,移除樣式,如果樣式中包含全域樣式,可能會照成頁面抖動或布局重繪,
整體思想
通過以上討論,以下列的方式進行統一規定
-
純js的模塊,如果有必要的會必須要有dispose方法,用來作為洗掉不需要的記憶體參考,然后在index.html引入該檔案,或用的時候異步引入,
簡單案例代碼1
var m1 = { list: [], doMethod: function () { for (var i = 0; i < 1000; i++) this.list.push(i); }, // list除了可以通過dispose()來洗掉,還可以直接通過m1.list.length = 0來輸出 dispose: function () { this.list.length = 0; } };簡單案例代碼2
var m2 = (function () { var list = []; var doMethod = function () { for (var i = 0; i < 1000; i++) list.push(i); } return { doMethod: doMethod, // list內的內容只能m2.dispose()來洗掉,不然永遠滯留在記憶體在, 從而保護私有變數 dispose: function () { list.length = 0; } } })(); -
如果帶有html的片段,首先以單獨html片段方式引入,由于ajax獲取html片段是需要短暫的時間,在特殊情況下可以酌情以字串的形式嵌入,對于dom的使用和事件的參考,要在模塊銷毀之前進行分離參考,
簡單案例代碼
var m3 = (function () { var domList = {}, eventList = {}, parentDom = null; // 加載html片段到dom中 var addDom = function (dom, bk) { ajax.fetch("temp.html", function (text) { parentDom = dom; dom.innerHTML = text; bk(); }) } // 系結事件,快取dom var attachDomEvent = function (key, type, eventFn) { domList[key] = domList[key] || parentDom.querySelector(key); if (eventList[key]) eventList[key].push(eventFn); else eventList[key] = [eventFn]; domList[key].addEventListener(type, eventFn); } // 清除參考,待系統回收記憶體 var dispose = function () { // 清除事件 for (var key in domList) { if (eventList[key]) { for (var i = 0; i < eventList[key].length; i++) domList.removeEventListener(domList[key], eventList[key][i]) } } // 移除dom parentDom.innerHTML = ""; // 移除參考 domList = null; eventList = null; } // 暴露外部使用 return { addDom: addDom, attachDomEvent: attachDomEvent, dispose: dispose } })(); -
如果是css的樣式,那就以全域的方式在最開始的時候在index.html引入多個css檔案,利用css的特性進行模塊篩選,
在index.html中引入 part1.css, part2.css,如果開發的人很多,可以引入更多, 通過后代選擇器進行樣式書寫,
part1.css簡單案例
.part1 container { margin: 0 ; }part2.css簡單案例
.part2 container { margin: 20px; }
框架中的使用
-
純js的模塊引入及定義
簡單案例說明,假設在/public/services/mk.js
// 引入模塊依賴,避免回圈參考和自參考 // strTool, otherTool就是其它定義的模塊,在內部就可以使用 App.require(["strTool", "otherTool"], function (strTool, otherTool) { // 其它定義和事務處理 // 定義模塊, 第一個引數為模塊名,第二個為模塊的物件 App.define("mk1", {}); }) -
帶有html的模塊使用,主要用于Page物件,Component物件
簡單代碼案例
// 通過參考外部模塊,如果不參考直接傳入一個回呼函式 App.require(["mapTool"], function (mapTool) { var app = App.getCurrent(); // 可以存盤私有物件和私有處理方案代碼 // 定義page1的Page物件 app.definePage("page1", { render: function (next) { this.fetch("./page1.html", function (text) { // 將html模板傳入,可以用來渲染頁面 next(text); }) }, // 快取dom和系結事件, getDomObj: function () { this.attachDom("button", "confirmBtn") .attachEvent("confirmBtn", this.confirmHandler, false); }, // 引入資源初始化,init方法在html放在頁面,并且快取dom和系結事件后觸發 init: function () { // 初始化 mapTool.init(); }, // 事件回呼 confirmHandler: function (ev) { // 原生事件ev, this.domList快取dom console.log(this.domList.confirmBtn === ev.target) }, dispose: function () { // 外部資源銷毀 mapTool.dispose(); } }); })頁面中的事件會通過切換頁面內部進行解綁,dom快取會在恰當時機進行洗掉,
-
css的引入,與傳統的方案一致
對于1,2兩點,可能會有點困惑,傳統的方式也能很好的作業,為什么還要進行一次封裝呢,主要由以下原因:
- 隨著業務發展,模塊可以是上百個,按照傳統方式也會由上百個全域變數,因此對于復雜的參考會產生管理困難;
- 為了按需參考,將每個模塊分成通過名稱參考,此時需要某個模塊的時候,可以判斷是否存在,如果不存在,去加載js檔案,并且注冊,從而提高了資源的利用率
通過以上方案,只要對模塊進行統一的注冊,內部的一個物件進行管理,使得最終易于管理和使用,
簡單案例
為了更好的理解將上一章節的內容代碼拷貝下來,并作以下修改
-
查看index.js, home.js, static.js中的代碼改為引入模塊的方式, 如下:
(function () {})();修改為
App.require(function () {}); -
在services檔案夾中添加兩個檔案,這里注意,要求services類的模塊的檔案名和定義的模塊名稱必須是一致,如果不一致需要手動設定模塊配置項,
添加/public/services/strTool.js:注意模塊名和檔案名要一致
App.require(function () { App.define("strTool", { scriptName: "https://webapi.amap.com/maps/modules?v=1.4.15&key=82c79f1397d8cf32f8e2c7f97f814a3c&vrs=1599633849065" }); });添加/public/services/mapTool.js:
App.require(["strTool"], function (strTool) { var map = null; var obj = { initMap: function (dom, lng, lat) { map = new AMap.Map(dom, { center: [lng, lat] }); }, dispose: function () { map.destroy(); map = null; } }; if (typeof AMap === "undefined") { var script = document.createElement("script"); script.src = https://www.cnblogs.com/stringWeb/p/strTool.scriptName; script.onload = function () { App.define("mapTool", obj); } script.onerror = function () { App.define("mapTool", { init: function () { alert("js引入失敗"); } }) } document.head.appendChild(script); } else { App.define("mapTool", obj); } }) -
修改second.js代碼
App.require(["mapTool"], function (mapTool) { var app = App.getCurrent(); app.definePage("second", { render: function (next) { this.fetch("./second.html", next); }, getDomObj: function () { this.attachDom("button", "btn") .attachDom("div", "div") .attachEvent("btn", "click", this.clickHandler, false); }, init: function () { app.staticPage.header = "次頁"; app.staticPage.footer = "從首頁跳轉到次頁"; // 初始化地圖 mapTool.initMap(this.domList.div, 120, 27.7); }, clickHandler: function (ev) { history.back(); }, dispose: function () { // 銷毀地圖 mapTool.dispose(); } }) }); -
查看效果(主要針對移動端,可以通過手機端查看或者用瀏覽器模擬移動端)將代碼放在可以訪問的服務器或本地服務上,啟動服務,通過瀏覽器訪問,如下地址
結語
主要探索了如何管理復雜專案的構建方法,然后介紹了模塊化方法,引出了框架層如何實作按需引入模塊從而提高頁面加載速度,分模塊開發可以讓開發更專注,效率更高,
推廣
底層框架開源地址:https://gitee.com/string-for-100w/string
演示網站: https://www.renxuan.tech/
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/172607.html
標籤:JavaScript
上一篇:1.框架概覽
下一篇:4.App物件的介紹和影片管理
