Laravel: Optimizing Mix/Webpack - laravel-5

I am new to Webpack. I am working on a multi-page application. I understand the basics of Laravel Mix, but what I don't understand is how to optimize my Laravel Mix and/or scripts so that is doesn't take 16GB of memory and 20+ minutes to run. Any tips on how I can optimize my webpack file would be greatly appreciated. I have attached it below.
Note: app.js contains all my global stuff
/* global require */
const { mix } = require('laravel-mix');
const webpack = require('webpack');
/*
|--------------------------------------------------------------------------
| Mix Asset Management
|--------------------------------------------------------------------------
|
| Mix provides a clean, fluent API for defining some Webpack build steps
| for your Laravel application. By default, we are compiling the Sass
| file for the application as well as bundling up all the JS files.
|
*/
mix.webpackConfig({
resolve: {
alias:{
"datatables": "mdbootstrap-pro/js/addons/datatables.min.js",
"mdbootstrap": "mdbootstrap-pro"
}
},
plugins: [
new webpack.ProvidePlugin({
$: "jquery",
Blazy: "blazy",
Flickity: "flickity",
jQuery: "jquery",
Popper: ["popper.js", "default"],
Vue: ["vue/dist/vue.esm.js", "default"],
Waves: "node-waves",
"window.jQuery": "jquery",
WOW: "wow.js"
})
],
profile: true,
stats: {
hash: true,
version: true,
timings: true,
assets: true,
chunks: true,
modules: true,
reasons: true,
children: true,
source: false,
errors: true,
errorDetails: true,
warnings: true,
publicPath: true
}
});
mix.copy('node_modules/font-awesome/fonts', 'public/fonts/vendor/font-awesome') // Font Awesome
.copy('node_modules/mdbootstrap-pro/css/addons', 'public/css/addons') // DataTables
.copy('resources/fonts', 'public/fonts')
// Service Worker
.js(['resources/js/service-worker.js'], 'service-worker.js')
// Home Page
.styles(['resources/css/vendor/flickity/flickity.css', 'resources/css/views/index.css'], 'public/css/views/index.css')
.js(['resources/js/app.js', 'resources/js/views/index.js'], 'public/js/views/index.js')
.js(['resources/js/app.js', 'resources/js/views/about.js'], 'public/js/views/about.js') // About
.js(['resources/js/app.js', 'resources/js/views/blogs/index.js'], 'public/js/views/blogs/index.js') // Blogs
.js(['resources/js/app.js', 'resources/js/views/blogs/show.js'], 'public/js/views/blogs/show.js') // Blogs
// Accounts
.js(['resources/js/app.js', 'resources/js/views/accounts/index.js'], 'public/js/views/accounts/index.js') // Accounts
.js(['resources/js/app.js', 'resources/js/views/accounts/cd.js'], 'public/js/views/accounts/cd.js') // CD
.js(['resources/js/app.js', 'resources/js/views/accounts/checking.js'], 'public/js/views/accounts/checking.js') // Checking
.js(['resources/js/app.js', 'resources/js/views/accounts/money-market.js'], 'public/js/views/accounts/money-market.js') // Money Markets
.js(['resources/js/app.js', 'resources/js/views/accounts/saving.js'], 'public/js/views/accounts/saving.js') // Savings
// Benefits
.js(['resources/js/app.js', 'resources/js/views/benefits/index.js'], 'public/js/views/benefits/index.js') // Benefits
.js(['resources/js/app.js', 'resources/js/views/benefits/investments.js'], 'public/js/views/benefits/investments.js') // Investments
.js(['resources/js/app.js', 'resources/js/views/benefits/merchant-partner-program.js'], 'public/js/views/benefits/merchant-partner-program.js') // Merchant Partner Program
.js(['resources/js/app.js', 'resources/js/views/benefits/mobile-services.js'], 'public/js/views/benefits/mobile-services.js') // Mobile Services
.js(['resources/js/app.js', 'resources/js/views/benefits/online-services.js'], 'public/js/views/benefits/online-services.js') // Online Services
// Business Accounts
.js(['resources/js/app.js', 'resources/js/views/business/index.js'], 'public/js/views/business/index.js') // Business Accounts
.js(['resources/js/app.js', 'resources/js/views/business/cd.js'], 'public/js/views/business/cd.js') // CD
.js(['resources/js/app.js', 'resources/js/views/business/checking.js'], 'public/js/views/business/checking.js') // Checking
.js(['resources/js/app.js', 'resources/js/views/business/credit.js'], 'public/js/views/business/credit.js') // Credit
.js(['resources/js/app.js', 'resources/js/views/business/debit.js'], 'public/js/views/business/debit.js') // Debit
.js(['resources/js/app.js', 'resources/js/views/business/loan.js'], 'public/js/views/business/loan.js') // Loans
.js(['resources/js/app.js', 'resources/js/views/business/money-market.js'], 'public/js/views/business/money-market.js') // Money Markets
// Cards
.js(['resources/js/app.js', 'resources/js/views/cards/credit.js'], 'public/js/views/cards/credit.js') // Credit
.js(['resources/js/app.js', 'resources/js/views/cards/debit.js'], 'public/js/views/cards/debit.js') // Debit
.js(['resources/js/app.js', 'resources/js/views/cards/index.js'], 'public/js/views/cards/index.js') // Cards
.js(['resources/js/app.js', 'resources/js/views/cards/reloadable.js'], 'public/js/views/cards/reloadable.js') // Reloadable
// Careers
.js(['resources/js/app.js', 'resources/js/views/careers/index.js'], 'public/js/views/careers/index.js') // Careers
// Loans
.js(['resources/js/app.js', 'resources/js/views/loans/auto.js'], 'public/js/views/loans/auto.js') // Auto
.js(['resources/js/app.js', 'resources/js/views/loans/home.js'], 'public/js/views/loans/home.js') // Home
.js(['resources/js/app.js', 'resources/js/views/loans/home-equity.js'], 'public/js/views/loans/home-equity.js') // Home Equity
.js(['resources/js/app.js', 'resources/js/views/loans/index.js'], 'public/js/views/loans/index.js') // Loans
.js(['resources/js/app.js', 'resources/js/views/alerts/index.js'], 'js/views/alerts/index.js') // Alerts
.sass('resources/sass/app.scss', 'public/css/app.css');
if (mix.inProduction()) {
mix.version();
} else {
mix.sourceMaps();
}

The problem you're having with memory and cpu/time is relating to doing too much build. You're not utilizing Single Page Application (SPA) framework for what it's good as, an SPA. Since you're doing a build anyway, why don't you build it all into a single app.js? Then you can dynamically load each component with something like:
<component :is="component_name"></component>
If your purpose is to optimize load of a single view on each page then you're actually doing extra work for nothing. Webpack already have a feature called 'codesplitting' https://webpack.js.org/guides/code-splitting/
Ref: https://v2.vuejs.org/v2/guide/components-dynamic-async.html#Async-Components
Read the entire page above as it is all helpful, not just the Async Component part.
Tip: Since laravel-mix is simply a Webpack wrapper, you can configure webpack inside of mix. Example, you can define the chunk file to specific location like so:
mix.webpackConfig({ output: { chunkFilename: 'js/chunks/[name].js', }, });
You will notice, when you do dynamic/async import, it will simply cause an async/ajax load of the specific javascript file/chunk.

Related

Strapi custom logger configuration

I have got a Strapi v4 app with the following:
./config/logger.js
'use strict';
const {
winston,
formats: { prettyPrint, levelFilter },
} = require('#strapi/logger');
module.exports = {
transports: [
new winston.transports.Console({
level: 'debug',
format: winston.format.combine(
levelFilter('debug'),
prettyPrint({ timestamps: 'YYYY-MM-DD hh:mm:ss.SSS' })
),
}),
],
};
and additionally in my ./config/functions/bootstrap.js i have got the following code
'use strict';
/**
* An asynchronous bootstrap function that runs before
* your application gets started.
*
* This gives you an opportunity to set up your data model,
* run jobs, or perform some special logic.
*/
module.exports = () => {
console.log("Hello World!");
logger.log("debug","Hello Strapi app!");
};
Unfortunately i do not see it anywhere printed in my folder Strapi app or Web browser. Any idea that could help me?
Regards

ServiceWorkers and Next.js: How would one integrate Service workers in production with a next.js app?

I am working with "next": "^9.3.2" and integrated a service worker (including this just in case someone else has a similar question):
File structure:
pages
public
static
serviceWorker.js
server
index.js
In server/index.js
async function start() {
const dev = process.env.NODE_ENV !== 'production';
const app = nextJS({ dev });
const server = express();
....
server.get('/serviceWorker.js', (req, res) => {
res.sendFile(path.resolve(__dirname, '../public', 'serviceWorker.js'));
});
/* later */
if (process.env.NODE_ENV === 'production') {
server.use(express.static('.next/static'));
server.get('/service-worker.js', (req, res) => {
res.sendFile(path.resolve(__dirname, 'public', 'serviceWorker.js'));
});
In public/serviceWorker.js
var currentCaches = {
css: 'CSS',
images: 'images'
};
const cacheFiles = {
css: [
// 'http://localhost:8016/semantic-ui-css/semantic.min.css',
// 'http://localhost:8016/font-awesome/css/font-awesome.min.css',
// 'http://localhost:8016/leaflet/dist/leaflet.css',
// 'http://localhost:8016/esri-leaflet-geocoder/dist/esri-leaflet-geocoder.css',
// 'http://localhost:8016/styles/styles.css',
// 'http://localhost:8016/leaflet-routing-machine/dist/leaflet-routing-machine.css'
],
images: [
// 'https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-green.png',
// 'https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-red.png',
// 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/images/marker-shadow.png',
// 'http://localhost:8016/public/static/media/search#2x.png',
// 'http://localhost:8016/public/static/uploads/profile-avatars/placeholder.jpg'
]
};
self.addEventListener('install', function(event) {
console.log('Hello world from the Service Worker 🤙');
event.waitUntil(
Promise.all([
caches.open(currentCaches.css).then(cache => {
return cache.addAll(cacheFiles.css);
}),
caches.open(currentCaches.images).then(cache => {
return cache.addAll(cacheFiles.images);
})
])
);
});
Right now I am declaring the css paths in that object above* like I would in my _app.js file:
import 'semantic-ui-css/semantic.min.css';
import 'font-awesome/css/font-awesome.min.css';
import 'leaflet/dist/leaflet.css';
import 'esri-leaflet-geocoder/dist/esri-leaflet-geocoder.css';
import '../styles/styles.scss';
import 'leaflet-routing-machine/dist/leaflet-routing-machine.css';
Figure this would apply to the images too
So my question is since next.js spits out a static/css on a production build:
.next
cache
server
static
chunks
css < -----
476a94f2.d9a9e468.chunk.css
dbd51271.19268786.chunk.css
styles.9ca4e15c.chunk.css
How would one specifically have the serviceWorker know what these file names would be (as well as images, fonts, svg's etc)? As i'm assuming the numbers are to help with caching!
Thanks!
Generally speaking, you need some way of integrating with your web app's build process if you want to get a list of hashed URLs for use within your service worker.
Given that you're using Next.js, a plugin like next-offline can help by doing two things:
Integrating with your build process to get a list of your hashed URLs.
Generating the entirety of your service worker for you, using workbox-precaching under the hood to ensure that your URLs are properly cached and kept up to date.
You can implement something similar yourself if you'd prefer not to use next-offline, but you'd need to use something like next-assets-manifest to obtain the list of hashed URLs, write your own service worker, and figure out how to inject those URLs into the service worker.

how to run vue app in the same domain with laravel sanctum for SPA

I need help in running my Vue spa in the same domain as my laravel app , when running "npm run serve" in terminal I think it's working but when I go to the browser it's refusing connection. I haven't done the backend which I will use sanctum for handling API. Has anybody here have the same project working on like me? love to make conversations to solve this.
Thanks in advance
here is the vue.config.js file
const path = require('path')
const webpack = require('webpack')
const createThemeColorReplacerPlugin = require('./config/plugin.config')
function resolve (dir) {
return path.join(__dirname, dir)
}
/**
* check production or preview(pro.loacg.com only)
* #returns {boolean}
*/
function isProd () {
return process.env.NODE_ENV === 'production'
}
const assetsCDN = {
css: [],
// https://unpkg.com/browse/vue#2.6.10/
js: [
'//cdn.jsdelivr.net/npm/vue#2.6.10/dist/vue.min.js',
'//cdn.jsdelivr.net/npm/vue-router#3.1.3/dist/vue-router.min.js',
'//cdn.jsdelivr.net/npm/vuex#3.1.1/dist/vuex.min.js',
'//cdn.jsdelivr.net/npm/axios#0.19.0/dist/axios.min.js'
]
}
// webpack build externals
const prodExternals = {
vue: 'Vue',
'vue-router': 'VueRouter',
vuex: 'Vuex',
axios: 'axios'
}
// vue.config.js
const vueConfig = {
configureWebpack: {
// webpack plugins
plugins: [
// Ignore all locale files of moment.js
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/)
],
// if prod is on, add externals
externals: isProd() ? prodExternals : {}
},
chainWebpack: (config) => {
config.resolve.alias
.set('#$', resolve('src'))
const svgRule = config.module.rule('svg')
svgRule.uses.clear()
svgRule
.oneOf('inline')
.resourceQuery(/inline/)
.use('vue-svg-icon-loader')
.loader('vue-svg-icon-loader')
.end()
.end()
.oneOf('external')
.use('file-loader')
.loader('file-loader')
.options({
name: 'assets/[name].[hash:8].[ext]'
})
// if prod is on
// assets require on cdn
if (isProd()) {
config.plugin('html').tap(args => {
args[0].cdn = assetsCDN
return args
})
}
},
css: {
loaderOptions: {
less: {
modifyVars: {
// less vars,customize ant design theme
'primary-color': '#00B4E4',
// 'link-color': '#F5222D',
'border-radius-base': '4px'
},
javascriptEnabled: true
}
}
},
}
if (process.env.VUE_APP_PREVIEW === 'true') {
vueConfig.configureWebpack.plugins.push(createThemeColorReplacerPlugin())
}
module.exports = vueConfig
module.exports = {
devServer: {
host: 'app.paymate-ui.test'
}
}
If I understand you correctly, you want to use Laravel and Vue.js together in the same application folder?
Should be pretty easy then.
First off, build your application with Vue scaffolding for the frontend.
Then, make a route that redirects everything to a single controller method that returns a spa view. (Or use a closure)
In this view, include your app.js as an asset and include the main Vue component (something like <app></app>).
Then build your Vue app. All requests will now be forwarded to the spa view, which includes your app.js, which should bootstrap Vue.

PayPal Checkout update cart totals dynamically after ajax

I have a shopping site which I'm converting to Ajax. It has a list of cart items.
On page load the PayPal button is set up correctly with the cart totals passed in. However, now that I have an Ajax cart I need to update the cart totals when the cart changes.
If I call the paypal.Button.render function again I get another button.
So I need to either delete the old button and rebuild or call something to update the totals.
Can anyone point me in the right direction?
This is my code that sets up the button.
function PayPalButton(authResult) {
// console.log(authResult);
// Render the PayPal button
paypal.Button.render({
// Set your environment
env: authResult.PayPalEnvironment, // sandbox | production
// Specify the style of the button
style: {
label: 'generic',
size: 'responsive', // small | medium | large | responsive
shape: 'rect', // pill | rect
color: 'blue', // gold | blue | silver | black
tagline: false
},
// PayPal Client IDs - replace with your own
// Create a PayPal app: https://developer.paypal.com/developer/applications/create
client: {
sandbox: authResult.PayPalClientId,
production: authResult.PayPalClientId
},
payment: function (data, actions) {
// recover transaction value here
var carttotal = authResult.cart.FTotal - (authResult.VATApplies ? 0 : authResult.cart.VAT);
var paymentGuid = authResult.cart.PaymentGuid;
//alert(paymentGuid);
return actions.payment.create({
payment: {
transactions: [{
amount: {
total: carttotal.toString(), currency: 'GBP'
},
custom: paymentGuid.toString()
}]
}
});
},
onAuthorize: function (data, actions) {
return actions.payment.get().then(function (data) {
return actions.payment.execute().then(function () {
processPayPal(data);
});
});
}
}, '#paypal-button-container');
};
Well this works but it's ugly. Surely there must be a better way.
After updating the cart. I remove the PayButton button from the DOM and put the HTML back how it was. Then I call the routine to reconfigure the button.
$("#PayPalSection").remove();
$("#paypalButtonContainer").append('<div id="PayPalSection"><div class= "col-8 offset-2"><div id="paypal-button-container"></div></div></div>');
ConfigurePayPalButton();
This does a round trip to the DB unfortunately but I will try and write something that doesn't require that step.

How to init Vuejs + VueRouter on certain pages within Laravel

To explain it better I am creating an app with Laravel back-end (for learning purposes) and I am trying too hook lots of stuff. But I want to create only one or two of the pages to run the vue/vue-router to display certain components. Like multi-page website with few single-page apps within it.
I cut it rough
<div id="{{ (Route::current()->getName() == 'vue-page1') ? 'app' : '' }}">
#yield('content')
</div>
but this is no solution I tried to limit the pages after that with JS using
if (!document.getElementById("app"))
doesn't get it, Vue is still initiated. I want to keep the current structure, just to stop it from initialization on pages where it shouldn't.
Based on the code that you posted try to build the options object beforehand than to pass it to the new Vue instance. Like that:
let options = {
el: '#app',
name: 'app',
render: app => app(App),
data: {
a: 1
},
created: function () {
// `this` points to the vm instance
console.log('a is: ' + this.a)
},
mounted() {
console.log('done');
auth.check()
}
}
if(document.getElementById("app-with-routes"))//yaah we have app with routes
{
window.VueRouter = require('vue-router');
let router = new VueRouter({
routes: [
{
path: '/',
name: 'home',
component: Vue.component('home', require('./components/Home.vue'))
},
// .... all the routes you need
]});
options['router'] = router
}
const app = new Vue(options);
That way you will be able to use all vue sweet parts without having to deal with the router.
Have you tried including your vue.js only in pages that you need to?
#section('styles')
{{ (Route::current()->getName() == 'vue-page1') ? '<script src="https://unpkg.com/vue/dist/vue.js"></script>' : '' }}
#endsection

Resources