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


86

Ich habe:

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

Ich hatte gehofft, diese als zwei separate Eingänge anzugeben und zwei separate Ausgänge zu haben (wahrscheinlich über das Extract-Text-Webpack-Plugin). Webpack verfügt über alle geeigneten Plugins / Loader zum Kompilieren, scheint die Trennung jedoch nicht zu mögen.

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

Ich habe versucht, mehrere Einstiegspunkte zu verwenden, wobei sowohl der Eintrag JS als auch die Hauptdatei WENIGER übergeben wurden. Bei Verwendung mehrerer Einstiegspunkte generiert das Webpack jedoch ein Bundle, das das JS beim Laden nicht ausführt - es bündelt alles, weiß es aber nicht Was soll beim Start ausgeführt werden.

Benutze ich Webpack nur falsch? Sollte ich separate Instanzen von Webpack für diese separaten Module ausführen? Sollte ich Webpack überhaupt für Nicht-JS-Assets verwenden, wenn ich sie nicht in mein JS mischen möchte?


Ich kann das folgende Tutorial medium.freecodecamp.org/…
wilo087

Antworten:


29

Sollte ich Webpack überhaupt für Nicht-JS-Assets verwenden, wenn ich sie nicht in mein JS mischen möchte?

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

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

Die Benutzer tun dies, weil sie einen Teil ihrer Anwendung (z. B. eine React-Komponente, eine Backbone-Ansicht) mit js definieren. Das Teil der Anwendung hat CSS, das dazu gehört. Abhängig von einer externen CSS-Ressource, die separat erstellt und nicht direkt vom js-Modul referenziert wird, ist sie fragil, schwieriger zu bearbeiten und kann dazu führen, dass Stile nicht mehr aktuell sind usw. Webpack empfiehlt Ihnen, alles modular zu halten, sodass Sie ein CSS haben (Sass, was auch immer) Teil, der zu dieser js-Komponente gehört, und die js-Komponente require(), um die Abhängigkeit zu verdeutlichen (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 von keinem js referenziert werden). Ich bin mir sicher, dass Sie etwas mit Plugins usw. verbinden könnten, aber nicht sicher, ob dies sofort möglich ist. Wenn Sie auf die CSS-Dateien von Ihrem js verweisen, können Sie das CSS einfach mit dem Plugin Text extrahieren in eine separate Datei bündeln, wie Sie sagen.


18

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

Der erste besteht darin, einen zusätzlichen Einstiegspunkt für main.less hinzuzufügen und dann das Plugin "Text extrahieren" 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 neben dem Bundle auch eine unerwünschte JS-Datei generieren. In diesem Beispiel style.jshandelt es sich lediglich um ein leeres Webpack-Modul.

Eine weitere Option besteht darin, die Hauptdatei weniger 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 einen 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 Hauptdatei weniger hinzugefügt werden soll.


10

Um die frühere Antwort von bdmason weiter zu verdeutlichen, scheint es wünschenswert zu sein, für jede Seite ein JS- und CSS-Bundle zu erstellen, wie folgt:

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

Und dann benutze den [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 WENIGER):

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

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

Das Webpack-Team empfiehlt die Verwendung von Mini-CSS-Extrakt über das Extrakt-Text-Plugin

Mit dieser Lösung können Sie einen separaten Block 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 ausgefeilteres 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 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 oder nicht). Dies wird angeblich in Webpack 5 behoben .

Sollte ich Webpack überhaupt für Nicht-JS-Assets verwenden, wenn ich sie nicht in mein JS mischen möchte?

Ich sehe daran nichts auszusetzen, 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, daher bleibe ich lieber im Webpack-Ökosystem.

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

7

Ja, dies ist möglich, aber wie andere gesagt haben, benötigen Sie dazu zusätzliche Pakete (siehe devDependencies unter package.json). Hier ist der Beispielcode, mit dem ich mein Bootstrap-SCSS -> CSS und Bootstrap-JS -> JS kompiliert 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 Datei 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"
  }
}

Das Tutorial finden Sie hier: https://florianbrinkmann.com/de/4240/sass-webpack


1

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

ExtractTextPlugin ist veraltet.

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

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

0

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
})
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.