我可以使用 webpack 分别生成 CSS 和 JS 吗?
Posted
技术标签:
【中文标题】我可以使用 webpack 分别生成 CSS 和 JS 吗?【英文标题】:Can I use webpack to generate CSS and JS separately? 【发布时间】:2016-05-21 06:18:14 【问题描述】:我有:
-
我要捆绑的 JS 文件。
我想编译成 CSS 的 LESS 文件(将 @imports 解析为单个包)。
我希望将它们指定为两个单独的输入并具有两个单独的输出(可能通过 extract-text-webpack-plugin)。 Webpack 有所有合适的插件/加载器来进行编译,但它似乎不喜欢这种分离。
我见过一些人直接从 JS 中要求他们的 LESS 文件的例子,例如 require('./app.less');
,只是为了告诉 webpack 将这些文件包含到包中。这允许您只有一个入口点,但对我来说似乎真的错了——为什么我的 JS 中需要 LESS,而它与我的 JS 代码无关?
我尝试使用多个入口点,同时处理入口 JS 和主 LESS 文件,但是当使用多个入口点时,webpack 会生成一个不会在加载时执行 JS 的包——它将所有内容捆绑在一起,但不会'不知道在启动时应该执行什么。
我只是用错了 webpack 吗?我应该为这些单独的模块运行单独的 webpack 实例吗?如果我不打算将它们混合到我的 JS 中,我什至应该将 webpack 用于非 JS 资产吗?
【问题讨论】:
我可以推荐以下教程medium.freecodecamp.org/… 【参考方案1】:如果我不打算将它们混合到我的 JS 中,我什至应该将 webpack 用于非 JS 资产吗?
也许不是。 Webpack 绝对是以 js 为中心的,隐含的假设是你正在构建的是一个 js 应用程序。它对require()
的实现允许您将所有内容视为一个模块(包括 Sass/LESS 部分、JSON,几乎所有内容),并自动为您进行依赖管理(您 require
的所有内容都已捆绑,仅此而已) .
当我的 JS 与我的 JS 代码无关时,为什么我需要 LESS?
人们这样做是因为他们正在使用 js 定义应用程序的一部分(例如 React 组件、Backbone 视图)。该应用程序的那部分具有与之配套的 CSS。依赖于一些单独构建并且不直接从 js 模块引用的外部 CSS 资源是脆弱的,难以使用,并且可能导致样式过时等。Webpack 鼓励您保持一切模块化,因此您有一个 CSS (Sass,无论如何)与该 js 组件一起使用的部分,以及 js 组件require()
s 它使依赖关系清晰(对您和构建工具而言,它从不构建您不需要的样式)。
我不知道你是否可以使用 webpack 自己捆绑 CSS(当 CSS 文件没有被任何 js 引用时)。我相信你可以用插件等连接一些东西,但不确定它是否可以开箱即用。如果你确实从你的 js 中引用了 CSS 文件,你可以很容易地将 CSS 与 Extract Text 插件捆绑到一个单独的文件中,正如你所说的。
【讨论】:
【参考方案2】:可以在您的任何 JS 中不使用 require('main/less)
来生成单独的 CSS 包,但正如 Brendan 在他的回答的第一部分中指出的那样,Webpack 并不是为与模块化 JS 一起使用的全局 CSS 包而设计的,但是有几个选项。
首先是为 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 文件添加到哪个入口点。
【讨论】:
ExtractTextPlugin 自 2017 年 10 月起停产(参见 npmjs.com/package/extract-text-webpack-plugin),继任者是 mini-css-extract 插件(对此问题的单独答案涵盖:***.com/a/59237067/1746685)【参考方案3】:为了进一步澄清 bdmason 以前的答案 - 似乎理想的配置是为每个页面创建一个 JS 和 CSS 包,如下所示:
entry:
Home: ["./path/to/home.js", "./path/to/home.less"],
About: ["./path/to/about.js", "./path/to/about.less"]
然后使用[name]
开关:
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
)
]
];
【讨论】:
【参考方案4】:您也可以将您的 Less 要求语句也放入您的条目 JS 文件中:
在 body.js 中
// CSS
require('css/_variable.scss')
require('css/_npm.scss')
require('css/_library.scss')
require('css/_lib.scss')
然后在 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
)
【讨论】:
【参考方案5】:是的,这是可能的,但就像其他人说的那样,您需要额外的包才能这样做(请参阅 package.json 下的 devDependencies)。这是我用来编译 bootstrap SCSS --> CSS 和 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'
]
]
;
附加 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
【讨论】:
【参考方案6】:带有 mini-css-extract 插件的 webpack 4 解决方案
the webpack team recommends using mini-css-extract over the extract text plugin
此解决方案允许您创建一个单独的块,其中仅包含您的 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。
如果我不打算将它们混合到我的 JS 中,我什至应该将 webpack 用于非 JS 资产吗?
我认为这没有任何问题,但最终这取决于您对管理多个构建系统的容忍度。对我来说,这感觉有点矫枉过正,所以我更愿意留在 webpack 生态系统中。
有关上述策略的更多信息,请参阅https://github.com/webpack-contrib/mini-css-extract-plugin#extracting-css-based-on-entry
【讨论】:
这应该是今天的默认答案 现在在文档中:webpack.js.org/guides/entry-advanced 虚拟 Js 文件问题 was not fixed in webpack 5 it seems。另外,有谁知道如何为 Js 和 CSS 输出文件使用相同的名称“索引”,例如index.js
和 index.css
?提问于:***.com/questions/70698775/…【参考方案7】:
像其他人提到的那样,您可以使用插件。
ExtractTextPlugin
已弃用。
你可以在你的 webpack 配置中使用当前推荐的MiniCssExtractPlugin
:
module.exports =
entry:
home: ['index.js', 'index.less']
,
plugins: [
new MiniCssExtractPlugin(
filename: '[name].css',
),
]
【讨论】:
以上是关于我可以使用 webpack 分别生成 CSS 和 JS 吗?的主要内容,如果未能解决你的问题,请参考以下文章
使用 Webpack,是不是可以只生成 CSS,不包括 output.js?
是否可以让 html-webpack-plugin 从 css 生成 <style> 元素?