乐趣区

关于nuxt.js:Nuxt学习笔记

常识脉络

Nuxt.js 根底
简介
Nuxt.js 是一个基于 Vue.js 的第三方开源服务端渲染利用框架
它能够帮咱们轻松的实现同构利用
通过对客户端 / 服务端基础架构的形象组织,Nuxt.js 次要关注的是利用的 UI 渲染
咱们的指标是创立一个灵便的利用框架,你能够基于它初始化新我的项目的根底构造代码,或者在已有 Node.js 我的项目中应用 Nuxt.js
Nuxt.js 预设了利用 Vue.js 开发服务端渲染的利用所须要的各种配置,相似于脚手架生成了我的项目的根本框架
提供了一种命令叫:nuxt generate,为基于 Vue.js 的利用提供生成对应的动态站 点的性能,是向开发集成各种微服务(Microservices)的 Web 利用迈开的新一 步
作为框架,Nuxt.js 为 客户端 / 服务端 这种典型的利用架构模式提供了许多有用的个性,例如异步数据 加载、中间件反对、布局反对等十分实用的性能

Nuxt 框架是如何运作的
Nuxt.js 集成了以下组件 / 框架,用于开发残缺而弱小的 Web 利用:

Vue.js
Vue Router
Vuex
Vue Server Renderer
压缩并 gzip 后,总代码大小为:57kb(如果应用了 Vuex 个性的话为 60kb)。

另外,Nuxt.js 应用 Webpack 和 vue-loader、babel-loader 来解决代码的自动化构建工作(如打包、代码分层、压缩等等)。

NuxtJS 个性
基于 Vue.js
Vue、Vue Router、Vuex、Vue SSR

主动代码分层
服务端渲染
弱小的路由性能,反对异步数据
动态文件服务
ES2015+ 语法反对
打包和压缩 JS 和 CSS
HTML 头部标签治理
本地开发反对热加载
集成 ESLint
反对各种款式预处理器:SASS、LESS、Stylus 等等
反对 HTTP/2 推送

NuxtJS 渲染流程
下图论述了 Nuxt.js 利用一个残缺的服务器申请到渲染(或用户通过 <nuxt-link> 切换路由渲染页面)的流程:

根本应用
NuxtJS 应用形式
初始我的项目,从零开始的形式
已有的 Node.js 服务端我的项目的形式
间接把 Nuxt 当作一个中间件集成到 Node Web Server 中

现有的 Vue.js 我的项目根底上减少 Nuxt 的形式
十分相熟 Nuxt.js
至多百分之 10 的代码改变

初始化 NuxtJS
Nuxt 提供了两种形式用来创立我的项目:

官网文档
应用 create-nuxt-app 脚手架工具
手动创立

上面以手动创立为例
筹备

# 创立示例我的项目
mkdir nuxt-app-demo
​
# 进入示例我的项目目录中
cd nuxt-app-demo
​
# 初始化 package.json 文件
npm init -y
​
# 装置 nuxt
npm install nuxt
在 package.json 文件的 scripts 中新增:"scripts": {"dev": "nuxt"}

下面的配置使得咱们能够通过运行 npm run dev 来运行 nuxt。

创立页面并启动我的项目
创立 pages 目录:

mkdir pages
创立咱们的第一个页面的 pages/index.vue:

<template>
<h1>Hello world!</h1>
</template>
而后启动我的项目:

npm run dev
这样就在 http://localhost:3000 上运行了
留神:Nuxt.js 会监听 pages 目录中的文件更改,因而在增加新页面时无需重新启动应用程序。

Nuxt 中的根底路由
Nuxt.js 会依据 pages 目录中的所有 *.vue 文件生成利用的路由配置,也就是路由的嵌套体现在文件夹之间的嵌套
根目录新建.gitignore 文件用来排出不须要用 git 托管的文件
node_modules 和.nuxt 文件都须要被忽视

Nuxt 目录构造

NuxtJS 路由
Nuxt.js 根据 pages 目录构造主动生成 vue-router 模块的路由配置,page 就相当于 /,从 page 外部开始写
根底路由
Nuxt.js 官网文档
假如 pages 的目录构造如下:

pages/
–| user/
—–| index.vue
—–| one.vue
–| index.vue
那么,Nuxt.js 主动生成的路由配置如下:


router: {
  routes: [
    {
      name: 'index',
      path: '/',
      component: 'pages/index.vue'
    },
    {
      name: 'user',
      path: '/user',
      component: 'pages/user/index.vue'
      //  那这个对应的路由地址就是 http://localhost:3000/user/index.vue
    },
    {
      name: 'user-one',
      path: '/user/one',
      component: 'pages/user/one.vue'
    }
  ]
}

路由导航
a 标签
它会刷新整个页面,走服务端渲染,不要应用

nuxt-link 组
编程式导航
https://router.vuejs.org/zh/g…



<template>
  <div>
    <h1>About page</h1>
    <!-- a 链接,刷新导航,走服务端渲染 -->
    <h2>a 链接 </h2>
    <a href="/"> 首页 </a>
​
    <!-- router-link 导航链接组件 -->
    <h2>router-link</h2>
    <router-link to="/"> 首页 </router-link>
​
    <!-- 编程式导航 -->
    <h2> 编程式导航 </h2>
    <button @click="onClick"> 首页 </button>
  </div>
</template>
​
<script>
export default {
  name: 'AboutPage',
  methods: {onClick () {this.$router.push('/')
    }
  }
}
</script>

动静路由
官网文档
Vue Router 动静路由
Nuxt.js 动静路由
动静路由指的是门路时动静的,门路中的某一部分是动态变化的、能够是任意的,须要用:进行表征,应用?能够定义其是否是必要项
在 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?',  
      // 这个就能够匹配到 /users/* 的门路来展现以后组件 * 为任意,? 示意以后这个字段是可选的
      // 并且如果想要拿到动静路由这个传递的参数能够应用 $route.params.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'
    }
  ]
}


// pages/user/_id.vue

<template>
  <div>
    <h1>User Pages</h1>
  </div>
</template>

<script>
export default {name: 'UserPage'}
</script>
应用参数时间接通过 $route.params.xx 的模式来获取

<template>
  <div>
    <h1>User Pages</h1>
    <h2>{{$route.params.id}}</h2>
  </div>
</template>

嵌套路由
官网文档
Vue Router 嵌套路由
Nuxt.js 嵌套路由

你能够通过 vue-router 的子路由创立 Nuxt.js 利用的嵌套路由。
创立内嵌子路由,你须要增加一个 Vue 文件,【同时增加一个与该文件同名的目录用来寄存子视图组件】
留神辨别父子路由和父子组件的区别
Warning: 别忘了在父组件 (.vue 文件) 内减少 <nuxt-child/> 用于显示【子视图】内容。子视图对应子路由,定义在父路由组件的 nuxt-child 路由进口组件中

假如文件构造如:

pages/
–| users/ // 与 users 同名的目录来寄存 users 的子路由组件
—–| _id.vue
—–| index.vue
–| users.vue // 父路由
Nuxt.js 主动生成的路由配置如下:


router: {
  routes: [
    {
      path: '/users',
      component: 'pages/users.vue',
      children: [
        {
          path: '',
          component: 'pages/users/index.vue',
          name: 'users'
        },
        {
          path: ':id',
          component: 'pages/users/_id.vue',
          name: 'users-id'
        }
      ]
    }
  ]
}
自定义路由
官网文档
新增一个 nuxt.config.js 文件进行配置
// nuxt.config.js

/**
 * Nuxt.js 配置文件
 */

module.exports = {
  router: {base: '/abc'}
}

// 留神,这样配置后路由地址都应该以 /abc 结尾:http://localhost:3000/abc/...
// 要是拜访首页的话就是 abc/,开端的 / 不能省略


如果不想配置文件,也能够应用 extendRoutes 办法
module.exports = {
  router: {
    base: '/abc',
    // routes: 一个数组,路由配置表
    // resolve: 解析路由组件门路
    extendRoutes(routes, resolve) {
      routes.push({
        name: 'hello',
        path: '/hello',
        component: resolve(__dirname, 'pages/about.vue')
      })
    }
  }
}

NuxtJS 视图
概述

在 NuxtJS 中页面构造个别由三局部组成:

第一局部是最外层的文档页面,也就是单页面或者说服务端渲染的 HTML 页面。
在 HTML 页面外面包裹着 Layout 布局组件(可选),相当于所有页面的父路由。
再往里面是页面组件,每个页面组件有本人额定的成员办法,包含页面的子组件之类的可选内容。
模板 (上图最外层 HTML file)
你能够定制化 Nuxt.js 默认的利用模板。

定制化默认的 html 模板,只须要在 src 文件夹下(默认是利用根目录)创立一个 app.html 的文件。


默认模板为:<!DOCTYPE html>
<html {{HTML_ATTRS}}>
  <head {{HEAD_ATTRS}}>
    {{HEAD}}
  </head>
  <body {{BODY_ATTRS}}>
    {{APP}}  <!-- 这 body 里就是渲染视图的地位,这个 app 就是根组件 -->
  </body>
</html>
布局(Layout)默认是所有页面组件的父路由
Nuxt.js 容许你扩大默认的布局,或在 layout 目录下创立自定义的布局。可通过增加 layouts/default.vue 文件来扩大利用的默认布局。提醒: 别忘了在布局文件中增加 <nuxt/> 组件用于显示页面的主体内容。新增 layouts 时须要重启我的项目
相当于一个更简便的嵌套路由
默认布局的代码如下:<template>
  <nuxt />
</template>
// layouts/default.vue  默认布局
// 一旦应用 default 默认布局,默认所有页面都会作用,不能取消,只能更改
// 除非在别的组件应用时指定 layout:'foo' 也就是指定别的组件作为布局组件,也就是 foo 会代替 default

<template>
  <div>
    <h1>layouts/default.vue 组件 </h1>
    <!-- 页面进口,相似于子路由进口,是另一种匹配规定,而不是走路由规定 -->
    <nuxt />
  </div>
</template>

<script>
export default {name: 'LayoutDefault'}
</script>
而后咱们必须通知页面 (即 pages/index.vue) 应用您的自定义布局:<template>
  <h1>Hello world!</h1>
</template>

<script>
export default {
  name: 'HomePage',
  // 默认 default 可批改
  layout: 'default'   // 示意应用默认布局应用的是 layouts 文件夹上面的 default 组件
}
</script>

NuxtJS 异步数据
Nuxt.js 扩大了 Vue.js,减少了一个叫 asyncData 的办法,使得咱们能够在设置组件的数据之前能异步获取或解决数据。
官网文档
外围就是 asyncData 办法
流程示例


首先定义一个数据文件:// static/data.json

{
  "posts": [
    {
      "id": 1,
      "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
      "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
    },
    {
      "id": 2,
      "title": "qui est esse",
      "body": "est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla"
    },
    {
      "id": 3,
      "title": "ea molestias quasi exercitationem repellat qui ipsa sit aut",
      "body": "et iusto sed quo iure\nvoluptatem occaecati omnis eligendi aut ad\nvoluptatem doloribus vel accusantium quis pariatur\nmolestiae porro eius odio et labore et velit aut"
    },
    {
      "id": 4,
      "title": "eum et est occaecati",
      "body": "ullam et saepe reiciendis voluptatem adipisci\nsit amet autem assumenda provident rerum culpa\nquis hic commodi nesciunt rem tenetur doloremque ipsam iure\nquis sunt voluptatem rerum illo velit"
    },
    {
      "id": 5,
      "title": "nesciunt quas odio",
      "body": "repudiandae veniam quaerat sunt sed\nalias aut fugiat sit autem sed est\nvoluptatem omnis possimus esse voluptatibus quis\nest aut tenetur dolor neque"
    }
  ],
  "title": "拉勾教育"
}
NuxtJS 默认在 Web 服务中将数据放到 static 目录中,能够间接通过门路来获取数据,并且是将 static 中的文件裸露到根文件夹下,能够不写 static 门路字段
http://localhost:3000/data.json  
// static 字段不必写


假如想在首页间接获取数据(服务端获取异步数据):首先要装置 axios 来发送申请


npm i axios
// pages/index.vue

<template>
  <div>
    <h1>{{title}}</h1>
  </div>
</template>

<script>
import axios from 'axios'

export default {
  name: 'HomePage',
  layout: 'default',
  async asyncData () {console.log('asyncData')
    console.log(this)
    const res = await axios({
      method: 'GET',
      url: 'http://localhost:3000/data.json'
    })
    return res.data
  },
  data () {
    return {foo: 'bar'}
  }
}
</script>
通过 asyncData 失去的数据将和组件本来的 data 文件进行混合


如果咱们尝试在 asyncData 外面进行 console.log (‘xxx’) 时,会发现在服务端的控制台输入 xxx 的同时,nuxt 为了不便调试和更加直观,会让客户端也会输入 xxx,并且客户端的 xxx 是包裹在 Nuxt SSR 对象中的,如果尝试打印 this 会发现是 undefined,因为服务端渲染时组件还没初始化。

asyncData 办法除了会在服务端渲染期间来运行,还会在客户端路由导航之前运行
实例:


// pages/index.vue

<template>
  <div>
    <h1>{{title}}</h1>
    <nuxt-link to="/about">About</nuxt-link>
  </div>
</template>

<script>
import axios from 'axios'

export default {
  name: 'HomePage',
  layout: 'default',
  async asyncData () {console.log('xxx')
    const res = await axios({
      method: 'GET',
      url: 'http://localhost:3000/data.json'
    })
    return res.data
  },
  data () {
    return {foo: 'bar'}
  }
}
</script>

先通过地址拜访首页,会发现客户端控制台打印 Nuxt SSR 上面输入了 xxx,阐明首屏是在服务端渲染的,而后输入了 xxx。紧接着通过 about 路由导航链接跳转到 about 页面,而后 about 页面中也有个导航链接能够跳转到首页,此时跳转回首页时也会调用首页组件 index.vue 中的 asyncData 办法,此时也会打印 xxx,然而这时候的 xxx 外层没有 Nuxt SSR 对象了,起因是这次导航跳转引起的调用是在客户端执行的

首屏服务端渲染数据好了解,间接拿到数据返回并返回了 SPA 页面脚本,然而如果客户端执行 SPA 程序时,不执行 asyncData 的话,将会导致数据无奈更新,所以当咱们通过导航链接进行跳转时也会在客户端进行一次调用 asyncData

留神,asyncData 只能在页面组件中应用,不能在非页面组件(比方页面组件的子组件)中应用,非页面组件中不会调用 asyncData 函数
如果想在子组件应用服务端渲染时的数据,只能通过页面组件进行获取,而后再利用父子组件中的传值办法来传递给页面组件的子组件
实例如下:


页面组件的子组件:// components/Foo.vue

<template>
  <div>
    <h1>FooPage</h1>
    <ul>
      <li
        v-for="item in posts"
        :key="item.id"
      >
      {{item.title}}
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  name: 'FooPage',
  props: ['posts'],
  // asyncData 只能在页面组件中应用
  // async asyncData () {//   console.log('foo asyncData')
  //   return {
  //     foo: 'bar'
  //   }
  // }
}
</script>


页面组件
// pages/index.vue
<template>
  <div>
    <h1>{{title}}</h1>
    <nuxt-link to="/about">About</nuxt-link>
    <br>
    <foo :posts="posts" />    
  </div>
</template>

<script>
import axios from 'axios'
import Foo from '@/components/Foo'

export default {
  name: 'HomePage',
  layout: 'default',
  components: {Foo},
  // 当你想要动静页面内容有利于 SEO
  // 或者是晋升首屏渲染速度的时候,// 就在 asyncData 中发申请拿数据
  async asyncData () {console.log('asyncData')
    console.log(this)
    const res = await axios({
      method: 'GET',
      url: 'http://localhost:3000/abc/data.json'
    })
    return res.data
  },

  // 如果是非异步数据或者一般数据,则失常的初始化到 data 中即可
  data () {
    return {foo: 'bar'}
  }
}
</script>

总结:

根本用法
它会将 asyncData 返回的数据交融组件 data 办法返回数据并一并给组件
调用机会:服务端渲染期间和客户端路由更新之前

注意事项
只能在页面组件中应用
没有 this,因为它是组件初始化之前被调用的

高低对象
假如这里给这些文章题目加了链接,心愿点击题目跳转到文章页面,所以筹备了 pages/article/_id.vue 详情页面(动静路由),这个路由将匹配所有的 pages/article/ 地址都将匹配到这个_id.vue 这个组件。而后拿到对应的 id 值(也就是 值),获取文章内容展现到页面。并且要使数据有利于 SEO,进步首屏渲染速度。
比方:http://localhost:3000/artical/5 这个门路就应该对应第五篇文章,那么问题是怎么获取这个 5 呢
首先咱们容易想到路由对象 this.$route.params,然而 asyncData 在服务端执行时是没有 this 的,也就是说 this 不指向以后 Vue 实例
所以咱们须要应用 asyncData 中的 context 上下文对象参数来获取,上下文对象中能够获取动静路由中的参数

// pages/article/_id.vue,依据门路中的最初一位 id 值的不同来拿到不同的对象,而后渲染页面中的不同内容


<template>
  <div>
    <!-- <h1>article page</h1> -->
    <h1>{{article.title}}</h1>
    <div>{{article.body}}</div>
  </div>
</template>

<script>
import axios from 'axios'

export default {
  name: 'ArticlePage',
  // asyncData 上下文对象
  async asyncData (context) {
    // 这里有咱们须要的数据
    console.log(context)
    const {data} = await axios({
      method: 'GET',
      url: 'http://localhost:3000/data.json'
    })
    // asyncData 外面没有 this
    // 不能通过这种形式获取 id
    // console.log(this.$route.params)

    // 能够通过上下文对象的 params.id 或者 router.params.id
    // 拿到后将字符串类型转换为数字类型
    // 依据门路中的最初一位 id 值的不同来拿到不同的对象,而后渲染页面中的不同内容
    const id = Number.parseInt(context.params.id)
    return {article: data.posts.find(item => item.id === id)
    }
  }
}
</script>
退出移动版