说明

本教程根据YouTube的大神Vue Mastery的教程《Scaling Vue with Nuxt.js》;有兴趣可以油管搜索一下,b站有人搬运,并且带上了机翻,还可以

b站搬运

建议配合食用,毕竟,这个是我个人的笔记。

简介

nuxt是一个vue的一个类似cli框架的东西,用于处理vue无法做到的后端渲染,简单点来说就是vue的写法,服务端进行渲染成静态网页。

目前有三个这种后端渲染框架:next、nuxt、nest;分别对应目前前端三种框架:React、Vue、Angular,作为国内的话,可能国内nuxt是用的比较多的,虽然这是一个由国外开发者发布的一个工具。

静态的页面优势就是在于seo的优化,在三款后端渲染框架里面,nuxt的seo得分是最高的,但是同时,性能也是最差的,但是差也没办法,没得选。233

创建一个nuxt项目

首先如果想使用npx命令,那么npm的版本不能低于5.2.0,之后的新版本,安装npm的同时自带了npx,当然,我们这里将会使用yarn,毕竟,这是一个非常好的包管理器。

//npx
npx create-nuxt-app <项目名>

//yarn
yarn create nuxt-app <项目名>

创建项目是会让你选择你的后端渲染框架,koa这些,前端渲染框架也有,目前没用到,可以直接选择none,然后就是一堆其他设置,下面是我的一些选择

完毕后我们通过vscode打开这个创建的项目目录,运行脚本dev,此时会提示你是否愿意提交匿名数据给nuxt,可以y也可以n,自己选择,回车后nuxt就会运行项目,然后会出现一条localhost地址段,我们复制从浏览器打开,一个简单的nuxt项目就创建完成了。

目录结构理解

打开项目可以看到如下的目录结构,nuxt给我们预设好了目录结构,我们只需要在正确的目录下书写正确的代码,就能快速生成静态网页。

  1. components目录 --- 用于存放vue的组件目录
  2. layouts目录 --- 用于存放页面布局文件
  3. pages目录 --- 顶级视图的页面目录,每个vue文件都对应生成相应的html文件
  4. store目录 --- vuex
  5. static目录 --- 静态文件,roboot.txt或者favicon
  6. assets目录 --- 也是存放文件,一般为stylus,sass,images,fonts
  7. plugins目录 --- 用于存放一些js的库或方法
  8. middleware目录 --- 中间件目录
  9. nuxt.config.js --- nuxt的配置文件

如果需要在vue文件中引用assets中的logo.png文件,可以在地址前加上波浪号:~

<img src="~/assets/logo.png"/>

nuxt使用了vue-loader,file-loader,url-loader解析器,所以,当图片文件大于1kb时,文件名会被转为hash名,hash名的好处是,如果图片名相同,而文件已经发生了变化,对应的hash值也会发生变化,那么浏览器就会重新加载该图片,如果只使用图片名的话,名字相同,并不会触发浏览器的更新。

<img src="~/assets/sadaghgqz.png"/>

大概是这么一个意思。

如果图片小于1kb,那么就会转为base64,这个就vue项目常用的一个功能了。用于节省下载请求数。

nuxt有很多智能的设置,如路由,我们不需要手动编写路由,nuxt会自动扫描pages目录,然后自动生成对应路由结构。

简单的修改

在pages目录下创建一个vue文件,取名:create.vue,内容如下:

<template>
  <div class="create">
    <h1>新建页面</h1>
  </div>
</template>

修改pages目录下的index.vue文件,改为如下:

<template>
  <div class="container">
    <h1>首页</h1>
  </div>
</template>

<script>
export default {};
</script>

<style>
</style>

在components目录下删除logo组件,新增一个NavBar.vue文件,内容如下:

<template>
  <div class="navbar">
    <nuxt-link to="/">首页logo</nuxt-link>
    <nav>
      <nuxt-link to="/">首页</nuxt-link>
      <nuxt-link to="/create">新建页面</nuxt-link>
    </nav>
  </div>
</template>

然后在layouts目录下,修改默认布局文件default.vue,内容如下:

<template>
  <div>
    <NavBar />
    <Nuxt />
  </div>
</template>
<script>
import NavBar from "~/components/NavBar";
export default {
  components: {
    NavBar,
  },
};
</script>
<style>
html {
  box-sizing: border-box;
}

*,
*::before,
*::after {
  box-sizing: border-box;
  margin: 0;
}
</style>

保存后可能会报错,建议修改下eslint的配置文件,打开项目根目录下的.eslintrc.js文件,在里面的rules对象中填写以下内容:

"prettier/prettier": "off",
"no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
"no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off",
"no-undef": "off",
"eol-last": "off",
"singleQuote": "off",
"semi": "off",
"trailingComma": "off",
"quotes": "off",
"semi": "off",
"comma-dangle": "off",
"space-before-function-paren": "off",
'vue/singleline-html-element-content-newline': 'off',
'vue/multiline-html-element-content-newline': 'off',
'vue/mustache-interpolation-spacing': 'off',

保存后,在从浏览器查看,可以看到如下效果:

点击首页和新建页面,可以进行路由跳转。

目前,一个简单的修改就完成了。

如果你想访问create.vue页面,大概是这样的一条地址段:

http://localhost:3000/create

nuxt的加载模式

单页应用的加载,往往首次加载是非常缓慢的,因为浏览器下载的html文件里面没有什么可展示的内容,所有的内容都需要通过js渲染出来,所以,在js文件没有下载下来之前,页面都是一直作为空白显示。

而nuxt会现在后端生成有内容的html文件,这个文件返回给浏览器渲染后,用户已经可以预览到了,然后再加载js文件,再初始化页面,最终成为一个单页应用。

预加载模式

nuxt有一个预加载模式,他会在你滚动到这个导航菜单时,开始下载导航菜单对应的页面js文件,然后当你点击导航时,文件差不多下载完成了,就可以很快的进入到对应的路由页。

快捷脚本

nuxt初始化后有几个快捷脚本指令,大概意思如下:

  1. dev --- 本地运行,开发用
  2. build --- 打包
  3. start --- 启动服务

一般流程是,先build,然后start运行服务。这样网站就可以看了。开发就直接运行dev

SEO

vue插件中有一个用于seo的插件:vue-meta;有的人可能已经用过了,有的可能不太了解,无所谓,nuxt中用法和插件用法略有不同,而且nuxt内置了vue-meta,所以无需再手动安装。

每个页面独立设置

打开index.vue文件,我们改动如下:

<template>
  <div class="container">
    <h1>首页</h1>
  </div>
</template>

<script>
export default {
  head() {
    return {
      title: "NUXT - 我是首页",
      meta: [
        {
          hid: "description",
          name: "description",
          content: "我是首页的简介",
        },
      ],
    };
  },
};
</script>

<style>
</style>

有一个head方法,return出一个对象,对象里面是键值对,这个head可以理解为html的head,他的里面有很多标签,seo中常用的就是title,meta标签,因为meta可以多个,所以他是一个数组。

meta数组中存放着一个个对象,其中有三个属性,name对应meta的name属性,content对应meta的content属性,hid则是唯一的id,为什么需要一个唯一的id,原因就是多个页面,页面之间肯定会有相同的meta标签,但是内容不同,加上nuxt加载完毕后本质上还是spa单页app,所以当路由页切换时,我们需要动态变化内容,既然要动态变化,那么一定要能拿到dom元素才行,hid就是作为获取dom元素的钥匙,所以,如果页面之间是description内容发生变化,那么两个页面的hid值也是要相同的。

使用该方式我们可以给每个页面都单独设置head,但是会有一个麻烦,因为有一部分内容是重复书写的,比如标题的一部分内容,前缀啥的,meta的标签,我们也可以设置一些默认值。

模板模式

使用模板可以减少重复书写代码,我们可以通过修改页面所所用的layout布局文件达到这个效果。

打开layouts中的default.vue文件,内容改动如下:

<template>
  <div>
    <NavBar />
    <div class="content">
      <Nuxt />
    </div>
  </div>
</template>
<script>
import NavBar from "~/components/NavBar";
export default {
  head() {
    return {
      titleTemplate: "NUXT - %s",
      meta: [
        {
          hid: "description",
          name: "description",
          content: "我是通用的简介",
        },
      ],
    };
  },
  components: {
    NavBar,
  },
};
</script>

title我们使用模板,所以他的key值由title改为titleTemplate,他接收一个关键字%s,表示其他页面设置的title内容,然后最终拼接成一个标题。

index.vue中改动一下标题:

 head() {
    return {
      title: "我是首页",
      meta: [
        {
          hid: "description",
          name: "description",
          content: "我是首页的简介",
        },
      ],
    };
  },

此时页面显示的内容就会根据预设的模板进行变化,title进行拼接,description则有就替换,没有就使用layout预设的值。

注意:

乍一看这个功能好像是js生成的,那么如果我们禁用js,页面的meta和title还会改变吗?

答案:会

因为当我们直接请求页面是,nuxt会现在后端生成html文件,此时html文件的一些内容就已经渲染好了,所以,即便你禁用了浏览器的js,但实际上这个功能依旧有效。比较用户手再长你也不能跑到后端来指手画脚。

其他

hid事实上并不是一个必填的选项,他是值用于更新同一个标签值所使用的,所以,在一些不需要动态变化的标签,或者预设的标签,可以不用这个hid

export default {
  head: {
    titleTemplate: '%s - Nuxt.js',
    meta: [
      { charset: 'utf-8' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' },

      // hid is used as unique identifier. Do not use `vmid` for it as it will not work
      { hid: 'description', name: 'description', content: 'Meta description' }
    ]
  }
}

这是官方的一个例子。

路由

四个问题:

  1. nuxt生成了什么样的路由结构
  2. 如何进行嵌套路由
  3. 如何使用动态路由参数
  4. 如何替换默认的错误页

nuxt生成了什么样的路由结构

事实上nuxt生成的路由并不是不可见的,我们可以展开.nuxt目录,旗下有一个router.js文件,这个文件就是nuxt生成的路由,我们可以通过查看这个路由文件来调整代码。

我截取其中路由结构代码展示:

 routes: [{
    path: "/create",
    component: _5246d105,
    name: "create"
  }, {
    path: "/",
    component: _437014d9,
    name: "index"
  }],

如何进行嵌套路由

如果我们想生成这样的结构:/event/create

只需要创建一个event文件夹,然后将create.vue文件拖放到该文件夹即可。

我们还可以在event文件夹创建index.vue文件,这个文件将在路径为/event时使用,也就是所谓的默认路由页了。

如何使用动态路由参数

vue中动态路由路径如下:path:"/event/:id"

如果我们想得到这个效果,可以创建一个_id.vue文件存放于event目录下,在nuxt中下划线代表了动态路由

_id.vue

<template>
  <div>
    我的动态路由参数:{{id}}
  </div>
</template>
<script>
export default {
  head() {
    return {
      title: "我是动态路由页标题",
      meta: [
        {
          hid: "description",
          name: "description",
          content: "我是动态路由页的简介",
        },
      ],
    };
  },
  computed: {
    id() {
      return this.$route.params.id;
    },
  },
};
</script>

这样就行了,如果我们想更复杂一点,路径如下:

{path: "/event/:id/create"}

动态路由参数是路径的一部分,这样做的话,我们只需要创建一个_id的文件夹即可,然后把create.vue文件移入该目录即可。

如何替换默认的错误页

替换默认的错误页,需要在layouts文件夹中创建一个error.vue文件。

这个文件的内容,我们可以参考官方的文件,路径在:.nuxt/components/nuxt-error.vue文件。

主要内容是:props、computed

props接收一个错误对象,computed里面有两个值,一个是网页状态码,一个错误的信息,其他的内容就可以自定义了。

注意:如果我们没有指定错误页所使用的模板,默认会使用layouts中的default.vue模板。

error.vue

<template>
  <div class="error">
    自定义错误页:{{message}}
  </div>
</template>
<script>
export default {
  props: {
    error: {
      type: Object,
      default: null,
    },
  },
  head() {
    return {
      title: this.message,
      meta: [
        {
          name: "viewport",
          content: "width=device-width,initial-scale=1.0,minimum-scale=1.0",
        },
      ],
    };
  },
  computed: {
    statusCode() {
      return (this.error && this.error.statusCode) || 500;
    },
    message() {
      return this.error.message || "Error";
    },
  },
};
</script>

大概是这么一个效果

事实上掘金网站也是用的nuxt,所以他的错误效果也是一样的布局

axios请求api

由于我们在初始化的项目的时候就已经选择了axios,所以我们不需要再手动安装axios库,那么如何使用axios呢?

注意:

如果初始化的时候没有选择axios,可以使用安装命令安装一下

yarn add @nuxtjs/axios

在此之前我们要明白nuxt是怎么请求api的。

api使用流程:

  1. 普通情况下,用户访问网页,浏览器发出请求,后端服务器找到对应的页面进行渲染,在渲染前会触发api请求,只有当api请求完毕后,得到数据,再将网页渲染好,返回给浏览器,浏览器渲染给用户看
  2. 当用户已经进入的页面后,进入下一个页面,此时api请求则是由浏览器发出。服务器将内容返回给浏览器,浏览器进行渲染,这里就是spa的效果范围了。

那么nuxt是怎么判断api是否存在,或者怎么去调用api呢?

nuxt在vue的生命周期中增加了很多钩子,其中就有一个名为asyncData(){}的生命周期。

我们将不再传统的created生命周期中做api请求,而是在asyncData中,并且该周期需要return出一个对象,这个对象最终会和vue上下文中的data对象合并,也就说,api获取的数据可以直接调用data的属性一样使用。

<template>
  <div class="container">
    <h1>首页</h1>
    {{apiData}}
  </div>
</template>

<script>
export default {
  asyncData({ $axios }) {
    return $axios.get("http://localhost:3000/db").then((res) => {
      return {
        apiData: res.data,
      };
    });
  },
  head() {
    return {
      title: "我是首页",
      meta: [
        {
          hid: "description",
          name: "description",
          content: "我是首页的简介",
        },
      ],
    };
  },
};
</script>

<style>
</style>

asyncData接收一个参数,这个参数里面常用的属性有:$axios,params,error,还有一些其他属性,常用就这三种。

通过$axios的请求,then回调处理,然后return出一个键值对对象,最终这个对象会和vue上下文的data属性合并,我们就可以直接调用apiData属性。

唯一需要注意的是一定要把整个axios请求先return出去,nuxt肯定是监听他的promise状态的。

效果图:

这里就不多说怎么做后端api返回了,这里是快速入门,知道nuxt的整个流程就是本教程的目的。

但是,这里我们只有成功的处理,如果api请求失败呢,怎么办?

请求失败或其他错误处理

asyncData({ $axios, error }) {
    return $axios
      .get("http://localhost:3000/db")
      .then((res) => {
        return {
          apiData: res.data,
        };
      })
      .catch((err) => {
        error({
          statusCode: 503,
          message: "服务端api请求发生错误" + err.message,
        });
      });
  },

其实也很简单,解构出error处理函数,对axios请求进行catch捕获,如果出现错误,我们调用error函数,传入一个对象,对象有两个键值对,分别对应自定义错误页中的两个computed属性,这个key值也都是约定俗成的。

当服务api请求发生错误后,会触发error方法,最终用户看到的,是通用的error页面。

如图:

async、await,nuxt进度条

我们都知道,async和await是异步改同步的方法,当我们滥用或者不得不对promise的回调进行多重嵌套的时候,那代码简直就是噩梦,维护性极差,所以我们可以通过async和await对代码进行改造。

async asyncData({ $axios, error }) {
    try {
      const { data } = await $axios.get("http://localhost:3000/db");
      return {
        apiData: data,
      };
    } catch (err) {
      error({
        statusCode: 503,
        message: "服务端api请求发生错误" + err.message,
      });
    }
  },

这样改造后,效果其实和上面那段是一样的,不过这种写法,在多个回调嵌套时非常有用。

并且这样写逻辑上更容易读懂。

这里我们再穿插一下 asyncData接收的参数content中的params属性,这个属性其实就是路由的params属性,所以,我们可以通过这个属性获取到动态路由参数。

async asyncData({ params }) {
  //params是路由的params属性   
},

nuxt进度条

nuxt进度条效果可以参考进度条插件nprogress,使用方式也很简单,打开nuxt.config.js文件,添加一条属性即可。

export default {
  //进度条
  loading: { color: "#39b982" }
}

保存即可。

进度条用在什么情况呢? 仔细想一想就能知道,在api流程中我们知道有两种情况,第一种情况服务器是一件渲染好了html网页,那么只有第二种情况才会触发了。

比如,当用户默认进入的是首页,首页的内容由服务器进行渲染,包括api请求,当用户进入第二个路由页时,如果路由页存在api请求,此时的请求是由浏览器发出的,所以进度条要在这里触发,这个触发nuxt已经设置好了,我们只需要设置loading的属性即可。

loading的属性除了颜色,我们还能设置进度条的宽度,属性为:height,除了这个还有很多属性设置

KeyTypeDefaultDescription
colorString'black'CSS color of the progress bar
failedColorString'red'CSS color of the progress bar when an error appended while rendering the route (if data or fetch sent back an error for example).
heightString'2px'Height of the progress bar (used in the style property of the progress bar)
throttleNumber200In ms, wait for the specified time before displaying the progress bar. Useful for preventing the bar from flashing.
durationNumber5000In ms, the maximum duration of the progress bar, Nuxt.js assumes that the route will be rendered before 5 seconds.
continuousBooleanfalseKeep animating progress bar when loading takes longer than duration.
cssBooleantrueSet to false to remove default progress bar styles (and add your own).
rtlBooleanfalseSet the direction of the progress bar from right to left.

这里就把官方文档的属性表搬过来了,虽然用的情况很少。

vuex

vuex的使用,一般是将api请求封装到vuex的actions中,然后vue页面直接调用vuex中对应的异步方法即可,nuxt对vuex开启了命名空间,所以在使用时注意用法。

首先我们在store文件夹创建一个events.js文件,文件名其实可以随意取,这个没有规定,nuxt一样会自动扫描store中的文件,所以我们需要像vue cli那样,创建一个index.js文件作为vuex的入口。

events.js

import axios from "axios";
const api = axios.create({
  baseURL: "http://localhost:3000"
})

export default {
  state() {
    return {
      apiData: {},
    }
  },
  getters: {
    apiData(state) {
      return state.apiData;
    }
  },
  mutations: {
    setApiData(state, value) {
      state.apiData = value;
    }
  },
  actions: {
    getApiData({ commit }) {
      return api.get("/db").then(res => {
        commit("setApiData", res.data);
      })
    }
  }
}

用法和平时用vuex差不多,主要是我们在这里import引入的axios,这就可以明白,我们可以像平时写项目一样,对axios进行二次封装,添加拦截器这些,然后导出,这里就不多详细写了,都是重复造轮子,没啥必要。

省的在asyncData中使用$axios束手束脚的。

我们在vue文件中就可以这样调用:

<template>
  <div class="container">
    <h1>首页</h1>
    {{apiData}}
  </div>
</template>

<script>
export default {
  async fetch({ store, error }) {
    try {
      await store.dispatch("events/getApiData");
    } catch (err) {
      error({
        statusCode: 503,
        message: "服务端api请求发生错误" + err.message,
      });
    }
  },
  head() {
    return {
      title: "我是首页",
      meta: [
        {
          hid: "description",
          name: "description",
          content: "我是首页的简介",
        },
      ],
    };
  },
  computed: {
    apiData() {
      return this.$store.getters["events/apiData"];
    },
  },
};
</script>

<style>
</style>

注意我们是在fetch钩子中,因为我们不需要和上下文data进行合并,所以我们需要将异步的方法写在fetch钩子上,fetch钩子本身就是用于处理异步函数的,并且在fetch中可以解构到store。

使用vuex请求的方式,在fetch中统一使用,进度条是依旧是有效的。

部署nuxt

之前我们了解过两个脚本,build和start,使用这两个脚本,我们将了解到nuxt的动态生成并部署的方式。

在部署之前我们要明白,nuxt底层上还是node的,所以部署的过程无非是这么一个流程,我们node运行这个项目,拿到本地的地址,localhost+端口号,然后如果需要绑定域名的话,我们需要通过反代的形式。

动态的部署

动态的部署,用户每次请求一个url地址,服务器都是即时渲染一个html文件出来并返回,这就可以保证用户每次刷新访问的,都是最新的。

这种有好处也有坏处,坏处就是非常浪费资源,但是如果你无所谓的话,其实这种还挺好用的。

我们将整个项目文件上传到服务器,服务器要有node,然后有对应的包管理器,我们先进入到服务站的项目目录,安装好所有的插件。

yarn install

安装完毕后运行build脚本:yarn build

运行完毕后我们在运行start脚本,这将会运行nuxt后端服务:yarn start

然后会返回一段地址,反代的话就反代这个地址段,如果不用的话,可以直接用了。本地访问使用localhost,外链就是ip+端口了,这个是网站部署的基本常识,不用再啰嗦一堆了。

如果文件发生了修改,还是需要重新build然后start,省点事我们可以两个命令合二为一:yarn build start

静态文件部署

如果你的文件不需要即时更新,那么可以先生成静态的html文件,然后用户每次访问直接拿对应的文件就行了,这种好处就是静态化,资源不会很浪费,但是也有缺点,如果你的文件发生了变化,那么就要重新生成一次。

我们运行nuxt提供的另一个快捷脚本generate,该脚本会在项目内生成dist目录,这个就和vue cli打包一样了,yarn generate

然后我们直接使用nginx或者其他程序来搭建网站服务,目录就指向这个dist文件夹。

注意:

如果存在动态路由页面,用户从首页进来是没有问题的,但是再刷新就会报错,因为这个动态页面,nuxt默认是不能直接生成的,动态动态,参数是不确定的,所以nuxt没法直接生成。

如果要生成,我们需要预设参数。

打开nuxt.config.js文件,在里面添加如下配置:

假设动态路径为:/events/:id

export default {
  generate: {
    routes: ["/events/one","/events/one"]
  }
}

我们手动配置好routes,再运行yarn generate即可生成对应的静态页面。

当然每次手动写都很麻烦,routes可以为一个函数,最终return出一个数组即可,所以我们可以在函数里面进行复杂的操作。

export default {
  generate: {
    routes() {
      return ["/events/one","/events/one"];
    }
  }
}

以上就是关于nuxt的快速入门教程。

额外知识

使用generate静态的文件,如果想要本地测试,可以使用一个插件:http-server

http-server是一个很简单的服务器程序,运行代码:http-server dist

如果你没有安装可以先安装:yarn add http-server

这个程序也可以用于vue cli打包后的文件,因为vue打包后的html文件,如果没有额外配置,直接打开是空白的,这是路径的问题。

分类: nuxt 标签: vuenodenuxt

评论

全部评论 1

  1. 呵呵哒
    呵呵哒
    Google Chrome Windows 10
    咕咕鸡

目录