如何防止ng-repeat引起的net :: ERR_INSUFICIENT_RESOURCES错误?

时间:2016-05-10 04:44:38

标签: javascript angularjs electron

是的,我遇到了性能问题,ng-repeat太快了!

我正在使用角度和电子,我在img内加载ng-repeat标签(缩略图~10kb)。如果我有超过600个,我从webkit中得到net::ERR_INSUFFICIENT_RESOURCES错误:

<div ng-repeat="photo in photoImport.data">
  <img ng-src="{{photo.thumbnail}}" alt="">
</div>

如何将ng-repeat降低到(或以其他方式阻止)由于请求过多导致的网络错误?

此外,请注意预期的行为是列表中的所有照片尽可能快地加载 - 用户应该能够尽快跳转到列表中的最后一个缩略图。

2 个答案:

答案 0 :(得分:0)

一个答案是使用limitTo过滤器和间隔:

<div ng-repeat="photo in photoImport.data | limitTo : ngRepeatLimit>

控制器中包含以下内容:

  function ngRepeatAllSlowly(photoImport) {
    let incr = 400,
        cycleCount = Math.ceil(photoImport.length / incr),
        stop;

      // Set initial limit
      $scope.ngRepeatLimit = 20;

      // Then reguarly increase it.
      stop = $interval(incrementLimit, incr, cycleCount, true).then(function(){
         $interval.cancel(stop);
      });

      // Increase ngRepeatLimit
      function incrementLimit() {
        $scope.ngRepeatLimit += incr;
      }
  }

  ngRepeatAllSlowly($scope.photoImport)

答案 1 :(得分:0)

我建议使用延迟加载方法,类似下面的代码。因此,只有在用户滚动到图像时才会加载图像:

   <!doctype html>
<html ng-app="Demo" ng-controller="AppController">
<head>
	<meta charset="utf-8" />

	<title>
		Lazy Loading Images With AngularJS
	</title>

	<style type="text/css">

		/* Add "click" styles because newer releases of AngularJS don't seem to add the HREF value. */
		a[ ng-click ] {
			cursor: pointer ;
			text-decoration: underline ;
		}

		a.box {
			background-color: #FAFAFA ;
			border: 1px solid #CCCCCC ;
			display: block ;
			height: 200px ;
			line-height: 200px ;
			text-align: center ;
			width: 684px ;
		}

		ul.photos {
			list-style-type: none ;
			margin: 16px 0px 16px 0px ;
			padding: 0px 0px 0px 0px ;
			width: 700px ;
		}

		ul.photos:after {
			content: "" ;
			clear: both ;
			display: block ;
			height: 0px ;
		}

		li.photo {
			background-color: #FAFAFA ;
			border: 1px solid #CCCCCC ;
			border-radius: 4px 4px 4px 4px ;
			float: left ;
			margin: 0px 10px 10px 0px ;
			padding: 5px 5px 5px 5px ;
		}

		li.photo img {
			border: 1px solid #EEEEEE ;
			border-radius: 3px 3px 3px 3px ;
			display: block ;
		}

		img[ bn-lazy-src ] {
			background-image: url( "./checkered.png" ) ;
		}

	</style>
</head>
<body>

	<h1>
		Lazy Loading Images With AngularJS
	</h1>

	<p>
		You have {{ photos.size }} photos in your set.

		<a ng-click="rebuildSet()">Rebuild set</a>.
		<a ng-click="changeSource()">Change src</a>.
		<a ng-click="clearPhotos()">Clear</a>.
	</p>

	<a ng-show="isBoxVisible" ng-click="hideBox()" class="box">
		This is a big thing that may change,
		causing the DOCUMENT HEIGHT to change.
	</a>

	<ul class="photos">

		<li ng-repeat="photo in photos" class="photo">

			<img
				bn-lazy-src="{{ photo.src }}"
				width="150"
				height="150"
				alt="Christina Cox"
				/>

		</li>

	</ul>



	<!-- Load scripts. -->
	<script
		type="text/javascript"
		src="../../vendor/jquery/jquery-2.0.3.min.js">
	</script>
	<script
		type="text/javascript"
		src="../../vendor/angularjs/angular-1.0.7.min.js">
	</script>
	<script type="text/javascript">


		// Create an application module for our demo.
		var app = angular.module( "Demo", [] );


		// -------------------------------------------------- //
		// -------------------------------------------------- //


		// I control the root of the application.
		app.controller(
			"AppController",
			function( $scope ) {

				// I flag the visibility of the big box.
				$scope.isBoxVisible = true;

				// Build up a large set of images, all with unique
				// SRC values so that the browser cannot cache them.
				$scope.photos = buildPhotoSet( 200 );


				// ---
				// PUBLIC METHODS.
				// ---


				// I change the SRC values of the existing photo set
				// in order to examine how changes to source will
				// affect rendered / non-rendered images.
				$scope.changeSource = function() {

					var now = ( new Date() ).getTime();

					// Update all SRC attribute to point to "1.jpg".
					for ( var i = 0 ; i < $scope.photos.length ; i++ ) {

						var photo = $scope.photos[ i ];

						photo.src = photo.src.replace( /\d\./i, "1." );

					}

				};


				// I clear the current photo set.
				$scope.clearPhotos = function() {

					$scope.photos = [];

				};


				// I hide the big box, allowing the document to change
				// its dimensions (and possibly show more images than
				// were visible beforehand).
				$scope.hideBox = function() {

					$scope.isBoxVisible = false;

				};


				// I rebuild the entire photo set.
				$scope.rebuildSet = function() {

					$scope.photos = buildPhotoSet( 20 );

				};


				// ---
				// PRIVATE METHODS.
				// ---


				// I return a photo set of the given size. Each photo
				// will have a unique SRC value.
				function buildPhotoSet( size ) {

					var photos = [];
					var now = ( new Date() ).getTime();

					for ( var i = 0 ; i < size ; i++ ) {

						var index = ( ( i % 3 ) + 1 );
						var version = ( now + i );

						photos.push({
							id: ( i + 1 ),
							src: ( "christina-cox-" + index + ".jpg?v=" + version )
						});

					}

					return( photos );

				}

			}
		);


		// -------------------------------------------------- //
		// -------------------------------------------------- //


		// I lazily load the images, when they come into view.
		app.directive(
			"bnLazySrc",
			function( $window, $document ) {


				// I manage all the images that are currently being
				// monitored on the page for lazy loading.
				var lazyLoader = (function() {

					// I maintain a list of images that lazy-loading
					// and have yet to be rendered.
					var images = [];

					// I define the render timer for the lazy loading
					// images to that the DOM-querying (for offsets)
					// is chunked in groups.
					var renderTimer = null;
					var renderDelay = 100;

					// I cache the window element as a jQuery reference.
					var win = $( $window );

					// I cache the document document height so that
					// we can respond to changes in the height due to
					// dynamic content.
					var doc = $document;
					var documentHeight = doc.height();
					var documentTimer = null;
					var documentDelay = 2000;

					// I determine if the window dimension events
					// (ie. resize, scroll) are currenlty being
					// monitored for changes.
					var isWatchingWindow = false;


					// ---
					// PUBLIC METHODS.
					// ---


					// I start monitoring the given image for visibility
					// and then render it when necessary.
					function addImage( image ) {

						images.push( image );

						if ( ! renderTimer ) {

							startRenderTimer();

						}

						if ( ! isWatchingWindow ) {

							startWatchingWindow();

						}

					}


					// I remove the given image from the render queue.
					function removeImage( image ) {

						// Remove the given image from the render queue.
						for ( var i = 0 ; i < images.length ; i++ ) {

							if ( images[ i ] === image ) {

								images.splice( i, 1 );
								break;

							}

						}

						// If removing the given image has cleared the
						// render queue, then we can stop monitoring
						// the window and the image queue.
						if ( ! images.length ) {

							clearRenderTimer();

							stopWatchingWindow();

						}

					}


					// ---
					// PRIVATE METHODS.
					// ---


					// I check the document height to see if it's changed.
					function checkDocumentHeight() {

						// If the render time is currently active, then
						// don't bother getting the document height -
						// it won't actually do anything.
						if ( renderTimer ) {

							return;

						}

						var currentDocumentHeight = doc.height();

						// If the height has not changed, then ignore -
						// no more images could have come into view.
						if ( currentDocumentHeight === documentHeight ) {

							return;

						}

						// Cache the new document height.
						documentHeight = currentDocumentHeight;

						startRenderTimer();

					}


					// I check the lazy-load images that have yet to
					// be rendered.
					function checkImages() {

						// Log here so we can see how often this
						// gets called during page activity.
						console.log( "Checking for visible images..." );

						var visible = [];
						var hidden = [];

						// Determine the window dimensions.
						var windowHeight = win.height();
						var scrollTop = win.scrollTop();

						// Calculate the viewport offsets.
						var topFoldOffset = scrollTop;
						var bottomFoldOffset = ( topFoldOffset + windowHeight );

						// Query the DOM for layout and seperate the
						// images into two different categories: those
						// that are now in the viewport and those that
						// still remain hidden.
						for ( var i = 0 ; i < images.length ; i++ ) {

							var image = images[ i ];

							if ( image.isVisible( topFoldOffset, bottomFoldOffset ) ) {

								visible.push( image );

							} else {

								hidden.push( image );

							}

						}

						// Update the DOM with new image source values.
						for ( var i = 0 ; i < visible.length ; i++ ) {

							visible[ i ].render();

						}

						// Keep the still-hidden images as the new
						// image queue to be monitored.
						images = hidden;

						// Clear the render timer so that it can be set
						// again in response to window changes.
						clearRenderTimer();

						// If we've rendered all the images, then stop
						// monitoring the window for changes.
						if ( ! images.length ) {

							stopWatchingWindow();

						}

					}


					// I clear the render timer so that we can easily
					// check to see if the timer is running.
					function clearRenderTimer() {

						clearTimeout( renderTimer );

						renderTimer = null;

					}


					// I start the render time, allowing more images to
					// be added to the images queue before the render
					// action is executed.
					function startRenderTimer() {

						renderTimer = setTimeout( checkImages, renderDelay );

					}


					// I start watching the window for changes in dimension.
					function startWatchingWindow() {

						isWatchingWindow = true;

						// Listen for window changes.
						win.on( "resize.bnLazySrc", windowChanged );
						win.on( "scroll.bnLazySrc", windowChanged );

						// Set up a timer to watch for document-height changes.
						documentTimer = setInterval( checkDocumentHeight, documentDelay );

					}


					// I stop watching the window for changes in dimension.
					function stopWatchingWindow() {

						isWatchingWindow = false;

						// Stop watching for window changes.
						win.off( "resize.bnLazySrc" );
						win.off( "scroll.bnLazySrc" );

						// Stop watching for document changes.
						clearInterval( documentTimer );

					}


					// I start the render time if the window changes.
					function windowChanged() {

						if ( ! renderTimer ) {

							startRenderTimer();

						}

					}


					// Return the public API.
					return({
						addImage: addImage,
						removeImage: removeImage
					});

				})();


				// ------------------------------------------ //
				// ------------------------------------------ //


				// I represent a single lazy-load image.
				function LazyImage( element ) {

					// I am the interpolated LAZY SRC attribute of
					// the image as reported by AngularJS.
					var source = null;

					// I determine if the image has already been
					// rendered (ie, that it has been exposed to the
					// viewport and the source had been loaded).
					var isRendered = false;

					// I am the cached height of the element. We are
					// going to assume that the image doesn't change
					// height over time.
					var height = null;


					// ---
					// PUBLIC METHODS.
					// ---


					// I determine if the element is above the given
					// fold of the page.
					function isVisible( topFoldOffset, bottomFoldOffset ) {

						// If the element is not visible because it
						// is hidden, don't bother testing it.
						if ( ! element.is( ":visible" ) ) {

							return( false );

						}

						// If the height has not yet been calculated,
						// the cache it for the duration of the page.
						if ( height === null ) {

							height = element.height();

						}

						// Update the dimensions of the element.
						var top = element.offset().top;
						var bottom = ( top + height );

						// Return true if the element is:
						// 1. The top offset is in view.
						// 2. The bottom offset is in view.
						// 3. The element is overlapping the viewport.
						return(
								(
									( top <= bottomFoldOffset ) &&
									( top >= topFoldOffset )
								)
							||
								(
									( bottom <= bottomFoldOffset ) &&
									( bottom >= topFoldOffset )
								)
							||
								(
									( top <= topFoldOffset ) &&
									( bottom >= bottomFoldOffset )
								)
						);

					}


					// I move the cached source into the live source.
					function render() {

						isRendered = true;

						renderSource();

					}


					// I set the interpolated source value reported
					// by the directive / AngularJS.
					function setSource( newSource ) {

						source = newSource;

						if ( isRendered ) {

							renderSource();

						}

					}


					// ---
					// PRIVATE METHODS.
					// ---


					// I load the lazy source value into the actual
					// source value of the image element.
					function renderSource() {

						element[ 0 ].src = source;

					}


					// Return the public API.
					return({
						isVisible: isVisible,
						render: render,
						setSource: setSource
					});

				}


				// ------------------------------------------ //
				// ------------------------------------------ //


				// I bind the UI events to the scope.
				function link( $scope, element, attributes ) {

					var lazyImage = new LazyImage( element );

					// Start watching the image for changes in its
					// visibility.
					lazyLoader.addImage( lazyImage );


					// Since the lazy-src will likely need some sort
					// of string interpolation, we don't want to
					attributes.$observe(
						"bnLazySrc",
						function( newSource ) {

							lazyImage.setSource( newSource );

						}
					);


					// When the scope is destroyed, we need to remove
					// the image from the render queue.
					$scope.$on(
						"$destroy",
						function() {

							lazyLoader.removeImage( lazyImage );

						}
					);

				}


				// Return the directive configuration.
				return({
					link: link,
					restrict: "A"
				});

			}
		);

	</script>

</body>
</html>

参考:GithubUserContent