Skip to content
← Wszystkie wpisy
5 min czytania Dawid Skłodowski

Od asset pipeline do Webpacka

Asset pipeline Rails zbudowano dla prostszego świata JavaScriptu. Gdy fronty wyrosły w prawdziwe aplikacje z npm i modułami, Webpack przejął pałeczkę. Deep-dive o tym, dlaczego nastąpiła zmiana, co Webpack robi inaczej i jak połączyć oba.

Latami asset pipeline Rails — Sprockets — był po prostu tym, jak obsługiwałeś JavaScript i CSS, i był w tym dobry. Konkatenował twoje pliki, minifikował je, odciskał palcem dla cache’owania i schodził ci z drogi. Ale świat JavaScriptu, dla którego go zaprojektowano, zmienił się nie do poznania, i widać napięcie. Front wyrósł z posypki jQuery w pełne aplikacje z własnym ekosystemem pakietów, systemem modułów i potrzebami budowania — a Webpack, nie Sprockets, jest narzędziem zbudowanym dla tego świata. Oto dlaczego nastąpiła zmiana i jak o niej myśleć.

Do czego zbudowano asset pipeline

Sprockets pochodzi z epoki, gdy „JavaScript” oznaczał kilka skryptów i wtyczkę jQuery czy dwie. Jego model pasuje do tego idealnie: wymieniasz pliki dyrektywami //= require, a Sprockets konkatenuje je w kolejności w jeden duży plik, gdzie wszystko dzieli pojedynczy globalny zasięg. Nie ma realnego pojęcia modułów — skrypty komunikują się przez zmienne globalne, a zarządzanie zależnościami to „upewnij się, że ten require jest przed tamtym”. Dla frontu z epoki 2012 to było dokładnie właściwe, a dla w większości renderowanej po stronie serwera aplikacji z lekkim JavaScriptem wciąż działa dobrze dziś.

Co się zmieniło: JavaScript dorósł

Trzy rzeczy się stały, których asset pipeline nigdy nie zaprojektowano:

  • Ekosystem pakietów (npm). Świat JavaScriptu ustandaryzował się na npm, z setkami tysięcy pakietów. Sprockets nie ma natywnego sposobu ich konsumowania — kończyłeś, vendorując biblioteki ręcznie albo polegając na opakowanych w gem kopiach (jquery-rails i spółka), które zostawały w tyle za prawdziwymi wersjami.
  • Prawdziwy system modułów. Moduły ES6 (import/export) i CommonJS dały JavaScriptowi porządne moduły z jawnymi zależnościami i zasięgowanymi zmiennymi — koniec globalnej zupy. Model Sprockets konkatenuj-w-jeden-globalny-zasięg nie potrafi tego wyrazić.
  • Frameworki komponentowe. React i model komponentowy ogólnie oznaczają, że front jest teraz prawdziwą aplikacją z własnymi wymaganiami budowania — JSX do skompilowania, drzewa zależności do rozwiązania, kod do podziału. To problem budowania innego rzędu niż „konkatenuj i minifikuj”.

Asset pipeline napinał się pod wszystkimi trzema. Mogłeś doczepić nowoczesny JavaScript do niego, ale walczyłeś z narzędziem, a tarcie rosło z każdą nowoczesną zależnością, którą dodawałeś.

Co Webpack robi inaczej

Webpack to bundler modułów, a zmiana modelu myślowego to kluczowa rzecz do uchwycenia. Tam, gdzie Sprockets myśli w kategoriach plików konkatenowanych w kolejności, Webpack myśli w kategoriach grafu zależności. Dajesz mu punkt wejścia, on podąża za każdym import rekurencyjnie, by zbudować kompletny graf tego, od czego zależy twój kod, i bunduje dokładnie to — transformując każdy plik przez loadery po drodze:

// punkt wejścia: app.js
import React from "react";          // rozwiązane z node_modules
import { Cart } from "./cart";      // twój własny moduł
import "./styles.css";              // tak, nawet CSS jest modułem

// webpack podąża za tymi importami, buduje graf, bunduje go
// webpack.config.js (uproszczony)
module.exports = {
  entry: "./app/javascript/packs/application.js",
  module: {
    rules: [
      { test: /\.jsx?$/, use: "babel-loader" },   // kompiluj ES6/JSX
      { test: /\.css$/,  use: ["style-loader", "css-loader"] },
    ],
  },
};

Z tego modelu opartego na grafie wypada kilka zdolności, których Sprockets po prostu nie może zaoferować:

  • Pierwszoklasowy npm. Webpack rozwiązuje importy z node_modules natywnie — cały ekosystem npm jest po prostu dostępny, w prawdziwych wersjach, z yarn/npm nimi zarządzającym.
  • Prawdziwe moduły. Każdy plik ma własny zasięg; zależności to jawne importy. Bez globalnej przestrzeni nazw, bez kolejności ręcznie.
  • Transformacja przez loadery. Babel kompiluje nowoczesny JavaScript i JSX, CSS staje się częścią grafu, obrazy mogą być inline’owane — wszystko zadeklarowane jako reguły.
  • Code splitting. Ponieważ Webpack zna pełny graf zależności, może podzielić twój bundle na kawałki i ładować je na żądanie, by użytkownik nie pobierał całej aplikacji z góry. To niemożliwe w modelu konkatenuj-wszystko.

Łączenie obu: Webpacker

Dobra wiadomość dla programistów Rails jest taka, że nie musisz wybierać wszystko-albo-nic i nie musisz okablowywać Webpacka od zera. Webpacker, stający się oficjalną częścią Rails 5.1, integruje Webpacka z aplikacją Rails z rozsądnymi defaultami — zarządza konfiguracją Webpacka, zależnościami yarn i tagami helperów, które wstawiają zbundlowane wyjście do twoich widoków, podobnie jak robił Sprockets:

<%= javascript_pack_tag "application" %>

Co kluczowe, oba mogą współistnieć podczas przejścia. Pragmatyczna ścieżka, którą bierze większość zespołów: trzymaj Sprockets do tego, w czym wciąż jest dobry — twojego CSS i prostego, globalnego, posypanego JavaScriptu — i używaj Webpackera do naprawdę aplikacjopodobnego kodu frontu (twoich komponentów React, czegokolwiek z prawdziwymi zależnościami npm i modułami). Migrujesz części, które na tym korzystają, gdy korzystają, zamiast przepisywać wszystko naraz. Z czasem mocno javascriptowa praca przenosi się do Webpacka, a Sprockets obsługuje długi ogon.

Czy złożoność jest warta?

Uczciwe zastrzeżenie: Webpack jest bardziej złożony niż Sprockets. Konfiguracja ma krzywą uczenia, folder node_modules jest ogromny, a build jest cięższy. Dla naprawdę prostej, renderowanej po stronie serwera aplikacji z odrobiną jQuery asset pipeline wciąż jest właściwym narzędziem — nie przyjmuj Webpacka jako mody, gdy twój front go nie potrzebuje. Zmiana jest warta dokładnie wtedy, gdy twój front stał się prawdziwą aplikacją: zależności npm, struktura modułów, framework komponentowy, potrzeba code splittingu. Dopasuj narzędzie do faktycznego wyrafinowania frontu.

Werdykt

Przejście z asset pipeline do Webpacka to ekosystem Rails nadrabiający to, czym stał się JavaScript. Sprockets był idealny dla plików-konkatenowanych-w-globalnym-zasięgu, a ten świat zniknął dla każdego frontu o realnej ambicji; model grafu zależności Webpacka wnosi pierwszoklasowy npm, prawdziwe moduły, transformację opartą na loaderach i code splitting — rzeczy, których nowoczesny front faktycznie potrzebuje. Webpacker (teraz oficjalny w Rails 5.1) czyni integrację bezbolesną i, co ważne, pozwala Webpackowi i Sprockets współistnieć, byś mógł migrować stopniowo. Używaj Webpacka tam, gdzie twój front jest prawdziwą aplikacją, i trzymaj Sprockets tam, gdzie wciąż jest posypką — i rozpoznaj przejście jako to, czym jest: nie Rails porzucający swoją przeszłość, lecz front, który przerósł narzędzie tak dobrze mu służące tak długo.