Posso usare webpack per generare CSS e JS separatamente?


86

Io ho:

  1. File JS che voglio raggruppare.
  2. MENO file che voglio compilare in CSS (risolvendo @imports in un unico pacchetto).

Speravo di specificarli come due input separati e di avere due output separati (probabilmente tramite extract-text-webpack-plugin). Webpack ha tutti i plugin / caricatori appropriati per fare la compilazione, ma non sembra gradire la separazione.

Ho visto esempi di persone che richiedono i loro file LESS direttamente da JS, ad esempio require('./app.less');, per nessun altro motivo se non per dire a webpack di includere quei file nel bundle. Ciò ti consente di avere un solo punto di ingresso, ma mi sembra davvero sbagliato: perché dovrei richiedere MENO nel mio JS quando non ha nulla a che fare con il mio codice JS?

Ho provato a utilizzare più punti di ingresso, consegnando sia la voce JS che il file LESS principale, ma quando si utilizzano più punti di ingresso, webpack genera un pacchetto che non esegue JS al caricamento: raggruppa tutto, ma non lo sa cosa dovrebbe essere eseguito all'avvio.

Sto solo usando webpack sbagliato? Devo eseguire istanze separate di webpack per questi moduli separati? Dovrei anche utilizzare webpack per risorse non JS se non ho intenzione di combinarle nel mio JS?


Posso consigliare il seguente tutorial medium.freecodecamp.org/…
wilo087

Risposte:


29

Dovrei anche utilizzare webpack per risorse non JS se non ho intenzione di combinarle nel mio JS?

Forse no. Webpack è decisamente js-centric, con l'assunto implicito che ciò che stai costruendo sia un'applicazione js. La sua implementazione di require()ti consente di trattare tutto come un modulo (inclusi i parziali Sass / LESS, JSON, praticamente qualsiasi cosa) e automaticamente fa la tua gestione delle dipendenze per te (tutto ciò che requireè in bundle e nient'altro).

perché dovrei richiedere MENO nel mio JS quando non ha nulla a che fare con il mio codice JS?

Le persone lo fanno perché definiscono una parte della loro applicazione (ad esempio un componente React, una vista Backbone) con js. Quella parte dell'applicazione ha CSS che lo accompagna. A seconda di alcune risorse CSS esterne create separatamente e non direttamente referenziate dal modulo js è fragile, più difficile da lavorare e può portare a stili obsoleti, ecc. Webpack ti incoraggia a mantenere tutto modulare, quindi hai un CSS (Sass, qualunque cosa) parziale che va con quel componente js e il componente jsrequire() è per rendere chiara la dipendenza (a te e allo strumento di compilazione, che non crea mai stili che non ti servono).

Non so se potresti usare webpack per raggruppare CSS da solo (quando i file CSS non sono referenziati da alcun js). Sono sicuro che potresti collegare qualcosa con plug-in, ecc., Ma non sono sicuro che sia possibile fuori dagli schemi. Se fai riferimento ai file CSS dal tuo js, ​​puoi facilmente raggruppare il CSS in un file separato con il plugin Extract Text, come dici tu.


18

Un bundle CSS separato può essere generato senza utilizzare require('main/less)in nessuno dei tuoi JS, ma come ha sottolineato Brendan nella prima parte della sua risposta, Webpack non è progettato per un bundle CSS globale da affiancare a JS modulare, tuttavia ci sono un paio di opzioni .

Il primo è aggiungere un punto di ingresso aggiuntivo per main.less, quindi utilizzare il plug-in Extract Text per creare il pacchetto 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
        })
    ]
};

Il problema con questo metodo è che si genera anche un file JS indesiderato oltre al bundle, in questo esempio: style.jsche è solo un modulo Webpack vuoto.

Un'altra opzione è aggiungere il file less principale a un punto di ingresso Webpack esistente:

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

Questo è l'ideale se hai solo 1 punto di ingresso, ma se ne hai di più, la configurazione del tuo Webpack sembrerà un po 'strana poiché dovrai scegliere arbitrariamente a quale punto di ingresso aggiungere il file less principale.


10

Per chiarire ulteriormente la precedente risposta di bdmason, sembra che la configurazione desiderabile sarebbe creare un bundle JS e CSS per ogni pagina, in questo modo:

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

E poi usa il file [name] interruttore:

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

Configurazione completa - con alcune aggiunte non collegate alla domanda (in realtà stiamo usando SASS invece di 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

soluzione webpack 4 con plugin mini-css-extract

il team di webpack consiglia di utilizzare mini-css-extract sul plug-in di testo di estrazione

questa soluzione ti consente di creare un blocco separato contenente solo le tue voci 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'],
      },
    ],
  },
};

Ecco un esempio più artificioso che utilizza più voci da uno dei miei progetti personali:

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

Mi rendo conto che questo approccio non è molto modulare, ma dovrebbe darti una base da cui partire ed è un'ottima strategia per adottare webpack in progetti in cui non desideri mescolare javascript e css.

Lo svantaggio di questo approccio è che css-loader genera ancora un file javascript aggiuntivo (indipendentemente dal fatto che tu scelga di usarlo o meno), questo sarà presumibilmente risolto nel webpack 5 .

Dovrei anche utilizzare webpack per risorse non JS se non ho intenzione di combinarle nel mio JS?

Non vedo nulla di sbagliato in questo, ma alla fine dipende dalla tua tolleranza per la gestione di più sistemi di compilazione. Per me questo sembra eccessivo, quindi la mia preferenza è rimanere nell'ecosistema webpack.

Per ulteriori informazioni sulle strategie descritte sopra, consultare https://github.com/webpack-contrib/mini-css-extract-plugin#extracting-css-based-on-entry


questa dovrebbe essere la risposta predefinita oggi
Giona Granata

8

Sì, questo è possibile ma, come altri hanno detto, avrai bisogno di pacchetti aggiuntivi per farlo (vedi devDependencies in package.json). ecco il codice di esempio che ho usato per compilare il mio bootstrap SCSS -> CSS e 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'
                    }
                ]
            }
        ]
    }
};

file postcss.config.js aggiuntivo:

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

Guarda il tutorial qui: https://florianbrinkmann.com/en/4240/sass-webpack


1

Come altri hanno menzionato, puoi usare un plugin.

ExtractTextPlugin è deprecato.

È possibile utilizzare l'attuale consigliato MiniCssExtractPluginnella configurazione del webpack:

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

0

Puoi anche inserire le tue istruzioni Less require nel file JS della voce anche:

in body.js

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

Quindi in 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
})
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.