如何进一步减少加载时间(Visualforce,Apex,Angular)

时间:2014-09-25 17:18:02

标签: javascript angularjs salesforce visualforce apex

我构建了一个Visualforce页面,它将Contacts和Leads拉到一个视图中。它的工作效果很好,但加载需要20-25秒,众所周知,可用性 HORRIBLE

我查看了日志,看起来Apex占用了70%的加载时间,30%用于javascript部分,我可以根据请求发布日志。

注意:此项目基于Mohit Shrivastav的工作。通过该项目的大约1/2,我决定我想使用datatables.net而不是Angular进行排序,因为我还在学习Angular。

这是控制器:

public with sharing class recentMqlExplorerController {
  public String LeadContactList {
    get; set;
  }

  public class ContactLeadWrap {
    public String id;
    public String name;
    public String dateofmql;
    public String company;
    public Decimal leadscore;
    public String country;
    public String state;
    public Integer employees;
    public String ownername;
    public String leadstatus;
    public String leadorcontact;
    public String team;
    public String url;

    ContactLeadWrap() {
      id = '';
      name = '';
      company = '';
      leadscore = 0;
      country = '';
      state = '';
      employees = 0;
      ownername = '';
      leadstatus = '';
      url = '';
      team = '';
    }
  }

  //Method to bring the list of Contacts and Leads and Serialize Wrapper Object as JSON
  public  static String getlstContactLead() {
    List <ContactLeadWrap> lstwrap = new List <ContactLeadWrap> ();
    List <Contact> lstcontact = new List<Contact>();
    List <Lead> lstlead = new List<Lead>();
    Datetime now = System.now();
    Datetime last = now.addDays(-60);
    Id ownerId = UserInfo.getUserId();


    lstcontact = [SELECT Id, FirstName, Name, Date_Became_MQL__c, Account.Name, mkto2__Lead_Score__c, Account.BillingCountry, Account.BillingState, Account.NumberOfEmployees, Owner.Name, z_Lead_Status_at_Convert_TEXT_if_applcbl__c, Account.AOU__r.sub_sub_Team_Picklist__c FROM Contact WHERE (Date_Became_MQL__c > :last AND Owner.Id = :ownerId) OR FirstName = 'westest' LIMIT 20000];

    lstlead = [SELECT Id, FirstName, Name, Date_Became_MQL__c, Company, mkto2__Lead_Score__c, Country, State, NumberOfEmployees, Owner.Name, status, z_Lead_Owner_User__r.sub_sub_Team_Picklist__c FROM Lead  WHERE (Date_Became_MQL__c > :last AND IsConverted = false AND Owner.Id = :ownerId) OR (FirstName = 'westest' AND IsConverted = false) LIMIT 20000];


    String convertedDateofmql = null;
    Date dateOnly = null;
    String fullRecordURL = null;

    for (Contact c: lstcontact) {
        if (c.Date_Became_MQL__c != null) {
            convertedDateofmql = c.Date_Became_MQL__c.format('MM/dd/YYYY');
        }
        if (c.z_Lead_Status_at_Convert_TEXT_if_applcbl__c == null) {
            c.z_Lead_Status_at_Convert_TEXT_if_applcbl__c = 'no status';
        }
        fullRecordURL = URL.getSalesforceBaseUrl().toExternalForm() + '/' + c.Id;
        ContactLeadWrap cwrap = new ContactLeadWrap();
        cwrap.id = c.id;
        cwrap.name = c.name;
        cwrap.url = fullRecordURL;
        cwrap.dateofmql = convertedDateofmql;
        cwrap.company = c.Account.Name;
        cwrap.leadscore = c.mkto2__Lead_Score__c;
        cwrap.country = c.Account.BillingCountry;
        cwrap.state = c.Account.BillingState;
        cwrap.employees = c.Account.NumberOfEmployees;
        cwrap.ownername = c.Owner.Name;
        cwrap.leadstatus = c.z_Lead_Status_at_Convert_TEXT_if_applcbl__c;
        cwrap.leadorcontact = 'Contact';
        cwrap.team = c.Account.AOU__r.sub_sub_Team_Picklist__c;
        lstwrap.add(cwrap);
    }
    for (Lead l: lstlead) {
        if (l.Date_Became_MQL__c != null) {
            convertedDateofmql = l.Date_Became_MQL__c.format('MM/dd/YYYY');
        }
        fullRecordURL = URL.getSalesforceBaseUrl().toExternalForm() + '/' + l.Id;
        ContactLeadWrap lwrap = new ContactLeadWrap();
        lwrap.id = l.id;
        lwrap.name = l.name;
        lwrap.url = fullRecordURL;
        lwrap.dateofmql = convertedDateofmql;
        lwrap.company = l.Company;
        lwrap.leadscore = l.mkto2__Lead_Score__c;
        lwrap.country = l.Country;
        lwrap.state = l.State;
        lwrap.employees = l.NumberOfEmployees;
        lwrap.ownername = l.Owner.Name;
        lwrap.leadstatus = l.status;
        lwrap.leadorcontact = 'Lead';
        lwrap.team = l.z_Lead_Owner_User__r.sub_sub_Team_Picklist__c;
        lstwrap.add(lwrap);
    }
    return JSON.serialize(lstwrap);
  }

  public class getInformation {

  }
}

这是Page:

<apex:page standardStylesheets="false" sidebar="false" showHeader="false" docType="html-5.0" controller="recentMqlExplorerController">
    <html xmlns:ng="http://angularjs.org" ng-app="hello" lang="en">
      <head>
        <meta charset="utf-8"></meta>
        <meta http-equiv="X-UA-Compatible" content="IE=edge"></meta>
        <title>MQL Explorer</title>
        <meta name="description" content=""></meta>
        <meta name="viewport" content="width=device-width"></meta>
          <link rel="stylesheet" href="{!URLFOR($Resource.smbMqlHunterAppMarkIiiDEV, '/bower_components/sass-bootstrap/dist/css/bootstrap.css')}"/>
          <link rel="stylesheet" href="{!URLFOR($Resource.smbMqlHunterAppMarkIiiDEV, '/styles/main.css')}"/>
          <link type='text/css' href='//fonts.googleapis.com/css?family=Open+Sans:300italic,400,300' rel='stylesheet'/>
      </head>
      <body>
          <!-- =========== Binding Controller to Body of Page ============= -->
            <div class="jumbotron ribbon">
            <h2>Hello {!$User.FirstName}:</h2> <h4> Welcome to the Recent MQL Dashboard</h4>
        </div>
        <div class="jumbotron banner row">
            <span class="chevron"><i class="fa fa-chevron-down fa-1x"></i></span>
            <div class="jumbotron jbot"></div>
                    <div class="dashboard">
                    </div>
                    <div class="dashboard-shadow"></div>
        </div>
          <div ng-controller="ctrlRead" class="container table-container">
                <div class="table-container-header">
                        <h4>Qualified Leads and Contacts</h4>
                </div>
              <table id="dashboard" class="table-borders display responsive" width="100%">
                <thead>
                    <tr role="row" class="tableFilters">
                        <th>Search by Name</th>
                        <th>From when?</th>
                        <th>Search by Company</th>
                        <th></th>
                        <th></th>
                        <th></th>
                        <th></th>
                        <th id="owner">Search by Owner</th>
                        <th>What Status?</th>
                        <!-- <th>What Type?</th> -->
                        <th>What Team?</th>
                    </tr>
                    <tr role="row" class="tableHeader">
                        <th scope="col" class="name">Name</th>
                        <th scope="col" class="dateofmql" width="250px">Date of MQL</th>
                        <th scope="col" class="company">Company</th>
                        <th scope="col" class="leadscore">Score</th>
                        <th scope="col" class="country">Country</th>
                        <th scope="col" class="state">State</th>
                        <th scope="col" class="employees">EE#</th>
                        <th scope="col" class="ownername">Owner Name</th>
                        <th scope="col" class="leadstatus">Lead Status</th>
                        <!-- <th scope="col" class="leadorcontact">Type</th> -->
                        <th scope="col" class="team">Sales Team</th>
                    </tr>
                </thead>
              <tbody class="table">
                      <tr ng-repeat="item in pagedItems[currentPage] | orderBy:sortingOrder:reverse">
                          <th><apex:outputlink style="text-decoration: underline; color: #01B2E4;" target="_blank" value="{{item.url}}">{{item.name}}</apex:outputlink></th>
                          <td>{{item.dateofmql}}</td>
                          <td>{{item.company}}</td>
                          <td>{{item.leadscore}}</td>
                          <td>{{item.country}}</td>
                          <td>{{item.state}}</td>
                          <td>{{item.employees}}</td>
                          <td>{{item.ownername}}</td>
                          <td>{{item.leadstatus}}</td>
                          <td>{{item.team}}</td>
                      </tr>
                  </tbody>
              </table>
            </div>

        <script src="{!URLFOR($Resource.smbMqlHunterAppMarkIiiDEV, '/bower_components/angular/angular.min.js')}"/>
          <script src="{!URLFOR($Resource.smbMqlHunterAppMarkIiiDEV, '/bower_components/jquery/jquery.min.js')}"/>
          <script src="{!URLFOR($Resource.smbMqlHunterAppMarkIiiDEV, '/bower_components/jquery/jquery-ui.min.js')}"/>
          <script src="{!URLFOR($Resource.smbMqlHunterAppMarkIiiDEV, '/bower_components/jquery/jquery.dataTables.min.js')}"/>
          <script src="{!URLFOR($Resource.smbMqlHunterAppMarkIiiDEV, '/bower_components/jquery/dataTables.tableTools.min.js')}"/>
          <script src="{!URLFOR($Resource.smbMqlHunterAppMarkIiiDEV, '/bower_components/jquery/dataTables.responsive.min.js')}"/>
          <script src="{!URLFOR($Resource.smbMqlHunterAppMarkIiiDEV, '/bower_components/jquery/jquery.dataTables.columnFilter-min.js')}"/>
          <script src="{!URLFOR($Resource.smbMqlHunterAppMarkIiiDEV, '/bower_components/angular-force/ngForce.min.js')}"/>
          <script src="{!URLFOR($Resource.smbMqlHunterAppMarkIiiDEV, '/scripts/app-min.js')}"/>
          <script type="text/javascript">
                'use strict';
                <!-- Name your application -->
                var myapp = angular.module('hello', []);
                var sortingOrder = 'name';
                <!-- Define Controller  -->
                var contrl=myapp.controller('ctrlRead', function ($scope, $filter) {
                <!--- Initialize Scope Variables --->
                $scope.sortingOrder = sortingOrder;
                $scope.reverse = false;
                $scope.filteredItems = [];
                $scope.groupedItems = [];
                $scope.itemsPerPage = 60000;
                $scope.pagedItems = [];
                $scope.currentPage = 0;
                $scope.items ={!lstContactLead};
                var searchMatch = function (haystack, needle) {
                    if (!needle) {
                        return true;
                    }
                    return haystack.toLowerCase().indexOf(needle.toLowerCase()) !== -1;
                };
                //Initialize the Search Filters
                $scope.search = function () {
                    $scope.filteredItems = $filter('filter')($scope.items, function (item) {
                        for (var attr in item) {
                            if (searchMatch(item[attr], $scope.query))
                                return true;
                        }
                        return false;
                    });
                    // Define Sorting Order
                    if ($scope.sortingOrder !== '') {
                        $scope.filteredItems = $filter('orderBy')($scope.filteredItems, $scope.sortingOrder, $scope.reverse);
                    }
                    $scope.currentPage = 0;

                    // Group by pages
                    $scope.groupToPages();
                };
                // Calculate Total Number of Pages based on Records Queried
                $scope.groupToPages = function () {
                    $scope.pagedItems = [];
                    for (var i = 0; i < $scope.filteredItems.length; i++) {
                        if (i % $scope.itemsPerPage === 0) {
                            $scope.pagedItems[Math.floor(i / $scope.itemsPerPage)] = [$scope.filteredItems[i]];
                        } else {
                            $scope.pagedItems[Math.floor(i / $scope.itemsPerPage)].push($scope.filteredItems[i]);
                        }
                    }
                };

                $scope.range = function (start, end) {
                    var ret = [];
                    if (!end) {
                        end = start;
                        start = 0;
                    }
                    for (var i = start; i < end; i++) {
                        ret.push(i);
                    }
                    return ret;
                };

                  $scope.prevPage = function () {
                      if ($scope.currentPage > 0) {
                          $scope.currentPage--;
                      }
                  };

                  $scope.nextPage = function () {
                      if ($scope.currentPage < $scope.pagedItems.length - 1) {
                          $scope.currentPage++;
                      }
                  };
                  $scope.setPage = function () {
                      $scope.currentPage = this.n;
                  };
                  // functions have been describe process the data for display
                  $scope.search();

                  // change sorting order
                  $scope.sort_by = function (newSortingOrder) {
                      if ($scope.sortingOrder == newSortingOrder)
                          $scope.reverse = !$scope.reverse;
                      $scope.sortingOrder = newSortingOrder;

                      // icon setup
                      $('th i').each(function () {
                          // icon reset
                          $(this).removeClass().addClass('icon-sort');
                      });
                      if ($scope.reverse)
                          $('th.' + new_sorting_order + ' i').removeClass().addClass('icon-chevron-up');
                      else
                          $('th.' + new_sorting_order + ' i').removeClass().addClass('icon-chevron-down');
                    };
                });
                contrl.$inject = ['$scope', '$filter'];
          </script>
        </body>
    </html>
</apex:page>

2 个答案:

答案 0 :(得分:2)

请查看salesforce提供的以下指南,它可能有助于提高网页性能。

大页面尺寸直接影响加载时间。要改进Visualforce页面加载时间:

缓存经常访问的所有数据,例如图标图形。

在Apex控制器getter方法中避免使用SOQL查询。

通过以下方式减少页面上显示的记录数:

限制从Apex控制器中的SOQL调用返回的数据。例如,在WHERE子句中使用AND语句,或删除null结果

利用列表控制器的分页来显示每页较少的记录

“延迟加载”Apex对象减少请求次数。

考虑在标记之外移动任何JavaScript,并将其放在结束标记之前的标记中。标签将JavaScript放在关闭元素之前;因此,Visualforce尝试在页面上的任何其他内容之前加载JavaScript。但是,如果您确定它对您的页面没有任何不利影响,则应该只将JavaScript移动到页面底部。例如,需要document.write或事件处理程序的JavaScript代码段应保留在元素中。

在所有情况下,Visualforce页面必须小于15 MB。

此致

纳温

http://www.autorabit.com

答案 1 :(得分:1)

以下是我的观察结果,粗略地看一下您的代码。

1)您正在使用存储在诸如angular,jquery,Bootstrap等脚本中的太多脚本。使用这些脚本会产生时滞。不确定您的要求,但寻找限制使用外部脚本的方法。

2)不使用Salesforce标签 - 几乎完整的页面是基于JAVA的,它插入到顶点标签中。例如,使用apex表而不是使用表tr和td标记。

使用标准的开箱即用功能将尝试减少页面的加载时间,并尝试利用本机Salesforce脚本。

3)如果你没有使用很多标签,我甚至建议使用自定义脚本而不是bootstrap。在Salesforce中尝试保持Salesforce的外观和感觉,这将产生最佳结果。

免责声明 - 这些是优化页面加载的一般建议,但具体要求可能会强制使用本机脚本。

与Ashish