What is your seo

Seo is Search Engine Optimization. (More than that, of course). But I won’t go into that here.

SEO principle

Actually the job that search engine does is quite complex, we say roughly process briefly here. Follow-up for SEO optimization, will also be based on these points to expand the description.

  1. Page fetching: the spider requests a page from the server and retrieves the page content
  2. Analysis and storage: analyze the obtained content and collect the high-quality pages
  3. Search sorting: when the user searches for keywords, the page is sorted according to certain rules, and the results are returned to the user

Why do SEO optimization

Of course, in order to “spider” can web to your content, so that your content can be known by more people. Of course, not all website systems need to do this, you like a company OA system, there is no need to do this, you use yourself, why to give others know. However, if you are selling oa system, you can simply do a pre-render can, and then configure the route inside only configure the home route.

Systems that need AD traffic, like news sites and online shopping sites, need this.

Vue pre-rendered

Prerendering is a simple plugin to use

Plug-ins that you need to use

prerender-spa-plugin

puppeteer

Webpack configuration

        new PrerenderSPAPlugin({
            staticDir: path.join(__dirname, '.. /dist'),
            // The page route that needs to be rendered
            routes: ['/'.'/aaa'].renderer: new Renderer({
                // The time to trigger the render, which is used to fetch data before saving the render result
                renderAfterTime: 10000.// Whether to open the browser. Can be used to debug to check rendering results
                headless: false.// Use 'document.dispatchEvent(new Event('render- Event '))' in the main.js entry of the project
                renderAfterDocumentEvent: 'render-event'.// render-event: declare the method name})})})Copy the code

The main js configuration

new Vue({
    el: '#app',
    router,
    components: { App },
    template: '<App/>'.render: h= > h(App),
    mounted() {
        document.dispatchEvent(new Event('render-event'))}})Copy the code

Pre-rendering is the easiest solution, but it only works if you have fewer pages (and fewer routes), because as you have more pages, you will find that pre-rendering becomes slow.

Vue-ssr server Rendering (official)

The constraint

  1. Note that the server only calls the beforeCreat and CREATED hooks, so do not initialize a created timer and destroyed the timer in Mounted or destroyed mode. Otherwise, the server will be exhausted by the timer. If I run in the Node environment, it will execute the two methods in the page.
  2. Because of the single-threaded mechanism, when rendering on the server side, there is a singleton-like operation in the process, so all requests share the singleton-like operation, so you should use factory functions to ensure independence between each request.
  3. If you use third-party apis in the beforeCreat and Created hooks, it is normal and reasonable to make sure that the API runs on the Node side without errors, such as initializing a data request in the Created hook. However, using XHR alone will cause problems in node rendering, so use axios, a third-party library supported by both the browser and server.
  4. Most important: Do not use browser-only apis like Document in common code, and vice versa, do not use browser-only apis like Document in common code. (It seems that the refs of vue can be obtained. According to the website, it can be set by webpack variable. The method I use will be mentioned below.)

Preparation (here we use the most basic Vue + Webpack project as an example)

Initialize the project

vue init webpack vue-ssr-demo
cd vue-ssr-demo
npm install
npm run dev
Copy the code

We need to see if the project can be started before we can proceed, right? If something goes wrong here, we can search for vue+webpack to initialize the project

package.json

This is just posted for everyone to see

start

Install dependencies and start projects

npm i -D vue-server-renderer
Copy the code

Vue-server-renderer and vUE versions need to match consistently

Add a test page

<template>
  <div>
    Just a test page.
    <div>
      <router-link to="/">Home</router-link>
    </div>
    <div><h2>{{mode}}</h2></div>
    <div><span>{{count}}</span></div>
    <div><button @click="count++">+ 1</button></div>
  </div>
</template>
<script>
  export default {
    data () {
      return {
        mode: process.env.VUE_ENV === 'server' ? 'server' : 'client'.count: 2}}}</script>
Copy the code

SRC two new JS files

Create two JS files in the SRC directory

SRC ├─ entry-client.js # only run in browser ├─ entry-server.js # Only run on serverCopy the code

entry.client.js

import { createApp } from './main'
const { app, router } = createApp()
// Wait for the Router to load all asynchronous components because there may be asynchronous components. This operation is also required for the server configuration
router.onReady((a)= > {
  app.$mount('#app')})Copy the code

entry.server.js

// entry-server.js
import { createApp } from './main'
export default context => {
  // Since it might be an asynchronous routing hook function or component, we'll return a Promise,
  // So that the server can wait for all the content before rendering,
  // We are ready.
  return new Promise((resolve, reject) = > {
    const { app, router } = createApp()
    // Set the router location on the server
    router.push(context.url)
    // Wait until the router has resolved possible asynchronous components and hook functions
    router.onReady((a)= > {
      const matchedComponents = router.getMatchedComponents()
      Reject if the route cannot be matched, reject, and return 404
      if(! matchedComponents.length) {// eslint-disable-next-line
        return reject({ code: 404})}// Promise should resolve the application instance so that it can be rendered
      resolve(app)
    }, reject)
  })
}
Copy the code

Modifying Route Configurations

To avoid singletons, only one new Router instance is exported for each request:

import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
 
Vue.use(Router)
 
export function createRouter () {
  return new Router({
    mode: 'history'.// Note that this is also history mode
    routes: [
      {
        path: '/'.name: 'Hello'.component: HelloWorld
      }, {
        path: '/test'.name: 'Test'.component: (a)= > import('@/components/Test') // Asynchronous components})}}]Copy the code

Modify the main js

import Vue from 'vue'
import App from './App'
import { createRouter } from './router'
 
export function createApp () {
  // Create a Router instance
  const router = new createRouter()
  const app = new Vue({
    // Inject router into the root Vue instance
    router,
    render: h= > h(App)
  })
  // Return app and router
  return { app, router }
}
Copy the code

Webpack configuration modified

Vue-cli initialization also has three configuration files: base, dev, prod, we still keep these three configuration files, just need to add webpack.server.conf.js.

├─ ├─ webpack.class.conf # ├─ Webpack.class.conf # ├─ Webpack.class.conf # ├─ Webpack.class.conf # ├─ Webpack.class.class.conf #Copy the code

Webpack.prod.conf.js configuration (client configuration)

Change the entry configuration of webpack.base.conf.js to./ SRC /entry-client.js. This will not affect the original dev and PROd configurations.

The server configuration also references the Base configuration, but overwrites the entry into server-entry.js by merging. Generate the client manifest

Advantages:

  1. Instead of htML-webpack-plugin to inject the correct resource URL when the generated filename has a hash in it.
  2. When rendering bundles through WebPack’s on-demand code-splitting feature, we can ensure optimized resource preloading/data prefetching of chunks, Moreover, the required asynchronous chunks can be intelligently injected into script tags to avoid waterfall request from clients and improve TTI – time-to-interactive time.

It is easy to add a plugin to the prod configuration and add it to the plugin:

const VueSSRClientPlugin = require('vue-server-renderer/client-plugin')
    // ...
    // ...
    plugins: [
      new webpack.DefinePlugin({
        'process.env': env,
        'process.env.VUE_ENV': '"client"' / / add process. The env. VUE_ENV
      }),
      / /...
      // We also need to remove the HtmlWebpackPlugin for prod, because after we have vue-ssr-client-manifest.json, the server side will do the work for us.
      // new HtmlWebpackPlugin({
      // filename: config.build.index,
      // template: 'index.html',
      // inject: true,
      // minify: {
      // removeComments: true,
      // collapseWhitespace: true,
      // removeAttributeQuotes: true
      // // more options:
      // // https://github.com/kangax/html-minifier#options-quick-reference
      / /},
      // // necessary to consistently work with multiple chunks via CommonsChunkPlugin
      // chunksSortMode: 'dependency'
      // }),
 
      // This plug-in is in the output directory
      // Generate 'vue-ssr-client-manifest.json'.
      new VueSSRClientPlugin()
    ]
// ...
Copy the code

Webpack.server.conf.js (server side configuration)

The server configuration is useful to run with a new plug-in: NPM i-d webpack-node-externals

const webpack = require('webpack')
const merge = require('webpack-merge')
const nodeExternals = require('webpack-node-externals')
const baseConfig = require('./webpack.base.conf.js')
const VueSSRServerPlugin = require('vue-server-renderer/server-plugin')
// Remove the configuration of packing CSS
baseConfig.module.rules[1].options = ' '
/ / baseConfig. Module. Rules [1]. The options = '/ / remove the separation of CSS packaging plug-in (with the installation and use of esline)
/ / baseConfig. Module. Rules [0]. Options = '/ / remove the separation of CSS packaging plug-in (in the case of no installation esline)
 
module.exports = merge(baseConfig, {
  // Point entry to the application's Server Entry file
  entry: './src/entry-server.js'.// This allows Webpack to handle dynamic imports in a Node-appropriate fashion,
  // Also when compiling Vue components,
  // Tell the VUe-loader to transport server-oriented code.
  target: 'node'.// Provide source map support for the bundle renderer
  devtool: 'source-map'.// Server bundle is told to use Node-style exports.
  output: {
    libraryTarget: 'commonjs2'
  },
  // https://webpack.js.org/configuration/externals/#function
  // https://github.com/liady/webpack-node-externals
  // Externalize application dependency modules. Can make server builds faster,
  // Generate smaller bundles.
  externals: nodeExternals({
    // Do not externalize dependent modules that WebPack needs to handle.
    // You can add more file types here. For example, the *.vue raw file is not processed,
    // You should also whitelist dependent modules that modify 'global' (e.g. polyfill)
    whitelist: /\.css$/
  }),
  plugins: [
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development'),
      'process.env.VUE_ENV': '"server"'
    }),
    // This is the entire output of the server
    // Build a plug-in as a single JSON file.
    // The default file name is' vue-ssR-server-bundle. json '
    new VueSSRServerPlugin()
  ]
})
Copy the code

Modifying package commands

If cross-env is not found, install NPM i-d cross-env

"scripts": {
    / /...
    "build:client": "node build/build.js"."build:server": "cross-env NODE_ENV=production webpack --config build/webpack.server.conf.js --progress --hide-modules"."build": "rimraf dist && npm run build:client && npm run build:server"
}
Copy the code

Modified index. HTML

Delete the original <div id=”app”> and leave only one tag in the body :<! – vue SSR – outlet – >. The server will automatically generate a <div ID =”app” data-server-Rendered =”true”>, and the client will mount it via app.$mount(‘#app’), rendered responsive. The dev mode in the project also applies to this template. The dev mode in the project also applies to this template.

  1. The simplest way is to create a separate HTML template for the dev pattern… (As mentioned below)
  2. The dev mode is also integrated with server-side rendering mode, so it makes sense to have both production and development environments in server-side rendering mode. (The official example is how it works)

      
<html>
  <head>
    <meta charset="utf-8">
    <title>vue-ssr-demo</title>
  </head>
  <body>
    <! --vue-ssr-outlet-->
  </body>
</html>
Copy the code

Run the command

npm run build
Copy the code

Then you can see two generated JSON files in the dist directory: vue-ssR-server-bundle. json and vue-SSR-client-manifest.json.

Both files are applied to the Node side for server-side rendering and injection of static resource files.

Server.js in the root directory (js running on the server)

(The official example uses Express, so this demo will use KOA2 as the server side, of course, it doesn’t matter whether KOA or Express…)

npm i -S koa
Copy the code

Create server.js in the project root directory as follows


const Koa = require('koa')
const app = new Koa()
 
// response
app.use(ctx= > {
  ctx.body = 'Hello Koa'
})
 
app.listen(3001)
Copy the code

Modify the server.js code

The KOA static resource middleware needs to be installed: NPM I-d KOA-static

const Koa = require('koa')
const app = new Koa()
const fs = require('fs')
const path = require('path')
const { createBundleRenderer } = require('vue-server-renderer')
 
const resolve = file= > path.resolve(__dirname, file)
 
// Generate a server-side rendering function
const renderer = createBundleRenderer(require('./dist/vue-ssr-server-bundle.json'), {
  / / recommend
  runInNewContext: false.// Template HTML file
  template: fs.readFileSync(resolve('./index.html'), 'utf-8'),
  // client manifest
  clientManifest: require('./dist/vue-ssr-client-manifest.json')})function renderToString (context) {
  return new Promise((resolve, reject) = > {
    renderer.renderToString(context, (err, html) => {
      err ? reject(err) : resolve(html)
    })
  })
}
app.use(require('koa-static')(resolve('./dist')))
// response
app.use(async (ctx, next) => {
  try {
    const context = {
      title: 'Server-side Rendering Test'.// {{title}}
      url: ctx.url
    }
    // Return the HTML rendered on the server side to the client
    ctx.body = await renderToString(context)
 
    // Set the request header
    ctx.set('Content-Type'.'text/html')
    ctx.set('Server'.'Koa2 server side render')}catch (e) {
    // If not, pass the request and continue with the middleware
    next()
  }
})
 
app.listen(8080)
    .on('listening', () = >console.log('Service started'))
    .on('error', err => console.log(err))
Copy the code

Local boot

Run the start service command:

node server.js
Copy the code

The browser visits: localhost:8080, and the screenshot shows the page successfully rendered by the server

Deploying to the server

NPM run build(NPM install is required if node_modules is not installed). Then NPM run build is followed by Nodeserver.js

How to debug code locally

Start with a new htmldev.html


      
<html>
<head>
    <meta charset="utf-8">
</head>
<body>
    <div id="app"></div>
</body>
</html>
Copy the code

Modify the commands in package.json

"dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js".Copy the code
  1. What dependencies should be installed if there are none
  2. Change versions if you encounter incompatible WebPack versions

How do I use the Document /window object

I found some answers on the Internet, but there are no examples. It may be that THE way I open is not right to find, so I can only practice according to the ideas of those answers. (Of course, the best way is not to use these two objects (manual dog head).)

But if you must, you can use webPack DefinePlugin to define a variable to determine the current runtime environment (this variable was already in the configuration file), let’s take a look

webpack.server.conf.js

    new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development'),
      'process.env.VUE_ENV': '"server"'
    })
Copy the code

webpack.prod.conf.js

        new webpack.DefinePlugin({
            'process.env': env,
            'process.env.VUE_ENV': '"client"' / / add process. The env. VUE_ENV
        })
Copy the code

We can use process.env.vue_env to determine the current operating environment, for example

    mounted() {
        if (process.env.VUE_ENV == "server") {
            console.log("Server");
        } else {
            console.log("Client");
            console.log(document.getElementById("ooooo")); }}Copy the code

Introduction of CSS style files

In main.js, require(‘ XXX ‘) is used to import, not import directly. If there are other methods, please leave a comment

Vue-ssr part of use is reprinted inhere(But above is my own knock once, and put out and solve the problems encountered, he there I do not have, I have here he does not have, haha)

Nuxt. Js framework

This is similar to the framework of VUE, you can use the basic vUE scaffolding to see the official document (here is to use, not skilled use).

It’s recommended to read a nuggets god’s personal blog

If there is anything wrong, please leave it in the comments section, I will make changes in time, thank you all…

Personal speech

Recently, the company needs this, so I went to investigate this, and I left my dear component library behind. I will continue to work hard to update it (mainly because I am lazy, because I have three days’ holiday during qingming Festival, which is sleeping, playing the computer and watching cartoons). All see here, can you by the way move the mouse, give a thumbs-up.