Rails 및 Webpacker를 사용한 중요한 CSS - SprocketsLess 1부

이것은 모든 자산을 Webpacker로 옮길 때 사용할 수 있는 새로운 고급 사용법에 대한 일련의 기사 중 첫 번째입니다. 첫 번째 부분에서는 CSS 크기를 최적화하는 방법을 살펴보겠습니다.

우리 모두는 빠르고 안정적인 웹 페이지를 원합니다. Page Speed Audit을 수행할 때 권장 사항으로 자주 나오는 것은 중요한 CSS입니다. 중요한 CSS, 특히 접힌 부분 위의 중요한 CSS는 페이지 상단(접힌 부분 위)을 렌더링하는 데 필요한 최소한의 CSS를 인라인(HTML에서)하는 기능입니다. 나는 한동안 Rails 앱에서 이것을 달성하기 위한 쉬운 해결책을 찾았지만 실제로 성공하지 못했습니다.

Webpack(예: Rails의 Webpacker)의 장점 중 하나는 주변의 모든 에코 시스템입니다. Rails의 JS 측은 대부분 문서화되어 있지만 CSS 및 이미지에 사용할 수 있는 많은 도구도 있습니다.

몇 달 전에 나는 GoRails 을 사용하는 훌륭한 비디오를 PurgeCss in a Rails application 에서 발견했습니다.

PurgeCSS 이해





PurgeCSS의 글로벌 개념은 일부 CSS 클래스가 사용되는 모든 파일을 PurgeCSS에 공급한다는 것입니다(보통 .html , html.erb , .js ). PurgesCSS는 CSS 선택자가 될 수 있는 모든 토큰 목록을 생성합니다.

반면에 Webpacker는 mini-css-extract-plugin를 사용하여 CSS 번들을 만듭니다. PurgeCSS는 토큰 목록을 추출합니다.

결과는 두 토큰 목록의 교차점입니다.

여러 규칙이 있는 여러 팩



Webpacker를 사용하면 여러 개의 팩을 쉽게 가질 수 있습니다. some-pack.js 디렉토리에 새 app/javascript/packs 파일을 생성하기만 하면 됩니다.

우리가 할 일에 대한 글로벌 아이디어는 다음과 같습니다.
  • 일부 CSS 가져오기만 있는 두 번째 팩critical.js을 정의합니다.
  • PostCss에서 PurgesCss 프로세스를 분할하여 critical.css에 훨씬 더 엄격한 규칙을 적용합니다.
  • HTML에서 중요한 CSS를 Dev.to로 인라인합니다.
  • 메인application.css을 지연 로드합니다.

  • critical.js 진입점



    다음과 같은 application.js가 주어집니다.

    // app/javascript/packs/application.js
    
    require("@rails/ujs").start();
    require("local-time").start();
    require("turbolinks").start();
    
    window.Rails = Rails;
    
    // import CSS
    import "stylesheets/application.scss";
    
    // import Stimulus controllers
    import "controllers/index";
    
    // import vendor JS
    import "bootstrap";
    

    기본 진입점은 일반적으로 다음과 같이 보이는 기본 application.scss를 가져옵니다.

    // app/javascript/stylesheets/application.scss
    
    // Fonts
    @import "config/fonts";
    
    // Graphical variables
    @import "config/colors";
    
    // Vendor
    @import "~bootstrap/scss/functions";
    @import "config/bootstrap_variables";
    @import "~bootstrap/scss/bootstrap";
    
    $fa-font-path: "~@fortawesome/fontawesome-free/webfonts";
    @import "~@fortawesome/fontawesome-free/scss/fontawesome";
    @import "~@fortawesome/fontawesome-free/scss/brands";
    
    // Components
    @import "components/index";
    
    // layouts
    @import "layouts/sticky-footer";
    

    우리는 매우 기본적인 critical.js 를 생성할 수 있으며, 새로운 critical.scss 스타일시트를 가져오기만 하면 됩니다.

    // app/javascript/packs/critical.js
    import "stylesheets/critical.scss";
    
    critical.scss 파일에서 PurgeCSS가 더 나은 작업을 수행하는 데 도움이 되도록 내부에 넣는 항목을 좀 더 선택적으로 시작할 수 있습니다. (약간의 차이가 있습니다)

    // colors
    @import "config/colors";
    
    // vendor
    @import "~bootstrap/scss/functions";
    @import "config/bootstrap_variables";
    @import "config/bootstrap_critical"; // pick only the Bootstrap module you need
    
    // Components
    @import "components/banner"; //just pick the components you need for the homepage
    

    PostCSS / PurgeCSS 구성



    그럼 중요한 부분입니다. 파일마다 다른 규칙을 적용하도록 PurgeCSS에 지시해야 합니다. 다행스럽게도 PostCSS에는 정보로 가득 찬 컨텍스트가 있습니다.

    따라서 정보 컨텍스트를 환경에 전달할 수 있습니다.

    module.exports = ctx => environment(ctx);
    

    환경에 컨텍스트 변수 추가

    const environment = ctx => ({
      plugins: [
        require("postcss-import"),
        require("postcss-flexbugs-fixes"),
        require("postcss-preset-env")({
          autoprefixer: {
            flexbox: "no-2009"
          },
          stage: 3
        }),
        purgeCss(ctx)
      ]
    });
    

    이 컨텍스트로 PurgeCss 플러그인을 호출합니다.

    const purgeCss = ({ file }) => {
      return require("@fullhuman/postcss-purgecss")({
        content: htmlFilePatterns(file.basename),
        defaultExtractor: content => content.match(/[A-Za-z0-9-_:/]+/g) || []
      });
    };
    

    이제 PurgeCss에 파일 이름이 있으므로 각 파일에 대해 다른 규칙을 지정할 수 있습니다. 내 중요한 CSS의 경우 홈 페이지와 관련된 페이지와 다른 모든 파일에 대한 일반적인 패턴 세트만 지정합니다.

    const htmlFilePatterns = filename => {
      switch (filename) {
        case "critical.scss":
          return [
            "./app/views/pages/index.html.erb",
            "./app/views/shared/_navbar.html.erb",
            "./app/views/layouts/application.html.erb"
          ];
        default:
          return [
            "./app/**/*.html.erb",
            "./config/initializers/simple_form_bootstrap.rb",
            "./app/helpers/**/*.rb",
            "./app/javascript/**/*.js"
          ];
      }
    };
    

    그래서 전체, 그것은 그렇게 보인다

    // postcss.config.js
    
    const environment = ctx => ({
      plugins: [
        require("postcss-import"),
        require("postcss-flexbugs-fixes"),
        require("postcss-preset-env")({
          autoprefixer: {
            flexbox: "no-2009"
          },
          stage: 3
        }),
        purgeCss(ctx)
      ]
    });
    
    const purgeCss = ({ file }) => {
      return require("@fullhuman/postcss-purgecss")({
        content: htmlFilePatterns(file.basename),
        defaultExtractor: content => content.match(/[A-Za-z0-9-_:/]+/g) || [],
      });
    };
    
    const htmlFilePatterns = filename => {
      switch (filename) {
        case "critical.scss":
          return [
            "./app/views/pages/index.html.erb",
            "./app/views/shared/_navbar.html.erb",
            "./app/views/layouts/application.html.erb"
          ];
        default:
          return [
            "./app/**/*.html.erb",
            "./config/initializers/simple_form_bootstrap.rb",
            "./app/helpers/**/*.rb",
            "./app/javascript/**/*.js"
          ];
      }
    };
    
    module.exports = ctx => environment(ctx);
    

    결과



    작은 테스트에서 나는 그 결과를 얻었습니다.
  • 초기 번들 크기 32kb
  • 제거 CSS를 사용하면 9kb로 떨어졌습니다
  • .
  • 내 critical.css는 3kb에 불과합니다!

  • BAMMMMM 🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉

    Webpacker의 인라인 CSS



    내 CSS 파일을 HTML에 인라인하기 위해 머리를 약간 긁었습니다. Stackoverflow 덕분에 여기에서 도움을 받을 수 있었습니다.

    <% if current_page?(root_path) %>
      <!-- Inline the critical CSS -->
      <style>
        <%= File.read(File.join(Rails.root, 'public', Webpacker.manifest.lookup('critical.css'))).html_safe %>
      </style>
    
      <!-- Lazy load the rest with loadCSS -->
      <link rel="preload" href="<%= Webpacker.manifest.lookup('application.css') %>" as="style" onload="this.rel='stylesheet'">
      <noscript><link rel="stylesheet" href="<%= Webpacker.manifest.lookup('application.css') %>"></noscript>
    <% else %>
      <%= stylesheet_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
    <% end %>
    

    짜잔!!!!



    데모: https://sprockets-less-rails6.herokuapp.com
    소스 코드: https://github.com/adrienpoly/sprockets-less-rails6

    좋은 웹페이지 즐겨찾기