简体   繁体   English

发布新版本的 SPA 时如何重新加载模板 (html)?

[英]How to reload templates (html) when releasing a new version of a SPA?

I'm currently running into trouble when releasing new versions of my SPA since the html templates are being cached by the browser.我目前在发布新版本的 SPA 时遇到了麻烦,因为浏览器正在缓存 html 模板。

My SPA uses Angular 1.5, ocLazyload and ui-router.我的 SPA 使用 Angular 1.5、ocLazyload 和 ui-router。 I have configured gulp tasks to use cache-busting technique (with gulp-rev ), and it is working pretty well, at least with the script and css files.我已经将 gulp 任务配置为使用缓存破坏技术(使用gulp-rev ),并且它运行得非常好,至少在脚本和 css 文件中是这样。 However, when we have changes on html files the browser keeps loading the old version.但是,当我们对 html 文件进行更改时,浏览器会不断加载旧版本。 Sometimes even refreshing the page with Ctrl+F5 doesn't solve the problem and it keeps showing the old version of html.有时,即使使用 Ctrl+F5 刷新页面也无法解决问题,并且一直显示旧版本的 html。

In addition, configuring the browser to not make cache of html files isn't desirable (specially for mobile), since html changes may occur not very often.此外,将浏览器配置为不缓存 html 文件是不可取的(特别是对于移动设备),因为 html 更改可能不会经常发生。

Is there any good solution that address this problem for fetching html files only when there is new versions of it?有没有什么好的解决方案可以解决这个问题,只有在有新版本的时候才获取 html 文件?

Thanks in advance!提前致谢!

I had this problem in a project that i worked on.我在我参与的一个项目中遇到了这个问题。 I tried these methods.我试过这些方法。

Method 1 : Setting cache property false of different states.方法一:设置不同状态的缓存属性false。

var core = angular.module('app.core');
core.cofigure(configure);

configure.$inject = ['$stateProvider'];

// Configuration Function
function configure($stateProvider){

    $stateProvider
          .state('login', {
            url: '/login/:redirect/:viewId',
            cache: false, // Here i have set the cache property to false
            templateUrl: 'app/layout/login.html' 
          })
          .state('home', {
            url: "/home",
            cache: false, // Here also
            abstract: true,
            templateUrl: 'app/layout/container.html'
          });
}

Now this method was fine, But it was not working at random times and in the Internet Explorer.现在这个方法很好,但它在随机时间和 Internet Explorer 中不起作用。

Method 2 : Adding a Timestamp to the template URL.方法 2:向模板 URL 添加时间戳。

 // I created a timestamp string with the current time.
 var date = new Date().getTime().toString();

 $stateProvider
          .state('login', {
            url: '/login/:redirect/:viewId',
            cache: false, 
            // Now i append it here
            templateUrl: 'app/layout/login.html' +'?' + date 
          })
          .state('home', {
            url: "/home",
            cache: false, 
            abstract: true,
            templateUrl: 'app/layout/container.html' + '?' + date // Here also.
          });

This method is what we decided on after searching for possible fixes to this problem.这种方法是我们在搜索此问题的可能修复方法后决定的。 However no caching takes place in this case.然而,在这种情况下不会发生缓存。 Each GET Request for the template file is different and it will guarantee that the template loaded will be the updated one.模板文件的每个 GET 请求都是不同的,它将保证加载的模板将是更新的模板。 But if you are building a web application that has a lot of templates and client side performance is essential, this method won't be helpful.但是,如果您正在构建具有大量模板且客户端性能至关重要的 Web 应用程序,则此方法将无济于事。

Method 3 : require templates方法 3:需要模板

I recommend this method.我推荐这种方法。 You can use require.js in your application to load the templates when javascript executes.您可以在应用程序中使用require.js来在 javascript 执行时加载模板。 This will load the templates when the running the application for development purpose and also when you minify your code for production.这将在为开发目的运行应用程序时以及在您为生产而缩小代码时加载模板。 Since the templates are loaded into the javascript code, you don't have to keep the html template files when deploying to production as well.由于模板已加载到 javascript 代码中,因此在部署到生产环境时也不必保留 html 模板文件。

$stateProvider
          .state('login', {
            url: '/login/:redirect/:viewId',
            // Note 'template' and not 'templateUrl'
            template: require('app/layout/login.html')
          })
          .state('home', {
            url: "/home",
            abstract: true,
            template: require('app/layout/container.html')
          });

As you are using gulp, you could make use of gulp-angular-templateCache , which is a gulp plugin to collect all your html files, concatenate them into a single javascript file and add them to AngularJS' $templateCache based on the file path.当您使用 gulp 时,您可以使用gulp-angular-templateCache ,它是一个 gulp 插件,用于收集所有 html 文件,将它们连接到一个 javascript 文件中,然后根据文件路径将它们添加到 AngularJS 的$templateCache

gulp.task('default', function () {
    return gulp.src('app/**/*.html')
        .pipe(templateCache({
            module: 'yourModuleName'
        }))
        .pipe(gulp.dest('dist'));
});

The resulting JavaScript file contains something like:生成的 JavaScript 文件包含如下内容:

angular.module('yourModuleName').run([$templateCache,
    function($templateCache) {
        $templateCache.put('app/path/to/some-template.html",
            // some-template.html content comes here (escaped)
        );
    }
]);

You could now add this file to your bundle, any changes to your html file will update the content of your bundle hence result in an updated cache buster.您现在可以将此文件添加到您的包中,对 html 文件的任何更改都将更新您的包的内容,从而导致更新的缓存破坏者。

But if you do not want the entire bundle to be updated, you could keep them seperated and ensure the file get's loaded after your bundle.但是如果你不想更新整个包,你可以将它们分开并确保文件在你的包之后被加载。

Note: both of the above approaches do break the cache for all html files once one of them has changed, but all of those html files are retrieved using a single call.注意:一旦其中一个发生更改,上述两种方法都会破坏所有 html 文件的缓存,但是所有这些 html 文件都是使用单个调用检索的。

An alternative approach could be to add cache busters to your html files and update all the usages of that file to include the cache buster's hash.另一种方法是将缓存破坏者添加到您的 html 文件并更新该文件的所有用法以包括缓存破坏者的哈希。 But I'm not really a fan of this approach.但我并不是很喜欢这种方法。

Edit: The first approach is the one I use in my gulp-based projects.编辑:第一种方法是我在基于 gulp 的项目中使用的方法。 However, these days I'm using AngularJS with Webpack (See my starter ) and I'm using an approach comparable to Midhun Darvin's 3th method (see here ), but instead of requirejs I'm making use of webpack .但是,最近我将 AngularJS 与 Webpack 一起使用(请参阅我的入门),并且我使用的方法与 Midhun Darvin 的第 3 种方法(请参阅此处)相当,但我使用的不是requirejs ,而是webpack However, keep in mind that this approach (using either requirejs or webpack ) will result in the html files being added to the javascript file containing your angularjs code.但是,请记住,这种方法(使用requirejswebpack )会导致将 html 文件添加到包含 angularjs 代码的 javascript 文件中。 This means any change to a javascript file will brake the cache for all your html files as the cache buster will be updated.这意味着对 javascript 文件的任何更改都将停止所有 html 文件的缓存,因为缓存破坏者将被更新。

I recommend an approach similar to option 2 from @Midhun Darvin (so I used their code as a base).我推荐一种类似于@Midhun Darvin 的选项 2 的方法(所以我使用他们的代码作为基础)。 Instead of using a date, use a version number.不要使用日期,而是使用版本号。 With each release you just need to remember to update the version number and all html files will have their cache busted.每次发布时,您只需要记住更新版本号,所有 html 文件的缓存都会被破坏。

 // update this with every release
 var version = 0.1.0;
 // make make this easier so you don't have to add query strings to all your template calls
 var v = "?v=" + version;

 $stateProvider
     .state('login', {
         url: '/login/:redirect/:viewId',
         cache: false, 
         templateUrl: 'app/layout/login.html' + v // version added to bust cache
     })
     .state('home', {
         url: "/home",
         cache: false, 
         abstract: true,
         templateUrl: 'app/layout/container.html' + v // version added to bust cache
     });

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM