I want to remove Laravel Mix from my project in order to adopt new features or releases quicker, without having to use Mix’s syntax or plugins or to put everything inside the webpackConfig
method.
I have created a Webpack configuration that works for styles and assets, but I’m not able to make the vue-loader
import the styles from vue single file components in a static .html
file: if I use the html-webpack-plugin
everything runs correctly, but if I include the .js
bundle in a static .html
file the styles do not load. I want it to work in a static .html
file in order to simulate the behavior of a blade file in php.
Here is a simplified version of my setup:
webpack.config.js
const path = require('path'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const { CleanWebpackPlugin } = require('clean-webpack-plugin'); const { VueLoaderPlugin } = require("vue-loader"); const htmlWebpackPlugin = require("html-webpack-plugin"); module.exports = { entry: { main: "./src/main.js", }, output: { filename: 'js/[name].js', path: path.resolve(__dirname, 'dist') }, resolve: { extensions: ['.js', '.vue'], alias: { "vue$": "vue/dist/vue.common.js" } }, module: { rules: [ { test: /.((c|sa|sc)ss)$/i, use: [ 'vue-style-loader', MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader', 'sass-loader' ], }, { test: /.js$/, exclude: /node_modules/, use: ['babel-loader'], }, { test: /.vue$/, loader: 'vue-loader' }, ], }, plugins: [ new MiniCssExtractPlugin({ filename: 'css/[name].css', }), new htmlWebpackPlugin({ template: path.resolve(__dirname, "public", "index.html") }), new CleanWebpackPlugin({ cleanStaleWebpackAssets: false, verbose: true }), new VueLoaderPlugin() ], };
main.js
import Vue from "vue"; import App from "./App.vue"; new Vue({ render: (h) => h(App), }).$mount("#app");
App.vue
<template> <div id="app"> <div class="nav"> Test nav </div> </div> </template> <style lang="scss"> .nav { padding: 30px 0 100px 0; font-family: sans-serif; } </style>
index.html
(used to generate another index.html
with html-webpack-plugin
)
๐ with this one everything works
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <title>Vue app</title> </head> <body> <div id="app"></div> <!-- built files will be auto injected --> </body> </html>
index.html (static file)
๐ this one doesn’t load the .css
files
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Test</title> </head> <body> <div id="app"></div> <script src="dist/js/main.js"></script> </body> </html>
What I’ve tried
- Not using
html-webpack-plugin
during the build (nothing changed) - Using a different alias for $vue: (
vue$: "vue/dist/vue.runtime.esm.js"
) (nothing changed) - Not using
mini-css-extract-plugin
(the css wouldn’t load even with thehtml-webpack-plugin
)
Also in Laravel Mix the styles are loaded inside <style>
tags and not in separate .css
files, but this is not an issue (both are fine for me).
Answer
By looking at this answer to the SO question How to migrate from laravel mix to pure Webpack? I’ve printed out the configuration of a new Laravel Mix project with minimal set-up.
Mix.listen('configReady', function (config) { RegExp.prototype.toJSON = RegExp.prototype.toString; console.log(JSON.stringify(config)); });
I figured out that Laravel Mix uses two different sets of loaders between css files coming from Vue and all the other css/scss files.
I didn’t copy their configuration because it involves lots of duplication, but luckily I was able to figure out a quick setup that does the same job:
// [...] module: { rules: [ { test: /.vue.css$/, // only vue css use: [ 'style-loader', 'css-loader', 'postcss-loader', 'sass-loader' ] }, { test: /.((c|sa|sc)ss)$/i, exclude(modulePath) { // exclude vue css return modulePath.includes('.vue'); }, use: [ { loader: MiniCssExtractPlugin.loader, options: { publicPath: '../', }, }, 'css-loader', 'postcss-loader', 'sass-loader' ] }, // [...] ] }
I’m not sure that this is the best way to load css in Vue applications, but at least now I can replicate the same situation I have in the original application.
Edit 01/28/21
When working with scss files you need to update the first test like this:
test: /.vue.((c|sa|sc)ss)$/i, // only vue css