vue nuxt2 ssr服务端渲染

1,151 阅读6分钟

原本想直接使用nuxt3的,但是跟着官网写了写发现还不完善,而且官网也说了nuxt3要到6月份才会发布生产版本,暂时还选用nuxt2。

Nuxt目录结构

assets 资源目录

assets 目录包含您未编译的资产,例如 Stylus 或 Sass 文件、图像或字体

在您的 vue 模板中,如果您需要链接到您的 assets 目录,请~/assets/your_image.png在资产前使用斜杠。

使用例子:

<template>
  <img src="~/assets/your_image.png" />
</template>

background: url('~assets/banner.svg');

使用scss

yarn add --dev sass sass-loader@10

有报错:

this.getOptions is not a function

解决方法

sass-loader 版本太高和wabpack4 不兼容

yarn add sass-loader@10.1.1 -D

components 组件目录

components目录包含您的 Vue.js 组件。组件构成了页面的不同部分,可以重复使用并导入到页面、布局甚至其他组件中。nuxt会默认导入,再vue中直接使用。

动态导入

要动态导入组件,也称为延迟加载组件,您只需Lazy在模板中添加前缀即可。

<template>
  <div>
    <TheHeader />
    <Nuxt />
    <LazyTheFooter />
  </div>
</template>

使用惰性前缀,您还可以在触发事件时动态导入组件。

<template>
  <div>
    <h1>Mountains</h1>
    <LazyMountainsList v-if="show" />
    <button v-if="!show" @click="show = true">Show List</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      show: false
    }
  }
}
</script>

嵌套目录

如果您在嵌套目录中有组件,例如:

components/
  base/
      foo/
         CustomButton.vue

组件名称将基于其自己的路径目录和文件名。因此,组件将是:

<BaseFooCustomButton />

layouts 布局目录

当您想要更改 Nuxt 应用程序的外观和感觉时,布局非常有用。无论您是想包含侧边栏还是为移动设备和桌面提供不同的布局。

默认布局

layouts/default.vue 您可以通过添加文件来扩展主布局 。它将用于所有没有指定布局的页面。确保 在创建布局时添加组件以实际包含页面组件。

您在布局中只需要三行代码即可呈现页面组件。

<template>
  <Nuxt />
</template>

您可以在此处添加更多组件,例如导航、页眉、页脚等。

<template>
  <div>
    <TheHeader />
    <Nuxt />
    <TheFooter />
  </div>
</template>

自定义布局

目录中的每个文件(顶级) layouts 都将创建一个自定义布局,该布局可通过 layout 页面组件中的属性访问。

假设我们要创建一个博客布局并将其保存到 layouts/blog.vue:

<template>
  <div>
    <div>My blog navigation bar here</div>
    <Nuxt />
  </div>
</template>

然后你必须告诉页面使用你的自定义布局

<script>
export default {
  layout: 'blog',
  // OR
  layout (context) {
    return 'blog'
  }
}
</script>

错误页面 error.vue

错误页面是一个 页面组件 ,在发生错误时始终显示(不在服务器端抛出)。

虽然这个文件被放置在 layouts 文件夹中,但它应该被视为一个页面。

如上所述,此布局很特殊,您不应将其包含 在其模板中。您必须将此布局视为发生错误时显示的组件(404、 500等)。与其他页面组件类似,您也可以按照通常的方式为错误页面设置自定义布局。

您可以通过添加 layouts/error.vue 文件来自定义错误页面:

<template>
  <div class="container">
    <h1 v-if="error.statusCode === 404">Page not found</h1>
    <h1 v-else>An error occurred</h1>
    <NuxtLink to="/">Home page</NuxtLink>
  </div>
</template>

<script>
export default {
  props: ['error'],
  layout: 'blog' // you can set a custom layout for the error page
}
</script>

页面目录 pages

pages 目录包含您的应用程序视图和路由。Nuxt 读取 .vue 此目录中的所有文件并自动为您创建路由器配置。

每个 Page 组件都是一个 Vue 组件,但 Nuxt 添加了特殊的属性和功能,以使您的通用应用程序的开发尽可能简单。

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

<script>
  export default {
    // page properties go here
  }
</script>

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

动态页面

当您不知道页面名称时,可以创建动态页面,因为它来自 API,或者您不想一遍又一遍地创建相同的页面。要创建动态页面,如果您希望目录是动态的,则需要在 .vue 文件名或目录名称之前添加下划线。您可以为文件或目录命名任何您想要的名称,但您必须在其前面加上下划线。

如果您_slug.vue在 pages 文件夹中定义了一个名为的文件,则可以使用带有 params.slug 的上下文访问该值

<template>
  <h1>{{ this.slug }}</h1>
</template>

<script>
  export default {
    async asyncData({ params }) {
      const slug = params.slug // When calling /abc the slug will be "abc"
      return { slug }
    }
  }
</script>

如果您在名为_slug.vue的文件夹中定义了一个名为的文件,_book则可以使用上下文访问该值params.slugparams.book

<template>
  <h1>{{ this.book }} / {{ this.slug }}</h1>
</template>

<script>
  export default {
    async asyncData({ params }) {
      const book = params.book
      const slug = params.slug
      return { book, slug }
    }
  }
</script>

asyncData 请求数据

每次加载组件之前都会调用 asyncData。它可以是异步的并接收上下文作为参数。返回的对象将与您的数据对象合并。

export default {
  asyncData(context) {
    return { name: 'World' }
  }
}

fetch

每次需要获取异步数据时,都可以使用 fetch。渲染路由时在服务器端调用 Fetch,在导航时在客户端调用。

<script>
  export default {
    data() {
      return {
        posts: []
      }
    },
    async fetch() {
      this.posts = await fetch('https://api.nuxtjs.dev/posts').then(res =>
        res.json()
      )
    }
  }
</script>

head

为当前页面设置特定 标签。Nuxt 用于vue-meta更新应用程序的文档头和元属性。

export default {
  head() {
     title: 'Home page',
      meta: [
        {
          hid: 'description',
          name: 'description',
          content: 'Home page description'
        }
      ],
    }
}

布局

指定布局目录中定义的布局。也就是说不使用默认布局

export default {
  layout: 'blog'
}

加载

如果设置为 false,则防止页面在this.nuxt.nuxt.loading.finish() 您进入和 this.nuxt.nuxt.loading.start() 离开时自动调用,允许您手动控制行为,如 本示例 所示。

export default {
  loading: false
}

过渡

定义页面的特定转换。

export default {
  transition: 'fade'
}

滚动到顶部

该scrollToTop属性允许您告诉 Nuxt 在呈现页面之前滚动到顶部。默认情况下,当您转到另一个页面时,Nuxt 会滚动到顶部,但是对于子路由,Nuxt 会保持滚动位置。如果你想告诉 Nuxt 在渲染你的子路由时滚动到顶部,设置 scrollToTop 为 true

export default {
  scrollToTop: true
}

中间件

定义此页面的中间件。在渲染页面之前将调用中间件。

export default {
  middleware: 'auth'
}

plugins 插件目录

plugins 目录包含您要在实例化根 Vue.js 应用程序之前运行的 Javascript 插件。也就是我们之前写在main.js 中的使用第三方组件的 Vue.use

这是添加 Vue 插件和注入函数或常量的地方。每次需要使用 Vue.use()时,都应该在 in 中创建一个文件, plugins/ 并将其路径添加到 plugins in 中 nuxt.config.js。

使用npm 包 举例axios

首先安装

yarn add @nuxtjs/axios

例如,您可以配置 axios 拦截器以对跨应用程序的 API 调用可能出现的错误作出反应。在此示例中,当我们从 API 收到 500 状态错误时,我们将用户重定向到名为“抱歉”的自定义错误页面。

export default function ({ $axios, redirect }) {
  $axios.onError(error => {
    if (error.response.status === 500) {
      redirect('/sorry')
    }
  })
}

最后但同样重要的是,将模块和新创建的插件添加到项目配置中。

module.exports = {
  modules: ['@nuxtjs/axios'],
  plugins: ['~/plugins/axios.js']
}

然后我们可以直接在你的页面组件中使用它:

<template>
  <h1>{{ post.title }}</h1>
</template>

<script>
export default {
    async asyncData ({ $axios, params }) {
      const  post  = await $axios.$get(`https://api.nuxtjs.dev/posts/${params.id}`)
      return { post }
    }
}
</script>

不安装模块的另一种使用方法是直接在标签中axios导入。axios

<script>
import axios from 'axios'

export default {
    async asyncData ({ params }) {
      const { data: post }  = await axios.get(`https://api.nuxtjs.dev/posts/${params.id}`)
      return { post }
    }
}
</script>

static 静态目录

static 目录直接映射到服务器根 () 并包含可能不会更改的文件。 所有包含的文件都将由 Nuxt 自动提供,并可通过您的项目根 URL 访问。


/static/robots.txt 将在http://localhost:3000/robots.txt

/static/favicon.ico 将在 http://localhost:3000/favicon.ico

此选项对 robots.txt, sitemap.xml 或 CNAME (这对于 GitHub Pages 部署很重要)之类的文件很有帮助。

\

如果您不想使用 assets 目录中的 Webpack 资产,可以将图像添加到静态目录。

然后,在您的代码中,您可以相对于根 ( /) 引用这些文件:

<!-- Static image from static directory -->
// static 用法
<img src="/my-image.png" />

<!-- webpacked image from assets directory -->
// assets 用法
<img src="~/assets/my-image-2.png" />

store vuex目录

store 目录包含您的 Vuex Store 文件。Vuex Store 自带 Nuxt,但默认禁用。在此目录中创建index.js 文件启用存储。

Nuxt 生命周期

nuxt 中可以使用的vue生命周期 只有 beformCreate created,记住nuxt中没用windos对象,无法操作dom元素。

@nuxt/axios 使用

安装

将@nuxtjs/axios依赖项添加到您的项目:

yarn add @nuxtjs/axios

然后将其添加到modules您的部分nuxt.config.js:

 modules: ['@nuxtjs/axios']

配置

向您添加一个axios对象以nuxt.config.js配置将应用于所有请求的全局选项:

export default {
  modules: [
    '@nuxtjs/axios',
  ],

  axios: {
    // proxy: true
  }
}

用法

asyncData

async asyncData({ $axios }) {
  const ip = await $axios.$get('http://icanhazip.com')
  return { ip }
}

$

Axios 插件还支持带有$前缀方法的快捷方式来直接获取数据:

// Normal usage with axios
let data = (await $axios.get('...')).data

// Fetch Style
let data = await $axios.$get('...')

环境变量

如果在生产环境中使用环境变量,则必须使用运行时配置,否则,这些值将在构建期间被硬编码并且不会改变。

支持的选项:

  • baseURL
  • browserBaseURL
export default {
  modules: [
    '@nuxtjs/axios'
  ],

  axios: {
    baseURL: 'http://localhost:4000', // Used as fallback if no runtime config is provided
  },

  publicRuntimeConfig: {
    axios: {
      browserBaseURL: process.env.BROWSER_BASE_URL
    }
  },

  privateRuntimeConfig: {
    axios: {
      baseURL: process.env.BASE_URL
    }
  },
}

https

  • 默认:false

如果设置为true,http://在两者中baseURL和browserBaseURL都会更改为https://.

retry

  • 默认:false

使用axios-retry自动拦截失败的请求并在可能的时候重试它们。

默认情况下,重试次数将为3 次,如果retry值设置为true. 您可以通过传递带有内联重试子选项的选项来更改它,如下所示:

axios: {
  retry: { retries: 3 }
}

代理 proxy

需要安装依赖

yarn add @nuxtjs/proxy

记得要添加到modules

{
  modules: [
    '@nuxtjs/axios'
  ],

  axios: {
    proxy: true // Can be also an object with default options
  },

  proxy: {
    '/api/': 'http://api.example.com',
    '/api2/': 'http://api.another-website.com'
  }
}

注意: 不需要手动注册@nuxtjs/proxy模块,但它确实需要在您的依赖项中。

注意: 在代理模块中,/api/会将所有请求添加到 API 端点。如果您需要删除它,请使用以下 pathRewrite选项:

proxy: {
  '/api/': { target: 'http://api.example.com', pathRewrite: {'^/api/': ''} }
}

示例代码

nuxt.config.js

<template>
  <div>
    新闻列表页面

    <ul>
      <li>
        <nuxt-link :to="{ path: '/news/3223231' }">新闻1</nuxt-link>
      </li>
    </ul>
    <br />
    <div>
      ssr 服务端渲染的数据
      <ul>
        <li v-for="item of dataList" :key="item.id" @click="gotoPage(item)">
          <h1>{{ item.title }}</h1>
          <p>{{ item.description }}</p>
          <img :src="item.image" alt="" />
        </li>
      </ul>
    </div>
    <br />
    <div>
      客服端渲染的数据
      <!-- <ul>
        <li v-for="item of dataList" :key="item.id">
          <h1>{{ item.title }}</h1>
          <p>{{ item.description }}</p>
          <img :src="item.image" alt="" />
        </li>
      </ul> -->
    </div>
  </div>
</template>
<script>
export default {
  name: 'NewsPage',
  async asyncData({ params, $axios }) {
    const dataList = await $axios.$get(`https://api.nuxtjs.dev/posts`)
    return { dataList }
  },
  data() {
    return {
      list: [],
    }
  },
  mounted() {
    this.$axios.$get('/api/posts').then((res) => {
      this.list = res
    })
  },
  methods: {
    gotoPage(item) {
      this.$router.push({
        path: `/news/${item.id}`,
      })
    },
  },
}
</script>
<style lang="scss"></style>

pages/news

<template>
  <div>
    新闻列表页面

    <ul>
      <li>
        <nuxt-link :to="{ path: '/news/3223231' }">新闻1</nuxt-link>
      </li>
    </ul>
    <br />
    <div>
      ssr 服务端渲染的数据
      <ul>
        <li v-for="item of dataList" :key="item.id" @click="gotoPage(item)">
          <h1>{{ item.title }}</h1>
          <p>{{ item.description }}</p>
          <img :src="item.image" alt="" />
        </li>
      </ul>
    </div>
    <br />
    <div>
      客服端渲染的数据
      <!-- <ul>
        <li v-for="item of dataList" :key="item.id">
          <h1>{{ item.title }}</h1>
          <p>{{ item.description }}</p>
          <img :src="item.image" alt="" />
        </li>
      </ul> -->
    </div>
  </div>
</template>
<script>
export default {
  name: 'NewsPage',
  async asyncData({ params, $axios }) {
    const dataList = await $axios.$get(`https://api.nuxtjs.dev/posts`)
    return { dataList }
  },
  data() {
    return {
      list: [],
    }
  },
  mounted() {
    this.$axios.$get('/api/posts').then((res) => {
      this.list = res
    })
  },
  methods: {
    gotoPage(item) {
      this.$router.push({
        path: `/news/${item.id}`,
      })
    },
  },
}
</script>
<style lang="scss"></style>

pages/news/_id.vue

<template>
  <div>
    新闻详情页面 {{ data.id }}
    <h1>{{ data.title }}</h1>
    <h2>{{ data.description }}</h2>
    <h3>height:{{ data.height }}</h3>
    <img :src="data.image" alt="" />
  </div>
</template>
<script>
export default {
  name: 'DetailPage',
  async asyncData({ params, $axios }) {
    const data = await $axios.$get(`https://api.nuxtjs.dev/posts/${params.id}`)
    return { data }
  },
}
</script>
<style lang="scss"></style>