角JS $编译服务会导致$看内存泄漏 [英] Angular JS $compile service causes $watch memory leak

查看:121
本文介绍了角JS $编译服务会导致$看内存泄漏的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我猜是由于误用下面这段code的创建一个内存泄漏,我不明白为什么,我录的短截屏来演示这个问题,你可以在这里找到:的 https://www.youtube.com/watch?v=IWCcOI5kN1c&feature=youtu.be

下面是失败code,它的作用是它只是动态编译存储在名为变量的HTML 内容,但只要你开始改变<$通过textarea的C $ C>内容变量中,code开始创建$ watch'es局势变得更加沃瑟只要你增加绑定的数量,每个角结合创造了新的范围(这显然是正确的),但它不删除旧的和它们保持在存储器

 &LT;!DOCTYPE HTML&GT;
&LT; HTML NG-应用=plunker&GT;  &LT; HEAD&GT;
    &LT;间的charset =UTF-8/&GT;
    &LT;标题&GT; AngularJS Plunker&LT; /标题&GT;
    &LT;脚本&GT;的document.write('&LT;基本href =+ document.location +'/&GT;');&LT; / SCRIPT&GT;
    &所述;脚本数据需要=angular.js@1.3.xSRC =的https://$c$c.angularjs.org/1.3.15/angular.js数据semver =1.3.15&GT ;&LT; / SCRIPT&GT;
      &LT;脚本&GT;
          VAR应用= angular.module('plunker',[]);          app.controller('MainCtrl',函数($范围){
              $ scope.name ='世界';
          });          app.directive('编译',函数($编译){
              返回{
                  限制:'A',
                  链接:功能(范围,ELEM,ATTRS){
                      范围。$腕表(attrs.compile,功能(的newval){
                          //创建一个跨度(内嵌元素),所以我们有一个实际的DOM节点
                          //设置的innerHTML。
                          VAR newElem =使用document.createElement('跨');                          newElem.innerHTML =的newval;                          //清除这个元素的内容
                          ELEM [0] .innerHTML ='';                          //与原始(未编译)节点替换它
                          ELEM [0] .appendChild(newElem);                          //现在该节点是DOM,所以我们可以编译
                          //但我们要使用在try..catch因为用户
                          //可能在输入新的前pression的中间,
                          //但是语法现在是无效所以
                          //前pression解析器将抛出一个错误。
                          尝试{
                              //编译DOM中的节点与现有的范围
                              $编译(newElem)(范围);
                          }赶上(E){/ *并不需要做任何事情* /
                          }
                      });
                  }
              };
          });
      &LT; / SCRIPT&GT;
  &LT; /头&GT;  &LT;机身NG控制器=MainCtrlNG-的init =变量= 3;内容='{{变量}}'&GT;
    &LT; D​​IV&GT;
      的$ scope.variable ===值{{变量}}
    &LT; / DIV&GT;
    &LT; D​​IV&GT;
      $ scope.content的价值==={{内容}}
    &LT; / DIV&GT;
    &LT; BR&GT;
    &LT; D​​IV&GT;
    $ scope.content的价值为&lt; B&GT; NG-模型&LT; / B&GT;'通过以下textarea的编:&LT; BR&GT;
    &LT; / DIV&GT;    &LT; textarea的行数=3NG-模式=内容&GT;&LT; / textarea的&GT;    &LT; D​​IV的风格=边界:1px的纯黑色&GT;
      相反,呈现$ scope.content领域这是目前相等的价值{{内容}}我需要渲染编译和评估值应等于{{变量}}
    &LT; / DIV&GT;    &LT;小时&GT;    &LT; P&GT;看!它的工作原理:&LT;跨度编译=内容&GT;&LT; / SPAN&GT;&LT; / P&GT;
  &LT; /身体GT;&LT; / HTML&GT;


解决方案

好吧,我很抱歉,我不知道该内存泄漏是多么糟糕! (这是我的指令LU4使用)

我相信我现在已经摆脱了泄漏,并清理好了很多。我创建了一个新的子范围内的每个我重新编译的时间和第一摧毁了旧的。而按<一个href=\"http://www.bennadel.com/blog/2706-always-trigger-the-destroy-event-before-removing-elements-in-angularjs-directives.htm\"相对=nofollow>这个优秀的技巧我确信破坏范围的的清理出的DOM元素。

更新Plunkr

  app.directive('编译',函数($编译){
  返回{
    限制:'A',
    链接:功能(范围,ELEM,ATTRS){
      VAR prevScope;
      范围。$腕表(attrs.compile,功能(的newval,OLDVAL){
        //创建一个跨度(内嵌元素),所以我们有一个实际的DOM节点
        //设置的innerHTML。
        VAR newElem =使用document.createElement('跨');
        newElem.innerHTML =的newval;
        //清理第一
        如果(prevScope){
          prevScope $ destroy()方法。
          prevScope = NULL;
        }
        //清除这个元素的内容
        elem.empty();
        //与原始(未编译)节点替换它
        ELEM [0] .appendChild(newElem);
        //现在该节点是DOM,所以我们可以编译
        //但我们要使用在try..catch因为用户
        //可能在输入新的前pression的中间,
        //但是语法现在是无效所以
        //前pression解析器将抛出一个错误。
        尝试{
          //编译DOM中的节点与现有范围的子
          prevScope = $范围新的()。
          $编译(newElem)(prevScope);
        }赶上(E){/ *并不需要做任何事情* /}
      });
    }
  }
});

I guess due to misuse the following piece of code creates a memory leak, I can't understand why, I've recorded short screencast to demonstrate the problem, you can find it here: https://www.youtube.com/watch?v=IWCcOI5kN1c&feature=youtu.be

Here is the failing code, what it does is it just dynamically compiles the html stored in variable called content, but as soon as you start changing content variable via the textarea, the code starts creating $watch'es the situations gets even worser as soon as you increase the amount of bindings, for each binding Angular creates new scope (which is obviously right) but it doesn't remove the old ones and they are kept in the memory

<!DOCTYPE html>
<html ng-app="plunker">

  <head>
    <meta charset="utf-8" />
    <title>AngularJS Plunker</title>
    <script>document.write('<base href="' + document.location + '" />');</script>
    <script data-require="angular.js@1.3.x" src="https://code.angularjs.org/1.3.15/angular.js" data-semver="1.3.15"></script>
      <script>
          var app = angular.module('plunker', []);

          app.controller('MainCtrl', function($scope) {
              $scope.name = 'World';
          });

          app.directive('compile', function($compile) {
              return {
                  restrict: 'A',
                  link: function(scope, elem, attrs) {
                      scope.$watch(attrs.compile, function(newVal) {
                          // create a span (an inline element) so we have an actual DOM node to
                          // set the innerHTML of.
                          var newElem = document.createElement('span');

                          newElem.innerHTML = newVal;

                          // clear out the contents of this element
                          elem[0].innerHTML = '';

                          // and replace it with the raw (uncompiled) node
                          elem[0].appendChild(newElem);

                          // now the node is in the DOM so we can compile it
                          // but we want to use a try..catch because the user
                          // might be in the middle of typing a new expression,
                          // but the syntax right now is not valid so the
                          // expression parser will throw an error.
                          try {
                              // compile the node in the DOM with the existing scope
                              $compile(newElem)(scope);
                          } catch(e) { /* don't need to do anything here */
                          }
                      });
                  }
              };
          });
      </script>
  </head>

  <body ng-controller="MainCtrl" ng-init="variable = 3; content = '{{ variable }}'">
    <div>
      The value of $scope.variable === "{{ variable }}"
    </div>
    <div>
      The value of $scope.content === "{{ content }}"
    </div>
    <br>
    <div>
    The value of $scope.content is <b>ng-model</b>'ed via the following textarea:<br>
    </div>

    <textarea rows="3" ng-model="content"></textarea>

    <div style="border: 1px solid black">
      Instead of rendering the value of the $scope.content field which is currently equal to "{{ content }}" I need to render compiled and evaluated value which should be equal to "{{ variable }}"
    </div>

    <hr>

    <p>Look! It works: <span compile="content"></span></p>
  </body>

</html>

解决方案

Okay, I apologise, I had no idea how bad the memory leak was! (It was my directive Lu4 was using)

I believe I have now got rid of the leak and cleaned up a lot better. I created a new child scope each time I compiled anew and destroyed the old one first. And as per this excellent tip I made sure to destroy the scope before clearing out the DOM element.

Updated Plunkr

app.directive('compile', function($compile) {
  return {
    restrict: 'A',
    link: function(scope, elem, attrs) {
      var prevScope;
      scope.$watch(attrs.compile, function(newVal, oldVal) {
        // create a span (an inline element) so we have an actual DOM node to
        // set the innerHTML of.
        var newElem = document.createElement('span');
        newElem.innerHTML = newVal;
        // clean up first
        if (prevScope) {
          prevScope.$destroy();
          prevScope = null;
        }
        // clear out the contents of this element
        elem.empty();
        // and replace it with the raw (uncompiled) node
        elem[0].appendChild(newElem);
        // now the node is in the DOM so we can compile it
        // but we want to use a try..catch because the user
        // might be in the middle of typing a new expression,
        // but the syntax right now is not valid so the
        // expression parser will throw an error.
        try {
          // compile the node in the DOM with a child of the existing scope
          prevScope = scope.$new();
          $compile(newElem)(prevScope);
        } catch (e) { /* don't need to do anything here */ }
      });
    }
  }
});

这篇关于角JS $编译服务会导致$看内存泄漏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆