Vue.js: Laravel Mix not combining files - javascript

I'm using Laravel 5.4 with Node.js 6, and Vue.js 2.
In resources/assets/js/app.js I have:
require('./bootstrap');
window.Vue = require('vue');
Vue.use(require('vue-resource'));
In webpack.mix.js I have:
mix.combine([
'resources/assets/js/app.js',
'resources/assets/js/enhanced.js',
'resources/assets/js/search.js'
], 'public/js/app.js')
.sass('resources/assets/sass/app.scss', 'public/css');
In resources/assets/js/enhanced.js I have:
require('./app')
var Search = require('./components/Enhanced.vue');
const app = new Vue({
el: '#app_page_enhanced',
render: app_page_enhanced => app_page_enhanced(Page_Enhanced)
});
In resources/assets/js/search.js I have:
require('./app')
var Search = require('./components/Search.vue');
new Vue({
el: '#app_search',
render: app_search => app_search(Search)
})
I have npm run watch-poll running in a Terminal window.
However, I keep getting an error on line 8 of app.js:
require('./bootstrap');
When I comment that line out, the process goes through without additional errors, but when I look at: public/js/app.js the separate .js files aren't included.
I've tried removing: require('./app') from the two .js files, and:
mix.combine([ ... ], 'public/js/')
... and:
mix.combine([ ... ], 'public/js/', 'public/js/app.js')
... but it made no difference.
It's worth mentioning that the code for Vue works when in the app.js file.
I've cobbled this together based on the bits I've scavenged, as I've not been able to find an official guide.
Update
In terms of the core problem, #tompec has helped fix it.
However, I thought this would cure the massive number of errors I get when running the search page and featured results page, where each page is executing the Vue code for both pages, causing the errors.

Try to do something like that
mix.js([
'resources/assets/js/app.js',
'resources/assets/js/enhanced.js',
'resources/assets/js/search.js'
], 'public/js/app.js')
.sass('resources/assets/sass/app.scss', 'public/css');
and remove require('./app') from resources/assets/js/enhanced.js and from resources/assets/js/search.js.
And here's the doc: https://github.com/JeffreyWay/laravel-mix/tree/master/docs

Related

Very high memory consumption while building multiple vue apps with vue-loader

We are using vue-loader (usually together with the html-webpack-plugin, but I omit this in the following because it is not the important part) to transpile multiple vue based applications within a single project. Our webpack configuration looks a little bit like the following:
const apps = [
{ html: 'app-1', js: 'app-1' },
{ html: 'app-2', js: 'app-2' },
...
]
module.exports = (_a, _b) =>({
entry: Object.fromEntries(apps.map(app => [app.js, './src/${app.js}.js'])),
...
plugins: [
new VueLoaderPlugin()
],
...
})
So for instance, app-1.html has a div with id app, which is referenced in app-1.js:
import Vue from 'vue'
import App1 from './app-1.vue'
...
new Vue({...}).$mount('#app')
This means our directory structure (very simplified) looks like the following:
src
app-1.html
app-1.js
app-1.vue
app-2.html
app-2.js
app-2.vue
...
package.json
webpack.config.js
Now here is our problem: We noticed a very high memory consumption, which increases drastically the more apps we have in our apps array. This is something like > 10 GB for a project containing 8 of these apps.
Could this be something like a memory leak in the vue-loader or are we somehow misusing the plugin? We are using version 15.10.0 of vue-loader.
It seems that vue-loader was not the culprit. I can reproduce the same high memory consumption when I remove the vue-loader completely and don't use any plugins at all. It seems that the terser plugin (which is automatically used by webpack) is the one using up that much memory (see also https://github.com/webpack/webpack/issues/13550). By default terser spawns a lot of threads. One can control this behaviour:
const TerserPlugin = require("terser-webpack-plugin")
...
module.exports = {
...
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({
parallel: 2,
})
]
}
}
Alternative solution would be to use another minimizer, e.g. esbuild-loader.

javascript file is not working in vue cli html template

I'm using vue cli to create a multipage web app.
The file structure is like below:
- public
- index.html
- tables.html
- us.html
- guide.html
- src
- main.js
and the main.js is:
import Vue from 'vue'
import router from './router'
Vue.config.productionTip = false
console.log('hello world!');
let appExists = document.querySelector('#app') != undefined;
if(appExists){
new Vue({
router,
render: h => h(App),
mounted(){
console.log('hi!');
}
}).$mount('#app')
}
What I'm looking for:
I want main.js file to be loaded into tables.html file.
What is happening:
main.js is not working in any file except index.html.
What I have done:
As I searched, I realized that vue cli loads the main.js only in index.html and not in any other file. when I try to load the main.js using <script src="/src/main.js"></script> in tables.html, I always get the error Uncaught SyntaxError: expected expression, got '<'. I tried some solutions like https://cli.vuejs.org/config/#pages but it didn't change anything at all.
And the question is:
What should I do to use main.js in other files?
I figured it out. Everything in vue cli works with the combination of webpack plugins.
The plugin which connects the files together is called "HtmlWebpackPlugin".
First, install the plugin using:
npm i html-webpack-plugin
Then you can tell this plugin: "Hey I have another page in my project! Please connect main.js to it" by writing the code below in vue.config.js:
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
configureWebpack: {
plugins: [
new HtmlWebpackPlugin({
filename: 'tables.html',
template: './public/tables.html',
}),
new HtmlWebpackPlugin({
filename:'index.html',
template: './public/index.html',
}),
]
}
}
Hope it will help other people who are facing the same issue!

Dynamically loading a file based on an environment variable in Webpacker (Rails 6)

First time for me on Stack Overflow, please be kind ;) I'll try to do my best!
The context:
I am working on a Rails 6 app with webpacker. This is a program that will be shared by several companies and in order to apply the 'one code, multiple setups' paradigm, we decided to move all the company related configuration files to separate folders, and to put the company name as a variable in our .env file. We need to change some config variables as well as some geofencing data (so our customers can create a new delivery to some address). Basically that's what it looks like:
Project folder
| config
| companies
| a_first_company
| rails_config.rb
| geofencing.js
| a_second_company
| rails_config.rb
| geofencing.js
| ....
In the .env file:
COMPANY=a_first_company
And in the rails configuration (application.rb), we are using a simple:
require_relative "companies/#{ENV['COMPANY']}/rails_config"
But now, here comes the JS part! And I am running into trouble.
The problem:
I would like to include dynamically a JSON object in an existing script. A sample geofencing.js looks like that:
module.exports = {
"countries": ["be"],
"polygon": [
50.8917729, 4.3004608,
...
50.9162381, 4.3450928,
50.8917729, 4.3004608
]
}
And I am trying to import it as a geofencing variable in my existing address autocompletion script:
/app/javascript/plugins/places.js
// I know it doesn't work that way, but basically that what I would like to do:
const geofencing = require(`/config/companies/${process.env.COMPANY}/geofencing`);
...
const initPlaceAutocomplete = () => {
...
var placesAutocomplete = places(
{
// And use the variable here...
insidePolygon: [geofencing.polygon],
type: 'address',
// And there...
countries: geofencing.countries,
templates: {
value: (suggestion) => {
return suggestion.name;
}
},
container: addressInput
}
);
...
}
export { initPlaceAutocomplete };
This file is imported in the view with a <%= javascript_pack_tag 'delivery_new' %>:
/app/javascript/packs/delivery_new.js
import { initPlaceAutocomplete } from '../plugins/places';
initPlaceAutocomplete();
...
The solution (that I haven't found yet):
I have tried several things, like importing the file in the webpack config (/config/webpack/environment.js), just like in the ProvidePlugin documentation:
const {environment} = require('#rails/webpacker')
const path = require('path');
const webpack = require('webpack')
environment.plugins.prepend('Provide',
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
Popper: ['popper.js', 'default'],
geofencing: path.resolve(path.join(__dirname, '..', '..', 'config', 'companies', process.env['COMPANY'], 'geofencing'))
})
)
module.exports = environment
... But it didn't work.
I also tried various places to import 'geofencing' in several places, with always the same result in the Chrome console: Uncaught ReferenceError: geofencing is not defined.
I noticed, though, that I had access to the process.env variables in the places.js script: writing console.log(process.env['COMPANY']); in the file prompts me the company name in the dev console when I reload the page in Chrome.
Apart from this, I have to say that I am lost. I am basically a newbie to the Webpack 'magic' ;)
Please tell me if you need more info about my setup.
Thanks in advance for your help!
You should be able to require modules using interpolation in the require() expression similar to your example. Try using a relative path, e.g. (assuming parent directories are siblings):
require(`../config/${process.env.COMPANY}/geofencing`)
The above expression will add only the COMPANY geofencing module to the bundle.
If you wish this to be dynamic, webpack can resolve the interpolation at runtime assuming the require paths are scoped to a directory; you're already doing this here with "../config/". As a result, webpack will include all COMPANY geofencing modules in the bundle. As for usage, I might move the require statement inside a function:
function initPlaceAutocomplete(company) {
const geofencing = require(`../config/${company}/geofencing`)
// ...
}
// usage
import { initPlaceAutocomplete } from "../plugins/places"
initPlaceAutocomplete(process.env.COMPANY)
At this point, this could is a good use case for dynamic imports, i.e., webpack supports the TC39 proposal for dynamically loading modules at runtime.
Instead of require("../config..."), you can use the import() function syntax. Unlike require(), the import() function syntax resolves asynchronously.
Now webpack will bundle all the COMPANY geofencing modules, but as separate "chunks" to keep the size of your initial script down. webpack will insert code to resolve these "chunks" asynchronously at runtime. To support this, the import() expression returns a Promise so, I'm using the async/await syntax here as a result.
async function initPlaceAutocomplete(company) {
const geofencing = await import(`../companies/${company}/config`)
// ...
}

Consolidating Bundles in Webpack 2 with System.import

In Webpack 1, we do code-splitting imports with require.ensure, which can take an array of modules. These modules are combined into a single bundle and fetched with one HTTP request:
require.ensure(['module1', 'module2'], (require) => {
const module1 = require('module1');
const module2 = require('module2');
// use these modules here...
});
// ==> both modules are served as a single bundle, e.g. '5.js'
With Webpack 2, we can now use System.import for a cleaner syntax... but it seems like System.import only accepts a single module to import. Fine -- I can use Promise.all -- but then I end up with two bundles:
Promise.all([
System.import('module1'),
System.import('module2')
]).then( (module1, module2) => {
// use these modules here...
});
// ==> each module served as its own bundle, e.g. '5.js', '6.js'
Is there a way to use System.import but still combine the requested modules into a single bundle?
(Yes, in some cases I can add a new module file that in turn imports and consumes both the dependencies, and that's often the best approach, but for several of my use cases it merely adds additional boilerplate)
According to Sean T. Larkin (webpack core team), using a single file that imports both resources is your best bet (like you already discovered).
Example (untested)
bundle.js
//http://stackoverflow.com/a/36878745/402706
export {default as module1} from './modules/module1';
export {default as module2} from './modules/module2';
Import the single file
System.import('bundle').then({ module1, module2 } => {
// do stuff
}).catch(err => {
console.log("Chunk loading failed");
});
However, there's no disadvantage to using require.ensure as you have it other than,
not being able to handle async load fails via a promise. src
He mentioned future changes that, "may help with this," but I didn't press for what those might be.
I hope that answer helps you...
I had the same problem to do dynamic module loading and archieve that combining an app folder as root context of webpack 2 and the Ignore plugin because I didn't understand the ContextReplacementPlugin. But that work doesn't bundle all in a single file.
Here's the snippet:
import angular from 'angular';
var paths = ["components/app.components.js", "shared/app.shared.js"];
Promise.all(paths.map(path => System.import('../app/' + path))).then(modules => {
let dependencies = [];
modules.forEach(module => {
dependencies.push(module.default.name);
})
angular.module("app", dependencies);
angular.bootstrap(document, ['app']);
}).catch(error => {
console.error(error.message)
});
Using this structure you can fetch an api then use it for dynamic loading for example.
Then in webpack I used IgonorePlugin to avoid *.html files from webpack build:
plugins: [
new webpack.IgnorePlugin(/vertx/),
new webpack.optimize.UglifyJsPlugin({
sourceMap: true
}),
new webpack.IgnorePlugin(/[A-Za-z]*\.(html)$/i)
],
Here is the project github: https://github.com/yurikilian/angular1es6

Webpack's define plugin: variable is not defined

I'm trying to pass some environment related variables into my React components using Webpack's DefinePlugin. Client side part works great, server side part returns 'MYVARIABLE is not defined'.
I'm using Webpack's Node.JS api. Important parts are below. Am I doing something wrong? Thanks
webpack.config.js
...
webpackConfig.plugins = [
new webpack.DefinePlugin({
MYVARIABLE: 'test-value'
})
]
...
server.js
...
import webpack from 'webpack'
import webpackConfig from '../config/webpack.config'
...
var compiler = webpack(webpackConfig)
...
component file
...
console.log(MYVARIABLE)
...
result
ReferenceError: MYVARIABLE is not defined
....
You have to JSON.stringify('your-value').
According https://webpack.js.org/plugins/define-plugin/ :
because the plugin does a direct text replacement, the value given to
it must include actual quotes inside of the string itself. Typically,
this is done either with either alternate quotes, such as
'"production"', or by using JSON.stringify('production').
so your webpack.config.js should be...
webpackConfig.plugins = [
new webpack.DefinePlugin({
MYVARIABLE: JSON.stringify('test-value')
})
]

Categories