我可以使用webpack分别生成CSS和JS吗?webpack 4 解决方案,带有迷你 css 提取插件

2022-08-30 05:26:05

我有:

  1. 我要捆绑的JS文件。
  2. 我想编译为CSS的更少文件(将@imports解析为单个捆绑包)。

我希望将它们指定为两个单独的输入,并具有两个单独的输出(可能通过提取-文本-webpack-plugin)。Webpack拥有所有适当的插件/加载器来进行编译,但它似乎不喜欢分离。

我见过一些例子,人们直接从JS需要他们的LESS文件,例如,没有其他原因,只是为了告诉webpack将这些文件包含在捆绑包中。这允许你只有一个入口点,但对我来说似乎真的错了 - 为什么在我的JS中需要更少的,当它与我的JS代码无关时?require('./app.less');

我尝试使用多个入口点,将入口JS和main LESS文件都传入,但是当使用多个入口点时,webpack会生成一个在加载时不执行JS的捆绑包 - 它捆绑了所有的JS,但不知道在启动时应该执行什么。

我只是用错了网络包吗?我应该为这些单独的模块运行单独的 webpack 实例吗?如果我不打算将它们混合到我的JS中,我甚至应该将webpack用于非JS资产吗?


答案 1

如果我不打算将它们混合到我的JS中,我甚至应该将webpack用于非JS资产吗?

也许不是。Webpack绝对是以js为中心的,隐含的假设是你正在构建的是一个js应用程序。它的实现允许您将所有内容视为一个模块(包括Sass / LESS部分,JSON,几乎任何内容),并自动为您执行依赖项管理(捆绑的所有内容,而不是其他任何内容)。require()require

为什么在我的JS中需要更少,而它与我的JS代码无关?

人们这样做是因为他们正在用js定义他们的应用程序的一部分(例如React组件,Backbone View)。该应用程序的该部分具有与之配套的CSS。依赖于一些单独构建的外部CSS资源,而不是直接从js模块引用是脆弱的,更难使用,并且可能导致样式过时等。Webpack鼓励你保持一切模块化,所以你有一个CSS(Sass,无论什么)部分与js组件一起,js组件s它来明确依赖关系(对你和构建工具,它永远不会构建你不需要的样式)。require()

我不知道你是否可以使用webpack单独捆绑CSS(当CSS文件不是从任何js引用时)。我相信你可以用插件等连接一些东西,但不确定它是否可能开箱即用。如果您确实引用了js中的CSS文件,则可以像您所说的那样,轻松地将CSS捆绑到具有提取文本插件的单独文件中。


答案 2

webpack 4 解决方案,带有迷你 css 提取插件

webpack团队建议使用mini-css-extract而不是提取文本插件

此解决方案允许您创建一个仅包含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'],
      },
    ],
  },
};

下面是一个更人为的例子,使用来自我的一个个人项目的多个条目:

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

我意识到这种方法不是很模块化,但它应该给你一个基础来构建,并且是在你不希望混合javascript和css的项目中采用webpack的一个很好的策略。

这种方法的缺点是css加载器仍然会生成一个额外的javascript文件(无论你是否选择使用它),这应该会在webpack 5中修复

如果我不打算将它们混合到我的JS中,我甚至应该将webpack用于非JS资产吗?

我不认为这有什么问题,但最终这取决于你对管理多个构建系统的容忍度。对我来说,这感觉有点过分了,所以我更喜欢留在webpack生态系统中。

有关上述策略的更多信息,请参阅 https://github.com/webpack-contrib/mini-css-extract-plugin#extracting-css-based-on-entry