Czy mogę używać webpacka do osobnego generowania CSS i JS?


86

Mam:

  1. Pliki JS, które chcę spakować.
  2. MNIEJ plików, które chcę skompilować do CSS (rozwiązywanie @importów w jeden pakiet).

Miałem nadzieję, że określę je jako dwa oddzielne wejścia i będę mieć dwa oddzielne wyjścia (prawdopodobnie za pomocą wtyczki extract-text-webpack-plugin). Webpack ma wszystkie odpowiednie wtyczki / programy ładujące do kompilacji, ale wygląda na to, że nie podoba mu się separacja.

Widziałem przykłady ludzi, którzy żądali swoich plików LESS bezpośrednio z JS, na przykład require('./app.less');bez innego powodu niż po to, aby webpack włączył te pliki do pakietu. Pozwala to na posiadanie tylko jednego punktu wejścia, ale wydaje mi się to naprawdę złe - dlaczego miałbym wymagać MNIEJ w moim JS, skoro nie ma to nic wspólnego z moim kodem JS?

Próbowałem użyć wielu punktów wejścia, przekazując zarówno wejściowy JS, jak i główny plik LESS, ale podczas korzystania z wielu punktów wejścia webpack generuje pakiet, który nie wykonuje JS podczas ładowania - zawiera wszystko, ale nie wie co należy wykonać przy starcie.

Czy po prostu używam WebPacka źle? Czy powinienem uruchamiać oddzielne wystąpienia pakietu WebPack dla tych oddzielnych modułów? Czy powinienem używać pakietu webpack dla zasobów innych niż JS, jeśli nie zamierzam ich mieszać w moim JS?


Mogę polecić następujący tutorial medium.freecodecamp.org/…
wilo087

Odpowiedzi:


29

Czy powinienem używać pakietu webpack dla zasobów innych niż JS, jeśli nie zamierzam ich mieszać w moim JS?

Może nie. Webpack jest zdecydowanie skoncentrowany na js, z domniemanym założeniem, że to, co tworzysz, to aplikacja js. Jego implementacja require()pozwala traktować wszystko jako moduł (w tym części składowe Sass / LESS, JSON, prawie wszystko) i automatycznie zarządza zależnościami (wszystko, co requirejest w pakiecie, i nic więcej).

dlaczego miałbym wymagać MNIEJ w moim JS, skoro nie ma to nic wspólnego z moim kodem JS?

Ludzie robią to, ponieważ definiują część swojej aplikacji (np. Komponent Reacta, Widok Backbone) za pomocą js. Ta część aplikacji ma CSS, który jest z nią związany. W zależności od jakiegoś zewnętrznego zasobu CSS, który jest zbudowany osobno i nie jest bezpośrednio powiązany z modułem js, jest delikatny, trudniejszy w obsłudze i może prowadzić do przestarzałych stylów itp. Webpack zachęca do zachowania wszystkiego modułowego, więc masz CSS (Sass, cokolwiek) częściowe, które pasują do tego komponentu js, a komponent js require()jest to, aby jasno określić zależność (dla ciebie i dla narzędzia do budowania, które nigdy nie buduje stylów, których nie potrzebujesz).

Nie wiem, czy możesz użyć webpacka do samodzielnego pakowania CSS (gdy pliki CSS nie są przywoływane z żadnego js). Jestem pewien, że możesz coś podłączyć za pomocą wtyczek itp., Ale nie jestem pewien, czy jest to możliwe po wyjęciu z pudełka. Jeśli odwołujesz się do plików CSS z twojego js, ​​możesz łatwo spakować CSS w oddzielny plik za pomocą wtyczki Extract Text, jak mówisz.


18

Oddzielny pakiet CSS można wygenerować bez użycia require('main/less)w żadnym ze swoich JS, ale jak zauważył Brendan w pierwszej części swojej odpowiedzi Webpack nie jest przeznaczony do globalnego pakietu CSS, który mógłby towarzyszyć modułowemu JS, jednak jest kilka opcji .

Pierwszym jest dodanie dodatkowego punktu wejścia dla main.less, a następnie użyj wtyczki Extract Text, aby utworzyć pakiet CSS:

var webpack = require('webpack'),
    ExtractTextPlugin = require("extract-text-webpack-plugin");

module.exports = {
    entry: {
        home: [
            'js/common',
            'js/homepage'
        ],
        style: [
            'styles/main.less'
        ]
    },
    output: {
        path: 'dist',
        filename: "[name].min.js"
    },
    resolve: {
        extensions: ["", ".js"]
    },
    module: {
        loaders: [{
            test: /\.less$/,
            loader: ExtractTextPlugin.extract("style", "css", "less")
        }]
    },
    plugins: [
        new ExtractTextPlugin("[name].min.css", {
            allChunks: true
        })
    ]
};

Problem z tą metodą polega na tym, że generujesz również niechciany plik JS, a także pakiet, w tym przykładzie: style.jsktóry jest po prostu pustym modułem Webpack.

Inną opcją jest dodanie głównego pliku less do istniejącego punktu wejścia Webpack:

var webpack = require('webpack'),
    ExtractTextPlugin = require("extract-text-webpack-plugin");

module.exports = {
    entry: {
        home: [
            'js/common',
            'js/homepage',
            'styles/main.less'
        ],
    },
    output: {
        path: 'dist',
        filename: "[name].min.js"
    },
    resolve: {
        extensions: ["", ".js"]
    },
    module: {
        loaders: [{
            test: /\.less$/,
            loader: ExtractTextPlugin.extract("style", "css", "less")
        }]
    },
    plugins: [
        new ExtractTextPlugin("[name].min.css", {
            allChunks: true
        })
    ]
};

Jest to idealne rozwiązanie, jeśli masz tylko 1 punkt wejścia, ale jeśli masz więcej, konfiguracja Webpack będzie wyglądać nieco dziwnie, ponieważ będziesz musiał arbitralnie wybrać punkt wejścia, do którego chcesz dodać główny plik less.


10

Aby dokładniej wyjaśnić poprzednią odpowiedź bdmason - wydaje się, że pożądaną konfiguracją byłoby utworzenie pakietu JS i CSS dla każdej strony, na przykład:

 entry: {
        Home: ["./path/to/home.js", "./path/to/home.less"],
        About: ["./path/to/about.js", "./path/to/about.less"]
    }

A potem użyj [name]przełącznika:

output: {
        path: "path/to/generated/bundles",
        filename: "[name].js"
    },
plugins: new ExtractTextPlugin("[name].css")

Pełna konfiguracja - z kilkoma dodatkami nie związanymi z pytaniem (w rzeczywistości używamy SASS zamiast LESS):

var ExtractTextPlugin = require("extract-text-webpack-plugin");
var debug = process.env.NODE_ENV !== "production";
var webpack = require('webpack');
require('babel-polyfill');

module.exports = [{
    devtool: debug ? "inline-sourcemap" : null,
    entry: {
        Home: ['babel-polyfill', "./home.js","path/to/HomeRootStyle.scss"],
        SearchResults: ['babel-polyfill', "./searchResults.js","path/to/SearchResultsRootStyle.scss"]
    },
    module: {
        loaders: [
            {
                test: /\.jsx?$/,
                exclude: /(node_modules|bower_components)/,
                loader: 'babel-loader',
                query: {
                    presets: ['react', 'es2015'],
                    plugins: ['react-html-attrs', 'transform-class-properties', 'transform-decorators-legacy']
                }
            },
            {
                test: /\.scss$/,
                loader: ExtractTextPlugin.extract("style-loader","css-raw-loader!sass-loader")
            }
        ]
    },
    output: {
        path: "./res/generated",
        filename: "[name].js"
    },
    plugins: debug ? [new ExtractTextPlugin("[name].css")] : [
        new ExtractTextPlugin("[name].css"),
        new webpack.DefinePlugin({
            'process.env':{
                'NODE_ENV': JSON.stringify('production')
            }
        }),
        new webpack.optimize.UglifyJsPlugin({
            compress:{
                warnings: true
            }
        })
    ]
}
];

9

rozwiązanie webpack 4 z wtyczką mini-css-extract

zespół webpacka zaleca używanie mini-css-extract zamiast wtyczki wyodrębniania tekstu

to rozwiązanie pozwala na stworzenie oddzielnej porcji zawierającej tylko Twoje wpisy css:

const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

function recursiveIssuer(m) {
  if (m.issuer) {
    return recursiveIssuer(m.issuer);
  } else if (m.name) {
    return m.name;
  } else {
    return false;
  }
}

module.exports = {
  entry: {
    foo: path.resolve(__dirname, 'src/foo'),
    bar: path.resolve(__dirname, 'src/bar'),
  },
  optimization: {
    splitChunks: {
      cacheGroups: {
        fooStyles: {
          name: 'foo',
          test: (m, c, entry = 'foo') =>
            m.constructor.name === 'CssModule' && recursiveIssuer(m) === entry,
          chunks: 'all',
          enforce: true,
        },
        barStyles: {
          name: 'bar',
          test: (m, c, entry = 'bar') =>
            m.constructor.name === 'CssModule' && recursiveIssuer(m) === entry,
          chunks: 'all',
          enforce: true,
        },
      },
    },
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name].css',
    }),
  ],
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader'],
      },
    ],
  },
};

Oto bardziej wymyślny przykład wykorzystujący wiele wpisów z jednego z moich osobistych projektów:

const ManifestPlugin = require('webpack-manifest-plugin')
const webpack = require('webpack')
const path = require('path')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const VENDOR = path.join(__dirname, 'node_modules')
const LOCAL_JS = path.join(__dirname, 'app/assets/js')
const LOCAL_SCSS = path.join(__dirname, 'app/assets/scss')
const BUILD_DIR = path.join(__dirname, 'public/dist')
const EXTERNAL = path.join(__dirname, 'public/external')

function recursiveIssuer(m) {
  if (m.issuer) {
    return recursiveIssuer(m.issuer);
  } else if (m.name) {
    return m.name;
  } else {
    return false;
  }
}

module.exports = {
  entry: {
    vendor: [
      `${VENDOR}/jquery/dist/jquery.js`,
      `${VENDOR}/codemirror/lib/codemirror.js`,
      `${VENDOR}/codemirror/mode/javascript/javascript.js`,
      `${VENDOR}/codemirror/mode/yaml/yaml.js`,
      `${VENDOR}/zeroclipboard/dist/ZeroClipboard.js`,
    ],
    app: [
      `${LOCAL_JS}/utils.js`,
      `${LOCAL_JS}/editor.js`,
      `${LOCAL_JS}/clipboard.js`,
      `${LOCAL_JS}/fixtures.js`,
      `${LOCAL_JS}/ui.js`,
      `${LOCAL_JS}/data.js`,
      `${LOCAL_JS}/application.js`,
      `${LOCAL_JS}/google.js`
    ],
    'appStyles': [
      `${EXTERNAL}/montserrat.css`,
      `${EXTERNAL}/icons.css`,
      `${VENDOR}/purecss/pure-min.css`,
      `${VENDOR}/purecss/grids-core-min.css`,
      `${VENDOR}/purecss/grids-responsive-min.css`,
      `${VENDOR}/codemirror/lib/codemirror.css`,
      `${VENDOR}/codemirror/theme/monokai.css`,
    ]
  },
  optimization: {
    splitChunks: {
      cacheGroups: {
        appStyles: {
          name: 'appStyles',
          test: (m, c, entry = 'appStyles') =>
            m.constructor.name === 'CssModule' && recursiveIssuer(m) === entry,
          chunks: 'all',
          enforce: true,
        },
      },
    },
  },
  module:  {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: [ 'script-loader'],
      },
      {
        test: /\.(scss|css)$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
        ],
      },
    ],
  },
  mode: 'development',
  resolve: {
    extensions: ['.js', '.css', '.scss']
  },
  output: {
    path: BUILD_DIR,
    filename: "[name].[chunkhash].js",
  },
  plugins: [
    new ManifestPlugin(),
    new MiniCssExtractPlugin({
      filename: '[name].css'
    }),
  ]
};

Zdaję sobie sprawę, że to podejście nie jest zbyt modularne, ale powinno dać ci podstawę do budowania i jest doskonałą strategią wdrażania pakietu webpack w projektach, w których nie chcesz mieszać javascript i css.

Wadą tego podejścia jest to, że css-loader nadal generuje dodatkowy plik javascript (niezależnie od tego, czy zdecydujesz się go użyć, czy nie), co prawdopodobnie zostanie naprawione w pakiecie webpack 5 .

Czy powinienem używać pakietu webpack dla zasobów innych niż JS, jeśli nie zamierzam ich mieszać w moim JS?

Nie widzę w tym nic złego, ale ostatecznie zależy to od twojej tolerancji dla zarządzania wieloma systemami kompilacji. Wydaje mi się, że to przesada, więc wolę pozostać w ekosystemie webpacka.

Aby uzyskać więcej informacji na temat strategii opisanych powyżej, zobacz https://github.com/webpack-contrib/mini-css-extract-plugin#extracting-css-based-on-entry


to powinna być domyślna odpowiedź dzisiaj
Giona Granata

8

Tak, jest to możliwe, ale podobnie jak inni powiedzieli, że będziesz potrzebować do tego dodatkowych pakietów (zobacz devDependencies w package.json). oto przykładowy kod, którego użyłem do skompilowania mojego bootstrapu SCSS -> CSS i Bootstrap JS -> JS.

webpack.config.js:

module.exports = {
    mode: process.env.NODE_ENV === 'production' ? 'production' : 'development',
    entry: ['./src/app.js', './src/scss/app.scss'],
    output: {
    path: path.resolve(__dirname, 'lib/modules/theme/public'),
    filename: 'js/bootstrap.js'
    },
    module: {
        rules: [
            {
                test: /\.scss$/,
                use: [
                    {
                        loader: 'file-loader',
                        options: {
                            name: 'css/bootstrap.css',
                        }
                    },
                    {
                        loader: 'extract-loader'
                    },
                    {
                        loader: 'css-loader?-url'
                    },
                    {
                        loader: 'postcss-loader'
                    },
                    {
                        loader: 'sass-loader'
                    }
                ]
            }
        ]
    }
};

dodatkowy plik postcss.config.js:

module.exports = {
    plugins: {
        'autoprefixer': {}
    }
}

package.json:

{
  "main": "app.js",
  "scripts": {
    "build": "webpack",
    "start": "node app.js"
  },
  "author": "P'unk Avenue",
  "license": "MIT",
  "dependencies": {
    "bootstrap": "^4.1.3",
  },
  "devDependencies": {
    "autoprefixer": "^9.3.1",
    "css-loader": "^1.0.1",
    "exports-loader": "^0.7.0",
    "extract-loader": "^3.1.0",
    "file-loader": "^2.0.0",
    "node-sass": "^4.10.0",
    "popper.js": "^1.14.6",
    "postcss-cli": "^6.0.1",
    "postcss-loader": "^3.0.0",
    "sass-loader": "^7.1.0",
    "style-loader": "^0.23.1",
    "webpack": "^4.26.1",
    "webpack-cli": "^3.1.2"
  }
}

Zobacz samouczek tutaj: https://florianbrinkmann.com/en/4240/sass-webpack


1

Jak wspominali inni, możesz użyć wtyczki.

ExtractTextPlugin jest przestarzałe.

Możesz użyć aktualnie zalecanego MiniCssExtractPluginw konfiguracji swojego pakietu:

module.exports = {
     entry: {
        home: ['index.js', 'index.less']
     },
     plugins: [
            new MiniCssExtractPlugin({
                filename: '[name].css',
            }),
     ]
}

0

Możesz również umieścić swoje instrukcje Less require w swoim wejściowym pliku JS również:

w body.js

// CSS
require('css/_variable.scss')
require('css/_npm.scss')
require('css/_library.scss')
require('css/_lib.scss')

Następnie w pakiecie internetowym

  entry: {
    body: [
      Path.join(__dirname, '/source/assets/javascripts/_body.js')
    ]
  },

const extractSass = new ExtractTextPlugin({
  filename: 'assets/stylesheets/all.bundle.css',
  disable: process.env.NODE_ENV === 'development',
  allChunks: true
})
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.