国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

如何在前端項目中實現熱更新

antz / 1394人閱讀

摘要:如果你的項目中使用了的話,你會很幸運,借助插件可以實現項目的熱更新。對模板更新的處理目前項目中使用的是的模板引擎。


這個是組內一位同學在平時開發中,發現調試不便,為團隊開發的熱更新工具。很厲害,文章中的技術實現內容也是我了解了他的具體實現思路后,整理出來的。

工具源碼EHU(esl-hot-update)

熱更新是什么

熱更新就是當你在開發環境修改代碼后,不用刷新整個頁面即可看到修改后的效果。

如果你的項目中使用了webpack的話,你會很幸運,借助webpack-dev-server插件可以實現項目的熱更新。

解決的問題

對于大型的系統級別項目會有下面幾個特點

模塊化(AMD)模式的廣泛使用后,開發環境散文件特別多,很容易上百,一不小心還能上千

初始化的內容特別多,各種底層庫,ui庫等等

這兩個特點直接導致每次調試后,刷新會很慢。如果初始化的js達到上千的數量級,每一次重新刷新都是5s,10s,甚至20s的等待。

而熱更新的目的就是為了在一定程度上減少這5s,10s,甚20s的浪費。

遇到的問題

我們使用的是百度自己的開發環境工具edp,首先他不支持熱更新

我們使用的AMD實踐也是百度自己的esl,而且即使是requirejs也暫時沒有找到對應的熱更新策略,假如requirejs有對應的,我們也無法直接使用

所以最終的結論是我們自己去實現一個基于我們自己業務的。這樣我們考慮的面不用太廣,并且解決方案的更有針對性,即面向我們現有的業務框架。最重要的是可以嘗試修改底層框架做配合。

等待路踩通了,我們再去考慮普適性。

解決的思路

從ehu/package.json 這個文件中,我們就可以看出一些具體的思路

需要一個watch功能,即能夠監聽到文件的修改

socket.io通知瀏覽器處理文件的改變

修改esl這個文件,達到能夠實時更新的效果

當時最簡單的考慮,就是文件改變了后,能夠通知瀏覽器,瀏覽器去重新load這個文件并且執行一次。這個時候再重新去打開這個模塊或者功能后,會發現新load的代碼在執行后會覆蓋上一次的。

所以當時的我的第一直覺是,esl重復require時,如果后面一次會覆蓋前面的,那么可以通過簡單的覆蓋思路去嘗試,結果發現覆蓋不了。經過驗證,發現是esl內部維護了一個map,即require過的模塊會存起來。我們如果希望更新這個模塊,只能將map中的對應模塊名刪除。(后面會詳細講述esl的改造)

對于工具的要求

對應這個工具,我當時也提出了幾個要求

esl必然是需要修改的,但是如何對開發人員透明?首先是不能讓大家都做這種修改。

頁面中也必須加入socket.io支持,那么我們如何在不影響其他人員開發的情況下加入?

我們做的屬于beta版本,如何選擇性的使用?ehu工具和以前的開發模式隨意切換?

安裝方便,能否只是作為一個工具,即插即用,不需要繁瑣的配置?

如何使用

npm install -g ehu(mac下需要sudo,windows下需要管理員權限)

在原來執行edp webserver start命令的路徑 執行 ehu(不再需要執行 edp webserver start)

原來端口號8848修改為8844(原8848依舊可以使用,但不支持熱更新)

首先使用的方式很簡單,為此特意將工具打包到npm上,以后就算有升級,僅僅需要大家update即可。

另外從使用角度,也盡量集成化(一句命令行即可),避免為了這個工具的使用而做太多額外的事情。

依賴的框架
"dependencies": {
    "async": "^1.5.0",
    "commander": "^2.9.0",
    "express": "^4.13.3",
    "express-http-proxy": "^0.6.0",
    "lodash": "^3.10.1",
    "socket.io": "^1.3.7",
    "watch": "^0.16.0"
  }

幾個必要的
watch——監聽文件變化
socket.io——和瀏覽器的實時通訊
express——搭建一個服務
express-http-proxy——代理
commander——便于自己寫node命令

工具類:
asynclodash

框架的思想

先看看昨天對于這個工具提出的幾個要求

esl必然是需要修改的,但是如何對開發人員透明?首先是不能讓大家都做這種修改。

頁面中也必須加入socket.io支持,那么我們如何在不影響其他人員開發的情況下加入?

我們做的屬于beta版本,如何選擇性的使用?ehu工具和以前的開發模式隨意切換?

安裝方便,能否只是作為一個工具,即插即用,不需要繁瑣的配置?

對于1和2,我們其實是需要修改/添加一些代碼的,但是代碼都不希望提交到項目的開發環境,因為這些代碼生成環境完全不需要。

所以我們的解決方案是:攔截,改寫(偷梁換柱)

舉個例子,當我們需要對esl做一些改造時,我們處理方式是當路由指向esl.js時,我們換成另外一個esl-ehu.js(esl-ehu.js是對esl.js改造后的)返回去,這樣就對開發環境的代碼透明了。

socket.io的支持也是同理,我們可以在返回html時,改寫html的代碼,加入對于socket.io的引入。

上面的思路其實來源于之前項目構建打包。

對于3,我們希望在使用工具時,任然能很快切換到以前模式,這樣做兼容的目的是希望工具更有競爭力,能吸引大家使用。

我們的解決方案是:內部實現一個子線程,端口號依然是以前的,而且訪問這個端口,就繞過了這個工具。

對于子線程child_process,我們還遇到一個問題,就是子線程跑系統的時候,經常掛掉,今天剛剛找到一個解決方案,后面會單開一個文章講這個坑。

對于4,其實就是使用npm方式

技術細節

第一步:搭建一個新服務作為底層,去托管住我們現在edp服務,新服務上有一個路由配置,對于我們需要處理的,攔截。對于不用處理的直接代理給edp

代碼參考

var mid = express();
mid.all("*", httpProxy(config.defaultServer, {
    // 先走特殊規則,否則就代理到默認web server
    filter: function(req, res) {
        return !ruleRoute(req, res);
    },
    forwardPath: function(req, res) {
        return URL.parse(req.url).path;
    }
}));
// 由express-http-proxy托管路由
app.use("/", mid);

ruleRoute就是一些攔截處理

在此之前,啟動下子進程

var child = require("child_process");
var cli = child.exec(defaultServerCLI);
cli.stdout.on("data", function (log) {
    !isServerStarted && (cb(null, log));
    isServerStarted && console.log(log);
});

此處有坑,后面單開文章描述

第二步: 因為上面攔截后的返回的文件已經支持socket.io,esl等底層已經修改了,所以下面是需要去監聽文件通知瀏覽器做對應處理。

// 啟動socket.io服務
io = require("socket.io")(server);
io.on("connection", function (socket) {
    socket.emit("hello");
});
// 監視文件改動
initWatch();

第三步: 做一些集成工作

program
    .version("0.0.6")
    .usage("[options]")
    .option("-p, --port ", "Set the port", setPort)
    .option("-n, --noServerCLI", "...", noServerCLI)
    .parse(process.argv);

集成到node命令中

第四步: 默認配置

module.exports = {
    // 默認的服務器
    defaultServer: "http://127.0.0.1:8848",
    // 默認的服務器啟動命令
    defaultServerCLI: "edp webserver start",
    // 從服務器根目錄到需要監控的文件夾中間path
    baseDir: "nirvana-workspace",
    // hot update 需要watch的文件夾(不包括baseDir)
    watchDirs: "src",
    // 入口文件(不包括baseDir)
    indexHTML: "main.html",
    // ehu啟動端口號(不可與默認的服務器端口號沖突)
    port: 8844
};

源碼中有很多邏輯是處理配置的

瀏覽器依賴

socket.io——瀏覽器端僅僅依賴socket這個去和服務端通信

通信邏輯

// 建立連接
socket.on("hello", function () {
    log(getLogMsgPrefix(), "HotUpdate已啟動!");
});
// 檢測到文件改動
socket.on("hotUpdate", function (file) {
    // log(getLogMsgPrefix(), "檢測到文件改動", file);
    // ....處理文件修改后對應熱更新邏輯
});
對css/less更新的處理

這個原理比較簡單,頁面監聽到樣式的修改,重新加載一次樣式即可,簡單的覆蓋。

但是存在一個潛在問題,因為樣式是簡單的覆蓋,所以,如果修改是刪除了樣式,是無法生效的。

舉例:
修改前:

display: none;
overflow: hidden;
position: relative;
background: #FFFFFF;
border: 1px solid #E8E8E8;
margin-top: 20px;

修改后:

display: none;
overflow: hidden;
position: relative;
background: #FFFFFF;

刪除的bordermargin-top其實是沒有生效的

這個也是后期需要解決的一個問題。

對模板更新的處理

目前項目中使用的是tpl的模板引擎。

現在就遇到一個問題,在熱更新時,模板引擎其實是重復加載模板的,那么就涉及到重復加載是否后面的會覆蓋前面問題。

查看加載模板的源碼后,發現根據配置有三個選擇,覆蓋忽略報錯, 我們業務中使用的配置是遇到重復后會報錯處理,所以我們需要在不修改業務默認屬性的情況下,添加一些邏輯。

// [esl-hot-update] 重新加載需要覆蓋
window.EHU_HOT_UPDATE_OPTIONS
&& window.EHU_HOT_UPDATE_OPTIONS.etpl.isOverride
&& (namingConflict = "override");
switch (namingConflict) {
    /* jshint ignore:start */
    case "override":
        engine.targets[name] = target;
        context.targets.push(name);
    case "ignore":
        break;
    /* jshint ignore:end */
    default:
        throw new Error("Target exists: " + name);
}

window.EHU_HOT_UPDATE_OPTIONS.etpl.isOverride這個是修改后自己實現的控制配置修改的邏輯。

然后這個文件加入到服務端的路由中,請求時替換。

對js更新的處理

這里邏輯比較復雜,因為需要修改底層的AMD模塊加載的邏輯。

js沒有模板那么簡單,不是直接覆蓋,因為在AMD模式中,每一個文件,都是被上一個文件調用執行的結果。

所以我們處理的邏輯是不僅需要重新加載修改的文件,并且遞歸所有直接或者間接調用他的文件,全部重新加載。

所以從上面的特點可以看出,這個工具目前階段主要適用于業務模塊的開發,因為業務的依賴不會特別深,對于dep中的核心文件修改,就不是很合適,一旦文件比較底層,熱跟新是重新加載的模塊也會非常多。

另外也有很多其他的坑,還在不斷優化中。

總結

這次實踐其實就是業務中遇到的問題(系統太龐大,調試太麻煩),如何解決問題,如何把解決的思路變成一個解決方案,分享給團隊。

因為自己解決了,和形成一個解決方案還是有非常大的差別的,例如我們在形成方案的過程中,就嘗試了很多新東西,踩了很多坑。

目前還有個坑就是chrome瀏覽器,調試的Source資源時,如果一個資源重復加載,內存中會更新,但是對應的資源沒有更新,導致斷點時,映射不對(斷點失效),目前暫時的解決方案是,每次請求時添加時間戳,讓Source映射的資源強制更新。這個可以正常斷點,但是斷點沒有記憶功能(坑啊,因為文件變了)。

微信公眾號

博客地址

http://tangguangyao.github.io/

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/78318.html

相關文章

  • 使用node子進程spawn,exec踩過的坑

    摘要:最后發現使用子進程打開還真的就是使用到一定程度就掛掉。上面的簡單流程就是啟動一個子進程。邏輯就是,記錄子進程的大小,一旦超過就掉子進程。我們在使用時,不知道設置,默認的是當我們子進程日志達到時,自動掉了。 showImg(https://segmentfault.com/img/bVrCnh); 如何在項目中實現熱更新中提到的一個坑child_process的exec使用問題,下面文章...

    cppprimer 評論0 收藏0
  • 2018年8月所遇知識點整理

    摘要:注本文章是在工作過程中所接觸的知識點的整理,涉及的東西比價雜亂,如有錯誤之處,歡迎糾錯與指導一,頁面的錨鏈接定義錨點錨點鏈接。類似于我們閱讀書籍時的目錄頁碼或章回提示。 *注:本文章是在工作過程中所接觸的知識點的整理,涉及的東西比價雜亂,如有錯誤之處,歡迎糾錯與指導 一, 頁面的錨鏈接 1,定義:錨點,錨點鏈接。常常用于那些內容龐大繁瑣的網頁,通過點擊命名錨點,不僅讓我們能指向文檔,還...

    silenceboy 評論0 收藏0
  • 2018年8月所遇知識點整理

    摘要:注本文章是在工作過程中所接觸的知識點的整理,涉及的東西比價雜亂,如有錯誤之處,歡迎糾錯與指導一,頁面的錨鏈接定義錨點錨點鏈接。類似于我們閱讀書籍時的目錄頁碼或章回提示。 *注:本文章是在工作過程中所接觸的知識點的整理,涉及的東西比價雜亂,如有錯誤之處,歡迎糾錯與指導 一, 頁面的錨鏈接 1,定義:錨點,錨點鏈接。常常用于那些內容龐大繁瑣的網頁,通過點擊命名錨點,不僅讓我們能指向文檔,還...

    guqiu 評論0 收藏0
  • React+Koa+MongoDB+Docker開發環境

    摘要:已經發布到,只要在環境下安裝即可。下面通過來構建開發環境,提高開發體驗。容器容器的實質是進程,但與直接在宿主執行的進程不同,容器進程運行于屬于自己的獨立的命名空間。部署開發環境部署開發環境其實很簡單,只需要配置和即可。 前言 本次博文依然是對 multi-spa-webpack-cli 的擴充和完善。 集成 mongoose。 集成 Docker 開發環境。 multi-spa-w...

    sarva 評論0 收藏0
  • WEEX系列 WEEX入門

    摘要:通過使用有限的類標簽閹割的及基于語法來快速構建原生應用。高性能本身對加載時間和資源占用進行了優化。站在巨人的肩膀上,我們也很容易開發出高性能的。我們可以把部署到服務器上實現熱更新。引擎運行這些實現與線程通信,達到和原生應用相同的體驗效果。 和一步一起從前端視角聊一聊WEEX WEEX是一套構建高性能、可擴展的原生應用跨平臺解決方案。就一個字吊。 通過使用有限的類HTML標簽、閹割的CS...

    nicercode 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<