مطالب
Performance در AngularJS قدم اول
به احتمال زیاد برنامه نویسانی که از AngularJS در پروژه‌های خود استفاده می‌کنند، در برخی موارد کند شدن در Rendering و Binding صفحات را تجربه کرده اند. این مقاله مطالب خیلی ساده و راحتی در خصوص استفاده درست و بهینه از Binding می‌باشد.

قدم اول و مهم بحث on time binding هست:
در برخی موارد ما اطلاعاتی که فقط یکبار Bind می‌شوند و در طول اجرا هیچ تغییری نمی‌کنند را درست Bind نمی‌کنیم. برای مثال فرض کنید می‌خواهیم عنوان صفحه را در یک تگ h1 نمایش دهیم. به صورت معمول همه‌ی ما از روش زیر استفاده می‌کنیم.
<h1>{{title}}</h1>

اما این روش درست نیست! چرا؟

AngularJS برای Rendering طرف View از Watcher‌ها استفاده می‌کند که در هر لحظه Binding‌ها را رصد می‌کنند و در صورت تغییر قسمت مورد نظر از View، دوباره Render می‌شود. بحث اصلی خود Watcher‌ها هستند که حتی Binding‌هایی که هیچ وقت مقادیر آنها تغییر نمی‌کنند نیز رصد می‌شود و این باعث کندی عمل Watching در AngularJS می‌شود. در AngularJS نسخه‌ی 1.3 به بعد امکانی فراهم شده‌است که شما بتوانید Binding‌هایی را که یک بار بیشتر تغییر نمی‌کنند، مشخص کنید. به کد زیر دقت کنید:
<h1>{{::title}}</h1>
بله دقیقا به همین راحتی! شما با اضافه کردن :: در ابتدای هر Binding مشخص می‌کنید که این قسمت از View فقط یک بار Render شود. این عمل باعث می‌شود Watcher AngularJS کار کمتری انجام دهد.

به زودی مقاله‌های بیشتری در خصوص Performance در AngularJS خواهم نوشت. امیدوارم لذت برده باشید.

مطالب
Performance در AngularJS قدم سوم
خیلی خوشحالم که تا این مرحله، این مقاله‌ها را دنبال می‌کنید. در مقالات قبل مسائل ساده و مهمی در بحث Performance مطرح شد. در این مقاله می‌خواهم قدم سوم در بهبود Performance را توضیح دهم که رعایت کردن این مسائل می‌تواند کمک زیادی در بهبود عملکرد برنامه‌های مبتنی بر AngularJS داشته باشد.

scope؟
همه‌ی برنامه نویسان و توسعه دهندگان، یکی از اولین مفاهیمی را که در AngularJS یاد می‌گیرند، scope هست. اما scope چیست؟ به صورت خیلی ساده می‌توان گفت scope مشخص کننده‌ی حوزه متغیر‌ها و توابعی هست که قرار است در View تاثیر داشته باشند. کد زیر را مشاهده کنید:
<div>نام و نام خانوادگی: {{name}}</div>
<div>معدل: {{avg()}}<div>
و کد سمت controller
scope.nums=[19,20,17,16,15,18,19];
scope.name='بهنام محمدی';
scope.avg= function(){
  return scope.nums.reduce(function(previousValue, currentValue) {
      return previousValue + currentValue;
   })/scope.nums.length;
}
و اما خروجی نهایی
نام و نام خانوادگی: بهنام محمدی
معدل:17.71
خوب چون ما در قسمت controller به صورت scope.name و scope.avg عمل کرده‌ایم، می‌توانیم در View به صورت name و avg از این متغیر‌ها استفاده کنیم. در نتیجه اگر ما در controller، به جای scope.name بنویسیم name و یا به جای scope.avg بنویسیم avg به مشکل بر می‌خوریم؛ چون قسمت View ما متغیر‌های داخل scope را در View دخیل می‌کند و متغیر‌های داخل scope توسط Watcher‌ها رصد می‌شود.

خوب سؤال، همه چیز که عالی هست پس مشکل در کجاست؟
مشکل در متغیر scope.nums هست! به کد زیر توجه کنید:
var nums=[19,20,17,16,15,18,19];
scope.name='بهنام محمدی';
scope.avg= function(){
  return nums.reduce(function(previousValue, currentValue) {
      return previousValue + currentValue;
   })/nums.length;
}
فکر کنم متوجه تفاوت این کد با کد بالایی شده‌اید. اما کدام کد درست است؟ یا بهتر بگویم کدام کد بر روی Performance تاثیر مناسبی دارد؟ کد پایینی Performance بالایی دارد. دلیل این موضوع این است وقتی ما از nums در View هیچ استفاده‌ای نمی‌کنیم، بهتر است به صورت var nums تعریف شود. در کد بالایی که این متغیر به صورت scope.nums تعریف شده بود، با اینکه در View استفاده نشده بود، ولی توسط Watcher AngularJS در هر لحظه رصد می‌شود و این کار باعث کندی و کاهش عملکرد AngularJS خواهد شد. بنابراین در کل متغیرهایی را که در View استفاده نمی‌کنید، به صورت var test استفاده نمایید تا Watcher AngularJS این متغیر‌ها را رصد نکند.
امیدوارم از این مقاله لذت برده باشید. منتظر مقاله بعدی من باشید.

مطالب
Performance در AngularJS قدم دوم
در مقاله‌ی قبل روش درست استفاده کردن از Binding را برای بهبود Performance، توضیح دادم. در این مقاله می‌خواهم در مورد ng-if و فرق آن با ng-show صحبت کنم و اینکه کدامیک Performance بهتری را برای AngularJS فراهم می‌کنند.

سول اول، کار ng-show چیست؟
ng-show یکی از پر کاربردترین Directiveهای AngularJS است که وظیفه‌ی Show و Hide قسمتی از Vew را به عهده دارد. به کد زیر توجه کنید:
<div ng-show="has">
     <div ng-repeat="item in items">
          <span>{{item.title}}</span>
     </div>
</div>
در این کد ما از ng-show استفاده کرده‌ایم. اگر has مقدار true داشته باشد div نمایش داده می‌شود و اگر false باشد، div نمایش داده نمی‌شود. در داخل div، لیستی وجود دارد که توسط ng-repeat تکرار شده و یک لیست ساده را درست می‌کند.

سول دوم، کار ng-if چیست؟  
کد بالا را دوباره تکرار می‌کنیم. ولی با این تفاوت که اینبار بجای ng-show از ng-if استفاده خواهیم کرد:
<div ng-if="has">
     <div ng-repeat="item in items">
          <span>{{item.title}}</span>
     </div>
</div>
خوب؟ آیا عملکرد این دو کد با هم تفاوت دارد؟ جواب سؤال در ظاهر خیر هست. یعنی مانند کد بالایی، اگر has مقدار true داشته باشد، div نمایش داده می‌شود؛ در غیر اینصورت، خیر.

سوال سوم، پس اگر عملکرد یکسانی دارند، تفاوت آن‌ها در چیست؟
تفاوت این دو Directive در Performance هست. اجازه دهید بیشتر توضیح دهم. ng-show اگر مقدار false دریافت کند، tag مورد نظر را نمایش نمی‌دهد؛ ولی تگ‌های داخلی آن توسط AngularJS پردازش می‌شوند. یعنی چه ng-show مقدار true بگیرید و یا false، لیست داخل tag، توسط AngularJS پردازش و Render می‌شود. ولی در ظاهر ما در View چیزی را نمی‌بینیم. ng-if بر حسب مقادیری که دریافت می‌کند، می‌تواند به بالا رفتن Performance AngularJS کمک کند. فرض کنید ng-if مقدار false گرفته است؛ یعنی has مقدار false دارد. علاوه بر اینکه tag div نمایش داده نمی‌شود، بلکه داخل tag نیز پردازش نمی‌شود. یعنی لیستی که ما در کد نوشته‌ایم، به هیچ عنوان توسط AngularJS پردازش نخواهد شد که باعث می‌شود Watcher، کار کمتری انجام دهد. پس در نتیجه بهبودی را در کارآیی Rendering و Binding خواهیم داشت.
مطالب
Performance در AngularJS 1.x قدم ششم
موضوع این مقاله استفاده مستقیم از توابع و عملیات محاسباتی برای Binding در View می‌باشد که در پروژه‌های بزرگ که حجم المنت‌ها در صفحه زیاد است عملکردی منفی در Performance دارد که قابل چشم پوشی نیست. برای اینکه این مورد ملموس باشد بنده مثالی را آماده کرده‌ام که هدف آن بیشتر درک درست شما از این موضوع است.
کد زیر را مشاهده کنید:
<input type="text" ng-model="newItemTitle">
<button type="button" ng-click="add()">افزودن</button>
<ul>
     <li ng-repeat="item in items">{{item.title}}</li>
</ul>
<div ng-show="showMSG()">شما بیش از ۱۰ استان ثبت کرده اید</div>
و قسمت Controller:
$scope.newItemTitle='';           

$scope.add=function(){
     $scope.items.push({
          title:$scope.newItemTitle
     });
}

$scope.items=[
     {title:'اردبیل'},
     {title:'تهران'},
     {title:'اصفهان'},
     {title:'شیراز'},
     {title:'مشهد'},
];  

$scope.showMSG=function(){
     return $scope.items.length>10;
}
 همانطور که مشاهده میکنید این کد یک مثال ساده است که شامل لیست استانها و قسمتی برای افزودن استان جدید و در قسمت پایینتر در صورتی که بیش از ۱۰ استان ثبت شده باشد به کاربر پیغامی نمایش می‌دهیم.

سوال اول، مشکل کجاست؟
این کد کاملا صحیح است، اما بهینه نیست. مشکل اصلی در View است که مستقیما تابع showMSG را صدا میزند. صدا زدن مستقیم توابع از View در قسمت‌هایی که Bind شده‌اند، در Performance نتیجه منفی دارند. منظور از صدا زدن مستقیم توابع در قسمت‌های Bind روش‌های زیر است:
ng-show="showMSG()"
ng-if="showMSG()"
ng-hide="showMSG()==false"
ng-class="{'red',getResult()}"
ng-style="{'width':getWidth()}"
سوال دوم، چرا Performance را کاهش میدهد؟
AngularJS برای اینکه بتواند تکلیف ng-show هایی را که در div نوشته شده‌است، مشخص کند، مجبور است تابع showMSG را صدا بزند. تا این قسمت هیچ مشکلی نیست. اما وقتی که AngularJS می‌خواهد در زمان‌های بعدی هم متوجه تغییرات بشود مجبود هست دوباره تابع showMSG را صدا بزند و این کار تکرار می‌شود و در مواقعی که تغییرات انجام شده هیچ ارتباطی با این Binding ندارند باز هم اجرا می‌شود و این تداوم در اجرا که اکثرا لازم نیستند باعث کاهش Performance می‌شود. حالا فرض کنید در پروژه‌ای بزرگ در بیشتر قسمت‌های صفحه از این روش استفاده کرده اید و پروژه با داده‌های حجیم کار میکند.

سوال سوم، راهکار چیست؟ 
راهکار این موضوع خیلی ساده است؛ اما در بهبود کارآیی پروژه، خیلی تاثیر مثبتی دارد. به طور کلی سعی کنید در ‌Binding‌های View از متغییر استفاده کنید و هیچ تابعی و یا عملیات محاسباتی را در Binding قرار ندهید . منظور از عملیات محاسباتی در Binding روش زیر است:
<div ng-show="items.length>10">شما بیش از ۱۰ استان ثبت کرده اید</div>
حتی این روش هم مناسب نیست. روشی که می‌توان برای حل این مشکل در نظر گرفت به شرح زیر است:
$scope.maxItem=false';           

$scope.add=function(){
     $scope.items.push({
          title:$scope.newItemTitle
     });
     $scope.maxItem=$scope.items.length>10;
}

<div ng-show="maxItem">شما بیش از ۱۰ استان ثبت کرده اید</div>
این مشکل با متفیر maxItem و انتقال منطق به تابع add حل می‌شود.
نتیجه گیری کلی، در پروژه‌هایی که با داده‌های حجیم کار میکنند، هیچ وقت از توابع و عملیات محاسباتی در View استفاده نکنید. 
مطالب
Performance در AngularJS قدم چهارم
امیدوارم از مقالات قبلی لذت برده باشید. در این مقاله می‌خواهم در مورد $watch صحبت کنم.

سوال اول: $watch چیست و چه کاربردی دارد؟
$watch همان عملکرد Watching در AngularJS را انجام می‌دهد؛ ولی کاربردهای جالبی دارد. به کد زیر دقت کنید.
var errorChat=false;
$scope.$watch(function () {
     return errorChat;
}, function (newValue, oldValue) {
     if(newValue ==true){
          alert('قسمت محاوره سامانه با مشکل روبرو شده است لطفا با مدیریت تماس بگیرید')
     }                        
});
فرض کنید سناریوی پروژه به این صورت است که ما قسمت‌های مختلفی را در صفحه داریم و یکی از این قسمت‌ها، چت روم می‌باشد. می‌خواهیم در صورتیکه خطای اتصال و یا هر نوع خطایی در این قسمت بود، با پیغامی به کاربر گزارش داده شود. ما از متغیر errorChat برای این کار استفاده کرده‌ایم. با فرض اینکه در توابع دیگر در صورتیکه خطایی وجود داشته باشد، مقدار این متغیر را true می‌کند و ما بر حسب رصد این متغیر پیغام خطا را نمایش می‌دهیم. 

سوال دوم: این کد به نحوه احسن کار می‌کند؛ پس مشکل کجاست؟
مشکل اینجاست بعد از اینکه متغیر errorChat مقدار true گرفت و ما هشدار را نمایش دادیم، باز این متغییر رصد می‌شود که لزومی به این کار نیست (فرض ما بر این است که بعد از بروز خطا دیگر قسمت چت روم کار نخواهد کرد) و متوقف کردن این متغیر باعث می‌شود Performance در نهایت بهتر شود. خوب برای اینکه رصد این متغییر را متوقف کنیم از کد زیر استفاده می‌کنیم. به کد زیر توجه کنید:
var errorChat=false;
var stop=$scope.$watch(function () {
     return errorChat;
}, function (newValue, oldValue) {
     if(newValue ==true){
          stop();
          alert('قسمت محاوره سامانه با مشکل روبرو شده است لطفا با مدیریت تماس بگیرید')
     }                        
});
خوب؛ فکر کنم تفاوت این دو کد با هم روشن است. ما یک متغییر stop به کد اضافه کردیم. این متغیر با تابع متوقف کننده $watch مربوط به errorChat پر می‌شود و در نهایت با اجرای این تابع، عمل watching متغییر errorChat متوقف می‌شود. اگر با setInterval یا setTimeout در Javascript کار کرده باشید، شباهت این موردها را متوجه خواهید شد.
مطالب
Performance در AngularJS قدم پنجم
در این مقاله موضوعی را مطرح خواهم کرد که شاید برای خیلی‌ها این نوع کد نویسی خوشایند نباشد. حتی برای خود من هم خوشایند نیست؛ ولی نهایتا در بهبود Performance تاثیر خیلی زیادی دارد. به کد زیر دقت کنید.
<div ng-repeat="item in items">
     <div ng-if="setting.header">{{item.header}}</div>
     <div>{{item.title}}</div>
     <div ng-if="setting.footer">{{item.footer}}</div>
</div>
توضیح کد: فرض کنید سناریوی پروژه ما به این صورت هست که ما یک لیست داریم، شامل 3 فیلد که header، title و footer را در تنظیمات می‌توانیم مشخص کنیم که header و footer در شرایطی نمایش داده شود و در شرایطی نمایش داده نشود و یا حالت‌های دیگر. 

خوب مشکل چیست و راهکار چیست؟
فرض کنید لیست ما شامل 100 رکورد هست و در تنظیمات مشخص کرده‌ایم که header نمایش داده شود و footer نمایش داده نشود. اما اتفاقی بدی که میفتد این است که وقتی لیست در View ساخته می‌شود، 100 بار ng-if مربوط به header و footer چک میشود؛ در جمع 200 بار می‌شود. چه این مقادیر true باشند چه false فرقی نمی‌کند و 200 بار بررسی می‌شود.
راهکار این مشکل به این صورت است که ما باید از ng-if داخل ng-repeat استفاده نکنیم. اما برای پیاده سازی تنظیمات باید ng-if‌ها را قبل از ng-repeat بررسی کنیم. پس مسلما ng-repeatها باید قالب پیش بینی کرده ما را نسبت به ng-if‌ها درست کند. نتیجه‌ی کار به صورت کد زیر است که شاید برای شما هم خوشایند نباشد:
<div ng-if="setting.header && setting.footer">
     <div ng-repeat="item in items">
          <div>{{item.header}}</div>
          <div>{{item.title}}</div>
          <div>{{item.footer}}</div>
     </div>
</div>
<div ng-if="setting.header && setting.footer==false">
     <div ng-repeat="item in items">
          <div>{{item.header}}</div>
          <div>{{item.title}}</div>      
     </div>
</div>
<div ng-if="setting.header==false && setting.footer">
     <div ng-repeat="item in items">         
          <div>{{item.title}}</div>   
          <div>{{item.footer}}</div>   
     </div>
</div>
<div ng-if="setting.header==false && setting.footer==false">
     <div ng-repeat="item in items">         
          <div>{{item.title}}</div>                
     </div>
</div>
درست است من هم با شما موافق هستم که خوشایند نیست. در این کد ما همه‌ی حالت‌ها را پیش بینی و قالب مناسب هر شرط را درست کرده‌ایم. حجم کد چند برابر شده، ولی از لحاظ Performance در ساخت لیست در View در حد 98% بهبود پیدا کرده‌است. همان مثال قبلی را در نظر بگیرید. ng-if مربوط به header و footer در این کد فقط 4 بار بررسی می‌شود. چه 100 رکورد باشد، چه 1000 تا، چه 10 تا رکورد. 
در مورد ng-repeat‌ها هم نگران نباشید فقط یک بار اجرا میشوند. اگر کارکرد ng-if را در مقاله‌ی قبلی من ، خوانده باشید، متوجه‌ی این موضوع می‌شوید که element‌های داخلی و direction‌های AngularJS داخلی ng-if زمانی پردازش می‌شوند که شرط true باشد. از این روش زمانی استفاده کنید که تعداد داده‌ها و حالت‌های زیادی دارید و Performance اهمیت بیشتری دارد. امیدوارم مقاله‌ی مفیدی باشد.
مطالب
فراخوانی یک تابع بعد از اتمام Render در AngularJS
در این مقاله در خصوص موضوعی صحبت خواهم کرد که شاید مشکل اکثر برنامه نویسان باشد؛ مخصوصا در استفاده از پلاگین‌های jQuery در پروژه‌های AngularJS.
مطمئنا برای شما هم پیش آمده که نیاز داشته باشید تابعی را بعد از اتمام Render در AngularJS صدا بزنید یا متوجه اتمام Render بشوید.

سوال اول: چرا این بحث مطرح هست؟
وقتی شما از AngularJS در پروژه‌ای استفاده می‌کنید و سبک کاری شما Model Based یا بهتر بگویم MVVM می‌باشد، عملیات Binding در View، توسط AngularJS انجام می‌شود و AngularJS توسط Watcher‌ها تغییرات را در View اعمال می‌کند.

سوال دو: مشکل کجاست؟
مشکل اینجاست که چه موقعی Binding یا Render تمام می‌شود؟ فرض کنید شما یک لیست دارید و می‌خواهید در View نمایش دهید. مسلما از ng-repeat استفاده می‌کنید و AngularJS از مدلی که مشخص شده است موارد را خوانده و در View نمایش می‌دهد. فرض کنید همه‌ی این موارد باید توسط jQuery UI دارای قابلیت Draggable بشوند. اما چه موقعی تابع مربوطه را صدا بزنیم؟ از کجا مطمئن شویم که element‌های لیست شهر‌ها در View موجود هستند؟ کد زیر را نگاه کنید.
<div class="city" ng-repeat="item in items">
     {{item.title}}
<div>
و در Controller
$scope.items=[
     {title:'اردبیل'},
     {title:'تهران'},
     {title:'تبریز'},
     {title:'مشهد'},
     {title:'اصفهان'}
];
ما فرض می‌کنیم بعد از اینکه شهر‌ها در View به صورت لیست نمایش داده شدند، کاربر باید بتواند شهر‌ها را Drag کند و مکان آنها را با ماوس جابجا کند. برای این کار ما از تابع jQuery UI زیر استفاده می‌کنیم:
$('.city').draggable();

سوال سوم: خوب مشکل کجاست؟
مشکل اینجاست که ما چه موقعی این تابع را صدا بزنیم تا مطمئن شویم که elementهای کلاس city در View موجود هستند و نسبت به تغییر لیست شهر که ممکن هست در طول اجرا این شهر‌ها کم یا زیاد شوند چه موقعی تابع jQuery UI را صدا بزنیم؟

سوال چهارم: راه حل چیست؟
در این چند سالی که من با AngularJS کار می‌کنم، از یک روش خیلی ساده استفاده میکنم و با همین روش از همه‌ی پلاگین‌های غیر AngularJS بدون تبدیل کردن این پلاگین‌ها به معادل AngularJS و یا گشتن چند ساعتی در اینترنت برای پیدا کردن پلاگین مشابه و منطبق با AngularJS که خیلی از مواقع هم پیدا نمی‌شوند، راحت شده‌ام. کد زیر را مشاهده کنید.
app.directive('ngFinishRender', function ($timeout) {
    return {
        restrict: 'A',
        link: function (scope, element, attr) {
            if (scope.$last === true) {
                $timeout(function () {
                    scope.$eval(attr.ngFinishRender);
                }, 0);
            }
        }
    }
});
این کد یک Directive جدید را در پروژه‌ی AngularJS شما تعریف می‌کند که توسط AngularJS قابل پردازش هست. این Directive را در View اضافه می‌کنم و کد View بالایی به کد زیر تغییر می‌کند:
<div class="city" ng-repeat="item in items" ng-finish-render="init()">
     {{item.title}}
<div>
اما این Directive چه عملی را انجام می‌دهد؟ این Directive توسط AngularJS پردازش شده و تابع init را که در مقدار ng-finish-render نوشته شده است، بعد از اتمام ng-repeat اجرا می‌کند. خوب تابع init را در controller می‌نویسیم؛ به کد زیر دقت کنید.
$scope.items=[
     {title:'اردبیل'},
     {title:'تهران'},
     {title:'تبریز'},
     {title:'مشهد'},
     {title:'اصفهان'}
];

$scope.init=function(){
     $('.city').draggable();
}
شما به همین راحتی می‌توانید از همه‌ی پلاگین‌های غیر AngularJS استفاده کنید و متوجه اتمام Render در View شوید.