教你如何创建、使用Nuxt.js框架实现SSR服务端渲染

# 前言

对于单页面的 SEO 大家可能都知道是服务器渲染,比如 Vue 是利用 SSR 来实现的,需要自己配置移动端与服务端等一顿操作,很是不简单。那么有没有一个框架既可以实现 SEO 同时配置还那么麻烦?答案是可定的,那就是我们今天将要分享的 Nust.js ,有兴趣的同学可以看下 官网

# 安装

npm install -g create-nuxt-app

# 创建项目

create-nuxt-app nuxt-test (这里是你的项目民称)

然后就是一顿选择操作,如下图。


image.png

注意:红框处一定选择 SSR 切记 切记 切记,其他选项可根据项目需要进行选择

# 启动项目

按上述操作完成并下载完依赖后,就可以启动项目了

npm run dev

看到以下这两个页面,说明你项目启动成功了


image.png
1625055718(1).png

# 路由

Nuxt 会自动检测 pages 文件夹下的所有 .vue 文件,并自动生成路由不需要配置,类似于 uni-app 的路由一样,因此如非必要,pages 文件夹的名称不要随意改动。

路由跳转

语法:
<nuxt-link :to="{name: '你的页面名称'}"> 页面名称  </nuxt-link>
举个栗子:
<nuxt-link :to="{name: 'details'}"> 查看详情页  </nuxt-link>

参数传递:
<nuxt-link :to="{name: '页面名称', params:{参数键: 参数值}}"> 页面名称 </nuxt-link>
<nuxt-link :to="{name: '页面名称', query:{参数键: 参数值}}"> 页面名称 </nuxt-link>
举个栗子:
<nuxt-link :to="{name: 'details', params:{id: '123456'}}"> 查看详情页  </nuxt-link>
<nuxt-link :to="{name: 'details', query:{id: '123456'}}"> 查看详情页  </nuxt-link>
获得参数值:
同 Vue =》this.$route.params.参数名
同 Vue =》this.$route.query.参数名

一级路由
pages 文件夹下的一级.vue文件或者是一级文件夹下的index.vue文件都是一级路由,如下图:

image.png

about 文件下的index.vue文件是一级路由about,只不过是放在了about文件夹下。当防访问about路由时,会查找有没有一节路由about, 如果没有就查找about文件夹并且默认会查找index.vue文件。不管是那种方式,访问路径都为:http://localhost:3000/about

而下边这个index.vuenews.vue上头紧挨着的)也是一级路由,只是直接放在了pages文件夹下的一级目录下,是通常的写法,访问路径为:http://localhost:3000/indexhttp://localhost:3000/news,分别对应着首页(index.vue)和 新闻页(news.vue)。

二级路由
所谓二级路由就是建立 一个一级路由同名的文件夹,这个文件夹下的.vue文件就是二级路由。例如news.vue需要建立有一个news文件夹来存放news的二级路由, 如下图:

1625800822(1).png

访问路劲分别为:http://localhost:3000/about/aboutChildhttp://localhost:3000/news/newsChild
一级路由里写一个可以存放二级路由的标签:<nuxt-child />,访问后的结果如下截图:

image.png

注意:我的about文件夹是包含了index.vue文件,在这个文件夹里再建立子组件/二级路由没实现。只能是一级路由news.vue同级创建一个同名文件夹news,并这这个文件里创建二级路由页面没有问题。不知道是不是我配置的不对,望大家多多留言。
还有就是,父组件(一级路由)里必须放一个可存放二级路由的地方,类似于solt,在这里名称叫<nuxt-child />,否则二级路由不展示。

动态路由

语法:
<a href="/details/1234"> 查看详情页 </a>

注意:如传递的是 id 则需要建立一个 _id.vue 的文件作为接收参数的页面,以此类推(页面目录结构如下图,必须这么建,这是一个 nuxt 的约定)。

image.png

如上图:about 文件夹里的 index.vue 就是 about 页面,跳转至此页面可以这么写:
<nuxt-link :to="{name: 'about'"> about </nuxt-link>
如果传递参数,则可以这么写(前提是已经创建好了以参数名为名称的页面,如 _id.vue ):
<nuxt-link :to="{name: 'about/123456'"> about </nuxt-link>
这么写就会跳转至 _id.vue 页面而不是 about.vue 页面

在 `_id.vue` 页面接收参数
法一:同 Vue =》 this.$route.params.id
法二:使用 validate 方法,切记、切记、切记 不能使用 this
validate({params, query, store}){
    return true;  // 直接呈现页面
    return false; // 将停止Nuxt.js呈现页面并显示错误页面
    return (校验规则:正则、store、promise ...)结果为 true 则呈现页面,否则显示错误页面
}

路由重定向
现在模拟这么一个场景,用户输入http://localhost:3000默认重定向到news.vue页面。

方法一:再nuxt.config.js配置文件里配置

在配置文件`nuxt.config.js`里配置一下内容
router: {
    extendRoutes(routes) {
        routes.push({
            path: "/",
            redirect: "/news"
        });
    }
}

方法二:利用中间件

  1. middleware文件夹下创建一个处理重定向的文件redirect.js(名称随意)。
  2. redirect.js文件书写处理重定向的逻辑:
export default function({ isHMR, app, store, route, parapms, error, redirect }) {
    // 如果是热更新则不做处理
    if (isHMR) return
    if (route.fullPath == '/') {
        return redirect('/news');
    }
}
  1. 在配置文件nuxt.config.js里引入中间件文件
 router: {
    middleware: 'redirect'
}

以上就是两种实现重定向的方法,记得重启,因为更改了配置文件。
法一我这没起作用,欢迎大家评论帮忙解决

# 错误页面跳转(404页面)

Nuxt 框架内置了一个错误页面,很生硬也不友好,如下图:

image.png

我们也可以自定义错误页面,只需要在 layouts 文件下创建一个 error.vue 页面作为我们的错误页面,切记名称不能更改哦,只能是 error.vue, 如下图:

image.png

具体容可根据需要自行设置

自定义错误页面,默认接受一个 error 属性

<template>
    <div style="text-align: center; font-size: 20px;padding-top: 50px;">
        我是自定义错误页面
        <br>
        {{error}}
    </div>
</template>

<script>
    export default {
        props:['error']
    }
</script>

并且 error 是个对象,属性如下图:

image.png

当我们使用 axiosasyncData 方法里发送请求时,也可以把捕捉的错误信息展现在错误页面:

image.png

可见,我需要在 asyncData 方法里接受一个上下文 ctx ,当捕捉到错误时,调用 ctx.error 方法向 error.vue 错误页面 传递错误信息。

# 路由跳转动画效果

全局设置,作用于 pages 下的所有页面

  1. 第一步:在 assets 文件夹下 创建一个 index.css 文件
  2. 第二步:在 index.css 文件下书写以下内容:
.page-enter-active,.page-leave-active {
    transition: opacity .5s;
}

.page-enter,.page-leave-active {
    opacity: 0;
}
  1. 第三步: 在 nuxt.config.js 配置我们写的样式
css: [
    '~assets/main.css',
    '~assets/index.css'
]
  1. 这时使用 nuxt-link 跳转就可以实现路由动画了,切记只能是 nuxt-link 跳转,否则无效。

针对特定页面,单独设置

  1. 第二步:在 index.css 文件下书写以下内容:
.about-enter-active,.about-leave-active {
    transition: opacity 1s;
    font-size: 14px;
    background: #fff;
}

.about-enter,.about-leave-active {
    opacity: 0;
    font-size: 56px;
    background: red;
}
  1. 第二步:在 about 页面增加一个 transition 属性,属性值必须是 about

注意,这里的 transition 的值不一定和页面名称一样,但必须和样式里的名称保持一致

# 默认模板与默认布局

默认模板

  1. 在项目根目录创建一个 app.html 文件
  2. app.html 文件里书写一下内容(不局限以下内容):
<!DOCTYPE html>
<html>
<head>
    <!-- 读取的是 nuxt.config.js 文件里的 head 配置 -->
    {{HEAD}}
</head>

<body>
    <h1> 我是测试的默认模板 </h1>
    <!-- 每个页面的实体 -->
    {{APP}}
</body>
</html>

提示: {{APP}} 是必填内容,除此之外可跟需要自行填写

  1. 重启运行之后,每个页面都可看到模板里设置的内容

默认布局
指的就是 layouts 文件夹下的 default.vue 文件,内容如下:

<template>
    <div>
        <Nuxt />
    </div>
</template>

注意:出<Nuxt />不能删除外(因为他相当于路由容器),其他内容可根据需求进行书写,所有页面也都能看到。

当然,我们也可以根据不同需要,特定页面可以制定不同的默认布局(也叫视图)。在不指定的情况下,默认所有页面都是使用的layouts 文件夹下的 default.vue 文件。若需指定只需如下步骤

  1. layouts文件夹下创建我们的视图暂且叫testLayouts.vue文件
  2. testLayouts.vue文件内容如下(具体内容肯根据需要自行填写):
<template>
    <div>
        <Nuxt />
    </div>
</template>

<style>
 
</style>
  1. 在需要指定这个视图的页面中增加下面这句话:
export default {
    layout: 'testLayout'
}

这时再运行指定新默认视图的页面可以看到,已经不是使用的全局默认的视图了,而是我们指定的新视图。

# 数据请求 asyncData

vue 里数据请求一般是在 mounted 里去请求,即在也页面挂载完成后去请求的。这样的话还是不能进行 SEO。我需要在页面加载之前去请求数据,由后端组装好数据返回到前端,这样才符合SEO
asyncData 就解决了这个问题,他和 mounted、methods 等属性方法平,如下:

export default {
  data(){
    return{
 
    }
  },
  asyncData({isDev, router, store, env, params, req, res, redirect, error}){
    
  },
  mounted(){
             
  }
}

这里注意下 asyncData 有这么几个特点:

  1. 在服务端调用 asyncData 时,可以访问用户请求的 reqres 对象
  2. 在当前页面刷新,服务器执行次函数
  3. 从其他页面跳转过来,客户端执行次函数

请求数据的话就正常使用axios就可以,但需要两次return,例如:

export default {
  asyncData(){
    return axios({
      url:'http://xxx.xxx.xxx'
    }).then(res=>{
      return { dataList: res.data.data }
    })
  }
}

第一个 return 是返回一个 promise 告诉服务端已经请求完成,在服务端也有类似的这么一句话:this.asyncData().then()(猜测一下)
第二个 return 是让服务端链式调用时保证 .then 有结果。

在渲染页面时我就可以用 dataList 作为数据源来渲染,例如:

<div v-for="(item, index) in dataList" :key="index">
  <p> {{ item}} </p>
</div>

注意:return { dataList: res.data.data }dataList 作为键名 和 res.data.data,可以按需修改 和 按需取值,但整体格式必须按照这个规则来。

传参 与 接收参
页面间相互传值大家都很熟悉了, 以动态路由为例:
场景:A 页面 跳转至 B 页面 并 传递参数 Id

在 A 页面写如下代码,跳转至 B 页面:

this.$router.push('/B/12345) 

在 B 页面接收参数并在 asyncData 中接收参数后请求数据:

 asyncData(data){
    return axios.post(url, {id: data.id}).then(res=>{
        ...
    })
}

# 反向代理

  1. 安装 @nuxt.js/proxy
npm install --save @nuxt.js/proxy
  1. 配置文件 nuxt.config.js 里配置反向代理
// Modules: https://go.nuxtjs.dev/config-modules
modules: [
    // https://go.nuxtjs.dev/axios
    '@nuxtjs/axios',
    // 引入反向代理
    '@nuxtjs/proxy'
],

// Axios module configuration: https://go.nuxtjs.dev/config-axios
axios: {
    // 开启反向代理
    proxy: true
},
proxy: {
    '/api': {
        target: 'http://xxx.xxx.xxx',
        changeOrigin: true,
        pathRewrite: {
            '^/api': '/'
        }
    }
}

看到 proxy 对象是不是很熟悉了,没错这里和 Vue 的配置内容是一致的。但写到这里有个问题:

我们手动点击页面去请求数据没有问题,配置了反向代理后 asyncData 可以跨域拿到数据,但加载页面时 asyncData 在服务器触发执行时 ,就会报错找不到路径。

思路:想要区分前后端请求,即前端请求时使用代理,在服务端请求时不使用代理。那该怎么配置呢?我们接着往下看:

解决方法也很简单,在 nuxt 框架中有个全局变量 proccess.server值为布尔类型。为 true 时标明是在服务端,否则是在客户端。这样我们可根据不同值来写对应的请求地址即可,完美解决。

# 配置使用 Element UI

  1. 安装 element ui
npm install --save element-ui
  1. plugins 文件夹 创建 element-ui.js 文件,内容如下
import Vue from 'vue'
import Element from 'element-ui'
import locale from 'element-ui/lib/locale/lang/en'

Vue.use(Element, { locale })
  1. 修改配置文件 nuxt.config.js 引入 element-ui
css: [
    'element-ui/lib/theme-chalk/index.css'
],
plugins: [
    '@/plugins/element-ui'
]

到此就在项目中引入了 element-ui,具体使用和在 Vue 中完全一样,是不是很简单呀。

赶紧动起手来试试吧,欢迎你评论~~~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容