Structure a Vue.js App from Containers and Components
Vue.js using Vuex for state management does not have a clear distinction between containers and components. This is in clear contrast to React using Redux for state management.
We argue that such a distinction between containers components in beneficial for Vue.js as well.
Dyploma includes Java Spring backend and a Python command-line tool (CLI). The command-line tool operates through API calls to the backend.
The Dyploma Web Application
To facilitate broader adoption of containers within Outbrain, we set up to develop a web application that will have the capabilities of the Dyploma CLI.
The web application will operate by fetching data from the backend and sending operations for execution at the backend. This will be done through the same REST API used by the CLI.
A Vue.js Web Application
The application has three generic screens:
All concepts have screens from each of these types with similar structure and look and feel, but with different actions and different data.
Containers vs Components in React
Let us first recap what are containers and components in React.
A container interacts with the Redux and contains a component. The container supplies data to the component through selectors on the store and provides the actions on the store to the component.
Components are given data and render HTML. They use the actions provided from their container to interact with the state. Such actions modify the state, resulting in the selectors fetching new data, and causing the component to be rendered again.
Vue.js with Vuex
Vue.js standard practice does not have the containers vs components distinction. While constructing the Dyploma web application we found it useful to make such a distinction for the benefits of better code structure and reusability.
Let us first describe how the structure of the Dyploma web application.
We constructed three generic components:
Which can be composed of a component tree that can have more than 3 levels.
Each of these generic screens was used with some variations by multiple types of data. But the look and feel could be configured through a common JSON describing for each type of data, the different fields.
Type Specific Actions and Getters
The getters and actions to be used for each type of data were different. We constructed our Vuex store with modules and needed to use a separate module for each type.
Distinguish Components and Containers
So we had to think how to resolve two opposite requirements. For the benefits of reusability, we need unified generic components. But for the type specific actions and data, we need to use separate modules. We decided up front that the whole app will be constructed as a set of single file components (SFC).
To resolve these two opposite directions, we found it useful to think of our app as consisting of two things:
containers - type-specific that interact with store
components - generic
We defined each component to a
data props for the data it should render, and a description of the structure of data. For any changes and actions required, it will
emit an event.
Data is passed from a component to its constituents with
Events are hooked up with
Components are composed of smaller components. Events are propagated up the tree of components. This bottom-up propagation may be disturbing to some, but it is the best approach for Vue.js. In this respect, Vue.js is definitely different from React.
The component is a single file component (SFC).
Such a component is not necessarily functional.
A Container for Each Type of Data
A container knows which module of the store it deals with, and knows its actions and getters. It fetches data from the store using getters. Such data is passed to the components as props.
It listens to events coming from the components using
v-on:search="search. In response to such events, it dispatches actions.
The container does not render anything itself, this is done by the component it contains.
The container is a single file component (SFC)s.
A Clean Separation Facilitates Reusability
This clean separation of components and containers make it simpler to see opportunities for reusability. Come to think of it, in most web apps, the real effort in reusability is reusability of the component. The mixing of components and containers causes many components to be coupled with the store. This makes it harder to identify reusability. By distinguishing components and containers, we isolate the components from the store and see more clearly opportunities for reusability.
Writing unit tests becomes easier with this separation. One can write three classes of tests:
Each becoming simpler.
We will discuss this further in a separate article.
Split your Vue.js web app into containers and components.