我可以使用webpack单独生成CSS和JS吗?

时间:2016-02-10 18:19:12

标签: javascript css webpack build css-preprocessor

我有:

  1. 我要捆绑的JS文件。
  2. 我想编译成CSS的LESS文件(将@imports解析为单个包)。
  3. 我希望将这些指定为两个单独的输入并具有两个单独的输出(可能通过extract-text-webpack-plugin)。 Webpack拥有所有适当的插件/加载器来进行编译,但它似乎并不喜欢分离。

    我已经看到过人们需要直接从JS获取LESS文件的示例,例如require('./app.less');,除了告诉webpack将这些文件包含在捆绑包中之外没有其他原因。这允许你只有一个入口点,但这对我来说似乎非常不对 - 为什么我的JS在与JS代码无关时需要LESS?

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

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

7 个答案:

答案 0 :(得分:17)

  

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

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

  

为什么我的JS在与JS代码无关时需要LESS?

人们这样做是因为他们用js定义了他们的应用程序(例如React组件,Backbone View)。应用程序的那一块有CSS随之而来。取决于一些单独构建的外部CSS资源而不是直接从js模块引用的资源是脆弱的,更难处理,并且可能导致样式过时等等.Webpack鼓励你保持所有模块化,所以你有一个CSS (Sass,无论如何)部分与js组件一起使用,而js组件require()则使依赖性变得清晰(对于您和构建工具,它永远不会构建您不需要的样式)。

我不知道你是否可以使用webpack自己捆绑CSS(当CSS文件没有从任何js引用时)。我确定你可以用插件等连接一些东西,但不确定它是否可以开箱即用。如果您确实引用了js中的CSS文件,您可以轻松地将CSS捆绑到一个单独的文件中,并使用Extract Text插件。

答案 1 :(得分:12)

可以在任何JS中使用require('main/less)生成单独的CSS包,但正如Brendan在其答案的第一部分中所指出的,Webpack不是为全局CSS包而设计的,而是与模块化JS一起使用,但有几个选择。

第一个是为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文件以及bundle,在这个例子中:style.js这只是一个空的Webpack模块。

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

如果你只有一个入口点,这是理想的,但如果你有更多,那么你的Webpack配置看起来有点奇怪,因为你必须随意选择哪个入口点添加较少的主文件。

答案 2 :(得分:4)

为了进一步澄清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
            }
        })
    ]
}
];

答案 3 :(得分:3)

是的,这是可能的,但是就像其他人所说的那样,您将需要其他程序包(请参阅package.json下的devDependencies)。这是我用来编译Bootstrap SCSS-> CSS and 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

答案 4 :(得分:1)

就像其他人提到的那样,您可以使用插件。

ExtractTextPlugin已过时。

您可以在Webpack配置中使用当前推荐的MiniCssExtractPlugin

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

答案 5 :(得分:0)

您还可以在条目JS文件中添加Less require语句:

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

答案 6 :(得分:0)

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

the webpack team recommends using mini-css-extract over the extract text plugin

此解决方案允许您创建一个仅包含CSS条目的单独块:

Booking.findOne({_id: req.params.id})
  .populate({path: 'experience',
    populate: [{path: 'experienceType', select: 'name'}, {path: 'location', select: 'name'}],
  })
  .exec((err, booking) => {
    if(err){
      console.log(err);
    }
    else {
      res.json(booking);
    }
  });

这是一个使用来自我的个人项目中的多个项的更人为的示例:

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

我意识到这种方法不是很模块化,但是它应该为您提供基础,并且是在您不希望将javascript和css相互混合的项目中采用webpack的绝佳策略。

此方法的缺点是css-loader仍会生成一个附加的javascript文件(无论您是否选择使用它),this will supposedly be fixed in webpack 5

  

如果我不将Webpack混入我的JS中,我是否应该甚至将webpack用于非JS资产?

我没有发现任何问题,但最终取决于您对多个构建系统的管理能力。对我来说,这感觉有些过头了,所以我更喜欢保留在webpack生态系统中。

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