I have registered a some components as a global components in js/app.js
file, But this makes the compiled app.js
file larger.
//example
register the component in another file rather than app.js
resources/js/example.js
window.Vue = require('vue');
Vue.component('example-component', require('./components/ExampleComponent.vue').default);
Compile your component to another file in webpack.mix.js
mix.js('resources/js/app.js', 'public/js')
.js('resources/js/example.js', 'public/js')
.sass('resources/sass/app.scss', 'public/css');
Include it in blade
<script src="/js/example.js"></script>
As like Caddy DZ answered it's the way to go with laravel-mix. (CaddyDz happen to be my close friend by the way hhhh)
https://stackoverflow.com/a/58122158/7668448
However if there is multi pages. And keeping doing it that way. It's a bit of hassle. Or not the coolest of the ways. For this purpose i developed a pakage laravel-mix-glob. Which is a wrapper around laravel-mix. That do some magic for you.
Which allow you to use globs and have all the files that get added automatically handled for you. In place of managing them file by file. Page by page.
The use is so simple. You can check the package here:
https://www.npmjs.com/package/laravel-mix-glob
The documentation explain everything. You have to check the portion about the compileSpecifier
You can give it a one read. And then you'll be more productive. And the magic just happen. Even everything is explained within the doc even how laravel-mix-glob work.
You can also check this issue. Which show some nice points:
https://github.com/MohamedLamineAllal/laravel-mix-glob/issues/5#issuecomment-537991979
And to clear things even just here. Here a use example:
// imports
const mix = require('laravel-mix'); // you need the laravel mix instance
const MixGlob = require('laravel-mix-glob');
// init
const mixGlob = new MixGlob({mix}); // mix is required
// or
const mixGlob = new MixGlob({
mix, // mix required
mapping: { // optional
// see the doc
},
// more options maybe added in future version (fill issues if you need anything, or a PR if you like)
});
// use mixGlob
mixGlob.sass('resources/sass/**/*.compile.scss', 'public/css', null, {
base: 'resources/sass/',
// compileSpecifier: {
// disabled: true // there is no compile specifier (disabled), and so it will not be removed from the extension (by default disabled = false, and the default specifier = 'compile', and it get removed from the path)
// ,
// specifier: 'cmp'
// }
// mapping: { // this take precedency over any other mapping // useless feature as laravel-mix doesn't support output in different formats. (until i find a workaround)
// ext: {
// 'scss': 'css' // multiple files separatly
// },
// or
// ext: 'css', // all to the same
//
// }
})
.js(['resources/js/**/*.compile.{js,jsm}', '!resources/js/secondPattern/**/*'], 'public/js/', null, {
base: 'resources/js/'
}) // multiple globs pattern as an array. Also with exclusion support (!)
.js('resources/js/secondPattern/**/*.compile.{js,jsm}', 'public/js', null, {
base: 'resources/js/secondPattern'
})
.ts(['resources/js/ts/**/*.compile.ts', 'resources/js/tsx/**/*.compile.tsx'], 'public/js', null, {
base: {
ts: 'resources/js/ts/', // per file extension mapping
tsx: 'resources/js/tsx/**/*.compile.tsx'
}
})
.mix('sass')('resources/sass/summernote.scss', '../resources/views/system/admin/dashboard/partials/_summernote_css.blade.php'); // laravel-mix instance
For the bellow
.js(['resources/js/**/*.compile.{js,jsm}', '!resources/js/secondPattern/**/*'], 'public/js/', null, {
base: 'resources/js/'
})
It translate to take all the js
or jsm
files in the directory resources/js/
or any of it's sub directories at all levels. And that are not part of resources/js/secondPattern/**/*
. And output them in public/js
. Holding the same structure from the base resources/js/
. Whenever you add a new file that respect that structure it will be automatically compiled for you (well laravel-mix watcher will be restarted and with it the whole build). And you don't have to do it file by file. At all.
For instance let say at start you have 6 files that match the patterns. laravel-mix-glob automatically will make all the 6 right calls. And then even when you add new file it know automatically and recompile.
And laravel-mix-glob leverage all the best glob patterns. In the most intuitive way. Going from simple to the most complex. And the people used to use the glob libraries. Gulp. Or many other tools. Will just find it too familiar. Everything is simple. And it's all explained in the doc. There is many examples too.
It's an important feature. Imaging you want to bundle only few files from many. Adding the specifier and having the feature to be automatically managed and stripped from the output is just interesting and effective. That's the motivation. By default is activated you can deactivated as shown in the example bellow.
Check the doc as it's more complete and tackle all the different parts. The package was been there for months now. And it was well tested in Linux. More less in windows. But many users of both platform used it. And it work perfectly and magically. To give you more comfort and allow you to be more productive.
Also as the author i'm too open to the community. I review and handle PR with great joy. And i like to have contributors. So any interested one can let me know. Here or by filling an issue.
To further extend on Salim Example, you can add Vue to the window and directly create the exported Vue Component within the Vue File.
webpack.mix.js
const mix = require('laravel-mix');
mix.autoload({vue: ['Vue', 'window.Vue']})
.js(...)
.css(...)
.version()
resources/js/components/profile/profile-image.vue
<template>
<div class='profile-image' @click='show(user)'>
<img :src='user.avatar' :alt='`${user.name} profile image`' />
</div>
</template>
<script>
/** Note: Global Component Registered Via Vue.component(...) **/
Vue.component('profile-image', {
props: ['user'],
methods: {
/**
* Show User Profile Page
*/
show(user) {
const { location } = window;
window.location = `${location.origin}/users/${user.id}`;
}
}
});
</script>
webpack.mix.js
const mix = require('laravel-mix');
mix.autoload({
vue: [
'Vue',
'window.Vue'
]
})
.js([
/* ---------------------------------
| Card Components
| ---------------------------------
|
| . Card.vue (Original)
| . IconCard.vue (Topic Contextually Relevant Icon)
| . DetailCard.vue (Shown On Detail Pages & Used To Summarize Index Tables)
|
*/
'resources/js/components/cards/card.vue',
'resources/js/components/cards/icon-card.vue',
'resources/js/components/cards/index-card.vue',
'resources/js/components/cards/detail-card.vue',
'resources/js/components/cards/organization-card.vue',
/* ---------------------------------
| Button Components
| ---------------------------------
|
| . Button.vue (Original)
| . ButtonRipple.vue (Interactive Click Effects)
| . ButtonFabIcon.vue (Rounded, Material Design Icons)
|
*/
'resources/js/components/buttons/button.vue',
'resources/js/components/buttons/primary.vue',
'resources/js/components/buttons/success.vue',
'resources/js/components/buttons/button-ripple.vue',
'resources/js/components/buttons/primary-ripple.vue',
'resources/js/components/buttons/success-ripple.vue',
'resources/js/components/buttons/button-fab-icon.vue',
'resources/js/components/buttons/primary-fab-icon.vue',
'resources/js/components/buttons/success-fab-icon.vue',
/* ---------------------------------
| Fields Components
| ---------------------------------
|
| . Form.vue (Create & Update)
| . Detail.vue (Show, Edit, & Cards)
| . Index.vue (Tables Ex: Sort, Search, Filter)
|
*/
'resources/js/components/fields/date/form.vue',
'resources/js/components/fields/date/index.vue',
'resources/js/components/fields/date/detail.vue',
/** Then that one component we actually created ;D **/
'resources/js/components/profile/profile-image.vue',
], 'resources/js/components/bootstrap.js')
.babel([
/* ------------------------------------------------------------------
| Mounting Vue & Using "Babel" (Vanilla JS For Every Browsers)
| ------------------------------------------------------------------
|
| . Our Components are compiled
| . Our Last File Being Added Will Mount Vue
| . We'll Use ".babel()" While Adding This File
| . "Babel" Simply Transforms All Javascript Into Vanilla JS
|
*/
'resources/js/components/bootstrap.js',
'resources/js/bootstrap/mount-vue.js'
], 'public/js/app.js')
/*------------------------------*/
/* Optimization Minification
/*------------------------------*/
.minify('public/js/app.js');
/*------------------------------*/
/* Cache Busting Versioning
/*------------------------------*/
if (mix.inProduction()) {
mix.version();
}
resources/js/mix-extensions/mix-every-vue-component.js
import upperFirst from 'lodash/upperFirst'
import camelCase from 'lodash/camelCase'
const requireComponent = require.context(
// The relative path of the components folder
'./components',
// Whether or not to look in subfolders
false,
// The regular expression used to match base component filenames
/Base[A-Z]\w+\.(vue|js)$/
)
requireComponent.keys().forEach(fileName => {
// Get component config
const componentConfig = requireComponent(fileName)
// Get PascalCase name of component
const componentName = upperFirst(
camelCase(
// Gets the file name regardless of folder depth
fileName
.split('/')
.pop()
.replace(/\.\w+$/, '')
)
)
// Register component globally
Vue.component(
componentName,
// Look for the component options on `.default`, which will
// exist if the component was exported with `export default`,
// otherwise fall back to module's root.
componentConfig.default || componentConfig
)
})
webpack.mix.js
const mix = require('laravel-mix');
class LaravelMixEveryVueComponent
{
public constructor() {
}
}
mix.autoload({
vue: [
'Vue',
'window.Vue'
]
})
.js([
/* ---------------------------------
| Card Components
| ---------------------------------
|
| . Card.vue (Original)
| . IconCard.vue (Topic Contextually Relevant Icon)
| . DetailCard.vue (Shown On Detail Pages & Used To Summarize Index Tables)
|
*/
'resources/js/components/cards/card.vue',
'resources/js/components/cards/icon-card.vue',
'resources/js/components/cards/index-card.vue',
'resources/js/components/cards/detail-card.vue',
'resources/js/components/cards/organization-card.vue',
/* ---------------------------------
| Button Components
| ---------------------------------
|
| . Button.vue (Original)
| . ButtonRipple.vue (Interactive Click Effects)
| . ButtonFabIcon.vue (Rounded, Material Design Icons)
|
*/
'resources/js/components/buttons/button.vue',
'resources/js/components/buttons/primary.vue',
'resources/js/components/buttons/success.vue',
'resources/js/components/buttons/button-ripple.vue',
'resources/js/components/buttons/primary-ripple.vue',
'resources/js/components/buttons/success-ripple.vue',
'resources/js/components/buttons/button-fab-icon.vue',
'resources/js/components/buttons/primary-fab-icon.vue',
'resources/js/components/buttons/success-fab-icon.vue',
/* ---------------------------------
| Fields Components
| ---------------------------------
|
| . Form.vue (Create & Update)
| . Detail.vue (Show, Edit, & Cards)
| . Index.vue (Tables Ex: Sort, Search, Filter)
|
*/
'resources/js/components/fields/date/form.vue',
'resources/js/components/fields/date/index.vue',
'resources/js/components/fields/date/detail.vue',
/** Then that one component we actually created ;D **/
'resources/js/components/profile/profile-image.vue',
], 'resources/js/components/bootstrap.js')
.babel([
/* ------------------------------------------------------------------
| Mounting Vue & Using "Babel" (Vanilla JS For Every Browsers)
| ------------------------------------------------------------------
|
| . Our Components are compiled
| . Our Last File Being Added Will Mount Vue
| . We'll Use ".babel()" While Adding This File
| . "Babel" Simply Transforms All Javascript Into Vanilla JS
|
*/
'resources/js/components/bootstrap.js',
'resources/js/bootstrap/mount-vue.js'
], 'public/js/app.js')
/*------------------------------*/
/* Optimization Minification
/*------------------------------*/
.minify('public/js/app.js');
/*------------------------------*/
/* Cache Busting Versioning
/*------------------------------*/
if (mix.inProduction()) {
mix.version();
}
laravel-mix-autoload-vuejs-extension.js
const mix = require('laravel-mix');
const CollectFiles = (folder, files = []) => {
const isFolder = to => File(path.resolve(to)).isDirectory();
const CombineFiles = (Files, Segments = []) => [ ...files, path.join(__dirname, Segments[0], '/', Segments[1])];
return fs.readdirSync(folder).reduce((filed, file) =>
isFolder(`${folder}/${file}`)
? CollectFiles(`${folder}/${file}`, files)
: CombineFiles(files, [folder, file]),
files
).map(string => string.replace(__dirname, ''));
};
class LaravelMixAutoloadVue
{
constructor()
{
this.LoadVueComponents = (to, output) => mix.js(CollectFiles(to), output);
return mix;
}
dependencies()
{
return ['fs', 'path'];
}
name()
{
return ['vuejs'];
}
register(to, output)
{
if (typeof to === 'undefined') {
return console.log(`Output is undefined for codesplit path ${to}`);
}
this.LoadVueComponents(to, output);
}
boot()
{
console.log("Booting Example");
}
}
mix.extend('vuejs', new LaravelMixAutoloadVue());
webpack.mix.js webpack.mix.js
const mix = require('laravel-mix');
require('./laravel-mix-autoload-vuejs`);
mix.autoload({
vue: [
'Vue',
'window.Vue'
]
})
/* -------------------------------------------------------------
| Laravel Mix Autoload Vue Extensions Handles All Components
| -------------------------------------------------------------
*/
.vuejs('resources/js/components/', 'resources/js/components/bootstrap.js')
.babel([
/* ------------------------------------------------------------------
| Mounting Vue & Using "Babel" (Vanilla JS For Every Browsers)
| ------------------------------------------------------------------
|
| . Our Components are compiled
| . Our Last File Being Added Will Mount Vue
| . We'll Use ".babel()" While Adding This File
| . "Babel" Simply Transforms All Javascript Into Vanilla JS
|
*/
'resources/js/components/bootstrap.js',
'resources/js/bootstrap/mount-vue.js'
], 'public/js/app.js')
/*------------------------------*/
/* Optimization Minification
/*------------------------------*/
.minify('public/js/app.js');
/*------------------------------*/
/* Cache Busting Versioning
/*------------------------------*/
if (mix.inProduction()) {
mix.version();
}