Vite vs. Parcel.js with Laravel
I swear I don't have anything against Vite for local JavaScript development! It's just that there's alternatives out there. One such alternative is Parcel.js. The Parcel.js project was started in 2017 by Devon Govett and to date has had over 400 contributors. Coincidentally, Devon also started pdfkit, which I use every day at More Vang. As of the writing of this blog, Parcel.js is on version 2.13.3.
Parcel.js is on par with Vite as far as features go.
Feature | Vite | Parcel.js |
---|---|---|
HMR | ||
Tree-shaking | ||
Chunking | ||
Code-Splitting | ||
Vue | ||
Glob Import |
One thing that Parcel does differently than Vite is that it will automatically install a plugin that it knows it needs. For instance, If it encounters a *.vue file, it knows to install @parcel/transformer-vue. This is a nice-to-have feature, but be wary and pay attention to what the command prints to the terminal.
Parcel claims to be a zero-config tool, which I can mostly agree with. However, having built a proof-of-concept app with Laravel + Inertia.js, I did make a few configurations.
How to use Parcel.js with Laravel
Setting up Parcel.js is pretty straitforward, although I did run into some errors along the way which I will detail below. But first, let's get a sample project up-and-running. If you want to follow along, check out the following repo:
Be sure to follow the README
instructions before you proceed.
Now, let's purge Vite from our project.
npm remove vite laravel-vite-plugin @vitejs/plugin-vue
Then let's add parcel.
npm install --save-dev parceljs @parcel/resolver-glob
Let's configure Parcel.js to build our JavaScript and Sass. One difference between Parcel.js and Vite is that Parcel.js is configured almost entirely from package.json
. Below is a diff of the configuration that we need to add.
diff --git a/package.json b/package.json
index 3b06186..37c05af 100644
--- a/package.json
+++ b/package.json
@@ -2,21 +2,37 @@
"private": true,
"type": "module",
"scripts": {
"build": "parcel build",
"dev": "parcel watch --watch-dir resources/"
},
+ "targets": {
+ "app.js": {
+ "distDir": "public/build/",
+ "source": "resources/js/app.js"
+ },
+ "app.css": {
+ "distDir": "public/build/",
+ "source": "resources/css/app.scss"
+ }
+ },
+ "alias": {
+ "@inertiajs/core": "@inertiajs/core/dist/index.esm.js"
},
"devDependencies": {
"@inertiajs/vue3": "^2.0.3",
The changes above are telling Parcel.js to build two targets: app.js and app.css.
It is also configuring an alias for @inertiajs/core, because without the alias there will be JavaScript errors on the page when using the Inertia.js router.
We will also need to add a .parcelrc
file (see! we need to configure it).
.parcelrc
{
"extends": "@parcel/config-default",
"resolvers": [
"@parcel/resolver-default",
"@parcel/resolver-glob",
],
}
The important thing here is that we're adding the @parcel/resolver-glob
resolver. This allows us to do things like import * as pages from './pages/**/*.vue
.
Now that we have things configured, let's get our source code ready to be built with Parcel.js.
resources/js/app.js
diff --git a/resources/js/app.js b/resources/js/app.js
index 8cbdc8f..4a8b881 100644
--- a/resources/js/app.js
+++ b/resources/js/app.js
@@ -2,12 +2,18 @@ import './bootstrap';
import { createApp } from 'vue';
import { createInertiaApp } from '@inertiajs/vue3';
+import pages from './pages/**/*.vue';
createInertiaApp({
- resolve(name) {
- const pages = import.meta.glob('./pages/**/*.vue');
+ resolve: name => {
+ const parts = name.split('/');
+ let page, part;
- return pages[`./pages/${name}.vue`]();
+ while (part = parts.shift()) {
+ page = (page || pages)[part];
+ }
+
+ return page;
},
setup({ el, App, props, plugin }) {
const app = window.app = createApp(App, props);
Above, we're reworking the way that our Inertia.js app resolves our Vue components. It's still the same general glob patter, but Parcel.js implements it different than Vite. Mainly, Parcel.js doesn't have an import.meta.glob()
function.
resources/js/package.json
diff --git a/resources/js/package.json b/resources/js/package.json
new file mode 100644
index 0000000..4720025
--- /dev/null
+++ b/resources/js/package.json
@@ -0,0 +1,3 @@
+{
+ "type": "module"
+}
A package.json
? In the resources/js
folder?! You may be wondering why this is here, and it is not a mistake. Placing a relatively empty package.json
file here helps Parcel.js to resolve our scripts via its tilde alias. Let me show you what I mean.
diff --git a/resources/js/pages/Auth/Login.vue b/resources/js/pages/Auth/Login.vue
index 7d71ebc..7ce5be2 100644
--- a/resources/js/pages/Auth/Login.vue
+++ b/resources/js/pages/Auth/Login.vue
@@ -21,7 +21,7 @@
<script setup>
import { ref, defineProps } from 'vue';
import { useForm } from '@inertiajs/vue3';
- import AppLayout from '@/layouts/App.vue';
+ import AppLayout from '~/layouts/App.vue';
const form = useForm({
email: '',
diff --git a/resources/js/pages/Auth/Register.vue b/resources/js/pages/Auth/Register.vue
index df59952..7448dfc 100644
--- a/resources/js/pages/Auth/Register.vue
+++ b/resources/js/pages/Auth/Register.vue
@@ -31,7 +31,7 @@
<script setup>
import { ref, defineProps } from 'vue';
import { useForm } from '@inertiajs/vue3';
- import AppLayout from '@/layouts/App.vue';
+ import AppLayout from '~/layouts/App.vue';
const form = useForm({
name: '',
diff --git a/resources/js/pages/Home.vue b/resources/js/pages/Home.vue
index 83666f0..2d13bc8 100644
--- a/resources/js/pages/Home.vue
+++ b/resources/js/pages/Home.vue
@@ -8,5 +8,5 @@
</template>
<script setup>
- import AppLayout from '@/layouts/App.vue';
+ import AppLayout from '~/layouts/App.vue';
</script>
Notice how we changed all of our @
signs to ~
? That's because Parcel.js doesn't support aliases in the same way Vite does.
We're now ready to run npm run build
to build our scripts and styles.
With our assets built, we can now make the final change.
resources/views/app.blade.php
diff --git a/resources/views/app.blade.php b/resources/views/app.blade.php
index 57e9e0e..477d002 100644
--- a/resources/views/app.blade.php
+++ b/resources/views/app.blade.php
@@ -11,9 +11,10 @@
<!-- Styles / Scripts -->
@inertiaHead
@routes
+ <link rel="stylesheet" href="{{ asset('build/css/app.css') }}" />
</head>
<body>
@inertia
- @vite(['resources/css/app.scss', 'resources/js/app.js'])
+ <script src="{{ asset('build/js/app.js') }}"></script>
</body>
</html>
Errors along the way
As I was preparing for this tutorial, I was stymied multiple times by errors along the way. I will put them here for reference.
vue.runtime.esm-bundler.js does not export 'default'
@parcel/core: node_modules/vue/dist/vue.runtime.esm-bundler.js does not export 'default'
I was attempting to import Vue from 'vue'
, which is the old Vue2 way. I changed to import { createApp } from 'vue'
and the error went away.
TypeError: P is undefined
Uncaught (in promise) TypeError: P is undefined
This came up before I realized that Parcel.js had a tilde alias for resolving imports. The solution was to change the @
s to ~
s in my *.vue
files.
TypeError: ed.default is not a function
Uncaught (in promise) TypeError: ed.default is not a function
This one was a nightmare to solve. It actually took me a coupled days, and I don't know exactly how and why it was broken, but it had to do with the way @inertiajs/core was importing Axios. It may be a bug with Parcel.js, or it may be a bug with Inertia.js, but in the end I simply added an alias in my package.json
forcing Parcel.js to use the ESM build for @inertiajs/core.
"alias": {
"@inertiajs/core": "@inertiajs/core/dist/index.esm.js"
},