AngularJS 在开发机器上禁用部分缓存

Posted

技术标签:

【中文标题】AngularJS 在开发机器上禁用部分缓存【英文标题】:AngularJS disable partial caching on dev machine 【发布时间】:2013-01-21 01:07:00 【问题描述】:

我在 AngularJS 中缓存部分有问题。

在我的 html 页面中:

<body>
 <div ng-view></div>
<body>

我的部分被加载的地方。

当我更改部分 HTML 代码时,浏览器仍会加载旧数据。

有什么解决办法吗?

【问题讨论】:

请注意:我遇到了一个问题,这与我的 Flask 应用程序发回的缓存控制标头有关。我通过将app.config.update(SEND_FILE_MAX_AGE_DEFAULT=0) 添加到我的flask_app.py 来解决这个问题。 (我想其他网络服务器也存在类似的情况)。 如果您使用的是 chrome,只需执行 Ctrl+Shift+R(即硬重新加载),无论使用什么缓存机制,chrome 都会忽略它并重新获取所有脚本、样式表等。跨度> ctrl+shift+R 在 Chrome 中对我不起作用,但在开发人员工具的“网络”选项卡上,单击“禁用缓存”效果很好。对我来说,这是一个客户端问题,不应该像下面的许多建议那样使用服务器上的黑客来解决;它应该在存在“问题”的客户端上修复。如果您在服务器上修复它,而忘记取消修复它,生产可能会受到不利影响。 ctrl+shift+R 绕过正常请求的缓存。此快捷方式不处理从角度为ng-include|ng-view|templateUrl 发出的ajax 请求 不能要求所有终端用户在访问网站时都按Ctrl+Shift+R,那么对于非开发案例,这个问题的答案是什么? “对我来说,这是一个客户端问题,不应该像下面的许多建议那样使用服务器上的黑客来解决” - 我不同意,您无法在 Web 环境中控制客户端,因此修复生产必须是应用程序驱动的。出于这个原因,我接受了: $rootScope.$on('$viewContentLoaded', function() $templateCache.removeAll(); ); 【参考方案1】:

对于开发,您还可以停用浏览器缓存 - 在 Chrome Dev Tools 的右下角单击齿轮并勾选选项

禁用缓存(在 DevTools 打开时)

更新:在 Firefox 中,调试器 -> 设置 -> 高级部分(检查版本 33)中有相同的选项

更新 2:虽然此选项出现在 Firefox 中,但一些报告称它不起作用。我建议使用 firebug 并遵循 hadaytullah 的回答。

【讨论】:

这应该是公认的答案,因为它不需要更改代码,并且更符合 OP 的要求。当然,您会希望生产应用程序缓存请求,因此按照上述人的建议进行操作,虽然意思很好,但如果将代码留在生产应用程序中,可能会出现问题。 对 Firefox 等其他浏览器有什么建议吗? 在 Firefox: Debugger > Settings(齿轮)有相同的选项。 这在 Firefox 中不起作用。即使禁用缓存并且工具箱打开,模板仍然会被缓存。 缓存也会影响生产吗?如果我将新的 Web 文件推送到服务器,是什么阻止了来自生产客户端的后续请求加载预发布的缓存版本?【参考方案2】:

以@Valentyn 的回答为基础,这是一种在 ng-view 内容更改时始终自动清除缓存的方法:

myApp.run(function($rootScope, $templateCache) 
   $rootScope.$on('$viewContentLoaded', function() 
      $templateCache.removeAll();
   );
);

【讨论】:

@user252690 可能还需要确保 html 模板未与缓存标头一起发送。有关可能的修复,请参阅 here 和 here 一点警告:清空 $templateCache 实际上可能会产生意想不到的后果。例如,UI Bootstrap 在初始化期间将默认部分直接添加到 $templateCache 中,然后希望它们在那里。 @Strille 我试图使用 angular-ui-bootstrap 模态。弹出窗口不可见。因为 $templateCache.removeAll();有什么办法解决吗? @Mukun:除了不使用 removeAll() 而是使用 remove() 删除您需要清除的键之外,没有简单的解决方法。您需要进行某种簿记才能知道要删除哪些键。 有什么方法可以只为特定的 ui 视图缓存清除缓存。【参考方案3】:

正如其他答案中提到的,here 和 here,可以使用以下方法清除缓存:

$templateCache.removeAll();

但是,正如 gatoatigrado 在 comment 中所建议的,这似乎仅在提供没有任何缓存标头的 html 模板时才有效。

所以这对我有用:

角度:

app.run(['$templateCache', function ( $templateCache ) 
    $templateCache.removeAll(); ]);

您可能会以多种方式添加缓存标头,但这里有几个适合我的解决方案。

如果使用IIS,请将其添加到您的 web.config:

<location path="scripts/app/views">
  <system.webServer>
    <staticContent>
      <clientCache cacheControlMode="DisableCache" />
    </staticContent>
  </system.webServer>
</location>

如果使用 nginx,您可以将其添加到您的配置中:

location ^~ /scripts/app/views/ 
    expires -1;   

编辑

我刚刚意识到问题提到了dev machine,但希望这仍然可以帮助某人......

【讨论】:

是的,尽管这不能直接回答原始问题,但这确实帮助我解决了实时网站上的缓存问题。【参考方案4】:

如果您谈论的是用于缓存模板而不重新加载整个页面的缓存,那么您可以通过以下方式清空它:

.controller('mainCtrl', function($scope, $templateCache) 
  $scope.clearCache = function()  
    $templateCache.removeAll();
  
);

在标记中:

&lt;button ng-click='clearCache()'&gt;Clear cache&lt;/button&gt;

然后按此按钮清除缓存。

【讨论】:

【参考方案5】:

使用 Firebug (22.0.6) 的 Firefox (33.1.1) 解决方案

    工具 > Web 工具 > Firebug > 打开 Firebug。 在 Firebug 视图中,转到“Net”视图。 “网络”(视图标题)旁边将出现一个下拉菜单符号。 从下拉菜单中选择“禁用浏览器缓存”。

【讨论】:

在 Mac 上尝试了 Firebug (2.0.11) 和 Firefox (38.0.1),但没有成功。【参考方案6】:

这个 sn-p 帮助我摆脱了模板缓存

app.run(function($rootScope, $templateCache) 
    $rootScope.$on('$routeChangeStart', function(event, next, current) 
        if (typeof(current) !== 'undefined')
            $templateCache.remove(current.templateUrl);
        
    );
);

以下sn-p的详细信息可以在这个链接上找到: http://oncodesign.io/2014/02/19/safely-prevent-template-caching-in-angularjs/

【讨论】:

挑剔但 current 什么时候不是一个对象?不确定它是否也不存在,但if (!current) return; 这会破坏 Angular 对基于路由的模板的缓存,但不会破坏 ng-include 的部分。【参考方案7】:

我发布此内容只是为了涵盖所有可能性,因为其他解决方案都不适合我(由于 angular-bootstrap 模板依赖性等原因,它们会引发错误)。

在开发/调试特定模板时,您可以通过在路径中包含时间戳来确保它始终刷新,如下所示:

       $modal.open(
          // TODO: Only while dev/debug. Remove later.
          templateUrl: 'core/admin/organizations/modal-selector/modal-selector.html?nd=' + Date.now(),
          controller : function ($scope, $modalInstance) 
            $scope.ok = function () 
              $modalInstance.close();
            ;
          
        );

注意templateUrl 变量中的最后一个?nd=' + Date.now()

【讨论】:

稍后您可以设置.value('DEBUG', true) 来启用或不启用该行。 我的解决方案是在 .run(function($rootScope) $rootScope.DEBUG = true; ... 下的主模块的初始化阶段使用以下内容,然后在指令中注入 $rootScope,如 .directive('filter', ['$rootScope', function($rootScope)... 并在返回的对象属性中:@ 987654327@。也许您可以详细说明您的 .value('DEBUG', true) 方法?点赞! .value('DEBUG', true 的使用与 $rootScope 的使用相同,但不会造成混乱 :) 您可以稍后将 DEBUG 注入控制器并作为普通服务进行查询。跨度> 如果不是太复杂,您能否在答案中扩展源代码以包含 .value(...) 东西?我想背后的概念是我不知道的角度最佳实践。 这个解决方案在使用 Ionic 时非常有用。这将节省我很多时间,因为它使 livereload 再次有用。非常感谢!【参考方案8】:

正如其他人所说,完全出于开发目的而取消缓存可以很容易地完成,而无需更改代码:使用浏览器设置或插件。在 dev 之外,要击败基于路由的模板的 Angular 模板缓存,请在 $routeChangeStart(或 $stateChangeStart,对于 UI 路由器)期间从缓存中删除模板 URL,如 Shayan 所示。但是,这不会影响 ng-include 加载的模板的缓存,因为这些模板不是通过路由器加载的。

我希望能够在生产中修补任何模板,包括那些由 ng-include 加载的模板,并让用户在浏览器中快速接收修补程序,而无需重新加载整个页面。我也不担心破坏模板的 HTTP 缓存。解决方案是拦截应用程序发出的每个 HTTP 请求,忽略那些不适用于我的应用程序的 .html 模板的请求,然后将参数添加到每分钟更改的模板 URL 中。请注意,路径检查特定于应用程序模板的路径。要获得不同的间隔,请更改参数的数学运算,或完全删除 % 以获取不缓存。

// this defeats Angular's $templateCache on a 1-minute interval
// as a side-effect it also defeats HTTP (browser) caching
angular.module('myApp').config(function($httpProvider, ...) 
    $httpProvider.interceptors.push(function() 
        return 
            'request': function(config) 
                config.url = getTimeVersionedUrl(config.url);
                return config;
            
        ;
    );

    function getTimeVersionedUrl(url) 
        // only do for html templates of this app
        // NOTE: the path to test for is app dependent!
        if (!url || url.indexOf('a/app/') < 0 || url.indexOf('.html') < 0) return url;
        // create a URL param that changes every minute
        // and add it intelligently to the template's previous url
        var param = 'v=' + ~~(Date.now() / 60000) % 10000; // 4 unique digits every minute
        if (url.indexOf('?') > 0) 
            if (url.indexOf('v=') > 0) return url.replace(/v=[0-9](4)/, param);
            return url + '&' + param;
        
        return url + '?' + param;
    

【讨论】:

我对您的解决方案很感兴趣 - 您是永久保留此代码 - 还是在您进行更改后保留一段时间?原因——会关心性能。此外,您提到副作用会破坏 HTTP。你的意思是这是一个很好的副作用方式正确 - 意思是你打算为了有'修补程序'?Thx 我确实保留了这段代码,尽管我们将缓存清除的长度回拨到 10 分钟。因此,每使用 10 分钟,用户就会重新加载新的 html 模板。对于我的商业应用来说,获得热补丁模板的能力是可以接受的成本,但显然它会对某些应用程序的性能造成太大影响。 ...不幸的是,HTTP 缓存也被击败了,但我没有看到一种方法可以智能地击败 Angular 模板缓存而不击败 etag 等。 IMO Angular 模板缓存配置不够。 +1 我有同样的想法,但我只拦截本地主机。你可以在这里看到我的拦截器实现:overengineer.net/… @joshcomley 如果您只需要在 localhost 上禁用缓存,为什么不使用禁用所有缓存的浏览器插件? @bradw2k 这样我就可以完全控制缓存和不缓存的内容,这对开发很有用。我还在所有浏览器中进行了测试,但并非所有浏览器都有可以满足我需求的扩展。我还可以在开发中的站点上设置一个指示器,告诉我缓存是否被禁用,因为有时我只想禁用和跨浏览器测试一段时间。【参考方案9】:

如果您使用 UI 路由器,那么您可以使用装饰器并更新 $templateFactory 服务并将查询字符串参数附加到 templateUrl,浏览器将始终从服务器加载新模板。

function configureTemplateFactory($provide) 
    // Set a suffix outside the decorator function 
    var cacheBust = Date.now().toString();

    function templateFactoryDecorator($delegate) 
        var fromUrl = angular.bind($delegate, $delegate.fromUrl);
        $delegate.fromUrl = function (url, params) 
            if (url !== null && angular.isDefined(url) && angular.isString(url)) 
                url += (url.indexOf("?") === -1 ? "?" : "&");
                url += "v=" + cacheBust;
            

            return fromUrl(url, params);
        ;

        return $delegate;
    

    $provide.decorator('$templateFactory', ['$delegate', templateFactoryDecorator]);


app.config(['$provide', configureTemplateFactory]);

我相信你可以通过修饰 $routeProvider 中的“when”方法来达到同样的效果。

【讨论】:

更好的选择是使用 gulp-angular-templatecache 之类的插件在 $templateCache 中注册 Angular js 模板。这应该与 gulp-rev 一起使用。每次模板更改时,都会创建一个具有不同修订号的新 javascript 文件,并且缓存永远不会成为问题。【参考方案10】:

我发现 HTTP 拦截器方法工作得很好,并且允许额外的灵活性和控制。此外,您可以使用发布哈希作为 buster 变量来缓存每个生产版本。

这是使用 Date 的 dev cachebusting 方法的样子。

app.factory('cachebustInjector', function(conf)    
    var cachebustInjector = 
        request: function(config)     
            // new timestamp will be appended to each new partial .html request to prevent caching in a dev environment               
            var buster = new Date().getTime();

            if (config.url.indexOf('static/angular_templates') > -1) 
                config.url += ['?v=', buster].join('');
            
            return config;
        
    ;
    return cachebustInjector;
);

app.config(['$httpProvider', function($httpProvider) 
    $httpProvider.interceptors.push('cachebustInjector');
]);

【讨论】:

【参考方案11】:

这是 Chrome 中的另一个选项。

点击 F12 打开开发者工具。然后资源 > 缓存存储 > 刷新缓存

我喜欢这个选项,因为我不必像其他答案那样禁用缓存。

【讨论】:

在 2016 年 11 月的 Chrome v. 54.0.2840.99 中,我在一个名为 Application.而不是 Resources 的选项卡上找到了这个。【参考方案12】:

没有解决方案来阻止浏览器/代理缓存,因为您无法控制它。

另一种向用户强制提供新鲜内容的方法是重命名 HTML 文件!就像 https://www.npmjs.com/package/grunt-filerev 对资产所做的那样。

【讨论】:

以上是关于AngularJS 在开发机器上禁用部分缓存的主要内容,如果未能解决你的问题,请参考以下文章

你会用AngularJS,但你会写AngularJS文档么?

在 MVC 3 中禁用局部视图上的缓存

黄聪:AngularJS最理想开发工具WebStorm

在iis上部署 AngularJS

AngularJs:为啥我们总是禁用表单提交按钮?

AngularJS 依赖注入