vue seo优化之nuxt脱坑指南
前言
公司的项目需要做seo,经过翻阅nuxt文档 、相关资料及自己实战后,特意整理分享这篇文章,方便入坑的同学脱坑,同时自己也做下笔记,如有说错的地方,还望指点。
没有了解过seo和ssr服务器渲染的同学,建议先把基础知识给补一下
vue ssr指南 ssr相关知识
站长工具 查相关网站的seo 收录
nuxt中文网 nuxt中文网
一、nuxt和vue有什么区别?
1.服务器端渲染和客户端渲染
Nuxt: ssr(服务器端渲染),正如字面上的意思,需要把自己编写的代码放在服务器上跑,对服务器性能有一定的要求,需要node环境配合pm2 nginx部署;
vue: spa单页应用程序(客户端渲染),npm run build 成功后即可把dist目录的静态文件放在服务器部署;
2.应用框架
Vue多数用于开发spa单页应用程序,不利于搜索引擎的seo操作;对ssr服务器端渲染的支持并不是很友好,听说在vue3会对这方面有很大的提高,这也让我很期待;
而nuxt简单来说,就是在vue的基础上加上了服务器端渲染的能力,再扩展自己的特点,如生命周期 路由 es2015+语法支持等
二、nuxt项目搭建和配置
1.项目搭建
为了快速入门,Nuxt.js团队创建了脚手架工具 create-nuxt-app。
npn create-nuxt-app <项目名>
详细流程可以参考nuxt官网,在此就不累赘说明了
2.nuxt.config.js配置文件
module.exports = {
globalName: "hq",
mode: "universal",
server: {
port: 3000,
// host: "127.0.0.1"
host: "localhost"
},
// buildDir: 'nuxt-dist', // Build 发布目录
/*
** Headers of the page
*/
head: {
title: "",
meta: [
{ charset: "utf-8" },
/*优先使用 IE 最新版本和 Chrome*/
{ "http-equiv": "X-UA-Compatible", content: "IE=edge,chrome=1" },
{ name: "format-detection", content: "telephone=no" },
{ hid: "renderer", name: "renderer", content: `webkit` },
{
hid: "viewport",
name: "viewport",
content:
"width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0,user-scalable=no"
},
{
hid: "keywords",
name: "keywords",
content: ` `
},
{
hid: "description",
name: "description",
content:
""
}
],
// 使用外部资源文件,自动生成 script 标签
script: [],
link: [{ rel: "icon", type: "image/x-icon", href: "/favicon.ico" }]
},
/*
** Customize the progress-bar color
*/
loading: { color: "#fa6e32", height: "2px" },//进行ajax请求时顶部进度条的颜色
render: {
resourceHints: false
},
/*
** Global CSS
*/
css: [
{ src: "ant-design-vue/dist/antd.less", lang: "less" },
"~/assets/css/common.css",
"~/assets/css/hover.css",
"~/assets/iconfont/iconfont.css",
{ src: "~/assets/scss/public.scss", lang: "scss" },
{ src: "~/assets/less/theme.less", lang: "less" }
],
/*
** 引入插件
** ssr: false 表示只在客户端生效
*/
plugins: [
"@/plugins/antd-ui",
"@/plugins/router",
"@/plugins/axios.js",
{ src: "@/plugins/polyfill", ssr: false },
{ src: "@/plugins/vueQr.js", ssr: false },
{ src: "@/plugins/vue-lazyload.js", ssr: false },
{ src: "@/plugins/swiper.js", ssr: false }
],
// 配置路由中间件
router: {
middleware: ["i18n"]
// base:"/en/"
// base:"/"
},
/*
** Nuxt.js dev-modules
*/
buildModules: [
// Doc: https://github.com/nuxt-community/eslint-module
],
/*
** Nuxt.js modules
*/
modules: [
// nuxt 模块扩展
// Doc: https://axios.nuxtjs.org/usage
"@nuxtjs/axios",
"@nuxtjs/style-resources",
["cookie-universal-nuxt", { parseJSON: false }]
],
styleResources: {
//预处理器配置 一般用于加载全局变量之类
// style
stylus: "./assets/css/css.styl",
scss: ["./assets/scss/public.scss"]
},
/*
** Axios module configuration
** See https://axios.nuxtjs.org/options
*/
axios: {
// 是否允许跨域
proxy: true
},
proxy: {},
/*
** Build configuration
*/
build: {
vendor: ["axios"], // 为防止重复打包
transpile: [/^ant-design-vue/],
//提取css到单独link文件
extractCSS: {
allChunks: true
},
loaders: {
less: {
javascriptEnabled: true,
/**
* ant-design-vue 修改样式变量
*/
modifyVars: {
"primary-color": "#fa6e32" // 全局主色
}
}
},
/*
** You can extend webpack config here
*/
extend(config, ctx) {}
}
};
3.axios配置
import { notification, Modal } from "ant-design-vue";
import configSettings from "@/config/defaultSettings";
import { getToken} from "@/utils/fn";
import { isBrowser } from "~/environment";
const ERRORS = new Map([
[500, "服务器异常..."],
[404, "未找到对应资源"],
[401, "未鉴权"]
]);
export default function({ $axios, redirect, store }) {
// 基本配置
// $axios.defaults.baseURL = 'https://xxxx'; //正式
$axios.defaults.baseURL = "http://xxxx:8070"; //测试
$axios.defaults.timeout = 20000;
// 请求回调
$axios.onRequest(config => {
const token = getToken();
//设置请求头公共toeken
if (token) {
config.headers.common[configSettings.token] = token;
}
return config;
});
// 返回回调
$axios.onResponse(response => {
return res;
});
// 错误回调
$axios.onError(error => {
return Promise.reject(error.response);
});
}
4.默认HTML文件
在根目录下新建app.html
{{ HEAD }}
{{ APP }}
app.html跟vue脚手架生成后的public目录里面的index.html是一样的功能,只不过nuxt的脚手架没有默认生成该文件,注意:这里的{{HEAD}} 、{{APP}}是区别大小写的,不要去动他;
5.配置error页面
在根目录找到layouts并创建error.vue文件
</p><p>export default {</p><p> name: 'Error',</p><p> props: ['error'],</p><p> computed: {},</p><p> data() {</p><p> return {</p><p> type: {</p><p> 403: {</p><p> img:</p><p> "https://gw.alipayobjects.com/zos/rmsportal/wZcnGqRDyhPOEYFcZDnb.svg",</p><p> title: "403",</p><p> desc: "抱歉,你无权访问该页面",</p><p> },</p><p> 404: {</p><p> img:</p><p> "https://gw.alipayobjects.com/zos/rmsportal/KpnpchXsobRgLElEozzI.svg",</p><p> title: "404",</p><p> desc: "抱歉,你访问的页面不存在或仍在开发中",</p><p> },</p><p> 500: {</p><p> img:</p><p> "https://gw.alipayobjects.com/zos/rmsportal/RVRUAYdCGeYNBWoKiIwB.svg",</p><p> title: "500",</p><p> desc: "抱歉,服务器出错了",</p><p> },</p><p> },</p><p> };</p><p> },</p><p> mounted() {</p><p> },</p><p> methods: {}</p><p>}</p><p>
</p><p> @import "~ant-design-vue/lib/style/index";</p><p> .exception {</p><p> display: flex;</p><p> align-items: center;</p><p> height: 80%;</p><p> min-height: 500px;</p><p> .imgBlock {</p><p> flex: 0 0 56.5%;</p><p> width: 56.5%;</p><p> padding-right: 152px;</p><p> zoom: 1;</p><p> &::before,</p><p> &::after {</p><p> content: ' ';</p><p> display: table;</p><p> }</p><p> &::after {</p><p> clear: both;</p><p> height: 0;</p><p> font-size: 0;</p><p> visibility: hidden;</p><p> }</p><p> }</p><p> .imgEle {</p><p> float: right;</p><p> width: 100%;</p><p> max-width: 430px;</p><p> height: 360px;</p><p> background-repeat: no-repeat;</p><p> background-position: 50% 50%;</p><p> background-size: contain;</p><p> }</p><p> .content {</p><p> flex: auto;</p><p> h1 {</p><p> margin-bottom: 24px;</p><p> color: #434e59;</p><p> font-weight: 600;</p><p> font-size: 72px;</p><p> line-height: 72px;</p><p> }</p><p> .desc {</p><p> margin-bottom: 16px;</p><p> color: @text-color-secondary;</p><p> font-size: 20px;</p><p> line-height: 28px;</p><p> }</p><p> .actions {</p><p> button:not(:last-child) {</p><p> margin-right: 8px;</p><p> }</p><p> }</p><p> }</p><p> }</p><p> @media screen and (max-width: @screen-xl) {</p><p> .exception {</p><p> .imgBlock {</p><p> padding-right: 88px;</p><p> }</p><p> }</p><p> }</p><p> @media screen and (max-width: @screen-sm) {</p><p> .exception {</p><p> display: block;</p><p> text-align: center;</p><p> .imgBlock {</p><p> margin: 0 auto 24px;</p><p> padding-right: 0;</p><p> }</p><p> }</p><p> }</p><p> @media screen and (max-width: @screen-xs) {</p><p> .exception {</p><p> .imgBlock {</p><p> margin-bottom: -24px;</p><p> overflow: hidden;</p><p> }</p><p> }</p><p> }</p><p>
三、nuxt生命周期
Nuxt.js 是一个基于 Vue.js 的通用应用框架,支持vue生命周期的同时,扩展了服务端的钩子,特别是asyncData,使得你能够在渲染组件之前异步获取数据。
export default {
middleware () {}, //服务端
validate () {}, // 服务端
asyncData () {}, //服务端 nuxtjs进行服务器渲染关键钩子,只支持page目录下的文件,不支持components目录下的组件
fetch () {}, // store数据加载
beforeCreate () { // 服务端和客户端都会执行},
created () { // 服务端和客户端都会执行 },
beforeMount () {},
mounted () {} // 客户端执行
}
四、页面模板
export default {
components: { },
head() {
return {
title: this.college.title,
meta: [
{
hid: 'description',
name: 'description',
content: this.college.title
},
{
hid : 'keywords',
name : 'keywords',
content: this.college.title
}
]
}
},
watchQuery: ['collegeId'],//路由上该参数发生改变时,asyncData fetch 会重新执行
async asyncData({app}) {
const query = app.context.query;
let [college,'回调1','回调2'] = await Promise.all([
app.$axios.get('/web/api/college', {
params: {
collegeId: query.collegeId,
}
}),
//可多个请求
]);
return {
college:college,
}
},
data() {
return {};
},
created() {},
watch: {},
mounted() {
},
};
五、项目部署
1.确保本地代码无误后,运行命令打包,成功后会在根目录出现 .nuxt的目录;
npm run build
2.除了node_modules和其他没必要的文件夹以外,其他文件全部上传到服务器新建好的目录下
nuxt文件目录.png
3.安装nodejs
4.在上传的根目录里cmd运行命令安装依赖包
npm install -production
5.运行npm start,出现http://localhost:3000 证明成功,在服务器访问预览正常后即可关闭服务
npm start
6.安装pm2,没有了解过这个的东西可以先百度下 了解更多pm2
npm i pm2 -g
7.设置pm2开机自启
安装并配置pm2-windows-service
npm i -g pm2-windows-service
添加系统环境变量(右键 [我的电脑] - [属性] - [高级系统设置] - [环境变量] - 新建 [系统变量] )
PM2_HOME=C:\Users\Administrator.pm2 //路径默认在当前用户下的.pm2
以管理员权限打开新的cmd命令行窗口,执行以下命令来安装服务
pm2-service-install
提示Perform environment setup ? 选 n, 继续,此时, PM2服务已安装成功并已启动, 可以通过 [win + r] - [services.msc]
来查看,服务名称为PM2
8.使用pm2在根目录启动nuxt项目,很多人因为命令报错会卡在这里,这里就相当于第五点的npm start ,
启动成功后,再运行pm2 save,
pm2 start ./node_modules/nuxt/bin/nuxt.js -- start //启动项目nodejs服务
pm2 save //保存当前pm2 正在启动的NodeJS服务
9.配置Nginx
完成以上操作后,基本上已经结束了,接下来需要用Nginx配置下项目,没有安装的同学可自行百度;
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
upstream nodenuxt {
server 127.0.0.1:3000; # nuxt 项目启动后地址
keepalive 64;
}
server {
listen 80;
server_name www.baidu.com;#项目的域名
location / {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Nginx-Proxy true;
proxy_cache_bypass $http_upgrade;
proxy_pass http://nodenuxt;
}
}
}
修改配置文件后再重新启动Nginx,如果出现报错,应该是服务器的80端口被占用了,可修改配置文件的listen 端口,也可以查看是哪个服务占用再去禁用该服务,看个人项目需求
start nginx //启动nginx
nginx -s reload //重新载入nginx(当配置信息发生修改时)
nginx -s quit //停止nginx
nginx -h 查看帮助信息
nginx 启动成功后,就可以通过域名访问项目了,大功告成!
总结
本文并非原创,欢迎转载,记录下学习nuxt的过程,也可以让入门的同学少走弯路,后面有时间我会整理相关nuxt的代码发布一套demo出来给大家参考,希望能帮助到大家,拜拜!
——By
kkc_hq
发表评论 (审核通过后显示评论):