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-railsi 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 znode_modulesnatywnie — cały ekosystem npm jest po prostu dostępny, w prawdziwych wersjach, zyarn/npmnimi 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.