zoukankan      html  css  js  c++  java
  • webpack + vue 项目 自定义 插件 解决 前端 JS 版本 更新 问题

           Webpack 是一个前端资源加载/打包工具。它将根据模块的依赖关系进行静态分析,然后将这些模块按照指定的规则生成对应的静态资源。

    它的异步加载原理是,事先将编译好后的静态文件,通过js对象映射,硬编码进打包后的 manifest.xxxx.js 文件中,然后通过JSONP原理按需加载每个chunk。

    每个子模块加载完毕之后,浏览器将会进行本地缓存,从而节省了网络带宽。

      Webpack编译后的目录结构如下:

    从结构目录来看,整个项目的入口就是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样式文件。

    我们看看manifest.f726e03ee32bcdee633a.js这个js加载器。

    找到这段代码:

    是不是发现很熟悉!这一段jsonp代码,这就是用来实现按需加载js资源文件的加载器。

    讲到了这,从上述代码就引出了一个问题:

    当前端脚本重新编译了以后,项目发布采用的是删除重置发布,由于静态文件只加载一次的缘故,会导致按需加载模块时报错: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导航钩子

    通过如下这段代码,我们就能实现我们想要的功能了。一、确保了检测的频率。同时也对系统内部的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('
    创建目录[' + 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("
    当前版本号:" + 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分钟快速入门

  • 相关阅读:
    SqlServer数据库正在还原的解决办法
    v-表单自动收集信息
    v-按键修饰符
    v-for列表排序
    class与style的绑定
    v计算属性的应用
    v子向父组件传值
    v传值和传引用
    v-父组件向子组件传值
    练习鼠标悬停切换图片页面
  • 原文地址:https://www.cnblogs.com/phpdragon/p/7300736.html
Copyright © 2011-2022 走看看