选择器是jQuery中最常用的功能之一,不恰当的使用会严重影响性能。
点击按钮测试不同选择器的性能...
<div id="container">
<ul class="product-list">
<li class="product-item">产品1</li>
<li class="product-item">产品2</li>
<!-- 更多li... -->
</ul>
<div class="sidebar">
<p class="highlight">内容</p>
</div>
</div>
ID选择器是最高效的,因为浏览器有原生方法getElementById()。
// ❌ 低效 - 使用类选择器查找ID元素
$(".myId"); // 浏览器会遍历所有元素查找类名
// ✅ 高效 - 直接使用ID选择器
$("#myId"); // 浏览器直接使用getElementById()
// ❌ 低效 - 不必要的上下文
$("#container .myClass"); // 如果.container就是#container,这是多余的
// ✅ 高效 - 直接从ID开始
$("#myId").find(".myClass");
// 或者
$(".myClass", $("#myId")); // 指定上下文
通用选择器*会匹配所有元素,应尽量避免。
// ❌ 低效 - 通用选择器开头
$("* .myClass"); // 先匹配所有元素,再找子元素中的.myClass
// ❌ 低效 - 在复杂选择器中用通用选择器
$("#container * .myClass");
// ✅ 高效 - 直接指定父元素
$("#container .myClass");
// ❌ 低效 - 不必要的通用选择器
$("div#container *"); // #container已经是div,*是多余的
// ✅ 高效
$("#container").children();
// 或者更具体
$("#container > *");
jQuery选择器是从右向左解析的,最右边的选择器是关键。
// ❌ 低效 - 右边的选择器太宽泛
$("#container div .myClass");
// 解析过程:1. 找到所有.myClass 2. 检查父元素是否有div 3. 检查是否在#container内
// ✅ 高效 - 右边的选择器更具体
$("#container .myClass");
// 或者
$(".myClass", "#container");
// 性能对比测试
console.time('低效选择器');
$("#container div .myClass").length;
console.timeEnd('低效选择器'); // 可能较慢
console.time('高效选择器');
$("#container").find(".myClass").length;
console.timeEnd('高效选择器'); // 更快
重复使用选择器时应缓存结果,避免重复查询DOM。
// ❌ 低效 - 重复查询DOM
for(let i = 0; i < 100; i++) {
$(".myClass").css("color", "red"); // 每次循环都查询DOM
}
// ✅ 高效 - 缓存选择器结果
const $myClass = $(".myClass"); // 只查询一次
for(let i = 0; i < 100; i++) {
$myClass.css("color", "red");
}
// ✅ 更高效 - 在循环外部操作
$(".myClass").css("color", "red"); // 一次性操作
// 复杂场景中的缓存
function updateUI() {
// 缓存常用的选择器
const $header = $(".header");
const $content = $(".content");
const $footer = $(".footer");
// 多次使用缓存的对象
$header.addClass("active");
$content.height($(window).height() - $header.height() - $footer.height());
$footer.show();
}
// 页面级缓存
const App = {
elements: {
$header: null,
$content: null,
$footer: null
},
init: function() {
// 初始化时缓存所有常用元素
this.elements.$header = $(".header");
this.elements.$content = $(".content");
this.elements.$footer = $(".footer");
// 后续直接使用缓存
this.updateLayout();
},
updateLayout: function() {
const h = this.elements.$header.height();
const f = this.elements.$footer.height();
this.elements.$content.height($(window).height() - h - f);
}
};
在某些情况下,原生DOM方法比jQuery更快。
// 性能对比
const $element = $("#myElement");
// jQuery方法
console.time('jQuery text');
const text1 = $element.text();
console.timeEnd('jQuery text');
// 原生方法
console.time('原生 text');
const text2 = $element[0].textContent;
console.timeEnd('原生 text');
// 选择器性能对比
console.time('jQuery选择器');
$("#myId .myClass");
console.timeEnd('jQuery选择器');
console.time('原生选择器');
document.querySelectorAll("#myId .myClass");
console.timeEnd('原生选择器');
// 实际应用:混合使用
function getElementText(id) {
// 对于ID选择器,原生方法更快
const element = document.getElementById(id);
if(element) {
return element.textContent;
}
return "";
}
// 批量操作时使用原生方法
const elements = document.querySelectorAll(".myClass");
for(let i = 0; i < elements.length; i++) {
// 直接操作DOM元素,避免jQuery开销
elements[i].style.color = "red";
}
// 需要jQuery功能时再转换
$(elements).addClass("processed");
$("#id")$("* .class")DOM操作是Web性能的主要瓶颈之一,优化DOM操作可以显著提升性能。
减少DOM操作次数,尽量批量处理。
// ❌ 低效 - 多次操作DOM
for(let i = 0; i < 100; i++) {
$("#container").append(`Item ${i}`); // 每次append都触发重排
}
// ✅ 高效 - 批量操作
let html = "";
for(let i = 0; i < 100; i++) {
html += `Item ${i}`;
}
$("#container").append(html); // 一次操作
// ✅ 更高效 - 使用DocumentFragment
const fragment = document.createDocumentFragment();
for(let i = 0; i < 100; i++) {
const div = document.createElement("div");
div.textContent = `Item ${i}`;
fragment.appendChild(div);
}
$("#container")[0].appendChild(fragment);
// 复杂元素的批量创建
function createUserList(users) {
const $list = $("").addClass("user-list");
// 创建文档片段
const fragment = document.createDocumentFragment();
users.forEach(user => {
const li = document.createElement("li");
li.className = "user-item";
li.innerHTML = `
${user.name}
${user.email}
${user.role}
`;
fragment.appendChild(li);
});
$list[0].appendChild(fragment);
return $list;
}
在操作完成前将元素从DOM中移除,操作完成后再添加回去。
// 批量修改元素样式
function updateElementsStyle($elements) {
// 1. 将元素从DOM中移除(离线操作)
const elementsArray = $elements.toArray();
const parent = $elements[0].parentNode;
// 2. 离线操作
elementsArray.forEach(element => {
// 这里操作不会触发重排
element.style.color = "red";
element.style.backgroundColor = "yellow";
element.className = "updated";
});
// 3. 一次性添加回DOM(只触发一次重排)
elementsArray.forEach(element => {
parent.appendChild(element);
});
}
// 更优雅的方式 - 使用detach()
function optimizeUpdate($elements) {
// 从DOM中分离元素(保留数据和事件)
const $detached = $elements.detach();
// 离线操作
$detached.css({
color: "red",
backgroundColor: "yellow"
}).addClass("updated");
// 重新插入
$("#container").append($detached);
}
// 隐藏元素进行操作
function updateHidden($element) {
// 隐藏元素
$element.hide();
// 进行操作(不会触发重绘)
$element.css({
width: "100%",
height: "200px",
padding: "20px"
}).addClass("processed");
// 显示元素(只触发一次重绘)
$element.show();
}
重排(reflow)和重绘(repaint)是性能杀手,应尽量减少。
// ❌ 低效 - 多次触发重排
$("#element")
.css("width", "100px") // 触发重排
.css("height", "200px") // 触发重排
.css("padding", "10px"); // 触发重排
// ✅ 高效 - 一次设置所有样式
$("#element").css({
width: "100px",
height: "200px",
padding: "10px"
}); // 只触发一次重排
// ❌ 低效 - 读写分离(强制同步重排)
const width = $("#element").width(); // 读 - 触发重排以获取准确值
$("#element").css("width", width + 100 + "px"); // 写 - 再次触发重排
// ✅ 高效 - 批量读写
// 方法1:使用requestAnimationFrame
function updateElement() {
requestAnimationFrame(() => {
const $element = $("#element");
// 读取
const width = $element.width();
const height = $element.height();
// 批量写入
$element.css({
width: width + 100 + "px",
height: height + 50 + "px"
});
});
}
// 方法2:使用cssText(原生方法)
const element = $("#element")[0];
element.style.cssText += ";width:200px;height:100px;padding:10px;";
// 方法3:添加class(最推荐)
$("#element").addClass("new-style");
// CSS
.new-style {
width: 200px;
height: 100px;
padding: 10px;
/* 所有样式一次性应用 */
}
// ❌ 低效 - 在循环中查询DOM
$(".item").each(function() {
const $children = $(this).find(".child"); // 每次循环都查询
// ...
});
// ✅ 高效 - 预查询
const $children = $(".item .child"); // 一次查询
$(".item").each(function(index) {
const $child = $children.eq(index);
// ...
});
// ✅ 更高效 - 使用原生循环
const items = document.querySelectorAll(".item");
const children = document.querySelectorAll(".item .child");
for(let i = 0; i < items.length; i++) {
const child = children[i];
// 直接操作DOM元素
child.style.color = "red";
}
// 使用while循环(最快)
let i = items.length;
while(i--) {
const item = items[i];
// 倒序操作
item.classList.add("processed");
}
// 链式操作优化
$(".item")
.addClass("highlight") // 一次操作
.css("color", "red") // 一次操作
.fadeIn(300); // 一次操作
// 避免在链式调用中重复查找
$(".item")
.filter(":visible") // 过滤
.addClass("active") // 添加类
.end() // 回到原始集合
.hide(); // 隐藏所有
事件处理不当会导致内存泄漏和性能问题。
// ❌ 低效 - 为每个元素绑定事件
$(".delete-btn").click(function() {
$(this).closest(".item").remove();
});
// ✅ 高效 - 事件委托
$("#container").on("click", ".delete-btn", function() {
$(this).closest(".item").remove();
});
// 动态添加的元素也能响应事件
function addNewItem(text) {
$("#container").append(`
${text}
`);
// 无需重新绑定事件,因为使用了事件委托
}
// 多个事件类型
$("#container").on("click mouseenter mouseleave", ".item", function(event) {
switch(event.type) {
case "click":
$(this).toggleClass("active");
break;
case "mouseenter":
$(this).addClass("hover");
break;
case "mouseleave":
$(this).removeClass("hover");
break;
}
});
// 性能对比:1000个元素
console.time('传统绑定');
for(let i = 0; i < 1000; i++) {
$(`#item-${i}`).click(handler);
}
console.timeEnd('传统绑定');
console.time('事件委托');
$("#container").on("click", ".item", handler);
console.timeEnd('事件委托');
// 防抖函数:连续触发时只执行最后一次
function debounce(func, wait) {
let timeout;
return function() {
const context = this;
const args = arguments;
clearTimeout(timeout);
timeout = setTimeout(() => {
func.apply(context, args);
}, wait);
};
}
// 节流函数:一定时间内只执行一次
function throttle(func, limit) {
let inThrottle;
return function() {
const context = this;
const args = arguments;
if(!inThrottle) {
func.apply(context, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}
// 应用示例
// resize事件 - 使用防抖
$(window).on("resize", debounce(function() {
console.log("窗口大小改变", $(window).width());
updateLayout();
}, 250));
// scroll事件 - 使用节流
$(window).on("scroll", throttle(function() {
const scrollTop = $(window).scrollTop();
updateNavigation(scrollTop);
}, 100));
// 输入框搜索 - 使用防抖
$("#search-input").on("input", debounce(function() {
const query = $(this).val();
search(query);
}, 300));
// 按钮点击 - 防止重复提交(节流)
$("#submit-btn").on("click", throttle(function() {
submitForm();
}, 2000)); // 2秒内只允许提交一次
// jQuery插件版节流/防抖
$.extend({
debounce: function(func, wait, immediate) {
let timeout;
return function() {
const context = this;
const args = arguments;
const later = function() {
timeout = null;
if(!immediate) func.apply(context, args);
};
const callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if(callNow) func.apply(context, args);
};
},
throttle: function(func, limit) {
let lastFunc;
let lastRan;
return function() {
const context = this;
const args = arguments;
if(!lastRan) {
func.apply(context, args);
lastRan = Date.now();
} else {
clearTimeout(lastFunc);
lastFunc = setTimeout(function() {
if((Date.now() - lastRan) >= limit) {
func.apply(context, args);
lastRan = Date.now();
}
}, limit - (Date.now() - lastRan));
}
};
}
});
// 使用jQuery扩展
$("#element").on("scroll", $.throttle(scrollHandler, 100));
// 使用命名空间管理事件
// 绑定事件
$("#element").on("click.myNamespace", function() {
console.log("点击事件 - myNamespace");
});
$("#element").on("click.otherNamespace", function() {
console.log("点击事件 - otherNamespace");
});
// 移除特定命名空间的事件
$("#element").off("click.myNamespace"); // 只移除myNamespace的点击事件
// 移除所有命名空间的事件
$("#element").off("click"); // 移除所有点击事件
// 插件开发中的事件命名空间
(function($) {
$.fn.myPlugin = function() {
return this.each(function() {
const $this = $(this);
// 使用插件名作为命名空间
$this.on("click.myPlugin", function() {
// 处理点击
});
$this.on("mouseenter.myPlugin", function() {
// 处理鼠标进入
});
});
};
// 销毁方法
$.fn.myPlugin.destroy = function(element) {
$(element).off(".myPlugin"); // 移除插件所有事件
};
})(jQuery);
// 一次性事件
$("#element").one("click", function() {
console.log("只执行一次");
});
// 带命名空间的一次性事件
$("#element").one("click.myPlugin", function() {
console.log("插件相关的一次性事件");
});
// 事件委托 + 命名空间
$("#container").on("click.myApp", ".item", function() {
console.log("委托事件");
});
// 性能优化:移除不需要的事件
function cleanupEvents() {
// 移除所有.myApp命名空间的事件
$(document).off(".myApp");
// 移除特定元素的所有事件
$("#temp-element").off();
// 移除特定类型和命名空间的事件
$(".dynamic-elements").off("click.handler");
}
动画和特效处理不当会导致页面卡顿,影响用户体验。
| 动画类型 | FPS | CPU占用 | 流畅度 |
|---|---|---|---|
| CSS3动画 | 60 | 低 | 优秀 |
| jQuery动画 | 60 | 中 | 良好 |
/* CSS3动画 - GPU加速 */
.animate-css3 {
transition: all 0.3s ease;
transform: translateZ(0); /* 触发GPU加速 */
}
.animate-css3:hover {
transform: scale(1.1) translateZ(0);
}
/* 关键帧动画 */
@keyframes slideIn {
from {
transform: translateX(-100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
.slide-in {
animation: slideIn 0.5s ease forwards;
}
/* 高性能动画属性 */
.optimized-animation {
/* 这些属性由GPU加速 */
transform: translate3d(0, 0, 0);
opacity: 1;
/* 这些属性性能较差,避免在动画中使用 */
/* margin, padding, width, height, top, left */
}
// ❌ 低效 - 使用jQuery动画改变布局属性
$("#element").animate({
width: "200px",
height: "100px",
marginLeft: "50px"
}, 500);
// ✅ 高效 - 使用CSS3 transform
$("#element").addClass("expanded");
// CSS
.expanded {
transform: scale(1.2) translateX(50px);
transition: transform 0.5s ease;
}
// ✅ 混合使用 - jQuery控制CSS3动画
$("#element").css({
transform: "translateX(100px)",
transition: "transform 0.5s ease"
});
// 检测动画完成
$("#element").on("transitionend", function() {
console.log("CSS3动画完成");
});
// 性能对比
function testAnimationPerformance() {
const $element = $("#test-element");
// jQuery动画
console.time('jQuery动画');
$element.animate({left: "500px"}, 1000, function() {
console.timeEnd('jQuery动画');
});
// CSS3动画
console.time('CSS3动画');
$element.addClass("move-right");
$element.on("transitionend", function() {
console.timeEnd('CSS3动画');
$(this).off("transitionend");
});
}
// 1. 使用stop()防止动画队列堆积
$("#element").hover(
function() {
$(this).stop().animate({height: "200px"}, 300);
},
function() {
$(this).stop().animate({height: "100px"}, 300);
}
);
// 2. 清除动画队列
$("#element").stop(true, true).animate({/* ... */});
// 3. 使用show/hide代替高度动画
// ❌ 低效
$("#element").animate({height: "toggle"}, 300);
// ✅ 高效
$("#element").slideToggle(300);
// 或
$("#element").toggle(300);
// 4. 批量动画
// ❌ 低效 - 串行动画
$("#element")
.animate({width: "200px"}, 300)
.animate({height: "200px"}, 300)
.animate({opacity: 0.5}, 300);
// ✅ 高效 - 并行动画
$("#element").animate({
width: "200px",
height: "200px",
opacity: 0.5
}, 300);
// 5. 使用缓动函数
$("#element").animate({
left: "500px"
}, {
duration: 1000,
easing: "easeOutExpo" // 使用缓动函数
});
// 6. 限制动画频率
let isAnimating = false;
$("#trigger").click(function() {
if(!isAnimating) {
isAnimating = true;
$("#element").animate({/* ... */}, 300, function() {
isAnimating = false;
});
}
});
// 传统setTimeout动画的问题
function animateWithTimeout() {
const element = $("#element")[0];
let position = 0;
function step() {
position += 2;
element.style.left = position + "px";
if(position < 500) {
setTimeout(step, 16); // 约60fps
}
}
step();
}
// 使用requestAnimationFrame(推荐)
function animateWithRAF() {
const element = $("#element")[0];
let position = 0;
let lastTime = 0;
function step(timestamp) {
if(!lastTime) lastTime = timestamp;
const delta = timestamp - lastTime;
// 基于时间差计算移动距离
position += (delta / 16) * 2; // 60fps基准
element.style.left = Math.min(position, 500) + "px";
if(position < 500) {
requestAnimationFrame(step);
}
}
requestAnimationFrame(step);
}
// jQuery动画与RAF结合
$.fn.animateWithRAF = function(properties, duration) {
const $element = this;
const startTime = performance.now();
const startProps = {};
// 记录起始值
Object.keys(properties).forEach(key => {
startProps[key] = parseFloat($element.css(key)) || 0;
});
function animate(currentTime) {
const elapsed = currentTime - startTime;
const progress = Math.min(elapsed / duration, 1);
// 计算当前值
const currentProps = {};
Object.keys(properties).forEach(key => {
const start = startProps[key];
const end = parseFloat(properties[key]);
currentProps[key] = start + (end - start) * progress;
});
// 应用样式
$element.css(currentProps);
if(progress < 1) {
requestAnimationFrame(animate);
}
}
requestAnimationFrame(animate);
return this;
};
// 使用自定义动画
$("#element").animateWithRAF({
left: "500px",
opacity: 0.5
}, 1000);
AJAX请求优化可以减少网络开销,提升用户体验。
// ❌ 低效 - 多个独立请求
function loadUserData(userId) {
$.get("/api/user/" + userId, function(user) {
// ...
});
$.get("/api/user/" + userId + "/posts", function(posts) {
// ...
});
$.get("/api/user/" + userId + "/friends", function(friends) {
// ...
});
}
// ✅ 高效 - 合并请求
function loadUserData(userId) {
$.when(
$.get("/api/user/" + userId),
$.get("/api/user/" + userId + "/posts"),
$.get("/api/user/" + userId + "/friends")
).then(function(userResponse, postsResponse, friendsResponse) {
const user = userResponse[0];
const posts = postsResponse[0];
const friends = friendsResponse[0];
// 统一处理
});
}
// ✅ 更高效 - 批量接口
function loadUserData(userId) {
$.get("/api/user-data/" + userId, function(data) {
// data包含用户、帖子、好友等所有信息
const {user, posts, friends} = data;
});
}
// 请求缓存
const requestCache = {};
function cachedRequest(url, callback) {
// 检查缓存
if(requestCache[url]) {
callback(requestCache[url]);
return;
}
// 发送请求
$.get(url, function(data) {
// 缓存结果
requestCache[url] = data;
callback(data);
});
}
// 带过期时间的缓存
const cacheWithExpiry = {
data: {},
set: function(key, value, ttl = 60000) { // 默认1分钟
this.data[key] = {
value: value,
expiry: Date.now() + ttl
};
},
get: function(key) {
const item = this.data[key];
if(!item) return null;
if(Date.now() > item.expiry) {
delete this.data[key];
return null;
}
return item.value;
}
};
// 请求取消
let currentRequest = null;
function search(query) {
// 取消之前的请求
if(currentRequest) {
currentRequest.abort();
}
currentRequest = $.ajax({
url: "/api/search",
data: {q: query},
success: function(data) {
displayResults(data);
currentRequest = null;
},
error: function(xhr, status) {
if(status !== "abort") {
showError("搜索失败");
}
currentRequest = null;
}
});
}
// 请求超时
function fetchDataWithTimeout(url, timeout = 5000) {
return $.ajax({
url: url,
timeout: timeout,
beforeSend: function() {
// 显示加载指示器
$("#loading").show();
},
complete: function() {
// 隐藏加载指示器
$("#loading").hide();
},
error: function(xhr, status) {
if(status === "timeout") {
showError("请求超时,请重试");
}
}
});
}
// 并发请求限制
class RequestQueue {
constructor(maxConcurrent = 3) {
this.maxConcurrent = maxConcurrent;
this.queue = [];
this.active = 0;
}
add(request) {
return new Promise((resolve, reject) => {
this.queue.push({
request,
resolve,
reject
});
this.process();
});
}
process() {
if(this.active >= this.maxConcurrent || this.queue.length === 0) {
return;
}
this.active++;
const {request, resolve, reject} = this.queue.shift();
$.ajax(request)
.done(resolve)
.fail(reject)
.always(() => {
this.active--;
this.process();
});
}
}
// 使用请求队列
const requestQueue = new RequestQueue(2); // 最大并发2
// 添加请求到队列
requestQueue.add({
url: "/api/data1"
}).then(data => {
console.log("data1 loaded");
});
requestQueue.add({
url: "/api/data2"
}).then(data => {
console.log("data2 loaded");
});
// 数据压缩
function compressData(data) {
// 移除不必要的数据
const compressed = {
items: data.items.map(item => ({
id: item.id,
name: item.name,
// 只保留必要的字段
}))
};
return compressed;
}
// 分页加载
class PaginatedLoader {
constructor(url, pageSize = 20) {
this.url = url;
this.pageSize = pageSize;
this.currentPage = 1;
this.isLoading = false;
this.hasMore = true;
}
loadNextPage() {
if(this.isLoading || !this.hasMore) {
return Promise.resolve([]);
}
this.isLoading = true;
return $.ajax({
url: this.url,
data: {
page: this.currentPage,
pageSize: this.pageSize
}
}).then(data => {
this.isLoading = false;
if(data.items.length < this.pageSize) {
this.hasMore = false;
} else {
this.currentPage++;
}
return data.items;
}).catch(() => {
this.isLoading = false;
return [];
});
}
reset() {
this.currentPage = 1;
this.hasMore = true;
this.isLoading = false;
}
}
// 使用分页加载器
const userLoader = new PaginatedLoader("/api/users", 10);
function loadMoreUsers() {
userLoader.loadNextPage().then(users => {
appendUsers(users);
if(!userLoader.hasMore) {
$("#load-more").hide();
}
});
}
// 无限滚动
$(window).on("scroll", throttle(function() {
const scrollBottom = $(window).scrollTop() + $(window).height();
const documentHeight = $(document).height();
if(documentHeight - scrollBottom < 500) { // 接近底部
loadMoreUsers();
}
}, 200));
良好的代码结构和压缩可以减少文件大小,提高加载速度。
// 立即执行函数表达式 (IIFE) - 避免污染全局命名空间
(function($, window, document) {
'use strict';
// 私有变量
const defaults = {
color: 'red',
size: 'medium'
};
// 私有函数
function privateHelper() {
// 内部使用的函数
}
// 公共接口
$.fn.myOptimizedPlugin = function(options) {
const settings = $.extend({}, defaults, options);
return this.each(function() {
// 插件实现
});
};
})(jQuery, window, document);
// 模块模式
const MyModule = (function() {
// 私有变量
let counter = 0;
// 私有函数
function increment() {
counter++;
}
// 公共接口
return {
getCount: function() {
return counter;
},
incrementAndGet: function() {
increment();
return counter;
}
};
})();
// 使用
console.log(MyModule.getCount()); // 0
MyModule.incrementAndGet(); // 1
// 基于类的组织
class Widget {
constructor(element, options) {
this.$element = $(element);
this.options = options;
this.init();
}
init() {
// 初始化
this.bindEvents();
}
bindEvents() {
// 事件绑定
this.$element.on('click', this.handleClick.bind(this));
}
handleClick(event) {
// 事件处理
}
destroy() {
// 清理
this.$element.off('click');
}
}
// package.json 示例
{
"name": "my-jquery-app",
"version": "1.0.0",
"scripts": {
"build": "npm run lint && npm run bundle && npm run minify",
"lint": "eslint src/**/*.js",
"bundle": "browserify src/main.js -o dist/bundle.js",
"minify": "uglifyjs dist/bundle.js -o dist/bundle.min.js -c -m",
"dev": "watchify src/main.js -o dist/bundle.js -v"
},
"devDependencies": {
"browserify": "^17.0.0",
"uglify-js": "^3.14.0",
"eslint": "^8.0.0",
"watchify": "^4.0.0"
}
}
// webpack配置示例 (webpack.config.js)
module.exports = {
mode: 'production', // 或 'development'
entry: './src/index.js',
output: {
filename: 'bundle.min.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
}
]
},
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({
terserOptions: {
compress: {
drop_console: true, // 移除console.log
drop_debugger: true // 移除debugger
}
}
})
]
}
};
// Grunt配置示例 (Gruntfile.js)
module.exports = function(grunt) {
grunt.initConfig({
uglify: {
options: {
mangle: true,
compress: true,
sourceMap: true
},
target: {
files: {
'dist/app.min.js': ['src/**/*.js']
}
}
},
cssmin: {
target: {
files: {
'dist/style.min.css': ['src/**/*.css']
}
}
}
});
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-cssmin');
grunt.registerTask('default', ['uglify', 'cssmin']);
};
// 图片懒加载
class LazyLoader {
constructor() {
this.images = [];
this.init();
}
init() {
// 收集所有需要懒加载的图片
this.images = $('img[data-src]').toArray();
// 监听滚动事件(节流)
$(window).on('scroll', throttle(this.checkImages.bind(this), 100));
// 初始检查
this.checkImages();
}
checkImages() {
const windowHeight = $(window).height();
const scrollTop = $(window).scrollTop();
this.images = this.images.filter(img => {
const $img = $(img);
const offsetTop = $img.offset().top;
// 如果图片进入视口
if(offsetTop < scrollTop + windowHeight + 100) { // 提前100px加载
this.loadImage($img);
return false; // 从数组中移除
}
return true; // 保留在数组中
});
// 如果所有图片都加载完成,移除事件监听
if(this.images.length === 0) {
$(window).off('scroll');
}
}
loadImage($img) {
const src = $img.data('src');
if(!src) return;
const image = new Image();
image.onload = () => {
$img.attr('src', src).removeAttr('data-src');
$img.addClass('loaded');
};
image.src = src;
}
}
// 初始化懒加载
new LazyLoader();
// 组件懒加载
function loadComponent(componentName) {
// 检查是否已加载
if(window[componentName]) {
return Promise.resolve(window[componentName]);
}
// 动态加载
return $.getScript(`/components/${componentName}.js`)
.then(() => {
return window[componentName];
});
}
// 使用
$('#open-modal').click(function() {
loadComponent('Modal').then(Modal => {
const modal = new Modal();
modal.open();
});
});
// 路由级懒加载
const routes = {
'/dashboard': () => import('./modules/dashboard.js'),
'/users': () => import('./modules/users.js'),
'/settings': () => import('./modules/settings.js')
};
function loadRoute(route) {
if(routes[route]) {
routes[route]().then(module => {
module.init();
});
}
}
// 监听路由变化
$(window).on('hashchange', function() {
const route = location.hash.slice(1) || '/dashboard';
loadRoute(route);
});
| 优化领域 | 优化技巧 | 性能提升 |
|---|---|---|
| 选择器 | 缓存选择器、优先ID、避免* | 减少DOM查询50-80% |
| DOM操作 | 批量操作、DocumentFragment、离线操作 | 减少重排重绘70% |
| 事件处理 | 事件委托、节流防抖、命名空间 | 减少内存占用60% |
| 动画 | CSS3动画、requestAnimationFrame、stop() | 提升FPS到60 |
| AJAX | 请求合并、缓存、取消、分页 | 减少请求数50% |
| 代码 | 模块化、压缩、懒加载、CDN | 减少文件大小70% |
请优化以下低效的jQuery代码:
// 低效代码示例
function inefficientCode() {
// 1. 选择器问题
for(let i = 0; i < 100; i++) {
$(".item").css("color", "red");
}
// 2. DOM操作问题
for(let i = 0; i < 50; i++) {
$("#container").append("Item " + i + "");
}
// 3. 事件处理问题
$(".btn").click(function() {
// 处理点击
});
// 4. 动画问题
$(".box").hover(function() {
$(this).animate({width: "200px"}, 300);
}, function() {
$(this).animate({width: "100px"}, 300);
});
}