Kann ich Webpack verwenden, um CSS und JS separat zu generieren?

Lesezeit: 9 Minuten

Benutzer-Avatar
Brent Traut

Ich habe:

  1. JS-Dateien, die ich bündeln möchte.
  2. WENIGER Dateien, die ich in CSS kompilieren möchte (auflösen von @imports in ein einzelnes Bundle).

Ich hatte gehofft, diese als zwei separate Eingaben anzugeben und zwei separate Ausgaben zu haben (wahrscheinlich über Extract-Text-Webpack-Plugin). Webpack hat alle richtigen Plugins/Loader, um die Kompilierung durchzuführen, aber es scheint die Trennung nicht zu mögen.

Ich habe Beispiele von Leuten gesehen, die ihre LESS-Dateien direkt von JS benötigen, wie z require('./app.less');, aus keinem anderen Grund, als webpack anzuweisen, diese Dateien in das Bundle aufzunehmen. Dadurch haben Sie nur einen einzigen Einstiegspunkt, aber das scheint mir wirklich falsch zu sein – warum sollte ich LESS in meinem JS benötigen, wenn es nichts mit meinem JS-Code zu tun hat?

Ich habe versucht, mehrere Einstiegspunkte zu verwenden und sowohl den Eintrag JS als auch die LESS-Hauptdatei einzureichen, aber wenn mehrere Einstiegspunkte verwendet werden, generiert Webpack ein Bündel, das das JS beim Laden nicht ausführt – es bündelt alles, weiß es aber nicht was beim Start ausgeführt werden soll.

Verwende ich das Webpack einfach falsch? Sollte ich separate Instanzen von Webpack für diese separaten Module ausführen? Sollte ich Webpack sogar für Nicht-JS-Assets verwenden, wenn ich sie nicht in mein JS mischen werde?

Sollte ich Webpack sogar für Nicht-JS-Assets verwenden, wenn ich sie nicht in mein JS mischen werde?

Vielleicht nicht. Webpack ist definitiv js-zentriert, mit der impliziten Annahme, dass Sie eine js-Anwendung erstellen. Seine Umsetzung von require() ermöglicht es Ihnen, alles als Modul zu behandeln (einschließlich Sass/LESS-Partials, JSON, so ziemlich alles) und führt automatisch Ihr Abhängigkeitsmanagement für Sie durch (alles, was Sie require gebündelt ist, und sonst nichts).

Warum sollte ich LESS in meinem JS benötigen, wenn es nichts mit meinem JS-Code zu tun hat?

Leute tun dies, weil sie einen Teil ihrer Anwendung (z. B. eine React-Komponente, eine Backbone-Ansicht) mit js definieren. Zu diesem Teil der Anwendung gehört CSS. Die Abhängigkeit von einigen externen CSS-Ressourcen, die separat erstellt und nicht direkt vom js-Modul referenziert werden, ist anfällig, schwieriger zu bearbeiten und kann dazu führen, dass Stile veraltet sind usw. Webpack ermutigt Sie, alles modular zu halten, damit Sie ein CSS haben (Sass, was auch immer) Teilweise, die zu dieser js-Komponente und der js-Komponente gehört require()Es soll die Abhängigkeit deutlich machen (für Sie und das Build-Tool, das niemals Stile erstellt, die Sie nicht benötigen).

Ich weiß nicht, ob Sie Webpack verwenden könnten, um CSS selbst zu bündeln (wenn die CSS-Dateien nicht von js referenziert werden). Ich bin mir sicher, dass Sie etwas mit Plugins usw. verkabeln könnten, aber ich bin mir nicht sicher, ob dies sofort möglich ist. Wenn Sie die CSS-Dateien von Ihrem js aus referenzieren, können Sie das CSS mit dem Extract Text-Plugin, wie Sie sagen, einfach in einer separaten Datei bündeln.

Webpack 4-Lösung mit Mini-CSS-Extrakt-Plugin

Das Webpack-Team empfiehlt die Verwendung von mini-css-extract anstelle des Plugins zum Extrahieren von Text

Mit dieser Lösung können Sie einen separaten Chunk erstellen, der nur Ihre CSS-Einträge enthält:

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'],
      },
    ],
  },
};

Hier ist ein erfundeneres Beispiel mit mehreren Einträgen aus einem meiner persönlichen Projekte:

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'
    }),
  ]
};

Mir ist klar, dass dieser Ansatz nicht sehr modular ist, aber er sollte Ihnen eine Grundlage bieten, auf der Sie aufbauen können, und ist eine hervorragende Strategie für die Einführung von Webpack in Projekten, in denen Sie Javascript und CSS nicht miteinander mischen möchten.

Der Nachteil dieses Ansatzes ist, dass der CSS-Loader immer noch eine zusätzliche Javascript-Datei generiert (unabhängig davon, ob Sie sie verwenden möchten oder nicht). Dies wird angeblich in Webpack 5 behoben.

Sollte ich Webpack sogar für Nicht-JS-Assets verwenden, wenn ich sie nicht in mein JS mischen werde?

Ich sehe nichts Falsches daran, aber letztendlich hängt es von Ihrer Toleranz für die Verwaltung mehrerer Build-Systeme ab. Für mich fühlt sich das wie ein Overkill an, also bevorzuge ich es, im Webpack-Ökosystem zu bleiben.

Weitere Informationen zu den oben beschriebenen Strategien finden Sie unter https://github.com/webpack-contrib/mini-css-extract-plugin#extracting-css-based-on-entry

  • Dies sollte heute die Standardantwort sein

    – Giona Granata

    26. April 2020 um 15:40 Uhr

  • Jetzt in der Doku: webpack.js.org/guides/entry-advanced

    – Ciro Santilli Путлер Капут 六四事

    13. Juli 2021 um 8:28 Uhr

  • Das Dummy-Js-Dateiproblem wurde anscheinend nicht in Webpack 5 behoben. Weiß jemand auch, wie man den gleichen Namen “Index” für die Js- und CSS-Ausgabedateien hat, z index.js und index.css? Gefragt unter: stackoverflow.com/questions/70698775/…

    – Ciro Santilli Путлер Капут 六四事

    13. Januar um 15:43 Uhr

  • Diese Antwort muss wahrscheinlich aktualisiert werden. Aus dem Mcep-Repo: It builds on top of a new webpack v5 feature and requires webpack 5 to work.

    – aderchox

    14. Mai um 19:33 Uhr

Ein separates CSS-Bundle kann ohne Verwendung generiert werden require('main/less) in einem Ihrer JS, aber wie Brendan im ersten Teil seiner Antwort betonte, ist Webpack nicht für ein globales CSS-Bundle konzipiert, das neben modularem JS verwendet werden kann. Es gibt jedoch einige Optionen.

Die erste besteht darin, einen zusätzlichen Einstiegspunkt für main.less hinzuzufügen und dann das Plugin Extract Text zu verwenden, um das CSS-Bundle zu erstellen:

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
        })
    ]
};

Das Problem bei dieser Methode ist, dass Sie in diesem Beispiel neben dem Bundle auch eine unerwünschte JS-Datei generieren: style.js das ist nur ein leeres Webpack-Modul.

Eine andere Möglichkeit besteht darin, die main less-Datei zu einem vorhandenen Webpack-Einstiegspunkt hinzuzufügen:

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
        })
    ]
};

Dies ist ideal, wenn Sie nur 1 Einstiegspunkt haben, aber wenn Sie mehr haben, sieht Ihre Webpack-Konfiguration etwas seltsam aus, da Sie willkürlich auswählen müssen, zu welchem ​​Einstiegspunkt die main less-Datei hinzugefügt werden soll.

  • ExtractTextPlugin ist seit 10/2017 abgekündigt (vgl npmjs.com/package/extract-text-webpack-plugin), der Nachfolger ist das Mini-CSS-Extract-Plugin (in der separaten Antwort auf diese Frage behandelt: stackoverflow.com/a/59237067/1746685)

    – Beobachter

    7. Januar um 21:32 Uhr

Um die frühere Antwort von bdmason weiter zu verdeutlichen: Es scheint, dass die wünschenswerte Konfiguration darin besteht, für jede Seite ein JS- und CSS-Bundle zu erstellen:

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

Und dann benutze die [name] Schalter:

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

Vollständige Konfiguration – mit einigen Ergänzungen, die nicht mit der Frage verbunden sind (wir verwenden tatsächlich SASS anstelle von 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
            }
        })
    ]
}
];

Benutzer-Avatar
geochanto

Ja, das ist möglich, aber wie andere bereits sagten, benötigen Sie dafür zusätzliche Pakete (siehe devDependencies unter package.json). Hier ist der Beispielcode, den ich zum Kompilieren meines Bootstrap SCSS –> CSS und Bootstrap JS –> JS verwendet habe.

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'
                    }
                ]
            }
        ]
    }
};

zusätzliche postcss.config.js-Datei:

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

Paket.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"
  }
}

Sehen Sie sich das Tutorial hier an: https://florianbrinkmann.com/en/4240/sass-webpack

Benutzer-Avatar
Repastifizierer

Wie andere erwähnt, können Sie ein Plugin verwenden.

ExtractTextPlugin ist veraltet.

Sie können die derzeit empfohlenen verwenden MiniCssExtractPlugin in Ihrer Webpack-Konfiguration:

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

Benutzer-Avatar
Ian Warner

Sie können Ihre Less require-Anweisungen auch in Ihre Eintrags-JS-Datei einfügen:

in body.js

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

Dann im Webpack

  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
})

1229200cookie-checkKann ich Webpack verwenden, um CSS und JS separat zu generieren?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy