我构建了一个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>
答案 0 :(得分:2)
请查看salesforce提供的以下指南,它可能有助于提高网页性能。
大页面尺寸直接影响加载时间。要改进Visualforce页面加载时间:
缓存经常访问的所有数据,例如图标图形。
在Apex控制器getter方法中避免使用SOQL查询。
通过以下方式减少页面上显示的记录数:
限制从Apex控制器中的SOQL调用返回的数据。例如,在WHERE子句中使用AND语句,或删除null结果
利用列表控制器的分页来显示每页较少的记录
“延迟加载”Apex对象减少请求次数。
考虑在标记之外移动任何JavaScript,并将其放在结束标记之前的标记中。标签将JavaScript放在关闭元素之前;因此,Visualforce尝试在页面上的任何其他内容之前加载JavaScript。但是,如果您确定它对您的页面没有任何不利影响,则应该只将JavaScript移动到页面底部。例如,需要document.write或事件处理程序的JavaScript代码段应保留在元素中。
在所有情况下,Visualforce页面必须小于15 MB。
此致
纳温
答案 1 :(得分:1)
以下是我的观察结果,粗略地看一下您的代码。
1)您正在使用存储在诸如angular,jquery,Bootstrap等脚本中的太多脚本。使用这些脚本会产生时滞。不确定您的要求,但寻找限制使用外部脚本的方法。
2)不使用Salesforce标签 - 几乎完整的页面是基于JAVA的,它插入到顶点标签中。例如,使用apex表而不是使用表tr和td标记。
使用标准的开箱即用功能将尝试减少页面的加载时间,并尝试利用本机Salesforce脚本。
3)如果你没有使用很多标签,我甚至建议使用自定义脚本而不是bootstrap。在Salesforce中尝试保持Salesforce的外观和感觉,这将产生最佳结果。
免责声明 - 这些是优化页面加载的一般建议,但具体要求可能会强制使用本机脚本。
与Ashish