중요한 리소스를 인라인하기 위한 Vite 플러그인 구축

18735 단어 csshtmltypescriptvite
Vite의 기본값은 페이지 로딩을 필요 이상으로 느리게 합니다.
Vite는 다음과 같이 html을 빌드합니다.

...
<head>
...
<script type="module" crossorigin="" src="myJavascript.js"></script>
<link rel="stylesheet" href="myCss.css">
</head>
...


브라우저는 헤드에서 이 두 태그를 찾은 다음 이를 요청해야 합니다.
페이지 렌더링을 계속하기 전에 평가하십시오. 이것은 당신의
사용자는 특히 몇 초 동안 빈 화면을 응시할 것입니다.
느린 연결 또는 캐싱 전에.

이것은 좋은 기본값입니다. 대부분의 최신 웹사이트에는 약간의 JavaScript 또는
페이지를 올바르게 렌더링하는 데 중요한 CSS이지만 Vite는
중요한 것을 식별합니다. 따라서 상단에 모두 로드하여 확인합니다.
아무것도 놓치지 않습니다.

그러나 우리는 중요한 것이 무엇인지 알고 있으므로 중요하지 않은 것은 모두 연기해야 ​​합니다.
따라서 중요한 자산만으로 조기에 렌더링할 수 있습니다. 현대적인 조언은 그것들을 인라인하는 것입니다.
중요한 요소. This great article ,
chromes dev 도구 덕분에 아마 실행했을 것입니다, 다음 패턴을 제안합니다.

<style type="text/css">
.my-critical-css {...}
</style>

<link rel="preload" href="myCss.cs" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="myCss.css"></noscript>


그리고 우리는 JavaScript에 대해 훨씬 더 쉽게 동일한 작업을 수행할 수 있습니다.

<script>
    runCriticalJS();
</script>

<script type="module" crossorigin="" src="myJavascript.js" defer></script>


구현



자바스크립트



자바스크립트로 이것을 구현하는 것은 내 사이트에 매우 쉬웠습니다.
JavaScript와 내가 가지고 있는 대부분은 렌더링에 필요하지 않습니다.

중요한 것은 내 테마뿐입니다. 사용자가 테마를 변경하고
색상 기본 설정 및 해당 정보를 로컬 저장소에 저장합니다.
따라서 무엇이든 렌더링하기 전에 사용자의 기본 설정을 확인해야 합니다. 그렇지 않으면 스타일이 잘못된 콘텐츠가 깜박일 것입니다.

이를 해결하기 위해 관련 JavaScript를 최소화하고
모두 헤드의 스크립트 태그 안에 있습니다.

<script>
var themePref=window.localStorage.getItem("theme-preference");themePref&&document.querySelector(":root").setAttribute("data-theme",themePref)
</script>


그런 다음 나머지는 연기합니다.

<script type="module" crossorigin="" src="/assets/main.57999a66.js" defer></script>


이 마지막 부분을 수행하기 위해 스크립트 파일을 찾고 추가하는 작은 플러그인을 작성했습니다.
그것의 끝으로 속성을 연기하십시오.

// deferNonCriticalJS.ts
export function deferNonCriticalJS(html: string): string {
    const jsRegex = /\n.*<script type="module" /;
    const nonCriticalJs = html.match(jsRegex);
    if (nonCriticalJs === null ) {
        return html
    }

    const deferredJs = nonCriticalJs[0] + 'defer ';

    return html.replace(jsRegex, deferredJs)
}

// criticalPlugin.ts
import {deferNonCriticalJS} from './criticalJS';

export default function (criticalCssDir: string) {
    return {
        name: 'html-transform',
        async transformIndexHtml(html: string) {
            return deferNonCriticalJS(html)
        }
    }
}
// vite.config.ts
export default defineConfig({
...
    plugins: [
        critical(),
    ]
...
}


인라인 CSS



CSS에 대해 기술적으로 동일한 작업을 수행할 수 있습니다. 그러나 더 많은 CSS가 있습니다.
그리고 변할 가능성이 더 높습니다. 그래서 자동화된 솔루션이 필요합니다.

이를 위해 CSS를 중요한 디렉토리와 중요하지 않은 디렉토리로 분리하기로 결정했습니다.
그런 다음 중요하지 않은 디렉터리의 모든 파일을 반복하고 콘텐츠를 축소합니다.
모든 CSS가 포함된 문자열을 반환합니다.

export async function findAndMinifyCritical(dir: string): Promise<string> {
    let criticalCss = '';

    fs.readdirSync(dir).forEach(file => {
        const f = `${dir}/${file}`;
        const content = fs.readFileSync(f).toString();
        criticalCss += csso.minify(content).css;
    });

    return criticalCss;
}


그런 다음 중요한 CSS를 head 태그 끝에 추가합니다.

export function inlineCritical(html: string, critical: string): string {
    return html.replace('</head>', `<style>${critical}</style></head>`);
}


마지막으로 중요하지 않은 CSS를 연기합니다.

export function deferNonCritical(html: string): string {
    const styleRegx = /\n.*<link rel="stylesheet" href=".*">/;
    const nonCriticalCss = html.match(styleRegx);
    if (nonCriticalCss === null) {
        return html;
    }

    const nonCritCss = nonCriticalCss[0]
        .replace(
            'rel="stylesheet"',
            'rel="preload" as="style" onload="this.onload=null;this.rel=\'stylesheet\'"');

    return html.replace(
        styleRegx,
        nonCritCss + `<noscript>${nonCriticalCss}</noscript>`
    );
}


함께 모아서



모두 합치기 위해 이 함수를 만듭니다.

import {deferNonCritical, findAndMinifyCritical, inlineCritical} from './criticalCss';
import {deferNonCriticalJS} from './criticalJS';

export async function deferAndInline(html: string, criticalCssDir: string): Promise<string> {
    const htmlWithDefferredJs = deferNonCriticalJS(html);
    return inlineCritical(
        deferNonCritical(htmlWithDefferredJs),
        await findAndMinifyCritical(criticalCssDir)
    )
}


그리고 플러그인 내에서 호출합니다.

export default function (criticalCssDir: string) {
    return {
        name: 'defer-and-inline-critical',
        async transformIndexHtml(html: string) {
            return await deferAndInline(html, criticalCssDir)
        }
    }
}


마침내 내 구성에 추가할 수 있습니다.

plugins: [
    {
        ...critical(__dirname + '/src/criticalCss'),
        apply: 'build'
    },
]


개발 속도가 많이 느려지기 때문에 빌드에서만 실행합니다. 개선할 수 있었습니다
코드 속도, 하지만 그만한 가치가 없을 것입니다. 속도 저하의 대부분은
CSS 파일로 가득 찬 디렉토리를 반복하고 모두 읽습니다.

결론



더 큰 프로젝트에서 작업하는 경우 다음과 같은 패키지를 살펴보고 싶을 것입니다.
critical .

그러나 개인 프로젝트의 경우 또는 중요도에 대한 세밀한 제어가 필요한 경우
자산 효과 렌더링, 이와 같은 설정을 시도하면 많은 것을 배울 수 있습니다.
자신을 위해.

원본 문서 사용 가능here

좋은 웹페이지 즐겨찾기