Nuxt实现SSR

1.SSR简介

1.1 传统web开发

传统web开发,网页内容在服务端渲染完成,一次性传输到浏览器。

在这里插入图片描述

打开页面查看源码,浏览器拿到的是全部的dom结构
在这里插入图片描述

1.2 单页应用 Single Page App

单页应用优秀的用户体验,使其逐渐成为主流,页面内容由JS渲染出来,这种方式称为客户端渲染。

在这里插入图片描述

打开页面查看源码,浏览器拿到的仅有宿主元素#app,并没有内容

在这里插入图片描述

spa问题:seo、首屏加载速度

1.3 服务端渲染 Server Side Render

SSR解决方案,后端渲染出完整的首屏的dom结构返回,前端拿到的内容包括首屏及完整spa结构,应用激活后依然按照spa方式运行,这种页面渲染方式被称为服务端渲染 (server side render)

在这里插入图片描述

  • 特点:首屏、seo
  • 缺点: 复杂度、库的支持性、性能

场景:
seo:预渲染,检测是不是爬虫,首屏到达时间
spa项目已经做完了puppeteer (Puppeteer 是 Chrome 开发团队在 2017 年发布的一个 Node.js 包,用来模拟 Chrome 浏览器的运行)
爬虫实现puppeteer:让它直接从spa项目中爬出结果
nuxt.js全新项目
优化手段:静态化
Vue SSR指南:https://ssr.vuejs.org/zh/

2.Nuxt.js简介
2.1 Nuxt简介   Nuxt.js中文官网:https://www.nuxtjs.cn/

Nuxt.js是一个基于Vue.js的通用应用框架。通过客户端/服务端基础架构的抽象组织,Nuxt.js主要关注的是应用的UI渲染。

2.2 Nuxt.js特性:

基于Vue.js
自动代码分层
服务端渲染
强大的路由功能,支持异步数据
静态文件服务
HTML头部标签管理

2.3 nuxt渲染流程
一个完整的服务器请求到渲染(或用户通过 切换路由渲染页面)的流程:**

在这里插入图片描述

2.4nuxt安装
运行create-nuxt-app

npx create-nuxt-app <项目名>

运行项目:

npm run dev
  1. 目录结构
    nuxt 项目目录结构
    在这里插入图片描述

4. 路由
1、基础路由

pages目录结构如下

pages/
--| user/
-----| index.vue
-----| one.vue
--| index.vue

.nuxt文件夹下的router.js自动生成的路由为

router: {
  routes: [
    {
      name: 'index',
      path: '/',
      component: 'pages/index.vue'
    },
    {
      name: 'user',
      path: '/user',
      component: 'pages/user/index.vue'
    },
    {
      name: 'user-one',
      path: '/user/one',
      component: 'pages/user/one.vue'
    }
  ]
}

2、动态路由
在 Nuxt.js 里面定义带参数的动态路由,需要创建对应的以下划线作为前缀的 Vue 文件 或 目录。

pages/
--| _slug/
-----| comments.vue
-----| index.vue
--| users/
-----| _id.vue
--| index.vue

Nuxt.js 生成对应的路由配置表为:

router: {
	routes: [
	    {
	        name: 'index',
	        path: '/',
	        component: 'pages/index.vue'
	    },
	    {
	        name: 'users-id',
	        path: '/users/:id?',
	        component: 'pages/users/_id.vue'
	    },
	    {
	        name: 'slug',
	        path: '/:slug',
	        component: 'pages/_slug/index.vue'
	    },
	    {
	        name: 'slug-comments',
	        path: '/:slug/comments',
	        component: 'pages/_slug/comments.vue'
	    }
	]
}

3、嵌套路由
创建内嵌子路由,你需要添加一个 Vue 文件,同时添加一个与该文件同名的目录用来存放子视图组件。

pages/
--| detail/
-----| _id.vue
--| detail.vue

自动生成的路由配置:

routes: [{
    path: "/detail",
    component: _49f428be,
    name: "detail",
    children: [{
      path: ":id?",
      component: _247e7f2e,
      name: "detail-id"
    }]
  }]

4、导航

添加路由导航,layouts/default.vue商品管理页

 <nav>
        <nuxt-link to="/">首页</nuxt-link>
    </nav>

功能和router-link等效 禁用预加载行为:

<n-link no-prefetch>page not pre-fetched</n-link>

5. 视图
下图展示了Nuxt.js如何为指定的路由配置数据和视图

nuxt视图

1、默认布局

可通过添加 layouts/default.vue 文件来扩展应用的默认布局。

默认布局的源码如下:

<template>
    <nuxt />   //用于显示页面的主体内容。
</template>

2、自定义布局
在layout下创建自定义的.vue文件。

使用自定义布局:

<template>
  <!-- Your template -->
</template>
<script>
  export default {
    layout: 'blog'
    // page component definitions
  }
</script>

3、自定义error页面

<template>
  <div class="container">
    <h1 v-if="error.statusCode === 404">页面不存在</h1>
    <h1 v-else>应用发生错误异常</h1>
    <p>{{error}}</p>
    <nuxt-link to="/">首 页</nuxt-link>
  </div>
</template>

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

4、页面

页面组件实际上是 Vue 组件,只不过 Nuxt.js 为这些组件添加了一些特殊的配置项(对应 Nuxt.js 提供的功能特性)以便你能快速开发通用应用。

给首页添加标题和meta等

<template>
  <h1 class="red">Hello {{ name }}!</h1>
</template>

<script>
  export default {
    asyncData (context) {
      // called every time before loading the component
      return { name: 'World' }
    },
    fetch () {
      // The fetch method is used to fill the store before rendering the page
    },
    head() {
        return {
            title: "课程列表",
            // vue-meta利用hid确定要更新meta
            meta: [
                { name: "description", hid: "description", content: "set page meta" }
            ],
            link: [{ rel: "favicon", href: "favicon.ico" }]
        };
    },
    // and more functionality to discover
    ...
  }
</script>

<style>
  .red {
    color: red;
  }
</style>

6. 异步数据
异步数据获取

asyncData方法使得我们可以在设置组件数据之前异步获取或处理数据

接口准备

安装依赖:

npm i koa-router koa-bodyparser -s

接口文件,server/api.js

 // 此文件并非nuxt生成,它为演示项目提供数据服务接口
    const Koa = require('koa');
    const app = new Koa();
    const bodyparser = require("koa-bodyparser");
    const router = require("koa-router")({ prefix: "/api" });

    // 设置cookie加密秘钥
    app.keys = ["some secret", "another secret"];

    const goods = [
    { id: 1, text: "商品1", price: 1000 },
    { id: 2, text: "商品2", price: 1000 }
    ];

    // 配置路由
    // 获取产品列表
    router.get("/goods", ctx => {
    ctx.body = {
        ok: 1,
        goods
    };
    });

    // 产品详情
    router.get("/detail", ctx => {
    ctx.body = {
        ok: 1,
        data: goods.find(good => good.id == ctx.query.id)
    };
    });

    // 登录
    router.post("/login", ctx => {
    const user = ctx.request.body;
    if (user.username === "jerry" && user.password === "123") {
        // 将token存入cookie
        const token = 'a mock token';
        ctx.cookies.set('token', token);
        ctx.body = { ok: 1, token };
    } else {
        ctx.body = { ok: 0 };
    }
    });

    // 解析post数据并注册路由
    app.use(bodyparser());
    // 注册路由
    app.use(router.routes());

    app.listen(8080, () => console.log('api服务已启动'))

整合axios

安装@nuxt/axios模块:

npm install @nuxtjs/axios -s
配置:nuxt.config.js
    const pkg = require('./package')
    module.exports = {
        mode: 'universal',
        // router配置
        router: {},
        /*
        ** Headers of the page
        */
        head: {},
        /*
        ** Customize the progress-bar color
        */
        loading: { color: '#fff' },
        /*
        ** Global CSS
        */
        css: [],
        /*
        ** Plugins to load before mounting the App
        */
        plugins: [],
        /*
        ** Nuxt.js modules
        */
        modules: [
            '@nuxtjs/axios'
        ],
        axios: {
            proxy: true
        },
        proxy: {
            "/api": "http://localhost:8080"
        },
        build: {}
    }

页面异步请求数据示例:

<template>
  <div>
    <h2>商品列表</h2>
    <ul>
      <li v-for="good in goods" :key="good.id">
        <nuxt-link :to="`/detail/${good.id}`">
          <span>{{good.text}}</span>
          <span>{{good.price}}</span>
        </nuxt-link>
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  data() {
    return {
      goods: []
    }
  },
  async asyncData({ $axios, error }) {
    const { ok, goods } = await $axios.$get("/api/goods");
    if (ok) {
      // 此处返回的数据会和data进行合并
      return {
        goods
      };
    }
    // 错误处理
    error({statusCode: 400, message: '数据查询失败请重试~'})
  }
};
</script>

7. 中间件
  中间件会在一个页面或一组页面渲染之前运行我们定义的函数,常用于权限控制、校验等任务。 范例代码:管理员页面保护,创建middleware/auth.js

export default function({ route, redirect, store }) {
// 上下文中通过store访问vuex中的全局状态
	// 通过vuex中令牌存在与否判断是否登录
	if (!store.state.user.token) {
	    redirect("/login?redirect="+route.path);
	}
}

局部注册中间件,admin.vue

<template>
    <div>
        <h2>admin page</h2>

    </div>
</template>

<script>
    export default {
        middleware: ['auth']
    };
</script>

全局注册中间件:

module.exports = {
  mode: 'universal',

  // router配置
  router: {
    extendRoutes(routes, resolve) {
      routes.push({
        path: '/foo',
        component: resolve(__dirname, 'pages/othername.vue')
      })
    },
     middleware: ['auth']
  },
}

8、状态管理VUEX
  应用根目录下如果存在store目录,Nuxt.js将启用vuex状态树。定义各状态树时具名到处state,mutations,getters,actions即可。 范例:用户登录及登录状态保存,创建store/user.js

 export const state = () => ({
        token: ''
    });

    export const mutations = {
        init(state, token) {
            state.token = token;
        }
    };

    export const getters = {
        isLogin(state) {
            return !!state.token;
        }
    };

    export const actions = {
        login({ commit, getters }, u) {
            return this.$login(u).then(({ token }) => {
            if (token) {
                commit("init", token);
            }
            return getters.isLogin;
            });
        }
    };

9、插件
  Nuxt.js会在运行应用之前执行插件函数,需要引入或设置Vue插件、自定义模块和第三方模块时特别有用。 范例代码:接口注入,引用插件机制将服务接口注入组件实例、store实例中,创建plugins/api-inject.js

 // 参数1上下文
    // 参数2注入函数
    export default ({ $axios }, inject) => {
        // 将来this.$login
        inject("login", user => {
            return $axios.$post("/api/login", user);
        });
    };

注册插件,nuxt.config.js

plugins: [
    '@/plugins/api-inject',
],

范例:添加请求拦截器附加token,创建plugins/interceptor.js

 export default function({ $axios, store }) {
        // onRequest是@nuxtjs/axios模块提供的帮助方法
        $axios.onRequest(config => {
            // 附加令牌
            if (store.state.user.token) {
            config.headers.Authorization = "Bearer " + store.state.user.token;
            }
            return config;
        });
    }

nuxtServerInit 通过在store的根模块中定义nuxtServerInit方法,Nuxt.js调用它的时候会将页面的上下文对象作为第2个参数传给他。当我们想将服务端的一些数据传到客户端时,这个方法非常好用。 范例:登录状态初始化,store/index.js

 export const actions = {
        nuxtServerInit({ commit }, { app }) {
            // nuxt-universal-cookie用法如下
            // app是server实例也就是koa实例
            const token = app.$cookies.get("token");
            // 表名是登录用户
            if (token) {
                console.log("nuxtServerInit: token:"+token);
                commit("user/init", token);
            }
        }
    };

安装依赖模块:cookie-universal-nuxt

npm i -S cookie-universal-nuxt

注册,nuxt.config.js

modules:["cookie-universal-nuxt"]

nuxtServerInit只能写在store/index.js
nuxtServerInit仅在服务端执行

10、发布部署
服务端渲染应用部署

先进行编译构建,然后再启动Nuxt服务

npm run build
npm start

生成内容在.nuxt/dis中

静态应用部署

Nuxt.js可依据路由配置将应用静态化,使得我们可以将应用部署至任何一个静态站点主机服务商。

npm run generate

注意渲染和接口服务器都需要处于启动状态

生成内容再dist中

11、Nuxt.js demo
Nuxt demo’s github地址 https://github.com/ajunc/nuxt-demo

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值