最近,由于公司官网是使用vue进行开发的,但vue单页面SPA的似乎对seo的支持性太差了,网上尝试了各种方式,比如修改mode为history,预渲染 prerender-spa-plugin + vue-meta-info,虽然是比之前的好,但动态路由仍无法生成单页面,所以,爬虫也就爬到了几个页面而已,所以,决定使用nuxt重构下。以下就是重构过程时边查找资料边操作的笔记。可能有点凌乱,但有必要记录下。
在开始之前分享几个关于nuxt.js开发网站,感兴趣可以看看。
掘金:https://juejin.cn/
https://www.freebuf.com/
1.Nuxt.js作用?
Nuxt.js 是一个基于 Vue.js 的通用应用框架。用来创建服务器端渲染(SSR)应用
,与传统 SPA (单页应用程序 (Single-Page Application)) 相比,服务器端渲染 (SSR) 的优势主要在于:
更好的 SEO,由于搜索引擎爬虫抓取工具可以直接查看完全渲染的页面。
更快的内容到达时间 (time-to-content),特别是对于缓慢的网络情况或运行缓慢的设备。无需等待所有的 JavaScript 都完成下载并执行,才显示服务器渲染的标记,所以你的用户将会更快速地看到完整渲染的页面。通常可以产生更好的用户体验,并且对于那些「内容到达时间(time-to-content) 与转化率直接相关」的应用程序而言,服务器端渲染 (SSR) 至关重要
nuxt.js项目创建
确保有安装npx(只要按照npm版本在5.2.0版本以上,则自动安装npx)
//方式一:使用npx安装
$ npx create-nuxt-app <项目名>
//安装完进入项目目录并启动
$ cd <project-name>
$ npm run dev
//编译2:项目编译
npm run build
npm run start
//编译1:生成静态文件
npm run generate
//方式二:可使用yarn安装
yarn create nuxt-app <项目名>
//启动项目
$ cd <project-name>
$ yarn dev
2.nuxt的文件目录
|-- .nuxt // Nuxt自动生成,临时的用于编辑的文件,build
|-- assets // 用于组织未编译的静态资源入LESS、SASS 或 JavaScript
|-- components // 用于自己编写的Vue组件,比如滚动组件,日历组件,分页组件
|-- layouts // 布局目录,用于组织应用的布局组件,不可更改。
|-- middleware // 用于存放中间件
|-- pages // 用于存放写的页面,我们主要的工作区域
|-- plugins // 用于存放JavaScript插件的地方
|-- static // 用于存放静态资源文件,比如图片
|-- store // 用于组织应用的Vuex 状态管理。
|-- .editorconfig // 开发工具格式配置
|-- .eslintrc.js // ESLint的配置文件,用于检查代码格式
|-- .gitignore // 配置git不上传的文件
|-- nuxt.config.json // 用于组织Nuxt.js应用的个性化配置,已覆盖默认配置
|-- package-lock.json // npm自动生成,用于帮助package的统一性设置的,yarn也有相同的操作
|-- package-lock.json // npm自动生成,用于帮助package的统一性设置的,yarn也有相同的操作
|-- package.json // npm包管理配置文件
3.nuxt的2种打包部署方式:
nuxt部署应用的方式:服务端渲染应用部署 和 静态应用部署。
服务端渲染应用部署
执行yarn build后,将以下static文件,.nuxt文件,nuxt.config.js文件,package.json拷贝并置于服务器目录下后,执行npm i命令安装,安装好后并运行,具体可以参照此篇https://blog.youkuaiyun.com/u012878818/article/details/100115408
配置nginx如下所示,并启动nginx
server {
listen 8091;
server_name wtqianyu.com;
root wxdb;//文件目录
location / {
proxy_pass http://127.0.0.1:3004 ;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
}
}
127.0.0.1 wtqianyu.com #域名可自定义
nuxt build
nuxt start
静态应用部署
注意:若要nuxt生成动态路由的静态文件,则需要在nuxt.config.js文件中配置如下:
//参考:https://segmentfault.com/q/1010000016156293
//https://nuxtjs.org/api/configuration-generate/#routes
generate: {
// interval:100,
routes: function (callback) {
axios.post("https://www.xxxxxxxess.com/api/newsInformation/findPage", {
pageNum: 1,
pageSize: 30,
param: {
isEnable: "1",
type: 'company',
},
}).then(res => {
const routes= res.data.data.content.map((path) => {
return {
route:'/previewNews/' + path.id,
payload:path.id
}
})
callback(null,routes)
}).catch(callback)
}
},
执行该命令,则生成静态文件的dist目录,
yarn generate或npm run generate
将打包生成后的dist此目录修改zwgw存放到服务器上配置nginx,配置如下
nginx配置
server{
listen 8092;
server_name localhost;
location / {
root zwgw;//dist目录
try_files $uri $uri/ /index.html;
index index.html index.htm;
}
}
Nuxt.js的生命周期
nuxt的生命周期主要是在服务端,而vue组件的生命周期内,只有beforeCreate和created这两个钩子会在浏览器端和服务端均被调用;其他的钩子都只会在浏览器端调用。但是keep-alive的activated及deactivated两个钩子是不存在的,因为服务段渲染是不存在缓存的。需要注意的是服务器端生命周期是无法访问到window对象,但客户端生命周期钩子是可以获取window对象(但beforeCreate和created获取window失败)
上图中,黄色部分是客户端声明周期,红色部分是在服务器端运行声明周期。
下面6个是运行在服务端,而客户端声明周期就vue组件那几个声明周期,所以就不介绍。
1.nuxtServerInit():服务器初始化。主要是store或数据初始化操作,只运行一次
2.middleware():中间件运行。
3.validate():校验参数
4.asyncData()&fetch():异步数据处理,组件运行之前
5.render():客户端渲染,定义渲染的配置,不适合写操作逻辑
1.在store目录中新建index.js
export const actions={
nuxtServerInit(store,context){
//初始化数据到store中 console.log('nuxtServerInit',store,context)
}
}
2.middileware中间件运行顺序:nuxt.config.js->layout布局->匹配页面
3.validate:主要是对路由参数进行校验,若校验错误,跳转错误页面
4.asyncData():读取异步数据,返回组件
5.fetch():读取数据后,返回vuex
nuxt.js启动端口修改
nuxt.js默认启动端口是3000,若要修改此端口,需要在package.json文件中天机启动端口地址配置,
在项目根目录中找到package.json文件,添加如下代码:
"config":{
"nuxt":{
"host":"127.0.0.1",//ip地址
"port":"3001"//启动端口
}
},
nuxt.js的全局样式配置
nuxt项目全局样式配置是在nuxt.config.js中配置,在assets中创建全局样式后,在nuxt.config.js中配置如下,注意~波浪线相当于别名@,当然你也可以直接使用@。
找到nuxt.config.js文件,配置如下:
nuxt.js路由
nuxt.js路由是可以自动生成的,只要在page文件创建文件,则自动生成路由。
路由参数传值
<nuxt-link :to="{name:'about',params:{newId:332}}">about</nuxt-link>
路由参数接收:
about{{$route.params.newId}}
//如果用params 应用name
this.$router.push({ name:'news_list_con', params:{id:id,title:news_title}});
路由获取:this.$route.params.id
//如果用query 就用path
this.$router.push({ path:'/news/news', query:{id:id,title:news_title}});
路由获取:this.$route.query.id
动态路由:nuxt的路由都是动态生成的。所以在pages文件中创建动态路由必须是下划线+传递值的命名,如news/_id.vue文件(注意,动态路由必须是下划线+传递值的命名)
index.vue
<template>
<div class="container">
<div>
<VTProductive />
<a href="/HomeNew">HomeNew</a>
<nuxt-link :to="{ name: 'HomeNew' }">HomeNew</nuxt-link>
<li><a href="/news/123">News-1-a</a></li>
<li><a href="/news/456">News-2-a</a></li>
<nuxt-link to="/news/123">News-111-nu</nuxt-link>
<nuxt-link to="/news/122423">News-222-nu</nuxt-link>
<li><nuxt-link :to="{name:'news-id',params:{id:1243323}}">News-1</nuxt-link></li>
</div>
</div>
</template>
在pages文件中创建news/_id.vue文件(注意,动态路由必须是下划线+传递值的命名)
<template>
<div>
<h2>新闻的id值 [{{ $route.params.id }}]</h2>
<ul>
<li><a href="/">Home</a></li>
<nuxt-link :to="{ name: 'HomeNew' }">---HomeNew</nuxt-link>
</ul>
</div>
</template>
<script>
export default {
head () {
return {
meta:
[
{
name: 'keywords',
content: '当前新闻id是'+this.$route.params.id
},
]
}
},
data() {
return {
newsId:'333'
}
},
mounted(){
console.log('获取新闻id',this.newsId,this.$route.params.id)
},
//动态路由的id校验方法
validate({ params }) {
// Must be a number
return /^\d+$/.test(params.id);
},
};
</script>
<style lang="scss" scoped>
h2{
color: red;
}
ul{
li{
color: blue;
}
}
</style>
动态路由的Meta标签填充
游戏详情页面举例子,由于数据是异步获取的,我们需要把数据获取写在asyncData钩子,待数据获取成功才会渲染该页面组件
<script>
export default {
async asyncData ({ app, params }) {
let data = await app.$axios.get(`/appinfo/${params.id}`);
return {
appname: data.appinfo.appname
}
},
head () {
return {
meta:
[
{
name: 'keywords',
content: `${this.appname},千千`
},
]
}
}
}
nuxt中使用axios
https://blog.youkuaiyun.com/TimOut/article/details/98754914?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-5.control&dist_request_id=1328761.433.16171676387236235&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-5.control
在nuxt.config.js中配置如下
// Modules: https://go.nuxtjs.dev/config-modules
modules: [
// https://go.nuxtjs.dev/axios
'@nuxtjs/axios',
],
proxy:{
'/api/':{
target:'http://h5api.zhefengle.cn',
pathRewrite:{
'^/api/': '/',
changeOrigin: true
}
}
},
// Axios module configuration: https://go.nuxtjs.dev/config-axios
axios: {
proxy:true,//开启代码
prefix:'/api',// 表示给请求url加个前缀 /api
credentials:true // 表示跨域请求时是否需要使用凭证
},
build: {
transpile: [/^element-ui/],
vendor: ['axios'] // 为防止重复打包
}
在组件中使用,webpack集成的时候需要安装fibers
export default {
// 使用Promise {isDev, route, store, env, params, query, req, res, redirect, error}
asyncData(context) {
console.log(context.axios)
return contxt.$axios.get().then(res=>{
console.log('请求成功过了吗?',res)
})
},
mounted() {
let that = this;
this.$axios.get("/api/index.html").then((res) => {
alert(res.data.code);
});
},
};
nuxt中使用scss
yarn add -D sass sass-loader@10 fibers
全局样式的配置:
在assets中创建common.css样式,切换页面时触发如下动画样式
<style lang="scss" >
.page-enter-active, .page-leave-active {
transition: opacity 2s;
}
.page-enter, .page-leave-active {
opacity: 0;
}
</style>
在nuxt.config.js的css配置就行
css: [
'element-ui/lib/theme-chalk/index.css',
'@/assets/css/common.css'
],
Nuxt的全局守卫配置
1在middleware的文件新建auth.js
在nuxt.config.js中配置router信息如下
nuxt的layout布局,定义布局名为blog后再对应组件中使用如下两种方式即可将该组件改为对应内容布局,比如布局名为blog。
//第一种方式如下
layout: 'blog',//定义布局为blog
//第二种如下:
// 这里应该可以定义部分逻辑,每个组件独有,不共享
layout (context) {
return 'blog'
}
nuxt的内部切换布局layout
$nuxt.setLayout(‘布局名’)
nuxt的图片显示问题
nuxt的静态文件参考:https://www.nuxtjs.cn/guide/assets#%E9%9D%99%E6%80%81%E6%96%87%E4%BB%B6
如果你的静态资源文件需要 Webpack 做构建编译处理,可以放到 assets 目录,否则可以放到 static 目录中去。
Nuxt 服务器启动的时候,该目录下的文件会映射至应用的根路径 / 下,像 robots.txt 或 sitemap.xml 这种类型的文件就很适合放到 static 目录中。
data中的图片导入
在data中绑定 require字符串
// data中 :
data(){
return : {
lists : [{
title: "学习中心",
iconpath: require("@/assets/img/personal/index/xuexi.webp")}]
}
}
static中的图片
nuxt中直接导入static图片不显示,如下
<img src="/static/image/foot-img/6.png"/>
需要去掉’/static’后才能正常显示
assets中的图片
<img src="@/assets/img/test.jpg">
<!-- 引用 assets 目录下经过 webpack 构建处理后的图片,推荐使用~波浪号-->
<img src="~/assets/my-image-2.png"/>
nuxt中添加jQuery
第一种方式:使用命令安装
npm i jquery --save
第二种方式:在head中嵌套cdn
head: {
title: process.env.npm_package_name || '',
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ hid: 'description', name: 'description', content: process.env.npm_package_description || '' }
],
script:[
{type: 'text/javascript'},
{src: 'https://ss1.bdstatic.com/5eN1bjq8AAUYm2zgoY3K/r/www/cache/static/protocol/https/jquery/jquery-1.10.2.min_65682a2.js'}
],
link: [
{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }
]
},
利用hid键为meta标签配一个唯一的标识编号。
为了避免组件中的meta标签不能正确覆盖父组件中相同的标签而产生重复的现象,建议利用hid键为meta标签配一个唯一的标识编号。
validate({ params }) {
return /^\d+$/.test(params.id);
},
head() {
//设置动态路由的页面头部信息
return {
title: this.row.name,
meta: [
{ hid: "description", name: "news", content: this.row.useScenes },
],
};
},
axios接口调用
当接口调用出错或无数据时就会跳转到错误提示页面
asyncData(context) {
try {
this.$axios
.post("/recruits/findJoinUs", {
isEnable: "1",
})
return { project: "nuxt" };
} catch (error) {
context.error({
statusCode:404,
message:'页面数据丢失'
})
}
},
nuxt第三方插件v-distpicker使用
安装第三方插件,比如v-distpicker(vue省市联动选择器插件)插件出处:https://www.npmjs.com/package/v-distpicker
yarn add v-distpicker --save
在plugins中创建v-dispicker.js (vue省市联动选择器插件)
import Vue from 'vue'
if(process.browser){
var Distpicker=require('v-distpicker');
Vue.use(Distpicker);
Vue.component('v-distpicker',Distpicker);
}
在nuxt.config.js中的plugins中使用,nuxt.config.js是全局文件配置,每次修改nuxt.config.js文件后都需要重启项目才能生效
plugins: [
'@/plugins/element-ui',
'@/plugins/v-distpicker'
],
在对应vue组件中使用
<el-col>
<label class="font-14 title3">地理位置: </label>
<span class="geo">
<v-distpicker
province="江苏省"
city="南京市"
area="玄武区"
@selected="citySelected"
></v-distpicker>
</span>
</el-col>
nuxt添加百度统计
在plugins中新建baidu.js文件
export default () => {
window._hmt = window._hmt || []
const hm = document.createElement('script')
hm.src = "https://hm.baidu.com/hm.js?f46313a4596d044f31f5bdfxxxxxx";
const s = document.getElementsByTagName('script')[0]
s.parentNode.insertBefore(hm, s)
}
在nuxt.config.js中使用
plugins: [
{src:'@/plugins/baidutj',mode:'client'},
'@/plugins/element-ui',
'@/plugins/v-distpicker',
'@/plugins/vue-countup',
],
参考:
http://www.zyiz.net/tech/detail-143116.html