webpack + vue 項目 自定義 插件 解決 前端 JS 版本 更新 問題

       Webpack 是一個前端資源加載/打包工具。它將根據模塊的依賴關係進行靜態分析,而後將這些模塊按照指定的規則生成對應的靜態資源。javascript

它的異步加載原理是,事先將編譯好後的靜態文件,經過js對象映射,硬編碼進打包後的 manifest.xxxx.js 文件中,而後經過JSONP原理按需加載每一個chunk。php

每一個子模塊加載完畢以後,瀏覽器將會進行本地緩存,從而節省了網絡帶寬。css

  Webpack編譯後的目錄結構以下:html

從結構目錄來看,整個項目的入口就是index.html,咱們來看看index.html的內容:前端

<!DOCTYPE html>
<html>
<head>
    <meta charset=utf-8>
    <title>www.phpdragon.com</title>
    <script></script>
    <link href=/static/css/app.4a4f13cfbe19aa22b83c451c96f49338.css rel=stylesheet>
</head>
<body>
<div id=app>
    <transition :name=transitionName>
        <router-view></router-view>
    </transition>
</div>
<script type=text/javascript src=/static/js/manifest.f726e03ee32bcdee633a.js></script>
<script type=text/javascript src=/static/js/vendor.fa5ac3dc9fb740bb48c5.js></script>
<script type=text/javascript src=/static/js/app.af822fb16a16ef70005b.js></script>
</body>
</html>

其中引入了3個js文件,和一個css樣式文件。vue

咱們看看manifest.f726e03ee32bcdee633a.js這個js加載器。java

找到這段代碼:webpack

是否是發現很熟悉!這一段jsonp代碼,這就是用來實現按需加載js資源文件的加載器。ios

講到了這,從上述代碼就引出了一個問題:git

當前端腳本從新編譯了之後,項目發佈採用的是刪除重置發佈,因爲靜態文件只加載一次的緣故,會致使按需加載模塊時報錯:Loading chunk " + e + " failed. 

緣由是瀏覽器已經緩存了manifest.f726e03ee32bcdee633a.js這個加載器,而後訪問新的導航欄的時候,服務器端已經不存在舊版本的JS靜態資源文件了,從而致使系統異常。

 

解決方法:

方案一:每次都加載項目入口文件 index.html。也就是說服務器端設置index.html文件不緩存、或者經過URL後綴添加隨機字符串來解決。

這個方案適合入口URL可變動的系統。但對於ERP這種內部系統來講,可行性不是很好。

因爲入口只加載一次,致使點擊系統的其餘導航欄URL操做,沒法作到再次加載index.html文件,刷新瀏覽器緩存的JS。

第一種方案能夠放棄了!

 

方案二:給每一個導航欄路由URL添加隨機數。 

這個也不行,並不會致使index.html被從新加載。具體緣由,請詳看源碼解析:vue-router源碼分析-總體流程

 

方案三: 系統內部經過ajax請求獲取版本信息從而提示更新。

這個可行,但對後端系統有侵入性,須要後端同窗配合。對與跨工種跨部門來講,這種方式屬於下策。

 

方案四:

方案二提到了路由,那麼vue的路由是否提供了鉤子機制,從而進行攔截呢?

官方是提供的,詳看官方文檔:vue導航鉤子

 

經過以下這段代碼,咱們就能實現咱們想要的功能了。1、確保了檢測的頻率。同時也對系統內部的AJAX請求減小侵入性代碼。

router.beforeEach((to, from, next) => {
  // ...
})

 

1.最終解決以下:在 main.js 中添加以下代碼:

router.beforeEach((to, from, next) => {
        axios.get('../static/version.json?_=' + Math.random()).then(response => {
            if (200 == response.status) {
                if (process.env.VERSION !== response.data.version) {

                    var message = "系統版本有更新,點擊確認加載最新,或按【CTRL + F5】!"
                    Vue.prototype.$alert(message, '系統提示', {
                      confirmButtonText: '肯定',
                      callback: function(){
                        window.location.reload(true);
                      }
                    });

                    return;
                }
                next();
            }
        }).catch(err => {
            console.error(err);
            next();
        });
    });

 

 

2.添加版本變量:

 

3.給編譯環境添加env變量:

 

4.經過Webpack的編譯插件機制,引入 diy-plugin.js 自定義插件腳本,生成版本信息:

'use strict';

var FStream = require('fs');
var Archiver = require('archiver'); //npm install archiver

/**
 * 版本信息生成插件
 * @author phpdragon@qq.com
 * @param options
 * @constructor
 */
function DiyPlugin(options) {
    this.options = options || {};
    this.options.outZipFile = this.options.path + '/front.zip';

    !this.options.versionDirectory && (this.options.versionDirectory = 'static');
}

//apply方法是必需要有的,由於當咱們使用一個插件時(new somePlugins({})),webpack會去尋找插件的apply方法並執行
DiyPlugin.prototype.apply = function (compiler) {
    var self = this;

    compiler.plugin("compile", function (params) {
        var dir_path = this.options.context + '/' + self.options.versionDirectory;
        var version_file = dir_path + '/version.json';
        var content = '{"version":' + self.options.env.VERSION + '}';

        FStream.exists(dir_path, function (exist) {
            if (exist) {
                writeVersion(self, version_file, content);
                return;
            }

            FStream.mkdir(dir_path, function (err) {
                if (err) throw err;
                console.log('\n建立目錄[' + dir_path + ']成功');

                writeVersion(self, version_file, content);
            });
        });
    });

    //編譯器'對'全部任務已經完成'這個事件的監聽
    compiler.plugin("done", function (stats) {
        console.log("開始打包壓縮編譯文件...");

        var output = FStream.createWriteStream(self.options.outZipFile);
        var archiveZip = Archiver('zip', {zlib: {level: 9}});

        archiveZip.on('error', function (err) {
            throw err;
        });

        archiveZip.pipe(output);
        archiveZip.directory(self.options.path + '/' + self.options.versionDirectory, self.options.versionDirectory);
        archiveZip.file(self.options.path + '/index.html', {name: 'index.html'});
        //archive.glob(self.options.path + '/*.*');
        archiveZip.finalize();
    });
};

const writeVersion = (self, versionFile, content) => {
    console.log("\n當前版本號:" + self.options.env.VERSION);
    console.log("開始寫入版本信息...");

    //寫入文件
    FStream.writeFile(versionFile, content, function (err) {
        if (err) throw err;
        console.log("版本信息寫入成功!");
    });

    //刪除以前的壓縮包
    FStream.exists(self.options.outZipFile, function (exists) {
        if (exists) {
            FStream.unlinkSync(self.options.outZipFile);
        }
    });
}

module.exports = DiyPlugin;

 

 

5.在webpack配置文件中添加 diy-plugin.js 編譯鉤子:

 

 

 

 6. ok,至此結束。 執行編譯:

npm run build

 

 

 7. 訪問項目,再次編譯版本,打開以前的項目界面,點擊其餘導航菜單,效果以下:

 

 

 

以上,平常的一些開發點滴。

 

 

 

 

 

 

PS:

Webpack插件開發

vue-router源碼分析-總體流程

Vue.js——vue-router 60分鐘快速入門

相關文章
相關標籤/搜索