The JSS Vue Sample App
JSS' Vue sample app is a practical sample that demonstrates many patterns of how to use JSS with Vue. It is designed to have just enough dependencies that Sitecore features can function (i.e. routing, translation), but also be as simple and approachable as possible. The sample is both a learning tool as well as a basis for writing production-ready applications; the sample content is designed to be easy to remove so you can replace it with production work.
The sample app was built using
@vue/cli
as a starting point. However, thesitecore-jss-vue
package has no dependencies on@vue/cli
.
Getting Started
The Vue sample supports running in all JSS application modes. For example, to start the app in disconnected mode, run jss start
to build the app and open a browser to view it.
Prefer reading code to documentation? The sample app is designed to be easily traceable and contains lots of explanatory comments about implementation details. Go play!
Component Format
The JSS Vue sample primarily uses Single-File Components (SFC) for presentational components. However, you are free to use whichever component declaration and registration method you choose, e.g. render
functions, inline template
properties, etc…
Routing + State Management
The sample app uses dynamic routing based on the Layout Service (or local route data files in disconnected mode), and uses route/navigation changes to trigger app state changes. Thus tracing the primary execution flow should begin with the route configuration.
Client-side routing
Starting from the main client-side entry point of the app in main.js
:
- In
main.js
SSR data and state is gathered and passed off tocreateApp.js
. - In
createApp.js
, the Vue app instance is created and configured along with the app router viarouter.js
, and rendering is passed off toAppRoot.vue
. If any initial state / route data is provided for the app, the state is set and managed using a customizable Vue plugin for JSS:lib/SitecoreJssStorePlugin.js
. - In
AppRoot.vue
, the<router-view />
component is declared and is responsible for rendering the components matched for the current route, as defined inrouter.js
. - In
router.js
the router is configured to respond to app routes and pass them off to theRouteHandler
component. - In
RouteHandler.js
, we bind any route (Layout Service) data provided by theSitecoreJssStorePlugin
to a Vue reactive data object. If no initial data is present, Layout Service data is acquired for the current route, and the route and language state of the app are updated. Subsequent route changes will update the global app state via theSitecoreJssStorePlugin
. - In
RouteHandler.js
, actual app markup rendering is passed off to theLayout.vue
component. Because we have bound the global app state to a reactive data object, theLayout.vue
component will re-render when new Layout Service / route data is received. Also, the global app state provides a way for components that live outside ofLayout.vue
to react to route / context data changes. - In
Layout.vue
, the shell HTML and global elements of the JSS app, along with its rootPlaceholder
(s) are rendered. - The remaining structure of the route is defined by the route data, which defines which components - and their content data - live in each placeholder.
Server-side routing
When the Vue app is pre-rendered by a Node server, thus returning HTML to the client in the initial response, the route data flow is similar to client-side routing but has a few key differences.
- [Integrated mode only] Sitecore will receive the request, parse the route server-side, and determine whether the requested item will be handled by a JSS application, and thus which bundle to execute.
- [Headless mode only] A request is received by the Node SSR proxy and passed on to a Sitecore layout service.
- The Node host will invoke the
renderView
function in theserver/server.js
file. The function arguments include the route data / Layout Service output. - The
renderView
function performs the following steps:- Receive the data to use when server-side rendering (layout service, dictionary)
- Create and initialize the app via the same
createApp.js
file used in client-side rendering. - In
createApp.js
, SSR data is set into the app's initial state using a customizable Vue plugin for JSS:lib/SitecoreJssStorePlugin.js
. - Render the app to HTML using Vue's SSR tools
- Embed the rendered app within its
index.html
template and set metadata and SSR state. The SSR state (window.__JSS_STATE__
) is used to rehydrate the app state on the client, preventing the need to call Layout Service for initial route data. - Invoke the render callback function with the final HTML
Note: while the
SitecoreJssStorePlugin
does provide some app-level state management capabilities, it is not intended to be a substitute for more robust tooling such as Vuex. If you choose to use Vuex (or similar) for app-level state management, you will likely not need to use the state management features of theSitecoreJssStorePlugin
.
App Build System
The JSS Vue app includes some build system helpers to make working with the app easier.
Scaffolding
Scaffolding a new JSS Vue component is provided via jss scaffold <componentName>
. The scaffold is defined by scripts/scaffold-component.js
, and is fully customizable to your needs. Scaffolding creates the Vue component and the disconnected component definition files, then provides helpful feedback about what to do to make your component work.
Note: Vue components are scaffolded using Single File Components. If you prefer not to use Single File Components, as mentioned above you can customize the scaffolding script to your needs.
Dynamic Config Generation
The JSS app needs to be able to read aspects of the JSS configuration, such as the current Layout Service endpoint config. To accomplish this, before a build runs, the scripts/generate-config.js
script is run which dynamically assembles the src/temp/config.js
file that the app can then import when it needs config access. This script is fully customizable - or removable - if you have different configuration requirements.
Dynamic Component Factory Module
When a build is started, the JSS Vue app will automatically generate the component factory, a mapping between JSS component names and their Vue component implementations. This file, src/temp/componentFactory.js
, is generated using conventions for defining your JSS components. This is useful to avoid needing to manually register new components. When the app is running locally, it is also smart enough to watch for new components and update the module. This auto generation is defined in scripts/generate-component-factory.js
, and is fully customizable.
Don't like conventions? Don't like code generation? We got you - this convenience feature is entirely removable in three steps:
- Remove
componentFactory.js
from.gitignore
in thesrc/temp/.gitignore
file. - Delete
/scripts/generate-component-factory.js
- Remove the reference to the deleted script from
/scripts/bootstrap.js
Disconnected Mode Support
The JSS disconnected mode enables development of JSS apps using a local mock version of the Sitecore JSS services - Layout Service and Dictionary Service. This is accomplished by running a small Express app on a different port - 3042 by default - that hosts the mock services (scripts/disconnected-mode-proxy.js
). @vue/cli
is then configured to proxy (via the vueConfig.devServer
section in vue.config.js
) requests to API paths to this mock service layer. The mock services layer is powered by a JSS manifest file that is automatically generated from your disconnected data definitions (/data
, /sitecore/definitions
). This manifest is automatically regenerated when the data is changed and live reloading is supported.
Using GraphQL + Vue
GraphQL is a popular and extremely powerful API platform that is well suited to JSS apps' data needs when they extend beyond simple route data. Sitecore GraphQL is supported to enable accessing content or other custom data schemas (for example, aggregating an existing set of backend REST services).
Sitecore GraphQL does not come with a disconnected mock service, so it can only operate with a JSS app in Connected, Integrated, or Headless application modes. If disconnected GraphQL functionality is required, graphql-tools has very powerful GraphQL mocking capabilities.
Refer to the JSS + GraphQL documentation to understand the overall capabilities first - we're only talking about Vue and Connected GraphQL specifically here. Integrated GraphQL works at the server level, so it is identical in any supported frontend framework.
The Vue sample app makes use of the vue-apollo library which in turn integrates with the Apollo GraphQL client. Usage is fairly straightforward and you can follow the vue-apollo
documentation for implementation specifics.
When server-side rendering (SSR), the sample app uses the
prefetch
functionality ofvue-apollo
to prefetch GraphQL query data and render the app to HTML on the server. This allows server-side rendering with the async results of GraphQL queries evaluated. If not using GraphQL, thevue-apollo
prefetching functionality can be removed. It is important to note that whenvue-apollo
is prefetching queries the queries do not have access to instantiated components. This means no access to component props, state, or other instance properties. If queries need that type of data to populate query variables, the data will need to be extracted from the SSR state available via therenderView
function.Complete examples of using connected and integrated GraphQL are provided with the sample app and are heavily commented, for example
src/components/GraphQL-ConnectedDemo
. Please refer to these samples for implementation details.
Sitecore Context and Route-Level Data Access
The JSS Vue app ships with a customizable Vue plugin that attaches Sitecore context and route data - in other words route-level data, as opposed to component-level data - to any Vue component instance. Common examples of needing context or route data might be to get at page title or meta keywords fields stored on the route level, or to conditionally alter rendering when in Experience Editor mode. Here's an abbreviated example of using it:
export default {
name: 'MyComponent',
computed: {
context() {
return this.$jss.sitecoreContext();
},
routeData() {
return this.$jss.routeData();
},
},
};
Usage of this.$jss
is dependent on installing the SitecoreJssStorePlugin
:
createApp.js (where your Vue instance is created, e.g. new Vue()
)
import SitecoreJssStorePlugin from './lib/SitecoreJssStorePlugin';
Vue.use(SitecoreJssStorePlugin);
The final piece of using this.$jss.sitecoreContext()
and this.$jss.routeData()
is to ensure that the store
used by the SitecoreJssStorePlugin
is updated when the Sitecore context/route data changes. This could be when the route changes in your app, or when server-side rendering passes down a state object - any time new layout data is pulled from Sitecore and rendered. To accomplish this, we use the this.$jss.store.setSitecoreData()
function to update the JSS plugin store with data received from Layout Service. The setSitecoreData
function accepts raw Layout Service data and updates the app-level context and route data accordingly. You can see an example of usage in RouteHandler.js
. An abbreviated example is below:
export default {
name: 'MyComponent',
methods: {
updateRouteData() {
const routeData = await getRouteData("/"); // makes a request for new Layout Service data, for example
// push the Layout Service data into the JSS plugin store
this.$jss.store.setSitecoreData(routeData);
}
}
}
UI Components
UI components are the most important part of the JSS app. Thankfully, they are no different from most other Vue components - except that they are stateless functional components and are dynamically added inside a Placeholder
component, which provides them with an ambient fields
prop.
<template>
<div>
<sc-text :field="fields.title" />
</div>
</template>
<script>
import { Text } from '@sitecore-jss/sitecore-jss-vue';
export default {
name: 'MyComponent',
props: {
fields: {
type: Object,
},
},
components: {
ScText: Text,
},
};
</script>
More information for the various JSS helper components can be found on the Vue Reference page
Handling Sitecore Field Types
You probably noticed the Text
component being used above. It is a special component that comes with JSS and is a helper for rendering field values properly for editing inside Experience Editor. There are a number of helpers for different field types, such as images, dates, and rich text fields. Consult the Styleguide page in the sample app for working live examples of all these field types.