ฉันสามารถใช้ webpack เพื่อสร้าง CSS และ JS แยกกันได้หรือไม่


86

ฉันมี:

  1. ไฟล์ JS ที่ฉันต้องการรวมไฟล์
  2. LESS ไฟล์ที่ฉันต้องการรวบรวมลงใน CSS (แก้ไข @imports เป็นบันเดิลเดียว)

ฉันหวังว่าจะระบุสิ่งเหล่านี้เป็นอินพุตแยกกันสองตัวและมีเอาต์พุตแยกกันสองตัว (น่าจะเป็นผ่าน extract-text-webpack-plugin) Webpack มีปลั๊กอิน / ตัวโหลดที่เหมาะสมทั้งหมดในการคอมไพล์ แต่ดูเหมือนจะไม่ชอบการแยก

ฉันเคยเห็นตัวอย่างของผู้ที่ต้องการไฟล์ LESS โดยตรงจาก JS เช่นrequire('./app.less');ไม่มีเหตุผลอื่นใดนอกจากบอกให้ webpack รวมไฟล์เหล่านั้นลงในบันเดิล สิ่งนี้ช่วยให้คุณมีจุดเข้าเพียงจุดเดียว แต่ดูเหมือนว่าฉันผิดจริงๆ - ทำไมฉันต้องใช้ LESS ใน JS ของฉันในเมื่อมันไม่เกี่ยวข้องกับรหัส JS ของฉัน

ฉันลองใช้จุดเข้าหลายจุดส่งทั้ง JS รายการและไฟล์ LESS หลักเข้ามา แต่เมื่อใช้จุดเข้าหลายจุด webpack จะสร้างบันเดิลที่ไม่เรียกใช้ JS เมื่อโหลด - มันรวมเข้าด้วยกันทั้งหมด แต่ไม่รู้ สิ่งที่ควรดำเนินการเมื่อเริ่มต้น

ฉันใช้ webpack ผิดหรือเปล่า? ฉันควรเรียกใช้ webpack อินสแตนซ์แยกต่างหากสำหรับโมดูลแยกเหล่านี้หรือไม่ ฉันควรใช้ webpack สำหรับเนื้อหาที่ไม่ใช่ JS ด้วยซ้ำหรือไม่ถ้าฉันจะไม่ผสมมันลงใน JS ของฉัน


ฉันสามารถแนะนำสื่อการสอนต่อไปนี้freecodecamp.org/…
wilo087

คำตอบ:


29

ฉันควรใช้ webpack สำหรับเนื้อหาที่ไม่ใช่ JS ด้วยซ้ำหรือไม่ถ้าฉันจะไม่ผสมมันลงใน JS ของฉัน

อาจจะไม่. Webpack เป็น js เป็นศูนย์กลางอย่างแน่นอนโดยมีสมมติฐานโดยปริยายว่าสิ่งที่คุณกำลังสร้างคือแอปพลิเคชัน js การใช้งานrequire()ช่วยให้คุณสามารถจัดการทุกอย่างเป็นโมดูล (รวมถึง Sass / LESS partials, JSON, เกือบทุกอย่าง) และทำการจัดการการพึ่งพาให้คุณโดยอัตโนมัติ (ทุกอย่างที่คุณrequireรวมไว้และไม่มีอะไรอื่น)

ทำไมฉันต้องใช้ LESS ใน JS ของฉันในเมื่อมันไม่เกี่ยวข้องกับรหัส JS ของฉัน

ผู้คนทำเช่นนี้เพราะพวกเขากำลังกำหนดส่วนของแอปพลิเคชัน (เช่นส่วนประกอบ React, Backbone View) ด้วย js ส่วนของแอปพลิเคชันนั้นมี CSS ที่มาพร้อมกับมัน ขึ้นอยู่กับทรัพยากร CSS ภายนอกที่สร้างขึ้นแยกต่างหากและไม่ได้อ้างอิงโดยตรงจากโมดูล js นั้นบอบบางใช้งานได้ยากขึ้นและอาจทำให้สไตล์ล้าสมัยเป็นต้น Webpack สนับสนุนให้คุณเก็บทุกอย่างแบบแยกส่วนดังนั้นคุณจึงมี CSS (Sass อะไรก็ได้) บางส่วนที่ไปพร้อมกับส่วนประกอบ js นั้นและส่วนประกอบ js require()เพื่อทำให้การอ้างอิงชัดเจน (สำหรับคุณและเครื่องมือสร้างซึ่งไม่เคยสร้างสไตล์ที่คุณไม่ต้องการ)

ฉันไม่ทราบว่าคุณสามารถใช้ webpack เพื่อรวม CSS ด้วยตัวเองได้หรือไม่ (เมื่อไฟล์ CSS ไม่ได้อ้างอิงจาก js ใด ๆ ) ฉันแน่ใจว่าคุณสามารถเชื่อมต่อบางอย่างกับปลั๊กอิน ฯลฯ แต่ไม่แน่ใจว่าเป็นไปได้นอกกรอบ หากคุณอ้างอิงไฟล์ CSS จาก js ของคุณคุณสามารถรวม CSS ไว้ในไฟล์แยกต่างหากด้วยปลั๊กอิน Extract Text อย่างที่คุณพูด


18

สามารถสร้างชุด CSS แยกต่างหากได้โดยไม่ต้องใช้require('main/less)ใน JS ใด ๆ ของคุณ แต่ตามที่ 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 ที่ไม่ต้องการเช่นเดียวกับบันเดิลในตัวอย่างนี้style.jsซึ่งเป็นเพียงโมดูล Webpack ว่างเปล่า

อีกทางเลือกหนึ่งคือการเพิ่มไฟล์หลักน้อยลงในจุดเริ่มต้น 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 ของคุณจะดูแปลก ๆ เล็กน้อยเนื่องจากคุณจะต้องเลือกจุดเข้าที่จะเพิ่มไฟล์หลักที่น้อยกว่าโดยพลการ


10

เพื่อชี้แจงคำตอบเดิมของ 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
            }
        })
    ]
}
];

9

โซลูชัน webpack 4 พร้อมปลั๊กอิน mini-css-extract

ทีมงาน webpack แนะนำให้ใช้ mini-css-extract บนปลั๊กอิน extract text

โซลูชันนี้ช่วยให้คุณสามารถสร้างกลุ่มแยกต่างหากที่มีเฉพาะรายการ 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'
    }),
  ]
};

ฉันตระหนักดีว่าวิธีนี้ไม่ใช่แบบแยกส่วนมากนัก แต่ควรให้พื้นฐานในการสร้างและเป็นกลยุทธ์ที่ยอดเยี่ยมสำหรับการนำ webpack มาใช้ในโครงการที่คุณไม่ต้องการใช้ javascript และ css ผสมกัน

downside แนวทางนี้ก็คือว่า CSS-loader ยังคงสร้างไฟล์จาวาสคริปต์เพิ่มเติม (ไม่ว่าคุณเลือกที่จะใช้มันหรือไม่) นี้จะคาดคะเนได้รับการแก้ไขใน webpack 5

ฉันควรใช้ webpack สำหรับเนื้อหาที่ไม่ใช่ JS ด้วยซ้ำหรือไม่ถ้าฉันจะไม่ผสมมันลงใน JS ของฉัน

ฉันไม่เห็นอะไรผิดปกติกับสิ่งนี้ แต่สุดท้ายแล้วมันขึ้นอยู่กับความอดทนของคุณในการจัดการระบบบิลด์หลายระบบ สำหรับฉันแล้วสิ่งนี้ให้ความรู้สึกเหมือน overkill ดังนั้นความชอบของฉันคือการคงอยู่ในระบบนิเวศของ webpack

สำหรับข้อมูลเพิ่มเติมเกี่ยวกับกลยุทธ์ที่ระบุไว้ข้างต้นโปรดดูhttps://github.com/webpack-contrib/mini-css-extract-plugin#extracting-css-based-on-entry


นี่ควรเป็นคำตอบเริ่มต้นของวันนี้
Giona Granata

8

ใช่มันเป็นไปได้ แต่อย่างที่คนอื่นบอกว่าคุณจะต้องใช้แพ็คเกจเพิ่มเติมในการทำเช่นนั้น (ดู devDependencies ภายใต้ package.json) นี่คือโค้ดตัวอย่างที่ฉันใช้ในการคอมไพล์ 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


1

เช่นเดียวกับคนอื่น ๆ ที่กล่าวถึงคุณสามารถใช้ปลั๊กอินได้

ExtractTextPlugin เลิกใช้แล้ว

คุณสามารถใช้สิ่งที่แนะนำMiniCssExtractPluginในปัจจุบันในการกำหนดค่า webpack ของคุณ:

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

0

คุณยังสามารถใส่คำสั่ง Less require ในไฟล์ 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
})
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.