Require packages like AlpineJS and MapBox GL JS as dependency or CDN?
We are using quite a lot of 3rd party packages like AlpineJS and MapBox GL JS and are facing an issue how to properly import these so they are available from scripts in blade files.
Let us say that we need to show a MapBox on a page using blades. We could do this like so:
// /resources/views/map.blade.php
@extends('layouts.app')
@section('content')
<div id='map' style='width: 400px; height: 300px;'></div>
<script>
mapboxgl.accessToken = '<your access token here>';
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v11',
center: [-74.5, 40],
zoom: 9
});
</script>
@endsection
Now the code above needs access to mapboxgl and this is the reson for this post. How do we make that available? As I see it we have the following options:
NPM dependency
Install MapBox as a npm dependency and make it available in resources/js/app.js like so:
//resources/js/app.js
window.mapboxgl = require('mapbox-gl');
where we make sure to include public/app.js in the header of the page (not the bottom since the script will run before the library is made available causing an error Uncaught ReferenceError: mapboxgl is not defined):
<!-- /resources/layouts/app -->
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
...
<script src="{{ mix('js/app.js') }}"></script><!-- Important this is in the head section -->
</head>
<body>
@yield('content')
</body>
</html>
but won't that affect all page loads forcing the browser to load a potential large app.js file before processing the HTML?
CDN in head-section
Use a CDN in the <head> of our /resources/layouts/app file like so:
<!-- /resources/layouts/app -->
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
...
<script src='https://api.mapbox.com/mapbox-gl-js/v1.11.0/mapbox-gl.js'></script>
</head>
<body>
@yield('content')
</body>
</html>
This script will still have to load on each pageview but loads through a potentially fast CDN and can be cached by the browser across sites. But now we do not get any information from NPM when an update is available and our site needs to fetch a lot of javascript files this way.
Conditional CDN
Use a CDN like above but only conditionally add it to the <head> section using the @push('scripts') syntax (require a @stack('scripts') part in the <head> section like described in the Laravel Docs)
<!-- /resources/layouts/app -->
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
...
@stack('scripts') <!-- This will output any scripts pushed to the 'scripts' stach -->
</head>
<body>
@yield('content')
</body>
</html>
Where we push the CDN on the pages where the map is needed:
// /resources/views/map.blade.php
@extends('layouts.app')
@push('scripts')
<script src='https://api.mapbox.com/mapbox-gl-js/v1.11.0/mapbox-gl.js'></script>
@endpush
@section('content')
<div id='map' style='width: 400px; height: 300px;'></div>
<script>
mapboxgl.accessToken = '<your access token here>';
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v11',
center: [-74.5, 40],
zoom: 9
});
</script>
@endsection
NPM dependency and wait for DOMContentLoaded
Install MapBox as a npm deendency (see above) but implemented the /public/app.js script in the bottom of the page and wait for the event DOMContentLoaded before initializing the mapbox:
// /resources/views/map.blade.php
@extends('layouts.app')
@section('content')
<div id='map' style='width: 400px; height: 300px;'></div>
<script>
var map;
document.addEventListener("DOMContentLoaded", function(event) {
mapboxgl.accessToken = '<your access token here>';
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v11',
center: [-74.5, 40],
zoom: 9
});
});
</script>
@endsection
Conclusion
Maybe I am missing something, but it does not seem to me like there is an optimal solution that encapsulates all scenarios? :/
Please or to participate in this conversation.