درس هفتم: بررسی فایل CSS و مبحث Browser Caching

Check CSS File + Browser Caching

23 شهریور 1399
درسنامه درس 7 از سری آموزش Webpack
02-Webpack-4-The-Complete-Tutorial-For-Beginners-lesson7

در جلسه قبل تمام استایل های خود را درون فایلی به نام styles.css قرار دادیم اما به خود این فایل نگاهی نکردیم. بنابراین به پوشه dist رفته و فایل styles.css را باز کنید تا محتویاتش را ببینیم:

.hello-world-button {
    font-size: 20px;
    padding: 7px 15px;
    background: green;
    color: white;
    outline: none;
}

.hello-world-text {
    color: red;
    font-weight: bold;
}

بنابراین همه چیز مرتب است و مشکلی نداریم اما سوالی پیش می آید. پروژه ما فقط یک فایل CSS دارد اما اگر چند فایل داشته باشیم آیا همه آن ها درون styles.css قرار خواهند گرفت؟ برای تست کردن این موضوع یک پوشه به نام heading در پوشه components می سازیم. این پوشه باید حاوی فایلی به نام heading.js باشد که محتویات آن به شکل زیر است:

import './heading.scss';

class Heading {
    render(pageName) {
        const h1 = document.createElement('h1');
        const body = document.querySelector('body');
        h1.innerHTML = 'Webpack is awesome. This is "' + pageName + '"page';
        body.appendChild(h1);
    }
}

export default Heading;

همانطور که می بینید من ابتدا فایل استایلی به نام heading.scss را وارد این فایل کرده ام. این فایل هنوز وجود ندارد اما بعدا آن را می سازیم. سپس یک عنصر h1 را ساخته ام و به آن متن خودم را داده ام. بر اساس سلیقه می توانید هر متنی را که خواستید به آن بدهید. در نهایت h1 را به body متصل کرده ام. توجه داشته باشید که متد render یک پارامتر ورودی دارد که قرار است شماره صفحه باشد. در آخر کار این کلاس را export کرده ایم تا بتوانیم در فایل های دیگر از آن استفاده کنیم. حالا فایل heading.scss را نیز در همان پوشه (heading) بسازید و محتویات زیر را برایش قرار دهید:

h1 {
    color: gray;
}

حالا به index.js رفته و این کامپوننت را وارد آن کنید:

import HelloWorldButton from './components/hello-world-button/hello-world-button.js';
import Heading from './components/heading/heading.js';

const heading = new Heading();
heading.render('the second')


const helloWorldButton = new HelloWorldButton();
helloWorldButton.render();

همانطور که می بینید من heading.js را وارد کرده و بعد از ساختن یک نمونه از آن، متد render را در آن صدا زده ام. حالا دستور npm run build را اجرا کرده و به فایل styles.css برویم، متوجه می شویم که استایل های جدید ما نیز درون همین فایل قرار گرفته اند. بنابراین باید حواستان را جمع کنید که استایل های شما سراسری خواهند بود؛ مثلا استایلی که برای h1 نوشته ام (در فایل heading.scss) روی تمام تگ های h1 اعمال خواهد شد!

Browser Caching چیست؟

مرورگرها برای اینکه هر بار مجبور به دانلود فایل های تکراری نشوند بر اساس دستورات سایتی که از آن بازدید می کنید، برخی از فایل های آن سایت را دانلود می کنند. شاید بگویید مرورگرها که تمام فایل های آن صفحه را دانلود می کنند بنابراین تفاوت چیست؟ تفاوت آنجاست که اگر سرور یک header مربوط به cache کردن را به همراه آن فایل به مرورگر بدهد، فایل های مشخص شده ای که دانلود شده اند در مرورگر شما و در کامپیوتر شما باقی می مانند تا در دفعات بعد نیازی به دانلود مجدد آن ها نباشد. اگر از caching برای کاربران استفاده نکنید، کاربر باید هر بار که به سایت شما می آید تمام فایل ها را از ابتدا دانلود کند. به حافظه ای که فایل های سایت در آن قرار می گیرد cache مرورگر می گویند.

مشکل اینجاست که ما در هنگام توسعه و کدنویسی برای سایت، دائما در حال تغییر دادن کدها هستیم تا ببینیم چه حالتی برای ما ایده آل است. حالا که مرورگر ها معمولا به صورت خودکار برخی از محتوا را کش می کنند، با تغییر دادن کدها دیگر شاهد تغییرات نخواهیم بود چرا که مرورگر به جای آنکه از فایل جدید استفاده کند از همان فایلی استفاده می کند که آن را درون حافظه کش خود دارد.

برای حل این مشکل از متدی به نام url fingerprinting استفاده می کنند که یعنی با هر خروجی جدید، نام فایل را نیز عوض می کنیم تا مرورگر بفهمد این یک فایل جدید است و نباید آن را از کش بردارد. البته تغییر دادن نام فایل در هر بار کار سختی است به همین دلیل Webpack می تواند این کار را به صورت خودکار برای ما انجام بدهد. به فایل webpack.config.js بروید تا این مورد را به شما نشان بدهم. در حال حاضر output ما در این فایل به شکل زیر است:

    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, './dist'),
        publicPath: 'dist/'
    },

من می خواهم کاری کنم که اگر محتوای فایل تغییر کرد، webpack نام فایل را تغییر دهد، بنابراین می گویم:

    output: {
        filename: 'bundle.[contenthash].js',
        path: path.resolve(__dirname, './dist'),
        publicPath: 'dist/'
    }

با اضافه کردن مقدار contenthash درون علامت های [] به webpack گفته ایم که بر اساس محتوای درون فایل bundle.js یک هش بساز (معمولا از نوع md5) و آن را بین نام فایل و پسوند آن قرار بده. با این کار اگر کدهای درون bundle.js تغییر کند، نام فایل ما هم تغییر می کند چرا که هش مورد نظر بر اساس محتوای درون فایل تولید می شود. از طرفی گفتیم که اگر نام فایلی در حافظه کش مرورگر نباشد، مرورگر آن را دوباره دانلود خواهد کرد. حالا دستور npm run build را اجرا کنید تا نتیجه را ببینیم.

با اجرای این دستور و مراجعه به پوشه dist می بینیم که نام فایل bundle.js ساختاری شبیه به ساختار زیر پیدا کرده است:

bundle.47d337d1feb332a85ffd.js

این عدد اضافه شده، همان هش md5 ما است. اگر حالا دوباره npm run build را اجرا کنید نام این فایل تغییری نخواهد کرد چرا که کدهای درون bundle.js تغییر نکرده است. حالا اگر به فایل index.js بروید و یک خط کد ساده (مثلا یک Console.log) را اضافه کنید، با اجرای npm run build نام فایل bundle.js تغیر خواهد کرد. توجه داشته باشید که فایل جدید، فایل قبلی را replace نمی کند چرا که نام های یکسانی ندارند. در جلسه بعد در مورد روش رفع این مشکل صحبت خواهیم کرد.

همین کار را می توان برای فایل های CSS نیز انجام داد:

    plugins: [
        new TerserPlugin(),
        new MiniCssExtractPlugin({
            filename: 'styles.[contenthash].css'
        })
    ]

اگر دوباره دستور npm run build را اجرا کنید یک فایل CSS جدید نیز برایتان ایجاد می شوند.

تمام فصل‌های سری ترتیبی که روکسو برای مطالعه‌ی دروس سری آموزش Webpack توصیه می‌کند:
نویسنده شوید
دیدگاه‌های شما

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.