Server pre-rendered Nuxt - (Crawl Point)

Keywords: Javascript Vue Attribute Nginx Linux

Nuxt is a more common solution to solve SEO. As Nuxt also has many pits, when breaking through a small technology point, there is a great sense of accomplishment, which really makes me painful and happy during this time.Here we make a summary and summary of the pits that we have traveled according to our individual learning situation.

Nuxt Development Cross Domain

Projects can use Nginx to reverse proxy external requests (note here that the Linux firewall is released on the appropriate port) to internal Nuxt on the default 3000 port. The simplest configuration file is as follows:

nuxtjs.config.js

{
    modules: [
      '@nuxtjs/axios',
      '@nuxtjs/proxy'
    ],
    proxy: [
        [
            '/api',
            { 
              target: 'http://localhost:3001', // api host
              pathRewrite: { '^/api' : '/' }
            }
        ]
    ]
}

@nuxtjs/proxy needs to be installed manually separately.

Nuxt Store uses

Using Vuex in Nuxt is not the same as using Vuex in Vue. First, Nuxt has integrated Vuex, no need for us to install it again, just refer to it directly. There is a folder of Store under the default Nuxt framework template, where we store Vuex.

Official Nuxt also provided Related Documents Can be simple, but in the official documents I see scratch.

Create two.Js files under the store file based on the official document, index.js and todo.js.And create index.vue under the pages folder.

store - index.js

export const state = () => ({
  counter: 0
})
export const mutations = {
  increment (state) {
    state.counter++
  }
}

store - todo.js

export const state = () => ({
  list: []
})
export const mutations = {
  add (state, text) {
    state.list.push({
      text: text,
      done: false
    })
  },
  remove (state, { todo }) {
    state.list.splice(state.list.indexOf(todo), 1)
  },
  toggle (state, todo) {
    todo.done = !todo.done
  }
}

pages - index.vue

<template>
  <section class="container">
    <div>
      <h2 @click="$store.commit('increment')">{{counter}}</h2>
      <ul>
        <li v-for="(item,index) of list"
            :key="index">{{item.text}}</li>
      </ul>
    </div>
  </section>
</template>

<script>
import Logo from '~/components/Logo.vue'
import {mapState} from "vuex";
export default {
  components: {
    Logo
  },
  computed:{
    ...mapState(["counter"]),
    ...mapState("todos",{
      list:state => state.list
    })
  },
  created(){
    for(let i =0;i<10;i++){
      this.$store.commit("todos/add",i);
    }
    console.log(this.list)
  }
}
</script>

this.$store can be used directly in Nuxt and is namespace enabled by default.Take another look at the code in computed. When using mapState, the counter attribute is obtained directly, whereas the todos attribute is obtained through the namespace.What's wrong with this?

Nuxt mounts all state s, mutations, actions, getters in the index.js file in the store as their public attributes, on the store instance, whereas other files use namespaces, whose corresponding namespace name is its file name.

When running a project, you can find store.js in the.nuxt folder to see how it's done.Simply explain what the code does and what it does.

.nuxt - store.js

//  Introducing vue
import Vue from 'vue'
//  Introducing vuex
import Vuex from 'vuex'
//  As Middleware
Vue.use(Vuex)
//  Save console function
const log = console
//  Properties of vuex
const VUEX_PROPERTIES = ['state', 'getters', 'actions', 'mutations']
//  store property container
let store = {}
//  Self-executing function with no return value
void (function updateModules() {
  // Initialize the root data, that is, the index file mentioned above as common data
  store = normalizeRoot(require('@/store/index.js'), 'store/index.js')
  // If store is a function, prompt for an exception, stop execution
  if (typeof store === 'function') {
    //  Warning: Stores in Classic mode are not approved and will be deleted in Nuxt 3.
    return log.warn('Classic mode for store is deprecated and will be removed in Nuxt 3.')
  }
  // Execute Storage Module
  // store - Modular
  store.modules = store.modules || {}
  // Solve Storage Module Methods
  //    Introduce the todos.js file, the data
  //    'todos.js'file name
  resolveStoreModules(require('@/store/todos.js'), 'todos.js')

  // If the environment supports hot overloading
  if (process.client && module.hot) {
    // Whenever a Vuex module is updated
    module.hot.accept([
      '@/store/index.js',
      '@/store/todos.js',
    ], () => {
      // Updated root.The latest definition of the module.
      updateModules()
      // Trigger hot updates in store s.
      window.$nuxt.$store.hotUpdate(store)
    })
  }
})()

// Create store instance
//   -Use store if store is function
//   -Otherwise, create a new instance
export const createStore = store instanceof Function ? store : () => {
  //  Return instance
  return new Vuex.Store(Object.assign({
    strict: (process.env.NODE_ENV !== 'production')
  }, store))
}
//  Solve Storage Module Methods
//  moduleData - Export data
//  filename - File name
function resolveStoreModules(moduleData, filename) {
  // Get export data to resolve es6 (export default) export
  moduleData = moduleData.default || moduleData
  // Remote store src +extension (. /foo/index.js -> foo/index)
  const namespace = filename.replace(/\.(js|mjs|ts)$/, '')
  // Space Name
  const namespaces = namespace.split('/')
  // Module name (state, getters, etc.)
  let moduleName = namespaces[namespaces.length - 1]
  // File Path
  const filePath = `store/${filename}`
  // If moduleName =='state'
  //  -Execute normalizeState - Normal state
  //  - Execute normalizeModule - Standardize Module
  moduleData = moduleName === 'state'
    ? normalizeState(moduleData, filePath)
    : normalizeModule(moduleData, filePath)
  // If executed (state, getters, etc.)
  if (VUEX_PROPERTIES.includes(moduleName)) {
    // module name
    const property = moduleName
    // Storage Module//Get Storage Module
    const storeModule = getStoreModule(store, namespaces, { isProperty: true })
    // Merge Attributes
    mergeProperty(storeModule, moduleData, property)
    // Cancel subsequent code execution
    return
  }
  // Special processing index.js
  // Module name equals index
  const isIndexModule = (moduleName === 'index')
  // If equal to
  if (isIndexModule) {
    // Namespace pop-up last
    namespaces.pop()
    // Get module name
    moduleName = namespaces[namespaces.length - 1]
  }
  // Get Storage Module
  const storeModule = getStoreModule(store, namespaces)
  // Traversing VUEX_PROPERTIES
  for (const property of VUEX_PROPERTIES) {
    // Merge Attributes
    //  storeModule - Storage Module
    //  moduleData[property] - Stores a property data in a module
    //  property - module name
    mergeProperty(storeModule, moduleData[property], property)
  }
  // If moduleData.namespaced === false
  if (moduleData.namespaced === false) {
    // Delete Namespace
    delete storeModule.namespaced
  }
}
//  Initialize root data
//  moduleData - Export data
//  filePath - File Path
function normalizeRoot(moduleData, filePath) {
  // Get export data to resolve es6 (export default) export
  moduleData = moduleData.default || moduleData
  // Throw an exception if commit method exists in the imported data
  // -You should export a method that returns a Vuex instance.
  if (moduleData.commit) {
    throw new Error(`[nuxt] ${filePath} should export a method that returns a Vuex instance.`)
  }
  // Use empty queue for merge processing if moduleData is not a function
  if (typeof moduleData !== 'function') {
    // Avoid typing errors: Set properties that only getter s have when overriding top-level keys
    moduleData = Object.assign({}, moduleData)
  }
  //  Return after processing modularization
  return normalizeModule(moduleData, filePath)
}
//  Normal state
//   -Modular data
//   -File Path
function normalizeState(moduleData, filePath) {
  // If moduleData is not a function
  if (typeof moduleData !== 'function') {
    //  Warning prompt
    //  ${filePath} should export a method to return an object
    log.warn(`${filePath} should export a method that returns an object`)
    //  Merge state
    const state = Object.assign({}, moduleData)
    //  Export state as a function
    return () => state
  }
  //  Processing modularity
  return normalizeModule(moduleData, filePath)
}
//  Processing modularity
//  moduleData - Export data
//  filePath - File Path
function normalizeModule(moduleData, filePath) {
  // If the state of the module data exists and is not a function warning prompt
  if (moduleData.state && typeof moduleData.state !== 'function') {
    //  "state" should be a method to return objects in ${filePath}
    log.warn(`'state' should be a method that returns an object in ${filePath}`)
    // Merge state
    const state = Object.assign({}, moduleData.state)
    // Override the original state using function returns
    moduleData = Object.assign({}, moduleData, { state: () => state })
  }
  // Return Initialization Data
  return moduleData
}
//  Get the store's Model
//      - StorModule store data model
//      - namespaces namespace name array
//      -Whether to use the namespace default value of false
function getStoreModule(storeModule, namespaces, { isProperty = false } = {}) {
  //  If namespaces do not exist, start the namespace with a namespace name length of 1
  if (!namespaces.length || (isProperty && namespaces.length === 1)) {
    //  Return model
    return storeModule
  }
  //  Get Namespace Name
  const namespace = namespaces.shift()
  //  Save data in namespace
  storeModule.modules[namespace] = storeModule.modules[namespace] || {}
  //  Enable Namespace
  storeModule.modules[namespace].namespaced = true
  //  Add Named Data
  storeModule.modules[namespace].modules = storeModule.modules[namespace].modules || {}
  //  recursion
  return getStoreModule(storeModule.modules[namespace], namespaces, { isProperty })
}
// Merge Attributes
//  storeModule - Storage Module
//  moduleData - Storage module attribute data
//  property - module name
function mergeProperty(storeModule, moduleData, property) {
  // If no pusher exists for moduleData
  if (!moduleData) return
  // If the module name is state 
  if (property === 'state') {
    //  Split state data into module space
    storeModule.state = moduleData || storeModule.state
  } else {
    // Other modules
    // Merge into corresponding module space
    storeModule[property] = Object.assign({}, storeModule[property], moduleData)
  }
}

This is the compiled store file, which roughly means traversing the store file, using different solutions for different files, and mounting model s using namespaces.

Page loading

Nuxt has a Loading component available for loading. Here's the configuration.

nuxtjs.config.js

module.exports = {
   loading: { color: '#3B8070' }
}

The loading provided by Nuxt does not meet the needs of the project. Possible projects do not need to load animations like this. so~, you need to configure one manually by yourself.Add a loading component (the official example below, see the official documentation for details) to reference it.

nuxtjs.config.js

module.exports = {
 loading: '~components/loading.vue'
}

A small episode in Nuxt, ~and @ both point to the root directory.

components/loading.vue

<template lang="html">
  <div class="loading-page" v-if="loading">
    <p>Loading...</p>
  </div>
</template>

<script>
export default {
  data: () => ({
    loading: false
  }),
  methods: {
    start () {
      this.loading = true
    },
    finish () {
      this.loading = false
    }
  }
}
</script>

Third Party Component Library

Component libraries are inevitably used in project development, which is the same as when used in Vue, and you need to add some dependencies to use them properly.

plugins - element-ui.js

import Vue from 'vue';
import Element from 'element-ui';
import locale from 'element-ui/lib/locale/lang/en';
export default () => {
  Vue.use(Element, { locale })
};

nuxtjs.config.js

module.exports = {
   css: [
       'element-ui/lib/theme-chalk/index.css'
   ],
   plugins: [
       '@/plugins/element-ui',
       '@/plugins/router'
   ]
};

Using Middleware

The middleware Nuxt does not give a specific usage document, but rather puts it into an editor.I feel a slight difference in this.Why do you want to do this?After a simple study, you can get a general idea.

Create the desired Middleware in middleware.Here's an example of an official website.

middleware - visits.js

export default function ({ store, route, redirect }) {
  store.commit('ADD_VISIT', route.path)
}

This creates a middleware, but how should it be used?There are two ways to use it, one globally, the other individually on a page, where the file name is used as the name of the middleware.

++ Global Use++.

nuxtjs.config.js

export default {
  router: {
    middleware: ['visits']
  }
}

Use alone on a page

export default {
  middleware: 'auth'
}

There is a code like this in the asyncData on your page on your official website.

export default {
    asyncData({ store, route, userAgent }) {
        return {
            userAgent
        }
    }
}

Continuous updates.

summary

Nuxt has a very small learning curve, just like the Vue framework, and is already an out-of-the-box state where we can develop directly across configurations.Interested in configuration can find SSR rendering documents in the official Vue document.

Posted by onlinegs on Sun, 05 May 2019 16:40:38 -0700