Photo by Caspar Camille Rubin on Unsplash

NativeScript-Vue with Vuex and Vue Devtools (No, really)

Brandon Gohsman
12 min readNov 18, 2019

--

One Man’s Struggle Against The Stack

So, you’ve heard about NativeScript? That alone is somewhat impressive, given the long shadow being cast by the likes of React Native (but that’s another story).

And, somewhere along the way, you fell in love with VueJS and its painfully simple Vuex store and data binding. And how about those ubiquitous devtools? Ah, true love. I’ve been there. That moment you first discovered that you can actually use the Vue framework on top of NativeScript, it was like the very first time someone handed you a bowl of vanilla ice cream with chocolate syrup. Makes it kind of hard to go back. Plus, NativeScript-Vue won’t give you diabetes or flare up your lactose intolerance.

But then you two started spending more time together. Maybe you thought that you would integrate vue-devtools to see what was going on in your store. Maybe you wanted to use one of the pre-built themes, at least as a starting point. You want to use Vue Router? Ok, just settle down. Maybe one of you kept leaving the cap off of the toothpaste.

Over time, the two of you just couldn’t seem to see eye-to-eye on things or work well together. And, in a disparagingly short period of time, the honeymoon was over. Your initial euphoria turned to cynicism and derision, leading you to the ultimate futility of begging for scraps of help on the Interwebs.

Well, don’t worry. I’m here to help. I’m here to bring closure to everyone. Let’s start at the beginning, shall we?

TL;DR

NOTE: While not tested/approved by the FDA, this article has been shown, in limited trials, to help users fall asleep.

More Than One Way to Skin a Cat

Sidenote: First off, I’d like to point out that I don’t know of anyone who has actually skinned a cat or where that phrase originated. Perhaps problem-solving affected more sociopaths in days of yore when taxidermy represented a larger set of issues than the current technological climate. Either way, I now feel like I need to apologize for it and I would gladly welcome a less grotesque, modern replacement to that particular colloquialism.

And with that, let’s meet Thing One and Thing Two…

Option 1 — Standard NativeScript CLI Project Creation¹

If you are already setup for creating NativeScript projects from the command line, then this should already be familiar to you. If not, I recommend a quick read of the Quick Start. In either case, make sure that you are running the latest version of the CLI which, as of this moment, is v6.2.0 as older versions will make…older things.

tns create thing-one

After that, you’ll be presented with a list of project types, which includes Vue.js. For today’s purposes, I went with that and a “blank” project.

Option 2 — The NativeScript-Vue CLI Project Creation²

This method involves installing a couple of Vue packages that the standard NativeScript setup doesn’t bother with, which turns out to be important.

npm install -g @vue/cli @vue/cli-init

This installs the Vue CLI and the Vue CLI Init tool, respectively. Once that is done, we can now use the Vue CLI, as opposed to the NativeScript CLI, to create a NativeScript-Vue project using a Vue template:

vue init nativescript-vue/vue-cli-template thing-two

This progresses in a similar way to Option 1. However, this time you are asked if you wish to create a JavaScript or a TypeScript project. Now, I’ve become rather fond of TypeScript as of late. But there are already enough variables in this article to make one’s head spin, so I won’t foist another one upon you and we’ll just stick with regular old JavaScript. I also went with the “simple” project (similar to “blank”, above). I chose to install Vuex and Vue Devtools when prompted. For the color scheme, I chose “blue”. Feel free to choose whichever you want as long as you don’t choose “none” (this will all make sense, I promise).

So, What’s the Difference?

Great. So we now have two NativeScript Vue projects. And you are probably wondering, “Why did we do that?” So glad you asked.

The first thing you probably noticed was that Option 2 had a few more questions for you about how you wanted to setup your project. If you open the two projects side-by-side, the next thing you will notice is that, right out of the gate, their file structure is quite different. Also notice that, despite specifying “JavaScript”, we got a /typesfolder and a tsconfig.jsonfile in Thing Two. For now, let’s just sigh heavily and pretend they aren’t there. The important thing to notice is that we made two NativeScript-Vue projects and they are very much not the same.

Let’s get to know our new-borns a bit. First, let’s run a couple of commands on each project in preparation:

npm installtns install

I suspect you’ve lost count, by now, of how many times you’ve typed npm install in your short life so I will assume you know what that’s all about. tns install sets up the /platforms directories and gets us configured to build for both Android and iOS. Why aren’t those there by default? Because it is actually possible to build web apps with NativeScript (but not today).

After running the above commands, our two projects look a bit more similar, but definitely not the same. Let’s see what happens when we try to run each of them. I happen to be using a Mac at the moment. However, in case some of you are on a PC, I’ll stick to the Android emulator for today and just point out any iOS-specific hiccups.

Thing One:

Because we weren’t given the option when we created the project using the NativeScript CLI, we have neither Vue Devtools to run or a Vuex store. So all there is to do is run what we’ve got:

tns run android --emulator

Nothing too surprising. We get a very simple, blank app.

Thing Two:

NOTE: Before moving on, you will want to kill off any running process and clear your emulator. Don’t cross the streams (have you ever seen Reanimator?)

Now, in this case, we DO have a Vuex store and Vue Devtools, so let’s start out by firing up the stand-alone, Electron version of the devtools:

npx vue-devtools

And then, as before:

tns run android --emulator

Vue Devtools come up without issue. However, executing the same run command this time we get:

OMGPWNDKTHXBYE!!!

What the heck happened? Well, somewhere in your terminal, amidst what looks like the debris from a sad and unpopular parade in New York, you’ll find the following line:

ReferenceError: HTMLElement is not defined

In the interest of time, I’ll just skip to the end of this story and spoil it for you; Vue Devtools uses a hook to know when Vuex gets initialized. When this happens, it attempts to make a clone of the store’s state. Part of that process is a routine which performs some type-checking on each object in the store. And one of the things it checks for is whether or not a given object is an instance of HTMLElement. Of course, since this is NativeScript and not a web site, we have no DOM. And no DOM means no HTML elements. And that means a big, fat lump of undefined coal for Christmas. So the app just ends up with dysentery and makes an awful mess of your console and cries itself to sleep (or something).

A quick [temporary] fix⁷ that I’ve seen several people use is to change the order in which libraries get imported in /app/main.js such that store is imported BEFORE we import nativescript-vue-devtools. Making this quick change does, indeed, get rid of the error. Unfortunately, it also means that we can’t connect to the Vuex store. In addition, on Android, nothing happens at all. The Vue Devtools just sort of hang there, waiting to connect. This makes it feel very similar to…not…having developer tools. But we’ll get back to that in a bit.

You may have also noticed an error about not being able to load ~nativescript-theme-core/scss/blue. So our theme is also borked. Not that we’ve really added anything “themeable” yet. We’ll eventually get this sorted as well.

You Are Here

I don’t know about you, but I’m not terribly in love with either of these two kids. We have two NativeScript-Vue projects. They are both [now] at least running and we can start writing an app. But Thing One needs some additional work adding dependencies and configuring things to be up to par with Thing Two. And Thing Two’s devtools are broken and our theme isn’t loading. If you take a look at the respective package.json files, in each project, you’ll notice a couple of things.

  1. They have different versions listed for the same dependencies.
  2. Thing Two isn’t using the current versions of NativeScript or Vue dependencies.

Overall, Thing Two is much more thoroughly setup for Vue development (apart from the whole not working thing). But the dependencies in Thing One, at least for NativeScript, are much more current. To be fair, maintaining and updating dependencies in any toolchain is a chore. It is often safer to not be right on the bleeding edge of things. And updating a single dependency can easily have a ripple effect through the rest of the stack. Plus, the development efforts of the community are largely pro-bono and done as people have free time. So I can’t really point the finger of [git]blame and say that either of these are “wrong”. That being said, let’s update them anyway.

AoTM

Below is a list of dependencies and their current version as of today [11.15.2019]. And, yes, I am painfully aware that this list may very well be outdated before I’ve finished writing it. In addition to manually checking the various repositories yourself, tns doctor will also give you a heads-up if technology has moved on without you, at least as far as NativeScript is concerned, and prompt you to run tns update.

Also, from this moment on, we will be making an orphan of Thing One and leaving it out on the curb (I shouldn’t have named it). But before we toss it out into the cold, take a look at the .gitignore file. This is one thing that is a lot more robust than the one we got with Thing Two, so let’s keep that (he won’t be needing it anymore).

Dependency Updates

Within the nativescript section of package.json, both Android and iOS should be at v6.2.0.

“tns-android”: {  “version”: “6.2.0”},“tns-ios”: {  “version”: “6.2.0”}

dependencies

Note that nativescript-theme-core has been replaced with @nativescript/theme (more on that shortly).

“@nativescript/theme”: “^2.2.0”,“vuex”: “3.1.2”,“nativescript-socketio”: “^3.3.1”,“nativescript-toasty”: “^2.0.1”,“nativescript-vue”: “^2.4.0”,“tns-core-modules”: “^6.2.1”

devDependencies

We’ll not only update Vue Devtools, we are going to move them from dependencies to devDependencies (because we don’t want to build them into production native apps). Similar story for nativescript-vue-devtools. I did have a brief point of confusion here: Both the created project and NPM list the most recent version of nativescript-vue-devtools as being v1.2.0. This despite the fact that the latest release on GitHub is v1.1.0. So I’m just going to go along with everyone else here.

“@babel/core”: “^7.0.0”,“@babel/preset-env”: “^7.0.0”,“@vue/devtools”: “^5.3.2”,“nativescript-vue-devtools”: “^1.2.0”,“babel-loader”: “^8.0.2”,“nativescript-dev-webpack”: “^1.3.0”,“nativescript-vue-template-compiler”: “^2.0.0”,“nativescript-worker-loader”: “~0.9.5”,“node-sass”: “^4.9.2”,“vue-loader”: “^15.7.2”

Growing Pains

As NativeScript continues to evolve, it is moving away from tns-core-modules and into a scoped name-space of @nativescript. For at least the near future, tns-core-modules is also being maintained for backward compatibility. Unfortunately, features and updates tend to be ahead of documentation. So we have some docs pointing to one convention and some pointing at another. And having outdated dependencies results in code which points at differing conventions, which does things like break your *cough* theme³. With that in mind, and an updated stack, let’s cure what ails ya.

Variations on a Theme

Within /app/app.scss, just delete…everything. Then replace it with this to match our updated dependency⁸:

@import “~@nativescript/theme/core”;@import “~@nativescript/theme/blue”;

Get Android on Speaking Terms With Devtools

To get Android working-and-playing-well with the devtools, we’ll need to perform some outpatient surgery⁶. Ready? Here we go…

Vue Devtools uses a socket connection to allow communication with the app. Unless the manifest tells Android that it’s allowed to do so, nothing gets out. Let’s reverse the gag order.

Navigate to the Android manifest file:

/app/App_Resources/Android/src/main/AndroidManifest.xml

Then, in the <application> block, add:

android:usesCleartextTraffic=”true”

Now we can at least get both iOS and Android to connect with Vue Devtools, but only while our temporary workaround is in place, leaving us without a view into the store. And I really want my chocolate syrup.

Room Without a Vue

Vue Devtools was designed with a web environment in mind. Because of this, it is entirely likely that someone might store one or more reusable HTML Elements within a Vuex store. So, when the devtools clones the store.state, it needs to iterate over what it got and figure out what the various object types are so it knows how to deal with them before stuffing them through the socket connection. Simple types are fine but anything more complex than that will need to be deep-copied, instantiated or ignored.

But NativeScript doesn’t have HTMLElement types (or even a DOM). So when devtools attempt to check an object against instanceof HTMLElement, it explodes with undefined⁷ (and there is no try/catch).

While technically not a bug, this is definitely a problem for us. So what are some of our possible options/workarounds? In order of most to least invasive:

  • Modify the Vue Devtools source within node_modules: Simply commenting out the 3 lines of offending code effectively solves the problem. However, the first time you clean, build or update the dependencies, the change will get blown away.
  • Fork vue-devtools and change those 3 lines: Gross. Just…no.
  • Overload the entire installHook() method in @vue/devtools/build/hook.js: While possible, that seems a bit…obtuse.
  • Create fake HTMLElement type: This actually seems like a potentially viable option. It doesn’t even have to be a correct/usable type. It just has to allow the conditional to [safely] fail so it can move on.

Fake It ’Til {{ someone }} Make It {{ better }}

First, let’s move the import line for nativescript-vue-devtools back to the top of main.js (which will immediately break our app).

import VueDevtools from 'nativescript-vue-devtools'import Vue from 'nativescript-vue'import store from './store'import App from './components/App'

Next, in your webpack.config.js, navigate your way to the plugins section. Specifically, the webpack.DefinePlugin() section⁹. This is where global values are defined for the app. Within that block, add the bottom line:

new webpack.DefinePlugin({  "global.TNS_WEBPACK": "true",  "TNS_ENV": JSON.stringify(mode),  "process": "global.process",  "HTMLElement": function(){return false},}),

Yep. That’s it. You’ll have to re-run the app as updates to the WebPack config won’t trigger Watchman. But this effectively creates a global type of HTMLElement, preventing the undefined condition. When Vue Devtools parses through items in your store, it will never return true when tested against instanceof HTMLElement. And that is totally fine, because you’ll never have one. Unless, of course, you decide to define your own HTMLElement type, which also only returns false and then you also decide to store instances of it in your store. But, if you do that, you kind of deserve whatever you get. But, even then, it wouldn’t crash the app with a JavaScript error. It will simply return object.cloneNode(false)…which is what we wanted in the first place.

Win, win.

Putting a Bow on It

And here we are! We now have a fully functional, robust and up-to-date foundation upon which to develop the NativeScript-Vue app of our dreams. It just goes to show that if you take the time to read the docs, scrape the web, sift through the source code, track down seemingly random and inconsistent errors and make a couple of lucky guesses…it just works. It is truly an amazing time that we live in.

Fly safe, my friends.

References

  1. NativeScript Quick Start — https://docs.nativescript.org/angular/start/quick-setup
  2. NativeScript-Vue Quick Start — https://nativescript-vue.org/en/docs/getting-started/quick-start/
  3. NativeScript Theme Docs — https://docs.nativescript.org/ui/theme
  4. Vue Devtools — https://github.com/vuejs/vue-devtools
  5. NativeScript Vue Devtools — https://nativescript-vue.org/en/docs/getting-started/vue-devtools/
  6. NativeScript Vue Devtools and Android — https://stackoverflow.com/questions/55407201/nativescript-vue-devtools-does-not-connect
  7. NativeScript Vue Devtools Crash — https://stackoverflow.com/questions/58672942/htmlelement-is-not-defined-nativescript-vue
  8. NativeScript Theme GitHub — https://github.com/NativeScript/theme
  9. WebPack and Global Variables — https://discourse.nativescript.org/t/webpack-and-global-variables/7857

--

--