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

I have:

  1. 我想捆绑的 JS 文件。
  2. 我想编译成 CSS (将@import 解析为一个包)的较少文件。

我希望将它们指定为两个独立的输入,并有两个独立的输出(可能通过提取-text-webpack-plugin)。Webpack 有所有合适的插件/加载程序来进行编译,但它似乎不喜欢这种分离。

我曾经看到过一些例子,人们需要直接从 JS 中获取较少的文件,比如 require('./app.less');,除了告诉 webpack 将这些文件包含到捆绑包中,没有别的原因。这只允许您有一个单一的入口点,但是对我来说这似乎真的是错误的——当它与我的 JS 代码没有任何关系的时候,我为什么要在我的 JS 中要求更少的入口点呢?

我尝试使用多个入口点,同时处理入口 JS 和主 LESS 文件,但是当使用多个入口点时,webpack 生成一个 bundle,不会在加载时执行 JS ——它将所有内容都打包,但是不知道在启动时应该执行什么。

Am I just using webpack wrong? Should I run separate instances of webpack for these separate modules? Should I even be using webpack for non-JS assets if I'm not going to mix them into my JS?

82474 次浏览

Should I even be using webpack for non-JS assets if I'm not going to mix them into my JS?

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

为什么我需要在我的 JS 更少的时候,它与我的 JS 代码没有任何关系?

People do this because they're defining a piece of their application (e.g. a React component, a Backbone View) with js. That piece of the application has CSS that goes with it. 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).

我不知道您是否可以使用 webpack 自己捆绑 CSS (当 CSS 文件没有从任何 js 引用时)。我相信你可以连接一些东西与插件等,但不确定它是否可能开箱即用。如果您确实引用了 js 中的 CSS 文件,那么您可以使用 Extract Text 插件轻松地将 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.

第一种方法是为 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
})
]
};

这个方法的问题是,您还生成了一个不需要的 JS 文件以及捆绑包,在本例中: style.js只是一个空的 Webpack 模块。

另一种选择是将 main 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
})
]
};

如果您只有1个入口点,那么这是理想的,但是如果您有更多的入口点,那么您的 Webpack 配置将看起来有点奇怪,因为您必须任意选择将 main less 文件添加到哪个入口点。

为了进一步澄清 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:

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

完整的配置-有一些附加的问题没有连接(我们实际上使用 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
}
})
]
}
];

你也可以在你的 JS 文件中输入你的“不太需要”语句:

在身体里

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

然后在网络包里

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

是的,这是可能的,但是正如其他人所说,您需要额外的包来完成这项工作(请参阅 package.json 下的 devDependency)。下面是我用来编译引导 SCSS 的示例代码—— > CSS 和引导 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'
}
]
}
]
}
};

附加的 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"
}
}

请参阅这里的教程: https://florianbrinkmann.com/en/4240/sass-webpack

Webpack 4解决方案,带有 mini-css 提取插件

Webpack 团队建议在提取文本插件 上使用 mini-css-tract

这个解决方案允许您创建一个单独的块,其中只包含您的 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-loader 仍然会生成一个额外的 javascript 文件(无论您是否选择使用它) this will supposedly be fixed in webpack 5

Should I even be using webpack for non-JS assets if I'm not going to mix them into my JS?

我不认为这有什么问题,但最终它取决于您对管理多个构建系统的容忍度。对我来说,这感觉像是过度杀伤,所以我的偏好是保持在 webpack 生态系统。

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

Like others mentioned you can use a plugin.

不推荐使用 ExtractTextPlugin

You can use the currently recommended MiniCssExtractPlugin in your webpack configuration:

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