Slider JavaScript làm chậm website khủng khiếp như thế nào

Câu chuyện về slider – kẻ “ăn cắp” performance

Slider (hay carousel) xuất hiện ở khắp mọi nơi trên web ngày nay. Dù bạn có thích hay không, thì slider vẫn là cách hay ho để trình bày nội dung, nhất là khi màn hình nhỏ.

Với các web bán hàng, slider được dùng để show sản phẩm hot, banner quảng cáo, khuyến mãi, gallery ảnh… đủ thứ trên đời! Mặc dù có nhiều tranh cãi về hiệu quả, nhưng người dùng đã quen mắt với nó rồi, nên trải nghiệm cũng khá mượt mà.

Tại sao các thư viện slider lại “nặng” đến thế?

Là developer frontend, chúng ta có cả trăm thư viện slider JavaScript để chọn. Nhưng buồn thay, hầu hết đều mắc chung một bệnh: làm website chậm như rùa. Tin tôi đi, tôi đã thử qua hết rồi!

Những “ông lớn” trong làng slider

Top 5 thư viện được dùng nhiều nhất:

  1. Slick Slider – Ông vua cũ
  2. Owl Carousel – Đối thủ nặng ký
  3. Flickity – Cái tên quen thuộc
  4. Keen-slider – Tân binh đầy hứa hẹn
  5. Glider.js – Nhỏ gọn nhưng mạnh mẽ

Điểm chung của tất cả: chúng chỉ cho bạn CSS cơ bản để chạy được, còn muốn đẹp thì phải tự làm thêm. Điều này thực ra khá ổn vì mình hay custom slider nhiều, nên càng ít CSS phải ghi đè càng tốt.

Thực nghiệm so sánh hiệu suất – Ai thắng ai thua?

1. Slick Slider – “Ông lão” nặng nề

Cách dùng đơn giản:

$(function () {
    $('.js-slider').slick({
        centerMode: true,
        centerPadding: '15px',
        arrows: false,
        infinite: false,
    });
})

Cái giá phải trả:

  • JavaScript: 126 KB (kèm theo jQuery – nặng như chì!)
  • CSS: 4.9 KB

Hiệu suất thực tế (test trên mạng 3G, CPU chậm 4 lần):

  • Thời gian chặn trang (TBT): 0ms
  • Hiện nội dung đầu tiên (FCP): 1989ms – gần 2 giây mới thấy gì!
  • Hiện nội dung chính (LCP): 2789ms – chờ gần 3 giây
  • Layout nhảy (CLS): 0.1344 – trang bị giật
  • Tương tác được (TTI): 2939ms – 3 giây mới bấm được

Vấn đề lớn: Website “đơ” gần 2 giây rồi mới hiện, 3 giây mới tương tác được. Còn có hiện tượng layout nhảy nữa.

Khắc phục layout nhảy bằng CSS:

.gallery > .slide:not(:first-child) {
    display: none;
}
.gallery > .slide:first-child {
    padding: 0 20px;
}

Kết quả sau khi fix:

  • FCP: 1356ms (cải thiện 31%)
  • LCP: 2568ms (cải thiện 8%)
  • CLS: 0 (hết nhảy layout!)
  • TTI: 2746ms (cải thiện 6%)

Slick page load performance profile

Cách setup:

$(function () {
    $('.js-slider').owlCarousel({
        center: true,
        stagePadding: 15
    });
});

So sánh tài nguyên:

  • JavaScript: 117 KB (nhẹ hơn Slick 7%)
  • CSS: 6.8 KB (nặng hơn 38%)

Hiệu suất có khá hơn: TTI: 2357ms (nhanh hơn Slick 14%)

3. Keen Slider – Ngôi sao sáng giá

Tài nguyên siêu nhẹ:

  • JavaScript: 10.3 KB (nhẹ hơn Owl tới 91%!)
  • CSS: 3.7 KB (nhẹ hơn 24%)

Hiệu suất ấn tượng:

  • FCP: 1275ms
  • LCP: 1667ms (nhanh hơn 34%)
  • TTI: 1740ms (nhanh hơn 26%)

Tại sao Keen Slider lại nhanh đến thế?

  • Keen Slider chỉ cần 2.73ms để render slider
  • Slick phải mất tới 125.23ms để làm cùng việc đó
  • Ít tính toán phức tạp, ít thao tác DOM hơn

Keen Slider page load performance profile

Bí kíp tối thượng: Slider chỉ bằng CSS

Thay vì dùng JavaScript, ta có thể tạo slider hoàn toàn bằng CSS với:

  • CSS Grid – xử lý bố cục
  • CSS Scroll Snap – làm hiệu ứng cuộn mượt mà

Code CSS ma thuật:

.gallery {
    display: grid;
    grid-gap: 10px;
    grid-auto-flow: column;
    grid-auto-columns: calc(100% - 20px);
    padding-left: 20px;
    scroll-snap-type: x mandatory;
    overflow: auto;
}

.slide {
    scroll-snap-align: center;
}

.slide:last-child {
    position: relative;
}

.slide:last-child::after {
    content: '';
    position: absolute;
    top: 0;
    width: 20px;
    height: 100%;
}

Kết quả đáng kinh ngạc:

Tài nguyên:

  • JavaScript: 0 KB (không cần JS!)
  • CSS: 3.5 KB

Hiệu suất thần thánh:

  • FCP: 1284ms
  • LCP: 1380ms (nhanh hơn 17%)
  • CLS: 0 (không bị giật)
  • TTI: 1358ms (nhanh hơn 22%)

Google Lighthouse Score: 100/100 🎉

Bonus: Còn có thêm tính năng accessibility tự động như điều khiển bằng phím!

Bổ sung: Các kỹ thuật tối ưu nâng cao

1. Lazy Loading cho Images

<img src="placeholder.jpg" data-src="real-image.jpg" loading="lazy" alt="Product image">
// Intersection Observer API
const imageObserver = new IntersectionObserver((entries, observer) => {
    entries.forEach(entry => {
        if (entry.isIntersecting) {
            const img = entry.target;
            img.src = img.dataset.src;
            img.classList.remove('lazy');
            observer.unobserve(img);
        }
    });
});

document.querySelectorAll('img[data-src]').forEach(img => {
    imageObserver.observe(img);
});

2. Preload Critical Images

<link rel="preload" as="image" href="hero-image.jpg">

3. Responsive Images với srcset

<img 
    srcset="image-320w.jpg 320w,
            image-640w.jpg 640w,
            image-1024w.jpg 1024w"
    sizes="(max-width: 320px) 280px,
           (max-width: 640px) 600px,
           1024px"
    src="image-640w.jpg"
    alt="Responsive image">

4. CSS Animation Performance

/* Sử dụng transform thay vì thay đổi width/height */
.slide {
    transition: transform 0.3s ease;
    will-change: transform;
}

.slide:hover {
    transform: scale(1.05);
}

/* Tránh animation trên thuộc tính layout */
/* Tệ */
.bad-animation {
    transition: width 0.3s ease;
}

/* Tốt */
.good-animation {
    transition: transform 0.3s ease;
}

5. Critical CSS cho Slider

/* Inline critical CSS */
.gallery {
    display: grid;
    grid-auto-flow: column;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
}

.slide {
    scroll-snap-align: center;
    min-width: 100%;
}

WordPress Integration

Custom Post Type cho Slider

// functions.php
function create_slider_post_type() {
    register_post_type('slider',
        array(
            'labels' => array(
                'name' => 'Sliders',
                'singular_name' => 'Slider'
            ),
            'public' => true,
            'supports' => array('title', 'editor', 'thumbnail'),
            'show_in_rest' => true
        )
    );
}
add_action('init', 'create_slider_post_type');

PHP Template cho CSS Slider

<?php
// slider-template.php
$slides = get_posts(array(
    'post_type' => 'slider',
    'numberposts' => -1,
    'post_status' => 'publish'
));
?>

<div class="css-slider">
    <div class="gallery">
        <?php foreach($slides as $slide): ?>
            <div class="slide">
                <?php echo get_the_post_thumbnail($slide->ID, 'large'); ?>
                <div class="slide-content">
                    <h3><?php echo $slide->post_title; ?></h3>
                    <p><?php echo $slide->post_content; ?></p>
                </div>
            </div>
        <?php endforeach; ?>
    </div>
</div>

Best Practices cho Production

1. Performance Budget

// webpack.config.js
module.exports = {
    performance: {
        maxAssetSize: 250000, // 250kb
        maxEntrypointSize: 250000,
        hints: 'warning'
    }
};

2. Bundle Analysis

# Phân tích bundle size
npm install --save-dev webpack-bundle-analyzer
npx webpack-bundle-analyzer dist/static/js/*.js

3. Core Web Vitals Monitoring

// Google Analytics 4 Web Vitals
import {getCLS, getFID, getFCP, getLCP, getTTFB} from 'web-vitals';

function sendToGoogleAnalytics({name, delta, value, id}) {
    gtag('event', name, {
        event_category: 'Web Vitals',
        event_label: id,
        value: Math.round(name === 'CLS' ? delta * 1000 : delta),
        non_interaction: true,
    });
}

getCLS(sendToGoogleAnalytics);
getFID(sendToGoogleAnalytics);
getFCP(sendToGoogleAnalytics);
getLCP(sendToGoogleAnalytics);
getTTFB(sendToGoogleAnalytics);

Checklist ra quyết định thông minh:

  1. Thực sự cần slider không? Nếu không, đừng tự hành hạ bản thân. Dùng grid hay layout khác đi.
  2. Có thể xài CSS-only không? Bạn có thật sự cần slider tự chạy mỗi 5 giây không? (Mà nói thật, cái đó phiền lắm!)
  3. Nếu bắt buộc phải dùng JS, chọn Keen Slider hoặc Glider.js – hai cái này nhẹ mà vẫn đủ tính năng.

Ngân sách Performance cho Slider:

  • nhất: CSS-only (0kb JS)
  • Tốt: Keen Slider (~10kb)
  • Chấp nhận được: Glider.js (~20kb)
  • Nên tránh: Slick + jQuery (~126kb)

Dùng cái nào khi nào:

Tình huống Nên chọn Điểm Performance
Gallery ảnh đơn giản CSS-only 100/100
Cần hỗ trợ touch/swipe Keen Slider 95-98/100
Nhiều tính năng phức tạp Swiper.js (hiện đại) 85-90/100
Phải hỗ trợ trình duyệt cũ Owl Carousel 70-80/100

Lời khuyên cuối: Slider có thể đẹp và tiện, nhưng đừng vì nó mà làm chậm website. Performance budget nên dành cho những thứ quan trọng hơn như framework hay state management.

Công cụ kiểm tra hiệu suất hữu ích:

  1. Google PageSpeed Insights – Kiểm tra Core Web Vitals
  2. GTmetrix – Phân tích chi tiết hiệu suất
  3. WebPageTest – Test trên nhiều điều kiện mạng
  4. Chrome DevTools – Debug và profile
  5. Lighthouse CI – Tự động kiểm tra trong CI/CD

Bài viết được viết lại dựa trên kinh nghiệm đau thương với những slider nặng nề 😅và tham khảo từ itnext.io

5/5 - (72 votes)

Từ khóa
Nếu bạn thấy bài viết có ích bạn có thể chia sẻ bài viết này. Yêu cầu thêm bài viết tại đây
Đã copy
user

Yêu thích Võ thuật và Công nghệ thông tin, thích viết và chia sẽ về 2 lĩnh vực này thế thôi :D

Bài viết liên quan