简体   繁体   English

我可以使用 webpack 分别生成 CSS 和 JS 吗?

[英]Can I use webpack to generate CSS and JS separately?

I have:我有:

  1. JS files that I want to bundle.我想捆绑的 JS 文件。
  2. LESS files that I want to compile down to CSS (resolving @imports into a single bundle).我想编译成 CSS 的 LESS 文件(将 @imports 解析为单个包)。

I was hoping to specify these as two separate inputs and have two separate outputs (likely via extract-text-webpack-plugin).我希望将它们指定为两个单独的输入并有两个单独的输出(可能通过 extract-text-webpack-plugin)。 Webpack has all the proper plugins/loaders to do compilation, but it doesn't seem to like the separation. Webpack 拥有所有合适的插件/加载器来进行编译,但它似乎不喜欢这种分离。

I've seen examples of people requiring their LESS files directly from JS, such as require('./app.less');我见过一些人直接从 JS 中要求他们的 LESS 文件的例子,比如require('./app.less'); , for no other reason than to tell webpack to include those files into the bundle. ,除了告诉 webpack 将这些文件包含在包中之外别无他法。 This allows you to only have a single entry point, but it seems really wrong to me -- why would I require LESS in my JS when it has nothing to do with my JS code?这允许你只有一个入口点,但对我来说似乎真的错了——为什么我需要在我的 JS 中使用 LESS,因为它与我的 JS 代码无关?

I tried using multiple entry points, handing both the entry JS and main LESS file in, but when using multiple entry points, webpack generates a bundle that doesn't execute the JS on load -- it bundles it all, but doesn't know what should be executed on startup.我尝试使用多个入口点,同时处理入口 JS 和主 LESS 文件,但是当使用多个入口点时,webpack 会生成一个不会在加载时执行 JS 的包——它捆绑了所有内容,但不知道启动时应该执行什么。

Am I just using webpack wrong?我只是使用 webpack 错了吗? Should I run separate instances of webpack for these separate modules?我应该为这些单独的模块运行单独的 webpack 实例吗? Should I even be using webpack for non-JS assets if I'm not going to mix them into my JS?如果我不打算将它们混合到我的 JS 中,我什至应该将 webpack 用于非 JS 资产吗?

Should I even be using webpack for non-JS assets if I'm not going to mix them into my JS?如果我不打算将它们混合到我的 JS 中,我什至应该将 webpack 用于非 JS 资产吗?

Maybe not.也许不吧。 Webpack is definitely js-centric, with the implicit assumption that what you're building is a js application. Webpack 绝对是以 js 为中心的,隐含假设您正在构建的是一个 js 应用程序。 Its implementation of require() allows you to treat everything as a module (including Sass/LESS partials, JSON, pretty much anything), and automatically does your dependency management for you (everything that you require is bundled, and nothing else).它的require()实现允许您将所有内容视为模块(包括 Sass/LESS 部分、JSON,几乎所有内容),并自动为您进行依赖管理(您require内容都已捆绑,没有其他内容)。

why would I require LESS in my JS when it has nothing to do with my JS code?当它与我的 JS 代码无关时,为什么我需要在我的 JS 中使用 LESS?

People do this because they're defining a piece of their application (eg a React component, a Backbone View) with js.人们这样做是因为他们正在用 js 定义他们的应用程序的一部分(例如一个 React 组件、一个主干视图)。 That piece of the application has CSS that goes with it.应用程序的那部分具有与之配套的 CSS。 Depending on some external CSS resource that's built separately and not directly referenced from the js module is fragile, harder to work with, and can lead to styles getting out of date, etc. Webpack encourages you to keep everything modular, so you have a CSS (Sass, whatever) partial that goes with that js component, and the js component require() s it to make the dependency clear (to you and to the build tool, which never builds styles you don't need).依赖于一些单独构建而不直接从 js 模块引用的外部 CSS 资源是脆弱的,更难使用,并且可能导致样式过时等。Webpack 鼓励你保持一切模块化,所以你有一个 CSS (Sass,无论如何)部分与该 js 组件一起使用,并且 js 组件require()是为了明确依赖关系(对您和构建工具而言,它永远不会构建您不需要的样式)。

I don't know if you could use webpack to bundle CSS on its own (when the CSS files aren't referenced from any js).我不知道你是否可以使用 webpack 自己捆绑 CSS(当 CSS 文件没有从任何 js 引用时)。 I'm sure you could wire something up with plugins, etc., but not sure it's possible out of the box.我确定你可以用插件等连接一些东西,但不确定它是否可以开箱即用。 If you do reference the CSS files from your js, you can easily bundle the CSS into a separate file with the Extract Text plugin, as you say.如果您确实从 js 中引用了 CSS 文件,则可以使用提取文本插件轻松地将 CSS 捆绑到一个单独的文件中,如您所说。

A separate CSS bundle can be generated without using require('main/less) in any of your JS, but as Brendan pointed out in the first part of his answer Webpack isn't designed for a global CSS bundle to go alongside modular JS, however there are a couple of options.无需在任何 JS 中使用require('main/less)即可生成单独的 CSS 包,但正如 Brendan 在其回答的第一部分中指出的那样,Webpack 并不是为与模块化 JS 一起使用的全局 CSS 包而设计的,但是有几个选择。

The first is to add an extra entry point for main.less, then use the Extract Text plugin to create the CSS bundle:首先是为 main.less 添加一个额外的入口点,然后使用 Extract Text 插件创建 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
        })
    ]
};

The problem with this method is you also generate an unwanted JS file as well as the bundle, in this example: style.js which is just an empty Webpack module.这种方法的问题是你还生成了一个不需要的 JS 文件以及包,在这个例子中: style.js ,它只是一个空的 Webpack 模块。

Another option is to add the main less file to an existing Webpack entry point:另一种选择是将主要的 less 文件添加到现有的 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
        })
    ]
};

This is ideal if you have only 1 entry point, but if you have more, then your Webpack config will look a bit odd as you'll have to arbitrarily choose which entry point to add the main less file to.如果您只有 1 个入口点,这是理想的,但如果您有更多入口点,那么您的 Webpack 配置将看起来有点奇怪,因为您必须任意选择哪个入口点来添加 mainless 文件。

webpack 4 solution with mini-css-extract plugin带有 mini-css-extract 插件的 webpack 4 解决方案

the webpack team recommends using mini-css-extract over the extract text plugin webpack 团队建议在提取文本插件上使用 mini-css-extract

this solution allows you to create a separate chunk containing only your css entries:此解决方案允许您创建一个单独的块,仅包含您的 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'],
      },
    ],
  },
};

Here is a more contrived example using mutliple entries from one of my personal projects:这是一个使用我个人项目中的多个条目的更人为的示例:

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

I realize this approach is not very modular, but it should give you a foundation to build from and is an excellent strategy for adopting webpack in projects where you do not wish to inter-mix javascript and css.我意识到这种方法不是很模块化,但它应该为您提供构建基础,并且是在您不希望混合使用 javascript 和 css 的项目中采用 webpack 的绝佳策略。

The downside to this approach is that css-loader still generates an additional javascript file (whether you choose to use it or not), this will supposedly be fixed in webpack 5 .这种方法的缺点是 css-loader 仍然会生成一个额外的 javascript 文件(无论您是否选择使用它), 这应该会在 webpack 5 中修复

Should I even be using webpack for non-JS assets if I'm not going to mix them into my JS?如果我不打算将它们混合到我的 JS 中,我什至应该将 webpack 用于非 JS 资产吗?

I don't see anything wrong with this but ultimately it depends on your tolerance for managing multiple build systems.我认为这没有任何问题,但最终取决于您对管理多个构建系统的容忍度。 To me this feels like overkill, so my preference is to remain in the webpack ecosystem.对我来说这感觉有点矫枉过正,所以我更喜欢留在 webpack 生态系统中。

For more information on the strategies outlined above, please see https://github.com/webpack-contrib/mini-css-extract-plugin#extracting-css-based-on-entry有关上述策略的更多信息,请参阅https://github.com/webpack-contrib/mini-css-extract-plugin#extracting-css-based-on-entry

To further clarify bdmason's former answer - it seems the desirable configuration would be to create a JS and CSS bundle for each page, like so:为了进一步澄清 bdmason 以前的答案 - 似乎理想的配置是为每个页面创建一个 JS 和 CSS 包,如下所示:

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

And then use the [name] switch:然后使用[name]开关:

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

Full configuration - with some additions not connected to the question (we're actually using SASS instead of LESS):完整配置 - 有一些与问题无关的添加(我们实际上使用的是 SASS 而不是 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
            }
        })
    ]
}
];

Yes, this is possible but like others said you will need additional packages to do so (see devDependencies under package.json).是的,这是可能的,但就像其他人所说的那样,您需要额外的包才能这样做(请参阅 package.json 下的 devDependencies)。 here is the sample code that I used to compile my bootstrap SCSS --> CSS and Bootstrap JS --> JS.这是我用来编译 bootstrap SCSS --> CSS 和 Bootstrap JS --> JS 的示例代码。

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

additional postcss.config.js file:额外的 postcss.config.js 文件:

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

package.json:包.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"
  }
}

See the tutorial here: https://florianbrinkmann.com/en/4240/sass-webpack请参阅此处的教程: https : //florianbrinkmann.com/en/4240/sass-webpack

Like others mentioned you can use a plugin.就像其他人提到的那样,您可以使用插件。

ExtractTextPlugin is deprecated.不推荐使用ExtractTextPlugin

You can use the currently recommended MiniCssExtractPlugin in your webpack configuration:您可以在 webpack 配置中使用当前推荐的MiniCssExtractPlugin

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

You can also put your Less require statements in your entry JS file also:你也可以把你的 Less require 语句放在你的入口 JS 文件中:

in body.js在 body.js 中

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

Then in webpack然后在 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
})

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM