I have an application which has the following configuration. Symfony (with laravel-mix) + Vue + Vue Router.
The problem: Webpack cannot find the changes applied in my Vue files which are loaded in the routes and update my JS files (when i'm running npm run watch). If i change something in my App.vue the webpack renews my app.js, but if i update for example the Conversation.vue (as provided bellow) it doesn't update my app.js. I have tried the laravel-mix-vue-auto-routing provided by laravel mix but it did nothing. In case you wanna know, Symfony is being used for the manipulation of the session of the app and like an API.
Bellow relies the config
//webpack.mix.js
const path = require('path')
const mix = require('laravel-mix')
const glob = require('glob')
const webpack = require('webpack')
const { VuetifyPlugin } = require('webpack-plugin-vuetify')
const { PurgeCSSPlugin } = require('purgecss-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
mix.setPublicPath('public').webpackConfig({
module: {
rules: [
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader"
]
}
]
},
plugins: [
new VuetifyPlugin({
styles: {
configFile: 'resources/sass/vuetify-config.scss'
}
}),
new webpack.DefinePlugin({
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__: 'false',
}),
new MiniCssExtractPlugin({
filename: "[name].css",
}),
mix.inProduction() ? new PurgeCSSPlugin({
paths: glob.sync(`${path.join(__dirname, 'public')}/**/*`, { nodir: true }),
}) : () => {} // Empty function to skip PurgeCSS in development
]
})
mix.js('resources/js/pages/app.js', 'public/js/pages/app.js').vue().version()
mix.sass('resources/sass/app.scss', 'public/css').version()
//router/index.js
import { createRouter, createWebHistory } from 'vue-router'
const routes = [
{
path: '/',
alias: ['/conversations'],
name: 'Conversations',
meta: { title: 'Conversations - Inbox' },
component: () => import('../components/Conversations/NoConversation.vue')
},
{
path: '/conversations/:id',
name: 'Conversation',
meta: { title: 'Conversations - Inbox' },
component: () => import('../components/Conversations/Conversation.vue'),
props: (route) => ({ conversationId: route.params.id })
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
//resources/js/pages/app.js
import router from '../router'
import { createApp } from 'vue'
import { pinia } from '../stores'
import { vuetify } from '../vuetify'
import { library } from '@fortawesome/fontawesome-svg-core'
import { fas } from '@fortawesome/free-solid-svg-icons'
import { far } from '@fortawesome/free-regular-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
import App from '../components/App.vue'
import { useUserStore } from '../stores/user'
library.add(fas, far)
const app = createApp(App)
app.component('font-awesome-icon', FontAwesomeIcon)
app.use(pinia).use(router).use(vuetify)
await useUserStore().fetchData()
app.mount('#app')
resources/js/components/App.vue
<script setup>
import Header from './App/Header.vue'
import Layout from './Conversations/Layout.vue'
import NotFound from './App/NotFound.vue'
const { notFound } = defineProps({
notFound: Boolean
})
</script>
<template>
<v-app>
<v-main>
<NotFound v-if="notFound" />
<template v-else>
<Header />
<Layout>
<router-view />
</Layout>
</template>
</v-main>
</v-app>
</template>
src/Controller/IndexController.php
<?php
namespace App\Controller;
use App\Helper\ViewHelper;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class IndexController extends AbstractController
{
private ViewHelper $viewHelper;
public function __construct(ViewHelper $viewHelper)
{
$this->viewHelper = $viewHelper;
}
#[Route('/', name: 'homepage', methods: ['GET'])]
#[Route('/conversations', name: 'conversations', methods: ['GET'])]
#[Route('/conversations/{route}', name: 'catch_all', methods: ['GET'])]
public function index(): Response
{
$data = [
'title' => 'Inbox',
'component' => 'app',
];
return $this->render('views/index.html.twig', array_merge($data, $this->viewHelper->getCommonViewData()));
}
}
//package.json
"scripts": {
"dev": "npm run development",
"development": "mix",
"watch": "mix watch",
"prod": "npm run production",
"production": "mix --production",
"format": "prettier . --write"
},
"dependencies": {
"@fawmi/vue-google-maps": "^0.9.79",
"@fortawesome/fontawesome-svg-core": "^6.5.1",
"@fortawesome/free-regular-svg-icons": "^6.5.1",
"@fortawesome/free-solid-svg-icons": "^6.5.1",
"@fortawesome/vue-fontawesome": "^3.0.6",
"@popperjs/core": "^2.11.8",
"@vueuse/core": "^10.7.1",
"axios": "^1.6.7",
"boxicons": "^2.1.4",
"glob": "^10.3.10",
"laravel-mix": "^6.0.49",
"mini-css-extract-plugin": "^2.8.0",
"mitt": "^3.0.1",
"moment": "^2.30.1",
"path": "^0.12.7",
"pinia": "^2.1.7",
"purgecss-webpack-plugin": "^5.0.0",
"vue": "^3.4.19",
"vue-auto-routing": "^1.0.1",
"vue-router": "^4.0.13",
"vuetify": "^3.5.5",
"webpack-plugin-vuetify": "^2.0.1"
},
"devDependencies": {
"@mdi/font": "^7.1.96",
"@rushstack/eslint-patch": "^1.7.2",
"@types/google.maps": "^3.54.4",
"@types/webpack-env": "^1.18.0",
"@vitejs/plugin-vue": "^5.0.4",
"@vue/compiler-sfc": "^3.2.45",
"@vue/eslint-config-prettier": "^9.0.0",
"autoprefixer": "^10.4.17",
"cross-env": "^7.0.3",
"eslint": "^8.49.0",
"eslint-plugin-vue": "^9.22.0",
"laravel-mix-vue3": "^0.7.0",
"postcss": "^8.4.35",
"resolve-url-loader": "^5.0.0",
"sass": "^1.57.1",
"sass-loader": "^13.2.0",
"tailwindcss": "^3.4.1",
"vue-loader": "^17.0.1",
"webpack": "^5.90.3"
}
The Fix
// webpack.mix.js
mix.setPublicPath('public').webpackConfig({
[...]
watchOptions: {
poll: true,
ignored: /node_modules/
},
[...]