Big front-end advanced - isomorphic application

Overview

In the front-end field, with the popularity of Vue, React, Angular and other frameworks, front-end engineering and modularization have become the mainstream technical solutions. The SPA single-page application built by this type of framework has the advantages of good user experience, good rendering performance, and high maintainability, but there are also the following two problems:

  1. Long loading time above the fold

SPA applications use client-side rendering, and users need to wait for the client-side js parsing to complete before they can see the page, which will lead to longer loading times for the first screen and poor user experience.

  1. Bad for SEO

Because the SPA application uses client-side rendering, the HTML of the website has no content until the js parsing is completed, which results in that the search engine cannot obtain the content when crawling the HTML of the website.

In order to solve these two problems, the industry has proposed a new solution, as shown below:

Use server-side rendering to solve the defects of slow first screen loading and unfavorable SEO. After the first screen rendering is completed, client-side rendering takes over the page and becomes a single-page application again to ensure a good user experience. This approach is called modern server-side rendering or isomorphic rendering.

Differences from traditional server-side rendering

Traditional server-side rendering, such as JSP, can be summarized in the following steps:

  1. Client sends request
  2. The server looks up the template and obtains the data according to the request.
  3. perform rendering
  4. Generate html and return to client display

This traditional server-side rendering method has the following disadvantages:

  1. The front and back ends are completely coupled, which is not conducive to development and maintenance.
  2. The front end has little room to play.
  3. Server pressure is high.
  4. User experience is average.

The isomorphic rendering is similar to the traditional server when rendering the first screen, and returns the rendered html page. However, in isomorphic rendering, when the client displays the rendered html page, the client rendering will take over the page. The control right, that is, the subsequent rendering is carried out by the client, which can ensure a good user experience.

Disadvantages of isomorphic rendering

Compared with single-page applications, because the first screen rendering uses server-side rendering, it has the following disadvantages:

  1. The development conditions are limited, and the development is limited (the server can only use nodejs, and not all toolkits can be used in server-side rendering).
  2. The requirements for construction and deployment are high (the client and server need to be deployed, and the static site can no longer be deployed like a single-page application).
  3. More server load.

Nuxt.js

Nuxt.js is an isomorphic application solution based on the vue technology stack, which shields the difficulty of building isomorphic applications for vuejs.

basic use

It is relatively simple to build a nuxtjs isomorphic application. The simplest isomorphic application can be built through the following four steps:

  1. Create a project folder.
  2. npm or yarn install nuxt.
  3. Add pages folder and add index.vue file in it.
  4. Execute npx nuxt.

Basic routing

All routing page vue files are stored in the pages folder, and nuxt will automatically generate routes according to the pages folder.

For example, the pages directory is as follows:

pages/
--| user/
-----| index.vue
-----| one.vue
--| index.vue

Then nuxt will automatically generate the following routing structure:

router: {
  routes: [
    {
      name: 'index',
      path: '/',
      component: 'pages/index.vue'
    },
    {
      name: 'user',
      path: '/user',
      component: 'pages/user/index.vue'
    },
    {
      name: 'user-one',
      path: '/user/one',
      component: 'pages/user/one.vue'
    }
  ]
}

dynamic routing

When nuxt parses the routing structure, if it encounters a vue file prefixed with _, it will define it as a dynamic route with parameters.

The file structure is as follows:

pages/
--| _slug/
-----| comments.vue
-----| index.vue
--| users/
-----| _id.vue
--| index.vue

The resulting routing table is:

router: {
  routes: [
    {
      name: 'index',
      path: '/',
      component: 'pages/index.vue'
    },
    {
      name: 'users-id',
      path: '/users/:id?',
      component: 'pages/users/_id.vue'
    },
    {
      name: 'slug',
      path: '/:slug',
      component: 'pages/_slug/index.vue'
    },
    {
      name: 'slug-comments',
      path: '/:slug/comments',
      component: 'pages/_slug/comments.vue'
    }
  ]
}

Nested routes

In vuejs, we can implement multi-level nesting of routing through sub-routes, and in nuxtjs, we can implement nested routing through a special file structure.

To create a nested route, you need to add a Vue file and a folder with the same name as the file to store the child routing components (you need to add a nuxt-child node similar to route-view in the Vue file.)

The file structure is as follows:

pages/
--| users/
-----| _id.vue
-----| index.vue
--| users.vue

The generated routing table is as follows:

router: {
  routes: [
    {
      path: '/users',
      component: 'pages/users.vue',
      children: [
        {
          path: '',
          component: 'pages/users/index.vue',
          name: 'users'
        },
        {
          path: ':id',
          component: 'pages/users/_id.vue',
          name: 'users-id'
        }
      ]
    }
  ]
}

custom routing

nuxtjs can not only automatically generate a routing table through the pages folder structure, but also provide a configuration file to configure custom routes.

The configuration file of nuxtjs is the same as vue.config.js. The nuxt.config.js file is created in the project root directory. This file exports a configuration object by default. The configuration object has a router attribute, and custom routes can be configured under this attribute.

export default {
    router: {
        extendRoutes(routes, resolve) {
            // You can use this method to clear the default generated routes
            // routes.splice(0)
            routes.push(...[
                {
                    path: '/',
                    component: resolve(__dirname, 'your.vue'),
                }
            ])
        }
    }
}

template

nuxtjs generates html files based on html templates, we can modify this template in the project.

To customize the template, you need to create an app.html file in the project root directory. The default html structure is as follows:

<!DOCTYPE html>
<html {{ HTML_ATTRS }}>
  <head {{ HEAD_ATTRS }}>
    {{ HEAD }}
  </head>
  <body {{ BODY_ATTRS }}>
    {{ APP }}
  </body>
</html>

We can add corresponding custom css and js references in the head.

asyncData

In the vuejs project, if a component wants to obtain data from the server, it generally initiates a request in the created declaration cycle function. In a homogeneous application, if you want the page content to be SEO-friendly, you can't do it this way.

The asyncData method is added to the vue component in nuxt. This method will be executed on the server side, and the result after execution will be merged into the data object of the current component.

The following example gets all articles and tags on the server and renders them:

async asyncData() {
    const [articleRes, tagRes] = await Promise.all([
      getAllArticles({
        offest: 0,
        limit: 10,
      }),
      getAllTags(),
    ])

    const { articles, articlesCount } = articleRes.data
    const { tags } = tagRes.data
    return {
      articles,
      articlesCount,
      tags,
    }
  }

It should be noted that although this method is a method of the vue component, since it is called on the server side, the vue instance cannot be obtained through the this object inside the method. If you want to obtain some information on the instance, you can use the context object .

context object

The asyncData method contains a parameter context, and the context object contains common information such as routing.

route: current route object
store: store object
params, query: route parameter
req, res: request, response object
redirect: for redirection

Authentication

In a homogeneous application, when a user logs in, their identity information needs to be available on both the client and server, so the information can be stored in a cookie.

The client gets the information and stores it in the cookie:

methods: {
    onSubmit() {
        let request = this.isLogin ? login : register
        request(this.user)
            .then(({ data }) => {
                // Get user information
                const { user } = data
                if (user) {
                    const Cookie = process.client ? require('js-cookie') : undefined
                    // Save the obtained user information in a cookie
                    Cookie.set('user', user)
                    this.$store.commit('setUser', user)
                }
            })
            .catch((err) => {

            })
    }
}

The server reads the cookie and stores it in the store for the client to use:

In the action of the store, you can add an asynchronous method named nuxtServerInit, which will be called when the nuxt application starts.

nuxtServerInit({ commit }, { req }) {
    let user = null
    if (req && req.headers.cookie) {
        const cookieParser = process.server ? require('cookieparser') : undefined;
        const parsed = cookieParser.parse(req.headers.cookie)
        try {
            user = JSON.parse(parsed.user)
        } catch (err) {
        }
    }
    commit('setUser', user)
}

plugin

If you want to perform some js operations before the vuejs program runs, such as registering a third-party component library, you can use a plugin at this time.

All plugins are placed in the plugins directory. If you want to use the elementui component library in the nuxt application, you can add the element-ui.js file in the plugins folder:

import Vue from 'vue'
import Element from 'element-ui'
import locale from 'element-ui/lib/locale/lang/en'

Vue.use(Element, { locale })

Then add the corresponding file path in the plugins property array of nuxt.config.js:

plugins: [
    '@/plugins/element-ui',
]

After that, you can use element-ui normally in vue components.

Tags: Javascript Front-end Vue.js

Posted by PhantomCode on Thu, 19 May 2022 03:51:44 +0300