目前的的前端项目为基于vuecli3搭建的spa项目,由于需求提出需要对首页,部分内容页面做seo优化,涉及到前端项目的框架和部分页面的改造。
SEO(Search Engine Optimization):汉译为搜索引擎优化。利用搜索引擎的规则提高网站在有关搜索引擎内的自然排名。目的是让其在行业内占据领先地位,获得品牌收益。
搜索引擎判断一个网站权重高低的尺度无非两个:收录和链接。
目前百度spider抓取新链接的途径有两个:
一是主动出击发现抓取
二就是从百度站长平台的链接提交工具中获取数据,其中通过主动推送功能“收”上来的数据最受百度spider的欢迎。
<head>
<title>PKS信创产业生态平台-为PKS生态建设赋能title>
<meta name="description" content="PKS信创产业生态平台是国内第一个PKS线上适配平台、第一个基于飞腾2000+ cpu的公有云服务、第一个以PKS为核心的知识内容平台,能为PKS生态合作伙伴、开发者和终端用户提供多样性PKS生态支撑性服务,提升生态服务能力。">
<meta name="keyword" content="PKS信创产业生态平台,信创,飞腾云主机,麒麟操作系统,自主云服务器,PKS适配迁移,飞腾处理器">
head>
<header id="header" class="web">
<h1 id="logo">
<a href="/" target="_blank" title="鹏翔书画装裱培训">书画字画装裱培训_裱字裱画学习 - 山东曹州(菏泽)鹏翔书画装裱培训中心a>
h1>
header>
<nav id="nav" class="web_">
<ul class="web">
<li class="nav_home"><a href="/" class="nav_on">网站首页a>li>
<li><a href="/single_About_1.html">中心简介a>li>
<li><a href="/article_ZhuangBiaoZhiShi_2.html">装裱知识a>li>
<li><a href="/article_ZhuangBiaoZaTan_3.html">装裱杂谈a>li>
<li><a href="/article_ZhuangBiaoZhaoSheng_4.html">招生信息a>li>
<li><a href="/ZhuangBiao_ZhuangBiaoZuoPin_5.html">装裱作品a>
li><li><a href="/video_ZhuangBiao_6.html">装裱视频a>li>
<li><a href="/ZhuangBiao_ZhuangBiaoJiaoXue_7.html">现场教学a>li>
<li><a href="/article_zhuanyejieshao_10.html">专业介绍a>li>
<li><a href="/single_contact_8.html">联系我们a>li>
ul>
nav>
<section id="banner" class="cf">
<ul>
<li>
<img src="/image/banner1.jpg" alt="三分画,七分裱——书画字画装裱的艺术魅力">
li>
<li>
<img src="/image/banner2.jpg" alt="继承传统技法,发扬中华文化——鹏翔书画装裱培训">
li>
ul>
section>
链接分为三种,一种是网站内部的链接,简称内链。比如从首页进入栏目页面,从栏目页面进入内容页面。第二种是网站外部链接,简称外站链接。第三种是别人的网站给你的网站的链接,简称外链。下面我逐一阐述。
目前前端所面临的的问题是内容无法被爬虫获取,页面的title,keyword,descriptions等无法动态变化
经过调研后,目前有以下几种解决方案提供:
构建阶段生成匹配预渲染路径的 html 文件(注意:每个需要预渲染的路由都有一个对应的 html)。构建出来的 html 文件已经有静态数据,需要ajax数据的部分未构建。
如果你调研服务器端渲染 (SSR) 只是用来改善少数营销页面(例如首页,关于我们,联系我们 等)的 SEO,那么你可能需要预渲染。
npm install prerender-spa-plugin puppeteer
config.plugins.push(
new PrerenderSPAPlugin({
// 生成文件的路径,也可以与webpakc打包的一致。
// 下面这句话非常重要!!!
// 这个目录只能有一级,如果目录层次大于一级,在生成的时候不会有任何错误提示,在预渲染的时候只会卡着不动。
staticDir: path.join(__dirname, 'dist'),
// Optional - The location of index.html
// indexPath: path.join(__dirname, '../dist', 'index.html'),
// 对应自己的路由文件,比如a有参数,就需要写成 /a/param1。
routes: ['/', '/school'],
// 预渲染代理接口
// server: {
// proxy: {
// '/api': {
// target: 'http://localhost:9018',
// secure: false
// }
// }
// },
// 这个很重要,如果没有配置这段,也不会进行预编译
renderer: new Renderer({
// headless: false,
renderAfterDocumentEvent: 'render-event', // 在 main.js 中 document.dispatchEvent(new Event('render-event')),两者的事件名称要对应上。
args: ['--no-sandbox', '--disable-setuid-sandbox']
})
})
)
new Vue({
router,
store,
// 添加mounted,不然不会执行预编译
mounted() {
document.dispatchEvent(new Event('render-event'))
},
render: h => h(App)
}).$mount('#app')
总结:
在 2.3 发布后我们发布了一份完整的构建 Vue 服务端渲染应用的指南。这份指南非常深入,适合已经熟悉 Vue、webpack 和 Node.js 开发的开发者阅读。请移步 ssr.vuejs.org
与传统 SPA (单页应用程序 (Single-Page Application)) 相比,服务器端渲染 (SSR) 的优势主要在于:
请注意,截至目前,Google 和 Bing 可以很好对同步 JavaScript
应用程序进行索引。在这里,同步是关键。如果你的应用程序初始展示 loading 菊花图,然后通过 Ajax
获取内容,抓取工具并不会等待异步完成后再行抓取页面内容。也就是说,如果 SEO
对你的站点至关重要,而你的页面又是异步获取内容,则你可能需要服务器端渲染(SSR)解决此问题。
针对spa项目的修改(vue-cli3):
npm install vue-server-renderer lodash.merge webpack-node-externals cross-env --registry=https://registry.npm.taobao.org --save-dev
npm install koa koa-static --save --registry=https://registry.npm.taobao.org
// server.js
// 第 1 步:创建一个 Vue 实例
const Vue = require("vue");
const Koa = require("koa");
const app = new Koa();
// 第 2 步:创建一个 renderer
const renderer = require("vue-server-renderer").createRenderer();
// 第 3 步:添加一个中间件来处理所有请求
app.use(async (ctx, next) => {
const vm = new Vue({
data: {
title: "ssr example",
url: ctx.url
},
template: `访问的 URL 是: {{ url }}`
});
// 将 Vue 实例渲染为 HTML
renderer.renderToString(vm, (err, html) => {
if(err){
ctx.res.status(500).end('Internal Server Error')
return
}
ctx.body = html
});
});
const port = 3000;
app.listen(port, function() {
console.log(`server started at localhost:${port}`);
});
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>Documenttitle>
head>
<body>
body>
html>
修改main.js
// main.js
import Vue from 'vue'
import App from './App.vue'
import { createRouter } from "./router";
// 导出一个工厂函数,用于创建新的
// 应用程序、router 和 store 实例
export function createApp () {
const router = createRouter();
const app = new Vue({
router,
// 根实例简单的渲染应用程序组件。
render: h => h(App)
})
return { app,router }
}
创建entry-client.js文件
import { createApp } from './main'
// 客户端特定引导逻辑……
const { app } = createApp()
// 这里假定 App.vue 模板中根元素具有 `id="app"`
app.$mount('#app')
创建entry-server.js文件
import { createApp } from "./main";
export default context => {
// 因为有可能会是异步路由钩子函数或组件,所以我们将返回一个 Promise,
// 以便服务器能够等待所有的内容在渲染前,
// 就已经准备就绪。
return new Promise((resolve, reject) => {
const { app, router } = createApp();
// 设置服务器端 router 的位置
router.push(context.url);
// 等到 router 将可能的异步组件和钩子函数解析完
router.onReady(() => {
const matchedComponents = router.getMatchedComponents();
// 匹配不到的路由,执行 reject 函数,并返回 404
if (!matchedComponents.length) {
return reject({
code: 404
});
}
// Promise 应该 resolve 应用程序实例,以便它可以渲染
resolve(app);
}, reject);
});
};
import Vue from 'vue'
import Router from 'vue-router'
import Home from './views/Home.vue'
Vue.use(Router)
export function createRouter(){
return new Router({
mode: 'history', //一定要是history模式
routes: [
{
path: '/',
name: 'home',
component: Home
},
{
path: '/about',
name: 'about',
component: () => import(/* webpackChunkName: "about" */ './views/About.vue')
}
]
})
}
// vue.config.js
const VueSSRServerPlugin = require("vue-server-renderer/server-plugin");
const VueSSRClientPlugin = require("vue-server-renderer/client-plugin");
const nodeExternals = require("webpack-node-externals");
const merge = require("lodash.merge");
const TARGET_NODE = process.env.WEBPACK_TARGET === "node";
const target = TARGET_NODE ? "server" : "client";
module.exports = {
css: {
extract: process.env.NODE_ENV === 'production'
},
configureWebpack: () => ({
// 将 entry 指向应用程序的 server / client 文件
entry: `./src/entry-${target}.js`,
// 对 bundle renderer 提供 source map 支持
devtool: 'source-map',
target: TARGET_NODE ? "node" : "web",
node: TARGET_NODE ? undefined : false,
output: {
libraryTarget: TARGET_NODE ? "commonjs2" : undefined
},
// https://webpack.js.org/configuration/externals/#function
// https://github.com/liady/webpack-node-externals
// 外置化应用程序依赖模块。可以使服务器构建速度更快,
// 并生成较小的 bundle 文件。
externals: TARGET_NODE
? nodeExternals({
// 不要外置化 webpack 需要处理的依赖模块。
// 你可以在这里添加更多的文件类型。例如,未处理 *.vue 原始文件,
// 你还应该将修改 `global`(例如 polyfill)的依赖模块列入白名单
whitelist: [/\.css$/]
})
: undefined,
optimization: {
splitChunks: undefined
},
plugins: [TARGET_NODE ? new VueSSRServerPlugin() : new VueSSRClientPlugin()]
}),
chainWebpack: config => {
config.module
.rule("vue")
.use("vue-loader")
.tap(options => {
merge(options, {
optimizeSSR: false
});
});
// fix ssr hot update bug
if (TARGET_NODE) {
config.plugins.delete("hmr");
}
}
};
"build:client": "vue-cli-service build",
"build:server": "cross-env WEBPACK_TARGET=node vue-cli-service build --mode server",
"build:win": "npm run build:server && move dist\\vue-ssr-server-bundle.json bundle && npm run build:client && move bundle dist\\vue-ssr-server-bundle.json",
npm run build:win
node server.js
总结:
从头搭建一个服务端渲染的应用是相当复杂的。幸运的是,我们有一个优秀的社区项目 Nuxt.js
让这一切变得非常简单。Nuxt 是一个基于 Vue 生态的更高层的框架,为开发服务端渲染的 Vue 应用提供了极其便利的开发体验。更酷的是,你甚至可以用它来做为静态站生成器。推荐尝试。
原理图:
1、用户打开浏览器,输入网址请求到Node.js
2、部署在Node.js的应用Nuxt.js接收浏览器请求,并请求服务端获取数据
3、Nuxt.js获取到数据后进行服务端渲染
4、Nuxt.js将html网页响应给浏览器
npm i xxx
安装依赖,避免了大量的重复代码和合并过程中的繁琐操作(只需修改依赖版本号即可)