what is the right way or the vuejs way to data bind the entire page? - vue.js

Coming from the knockoutJs background. If you don't specific the binding to an element. You can use the model to cover the whole page of elements. For example, i can make a div visible if a click event happened. I'm learning VueJs and from the documentation. I see the vue instance required you to speicif an element with el.
like this:
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
}
})
what if my button is not in the same div as the '#app' div. How do i communicate between two vue instance or can I use one vue instance to cover more than one element. what's the vuejs way?

It's very common to bind to the first element inside <body>. Vue won't let you bind to body, because there are all sorts of other things that put their event listeners on it.
If you do that, Vue is managing your whole page, and away you go. The docs cover the case where you have more than one Vue instance on a page, but I haven't come across this outside the docs, and I can't think of a good reason off the top of my head. More commonly, you will be constantly chopping bits out of your root Vue instance and refactoring them into "child" components. This is how you keep file sizes manageable and structure your app.
This is where a lot of folk needlessly complicate things, by over-using props to pass stuff to components. When you start refactoring into components, you will have a much easier time if you keep all your state in a store, outside vue, then have your components talk directly to your store. (put the store in the data element of all components). This pattern (MVVM) is fabulous, because many elements of state will end up having more than one representation on screen, and having a "single source of truth", normalized, with minimal relationships between items in the store, radically reduces the complexity and the amount of code for most common purposes. It lets you structure your app state independently of your DOM.
So, to answer your question, Vue instances (and vue components), don't need to (and shouldn't) talk much to each other. When they do need to (third party components and repeated components), you have props and events, refs and method calls (state outside the store), and the $parent and $root properties (usage frowned on!). You can also create an event bus. This page is a really good summary of the options.
Should your store be Flux/Redux? Vuex is the official implementation of the flux/redux pattern for vue. The common joke goes: when you realize you need it, it's too late. If you do decide to leave Vuex for now, don't just put state in Vue components. Use a plain javascript object in window scope. The right way is easier than the wrong way, and when you do transition to Vuex, your job will be much simpler. Your downstream references might be alright as they are.
Good luck. Enjoy the ride.

You usually put the main Vue instance on the first tag inside the body, then build the rest of your site within it. Everything directly inside that instance (not in a nested component) will have access to the same data.
You can then do this in your HTML:
<body>
<div id="#app">
<p v-if="showMessage">{{message}}</p>
<button v-on:click="showMessage = !showMessage"></button>
</div>
</body>
And set your data to something like this:
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!',
showMessage: true
}
})
If you want to pass data between components later on you'll have to look up how to emit events, use props, or possibly use Vuex if you got Vue running with the Vue-CLI (which I highly recommend).
If you want to reach tags (such as head tags) outside of the main Vue instance, then there are tools for that. For example you could try: https://github.com/ktquez/vue-head
I haven't tested it thought.

Related

Pass ref to default slot in renderless component

I am trying to build a renderless component in vue 3 and want to pass a ref to the default slot.
When I am using the h render function I can just pass a ref:
return h('div', {ref: someRef}); // works
If I try to do the same with the default slot, it does not work:
return slots.default({ ref: someRef}) // does not work
return slots.default({ someRef}) // also does not work
Is there any way to do this without wrapping the default slot into another div or similar?
Checked already the documentation and other resources, but couldn't find any solution.
Direct answer
Yes return a function from your setup hook!
setup(_, slots) {
const someRef = ref()
return () => slots.default({ ref: someRef })
}
vue3 docs link
vue3 docs for renderless component pattern
Contextual answer for those in the comment section questioning the renderless/headless pattern
Yes, sometimes you just want a renderless (or headless as the kids these days say) wrapper for functionality (logic), without limiting the HTML that consumers can use.
But for that functionality to work, the renderless/headless component still needs to identify some of the HTML that consumers render into the default slot, in order to provide DOM-related functionality for example.
I have the same struggle when using this pattern, and have been relying on a "hack": passing specific data attributes via slot scope, that consumers need to bind to the desired elements (not only root) and then using good old document.querySelector to grab them
I has served me well in vue2 and I've been using it in production with no problems, but I was wondering if with the new dynamic :ref attribute of vue3, we can do it differently: probably passing a reactive ref variable and then doing a simple assign, and apparently it works.
<renderless #default="{ someRef }">
<some-consumer-comp :ref="(el) => someRef.value = el" />
</renderless>
Here's a sandbox demo old way for vue 2
Here's a sandbox demo new way for vue 3
Do note that if you want to handle edge cases, like handling conditional rendering of slot content, you probably need to do a bit more work and add some watchers.
This pattern is a bit out of fashion these days, with the introduction of vue composables since you can abstract the logic into a useSomeFunctionality and use it directly on the component you want, but it's sill a valid one IMO.
Cheers!

Is overriding a global Vue component safe?

Let's say there is a global component BIcon.vue available everywhere.
And another component, but regular not global, called BIconFake.vue.
We can override BIcon.vue by BIconFake.vue like that:
<template>
<div>
<b-icon icon="plus"><!-- <- Here is it BIconFake component! -->
</div>
</template>
<script>
import BIcon from '~/components/BIconFake'
export default {
components: {
BIcon // <- BIconFake component inside!
}
}
</script>
By this way, Vue.js will display BIconFake component instead of regular BIcon component.
I tried to pass props, events or attributes and it works like expected.
Vue.js is awesome... and big. Really, I don't know everything about it, and I don't want to see side effects or unexpected behavior when doing this override.
So, I want to know if it's safe to do that? Does it make a mess in Vue.js instance? What about memory?
we can override component with pure vue.js. Also, I made this example for Buefy, but we can do that with any UI frameworks like Quasar, Vuetify...
Thinking globaly, Is it good to override components of UI frameworks? What about security, scalability and maintenability?
In fact, I searched a way to build a plugins or addons system to my Nuxt.js app, like wordpress plugins.
Is it a good architecture to start building my app by overriding vue component? Is there another way to build app addons for vue, by using npm or webpack?
If you are going to wrap existing components like that then you should keep in mind the Liskov substitution principle. <b-icon-fake> can likely be used in place of <b-icon> provided that it:
accepts the same props
emits the same events
exposes the same public methods (if it is used with a ref)
behaves in the same way
Most of those points probably do not apply for a simple <b-icon> component.
Also keep in mind the template of your wrapped component now includes an extra <div> around it. This can interfere with styling and things like that.
You can eliminate the additional memory overhead by using a functional component instead, but you will need to write the render function manually to preserve the behavior of the wrapped component. But honestly I wouldn't worry too much about memory usage unless you have determined it to be an issue (after profiling your app).
In terms of whether it is "good" to do this or not, I can't say. There are advantages and disadvantages. In my opinion, wrapping components is fine as long as you are the only consumer of the wrapper component and doing so doesn't affect any existing usage of the wrapped component outside of your code.

Target and manipulate single DOM element in vue

Somehow I still can't wrap my head around some core vue concepts.
I have made some simple webpage using phalcon. Created it so, that it would work without JS and now is the time to add some bells and whistles - ajax queries and the like, for the user experience to be better.
I wanted to do everything using vue, to see how it all adds up. But after hours of googling I still can't find solution for the simplest of tasks.
Say: I want to get a text paragraph in a series of <li>-s and change it somewhat. Maybe make excerpt of it and add 'see more' button behind it. Now, in jQuery I would just iterate with each() and perform the tasks. With vue targeting set of DOM elements is much harder for me, probably because of whole paradigm being "the other way round".
I know I could iterate with v-for, but these elements are already in the DOM, taken from the database and templated with volt. I had even this wild idea of creating .js files from phalcon, but it would completely negate my strategy of making functional webpage first and then enhance it progressively.
Frankly speaking I feel like I'm overcomplicating for the sake of it, right now. Is vue even fit for a project like this, or is it exclusively a tool to build app from the ground up?
Vue's templating is client-side, which means if you are delivering an already templated html page (by your backend) there is little vue can still do for you. Vue needs data, not DOM elements to build its viewmodels.
This becomes pretty obvious when building a single page application for example, which would be rendered only on the client-side. You'd simply load the data asynchronously from a backend api (REST for example) and then do all the rendering on the client.
As far as I understand your usecase you want to mix client and server side rendering, rendering most of the non-interactable content using your backend's templating engine and adding some interactivity using vue. In this case you'll need to add some vue components (with their own rendering logic) to your backend template and pass data to that component using vue's data-binding.
Here's an example:
...
<div id="app">
<my-vue-list :products="{% products %}"></my-vue-list>
</div>
...
And in your JS:
var app = new Vue({
el: '#app',
data: {
components: {MyVueList} // You will have to register all the components you want to use here
}
})
Vue provides the ref attribute for registering a reference to a dom element or child component:
// accessible via this.$refs.foo
<li ref="foo">...</li>
Do note, however, that refs are not reactive, as stated in the docs:
$refs is also non-reactive, therefore you should not attempt to use it in templates for data-binding.

Understanding the lifecycle of Vue Instance

I'm going through the official documentation of Vue.js and found this diagram about different stages and steps in a Vue instance's lifecycle. I understand the basic features of Vue but I am unable to understand the meaning of many stages mentioned in the diagram. For example, "Compile template into render function" or "Virtual DOM re-render and patch". I have no idea what they mean.
I know it says in the documentation that you don’t need to fully understand everything right now, but I was hoping if some Vue(or frontend) expert could briefly explain these steps in simple language.
It can all be a little overwhelming, here's what those things are
Render Functions
When Vue compiles your Vue instance, it creates a render function, which is a pure JavaScript representation of your HTML. Something like this:
new Vue({
template: `<div>{{msg}}</div>`,
data:{
msg: 'Hello Vue
}
}).$mount('#app')
Will actually turn into something like this:
new Vue({
render: function(createElement) {
return createElement('div', this.msg)
},
data: {
msg: 'Hello Vue'
}
}).$mount('#app')
Here's a JSFiddle: https://jsfiddle.net/bvvbmpLo/
You don't need to handle that, Vue does it for you, and most of the time you won't find yourself writing render functions. However, it is important to understand that Vue is doing some behind the scenes work to represent your templates in pure JavaScript.
Virtual DOM re-render and patch
You really don't need to know about this, but Vue uses a virtual DOM, because it's is easier to track changes and decide which parts of the DOM need updating.
In reality, what happens is that Vue builds up a tree that represents the DOM (called a vTree), then when you change state it uses something called a diffing algorithm which compares the previous vTree to the current vTree as it now stands, and attempts to figure out which part of the page it needs to change to reflect that state in your view. The changing of a small part of your page to represent the new state is called patching.
That's a pretty high-level overview of a virtual DOM, it's fiendishly complex to get this working efficiently which is why frameworks like Vue exist in the first place. If you're interested in learning more about that then try taking a look at Matt-Esch/virtual-dom on Github, which does a great job of explaining this concept in more detail.

Differences between vue instance and vue component?

I'm new to vue js and have some questions when learning it.
I'm now a little confused about the relationship between its instance and component. As far as I learned, every app build by vue should only have one instance and one instance only, it can have as many components as you like if needed. But recently I've seen a demo, and in that demo it has more than one instance.
So my question is, is that ok to have multiple intances in one app( the demo code works fine, but is that the correct way)? What's the best practice to use instance and component in vue app?
It's ok to have two instances in the same project, however, you probably don't want to do that.
A good scenario is to have one main instance to control your app, specially if you are creating a Single Page Application (SPA). Then use as many components as you want.
Components are a great way to reuse code and keep it organized, and, with Vue.js, is very easy to communicate between your components and your "main" Vue instance.
It depends very much on your situation.
I work on a large web project which is not an SPA. We have a Vue instance for each "silo" of the application. We are slowly transitioning an older project from a mostly jQuery frontend so it's possible that as it evolves We'll be able to condense down to a single Vue instance but we're having no trouble with multiple instances. The ease of using Vue in existing projects was one of the biggest deciding factors in choosing it over another library such as react.
I have taken a different approach with green development, one instance and many components.
There are something in common, and some difference between Vue instance and Vue component.
From Vue docs:
all Vue components are also Vue instances, and so accept the same options object (except for a few root-specific options).
The root Vue instances accept properties like el, router, the Vue components cannot.
The data property in root Vue instances is an object, but in Vue components must be a function.
The design target is different:
A root Vue instance is a Vue application launcher, Vue component is an extension of the Vue instance.
Vue components can create elements to be reused in many places. This is Vue characteristic of componentization mainly reflect point.
Vue instance can associated with and manipulate an element which is already exist.
Vue component more suitable for create new element and reuse it at anywhere.
Think of a Vue Component as a blueprint or set of rules on how to create something that can be inserted into the DOM that the user can interact with.
So when you create a Vue file you are going to define exactly one component with a set of rules that tells Vue how to display stuff on the screen and tells a user how to interact with it.
On the other hand is a Vue instance, its an instance of a Vue component, it represents something that has been inserted into the DOM and is something that a user can interact with.
If you have a background in Object-Oriented Programming, think of a Vue Component as being like a class and a Vue instance as an instance of that class.
What has not been mentioned in previous answers that I will cover in regards to the difference between Vue instance and Vue component is how we define the data property that we are working with.
If we are working with a Vue instance, then we can define the data property as an object or a function that returns an object like so:
With a Vue Component that data property must be a function that returns an object.
So for example this is a Vue component:
export default {
name: "App",
components: {
SearchBar,
VideoList
},
And if we want to make use of data inside of that Vue component, we have to make a function that returns an object.
Vue components extends Vue instances
but Vue instances accept properties like el, router, the Vue components cannot.
best practice:
one Vue instance
many Vue component