It was a tough time to be alone, application codebases across the platforms lay dispersed throughout the cloud. How could one hacker deploy an app to every relevant platform without expending a life of development time reaching these targets?
Isomorphic — Having identical relevant structure; being structure-preserving while undergoing certain invertible transformations.
Enter the Vue CLI, in combination with some godlike plugins we can have an isomorphic progressive client that lives alongside a fully ECMAScript 2018 spec’d Node API. Where, each piece is contained in one source of truth for all application deployments. As a lone wolf developer, this unlocks the full spectrum of the stack to hit as many targets as possible in one shot.
We start with a single directory named for our project. We will take advantage of yarn wokspaces to sync the
node_modules/ of our pieces.
mkdir the-one && cd $_yarn init -ygit init
We can then edit the newly created root
package.json to include the workspace information. Our workspaces in this case are
backend/ because they all have their own
package.json. This allows us to install all dependencies across the project stack with one command;
frontend/ groundwork is laid by the Vue CLI.
yarn add global @vue/clivue create frontendcd frontendvue add cordovavue add @vue/pwavue add @uvue/ssr
backend/ I prefer a custom pattern to generated norms. It is based on a fractal, essentially every directory is a connected expansion of the root.
However, this API pattern is up to the architect and can be interchanged freely!
Let’s take a closer look at the initial scaffolding of the
frontend/ and what it did. We have three Vue CLI plugins Cordova, PWA, and last but definitely not least UVue. These are the magic beans unlocking our three targets.
- Progress web with SSR and a service worker to be accessible via a url.
- Android native build to be available in the play store.
- iOS native build to be available in the app store.
frontend/ has one entry point that effects every instance of the clients. For example all updates to
frontend/src/App.vue are reflected across targets, meaning changes to the browser app are seen on the native platforms. Furthermore, when we execute our serve commands the app runs in two different modes, SSR and SPA.
Let’s examine these commands and expose their key differences, keep in mind they are run from the project root via workspaces.
# serve the browser bundle in ssr modeyarn workspace frontend run ssr:serve# serve the android bundle to a simulator or deviceyarn workspace frontend run cordova-serve-android# serve the ios bundle to a simulator or deviceyarn workspace frontend run cordova-serve-ios
All this is already right at your fingertips, but we need a uniquely simple configuration to mesh things together.
One great thing to mention about this workflow is it maintains an extensible, upgrade-able, and config driven base that is flexible in what suits your style. That being said, the
frontend/ root requires three separate configuration files. One file for the CLI , plus two for UVue’s server and core. These files will be automatically generated and augmented by the CLI, but in our case we need to tweak some.
Config — the options given to us by the greater creators; passed down like heirlooms through the generations.
The files below contain some basic config related to both Cordova and Workbox, the development server proxies, and UVue.
baseUrl depends if we are running on Cordova or not, while the
cordovaPath is where the Cordova shell lives.
workBoxOptions contain two important properties. The
runtimeCaching which applies a
'networkFirst' strategy that keeps a record of all API requests in the cache, but always asks for for updated content first. Next, we need
templatedUrls specified as our base route required for SSR.
devServer here bundles and serves the
frontend/ when running as an SPA in Cordova. REST and real-time APIs, the
backend/ use a
proxy to curry their requests.
Here we have UVue’s core configged to our needs. This mounts the core’s built-in plugin system along with plugins I had created. Also know this, order of the plugins matters.
httpClient handles requests to REST API including authorization, while
guards are responsible for protection of routes. The
sockets plugin uses a Vue middleware when the store becomes available.
Finally, the UVue connect server that renders the page before delivering it to a web based client has a lot out of the box plugins, but is also extremely customize-able. Again the order does matter.
The only plugin we need to create is a
proxy from the SSR server to the API server. We also need some important cache control headers for the
service-worker.js file defined in the static plugin.
The UVue server also includes adapters to build an API layer on top of it. But for our use case I believe the decoupled server approach can be more easily scaled across platforms. That’s it for our
That’s it for this post, but if there is interest I can expand with backend, environment and deployment stories in a part two.
With Appearances By
- uvue — universal server rendering or single page micro framework
- pwa — progressive web app workbox
- cordova — web view native builds
About the Author
Inspired by what I see as new digital forms of life I look at code as the interface to shaping this world.
Want to know more about me? I stream my work almost daily on twitch. Come hangout!