Vue.JS + Vanilla = opinions?



  • So in another thread, a user mentioned Vue.JS. I had heard of it before, but if you haven’t observed by now in my posts, I am pretty anti-JS frameworks. I find vanilla to be able to do everything you need, standards based, without worry regarding conflict with your chosen UI framework, such as Onsen. The only real downside, imo, to not using a JS framework is the rare feature, such as data binding, that takes a few extra steps to create in vanilla.

    Knowing all of that, when the other user mentioned Vue.JS, I took a look at it just to see if I was able to help out. What I discovered was a very minimalistic framework that really is just for data binding. You can do other things with it, but most important to me was how light it was. You don’t have to learn any real new way of doing your business vs vanilla. So, I thought I would post and see what a few of you think about Vue. I “might” and I stress might, start using Vue in a few projects! :stuck_out_tongue:


  • Onsen UI

    @munsterlander I’m also quite interested in Vue.js + Onsen UI. @argelius made some simple apps with it few months ago and said it was very nice. I’ll definitely try it when I have the chance :D



  • @Fran-Diox It definitely seems to “fit” with how I code and most importantly - to me at least - is front the onset, it is very intuitive. I just get what it wants and is easy to code. With their fiddle, I was immediately able to do some simple two-way data binding which I think will be super powerful for an app I have coming up.

    Right now, I had to put all mobile dev on hold as we are entrenched in a huge desktop scheduling project. As soon as that is done, I think I will make a new front-end for it with a tablet interface using Onsen and Vue. I may be sick of scheduling though and move onto something more fun like @Leonardo-Augusto awesome trail app.



  • You guys should check out Riot.js, it’s like a minimalistic React which uses markup + mostly vanilla JS in order to define components (instead of JSX, which strangely involves pumping out HTML through Javascript functions). It’s even more minimalistic than Vue with very little “brain load” needed to understand how it works. Riot.js 3.0.0 alpha performs really well on mobile too.

    I’ve actually been trying to get it to work well with onsen-ui for the last few days. It’s proven quite challenging due to the concept of “mounting” which exists in frameworks like Riot, React and Mithril, where you inject custom components into the pages as they load.

    The problem is that ons-navigator loads pages from ons-templates, which cannot include scripts, so I can’t just ‘mount’ my custom components at startup. What I’m doing right now is using the document ‘init’ event to detect a page change, and ‘mounting’ my Riot components in the init event, but it messes up the page transitions on mobile (though not on desktop chrome…). I found that including ons-toolbar in the component caused most of the problem, so I moved it out of the component. Now the last problem is there’s a ‘flicker’ on mobile during animations if I do things in the ‘init’ tag such as mounting my Riot components. Still working on it.

    If I can get riot.js to play nice with ons-navigator, I’ll be really happy. :)

    ps- @munsterlander I agree with the general idea of being against frameworks. I personally hate angular with a passion. The only thing you really need is data-binding and ‘repeater’ functionality, but that tends to run poorly unless you have a virtual dom engine. With that in mind, I settled on Riot in the end as I don’t ever want to go through a 200 page framework manual again.



  • @theprotocol We wrote a Navigator component for React that is using <ons-navigator> under the hood:

    https://github.com/OnsenUI/react-onsenui/blob/master/src/components/Navigator.jsx

    This code might help in making it work for Riot.js :)



  • @argelius Awesome, I’ll give that a look and hopefully figure something out. :)



  • @theprotocol I very much agree about relearning a language just because they wanted to make it unique and thus you have a 200 page manual. The one advantage Vue has over Riot (based on what you wrote as I have zero experience with Riot) is that Vue provides binding and repeaters but most importantly, it has no conflicts with Onsen - that I have discovered so far.

    I took a brief look at Riot and it looks pretty nice. I look forward to your report on getting it to work with Onsen! I am glad there are other anti-framework programmers out there like me! :smile: Personally, I am tired of everyone trying to reinvent the wheel and that is why I really like Onsen. Although it is a brand new wheel, so to speak, this wheel fits your car like it is supposed too and it works from the start like you would expect it too. Great stuff all around!

    Edit: Doing a bit more research, I found this information from Vue - now do take it with a grain of salt as it is their own comparison which one would assume is biased, but none the less, the last point was the most intirguing:

    Riot 2.0 provides a similar component-based development model (which is called a “tag” in Riot), with a minimal and beautifully designed API. I think Riot and Vue share a lot in design philosophies. However, despite being a bit heavier than Riot, Vue does offer some significant advantages over Riot:

    • True conditional rendering (Riot renders all if branches and simply show/hide them)
    • A far-more powerful router (Riot’s routing API is just way too minimal)
    • More mature tooling support (see webpack + vue-loader)
    • Transition effect system (Riot has none)
    • Better performance. (Riot in fact uses dirty checking rather than a virtual-dom, and thus suffers from the same performance issues as Angular.)


  • @munsterlander Choosing a framework is a really tough decision! There are definitely pros and cons to each one, and it’s a huge hassle to look into them. I actually researched Vue.js just before I very narrowly made the decision to settle on Riot. I’m sure many of us are all too familiar with the Javascript Framework selection dance. :)

    FYI true conditional rendering has been added to Riot as of 3.0.0 which is still in alpha. The performance of 3.0.0 also benchmarks pretty highly. The other points are valid though, the router is basic and there’s no transition system.

    The reason for the incompatibility with Onsen is that ons-navigator loads from an ons-template. I was trying to approach my app design as 1 page = 1 component (with sub-components inside it) for the most part, meaning some markup and a script – and ons-template essentially won’t load scripts. So I wonder if Vue.js components would have the same limitation with the component approach. I would imagine that it’s the same issue with Vue.js, you’d have to mount the Vue.js component representing the page contents in the ‘init’ event, and not before, as the ons-template must be loaded by the navigator first.

    It basically looks like this with Riot.js:

    <ons-template>
    <ons-page>
    <custom-riot-tag></custom-riot-tag>
    </ons-page>
    </ons-template>
    

    …and I quickly discovered I can’t immediately call riot.mount(‘custom-riot-tag’) to attach it to the DOM, because the custom tag contains a script which won’t be loaded when the ons-template gets loaded by the navigator. I’m currently calling it in the document ‘init’ event as recommended by Onsen experts (actually I learned about this from one of your responses on Stackoverflow), with a giant select statement that determines which page is being loaded, and that’s where I attach the component to the DOM. But it’s very flaky in terms of animation when I do this, for some reason.

    Do you think Vue.js would be different in this regard if one were to use the Vue.js ‘component’ approach?



  • @theprotocol Ah, I see what you are encountering now. Yes, you would have to use init or my preferred is the show event. I wrote a function that on every page show checks to see if the the DOM element exists and if so run the appropriate functions that are in an associated array in the global scope. Kind of like an Angular controller except much much simpler.

    So I create my functions, register them to the element in an array, on the show event check and see if the element exists and then execute the necessary functions. It has worked quite well for me and have only found 1 or 2 cases where I had to do something a bit different.



  • @munsterlander Yep! The problem with mounting/unmounting elements to the DOM during the ‘init’ event is that very strange things happen to the transition animation, but only on mobile (iOS) with a Cordova webview, and not on desktop Chrome / Ripple. It’s very strange and has something to do with Toolbars, as moving toolbars out of the custom component resolves a huge part of the animation problem (but it’s still not good enough and looks wrong).

    I’m also experiencing some double-mount issues which are only happening on iOS and not on desktop. A Riot component auto-mounts any sub-components. I placed some sub-components in ons-template, and on desktop they don’t get automatically mounted anymore (desired behavior, due to ons-template), but they do on iOS - so I’m wondering now if this is a Riot bug, or a quirk of ons-template. :sweat:

    Could you tell me why you prefer show over init? Thanks. :)

    PS- I guess any component-based framework that combines markup with script will have this issue, as ons-template will just remove the script half of the component.



  • @theprotocol Well, in my experience due to the lifecycle of events with show being after init, that split second delay in firing solved a lot of occasional null pointer issues. It also allows for my vanilla bindings to fire.

    As show fires every time it is displayed vs init which only fires once - although I had some weird issues where they would fire twice - I am able to do much more with dynamic forms and dynamic adding of elements and such. Luckily, Javascript by default ignores a create event if the event already exists.

    I guess in short as I don’t think I am explaining myself very well, I just found the performance of show to work better than init. Does that make sense and also your mileage may vary?

    EDIT:

    I use this a lot as I can then only fire events when those elements are shown thus giving me more granular control over the logic and processing of the data:

    document.addEventListener("show", function(event){
        if(event.target.id=='pageID') {
    etc....
    


  • @munsterlander Alright, thanks for the explanation. I’ll definitely give ‘show’ a try for mounting DOM elements. This is pretty interesting, as whatever solution ends up working will probably be the same for any component-based framework.



  • I’ve found the solution at last. I was just about to rip my hair out when it occurred to me what the solution could be:

    1. Attach (a.k.a. mount) Riot/Vue/whatever component to the incoming page during the ‘init’ event (it’s prudent to keep track in an array of what tags are mounted so you don’t double-mount them in the case of multiple init fires for the same page, which can occur in some circumstances)

    2. Instead of navigator.pushPage, use:

    navigator.insertPage(0, 'page.html');
    navigator.bringPageTop('page.html');
    

    …to sort of fake double buffering. I’ve got no more animation issues on iOS as of now, and hopefully it’ll stay that way.

    Edit: the ‘init’ event fires too many times already when using nested tabviews, and with the above change, it fires even more frequently, making it hard to attach the components. Should be fixable though.



  • @theprotocol Awesome! I agree on the init firing way too many times. That was the reason I went to the show event. I would assume init would only fire once but it seems to always be firing for some reason.


  • Onsen UI

    @munsterlander @theprotocol init event should fire only once per page. Otherwise it’s a bug :sweat_smile:
    Any short way to reproduce that behavior?



  • @Fran-Diox I will try to come up with a test. It just seemed like with the tabbar it would fire all the inits at once for all the tabs on startup and then all of them again when you would switch tabs. It may have been a coding issue on my side though.

    I will need to get more specific and see what I can come up with.


  • Onsen UI

    @munsterlander Oh! Well, now all the tabs are persistent so it’s normal that they throw init events at the beginning, but they should not throw them again after initialization.



  • Sorry for the delay in responding, I got swamped with some work. :sweat:

    In my case, the init event was firing many times because I’m using navigator.insertPage and navigator.bringPageTop to simulate double-buffering. I tried chaining them like insertPage(...).then(function(){bringPageTop()}) to avoid concurrent inits but it didn’t work and the same pages would init multiple times.

    I agree with @munsterlander though that sometimes init would fire multiple times even when using the regular pushPage too, but I can’t quite determine what circumstances cause that and can’t re-produce it. There’s definitely something related to <ons-template> containing a <ons-tabbar>, and/or nested tabbars and navigators.

    In the specific case of DOM injection on init, the source seems to be a mix of <ons-template> + <ons-tabbar> inside riot.js components. The <ons-template> seems to prevent the tabbar pages from being initialized on desktop (chrome + ripple emulator), but on mobile they did initialize (although I don’t know how to recreate this)! It’s extremely hard to diagnose because the above are inside riot.js tags, which are basically components that include html+javascript which I’m injecting into the DOM at runtime. So what happens when you inject a component with <ons-template> containing <ons-tabbar> where 1 of the tab pages also contains an <ons-tabbar> at runtime, and then load this <ons-template> from a navigator or tabbar? A huge mess. :)

    Switching back to navigator.pushPage seems to resolve the issue somewhat (with some fiddling, I managed to get init to only be called once per page now) but I’m back to the animation problems (iOS only, haven’t tested Android).

    The exact nature of the animation problems is twofold:

    1. If I inject a riot.js component into the incoming page DOM on page init, the animation shows the last frame for a split second (flicker) before occurring normally.

    2. Doing the same with an <ons-toolbar> inside the component that we inject to DOM on init completely cancels the animation (resulting in a heavy flicker and instantly going to the last frame).

    In summary, pretty much any DOM manipulation of the incoming page in page init ruins animations severely on mobile.

    I tried a gazillion workarounds, e.g. setting the page visibility to hidden for 20ms in the init event to prevent the last-frame appearing at animation start -> entire animation canceled with a flicker like in point #2 above.

    When I get some time I might try creating a plunkr which shows the animation issues, assuming I can recreate them.



  • Here is a plunkr which shows the worst case scenario of the animation problem (#2 in my last post - the transition animation is canceled when injecting an ons-toolbar with ons-back-button into the incoming page), which occurs on iOS Mobile Safari but not on desktop Chrome. In my Cordova app, it’s even worse than in this example (heavier flickering when the animation fails to occur), possibly because I have more elements on the pages.

    I couldn’t recreate animation problem #1 (last animation frame shows for a split-second upon animation start) on mobile Safari; it might only happen in Cordova, or I might be missing something about what’s causing it.

    I wonder if a different navigation approach is needed than loading a ons-template and doing DOM injection into it on ‘init’ or ‘show’. I feel like this should also be an issue with the React version of Onsen-UI… Or is the DOM injection into the ons-template done differently there? One thing is certain: DOM injection is how every framework is doing things nowadays.

    Ideally, I’d love for navigator.pushPage to expose some kind of ‘page setup’ function, specifically for injecting into the DOM of the incoming page, rather than doing everything in ‘init’, something like navigator.pushPage(page, options, callback, pageSetup).



  • I agree with @theprotocol. It definitely involved ons-template and ons-tabbar. Quick question though and maybe an observation, when you have a multi-tab tabbar, are all the tab inits supposed to fire on the first initial page load? If that is the expected behavior then ok, but it seems odd to do it when the items haven’t been attached to the DOM (or maybe they are supposed to be and there is a different issue at hand.)

    This is why I went to show. In that event, I would create additional event listeners for data binding. I would consistently get null pointer errors because the item was not attached to the DOM; however, the init event had fired. I in turn went to the show event which resolved my issue. I have yet to test everything with the new implementation of show, but I doubt it will affect my code much.



  • @munsterlander I believe ons-tabbar is persistent by default in 2.0, so all of the ons-tab do get initialized before they’re shown.


Log in to reply