Skip to content

Nuxt.js 基础 ​

一、安装 ​

1. 使用淘宝镜像 ​

bash
npm config set registry https://registry.npmmirror.com

2. 脚手架安装 ​

bash
npx create-nuxt-app nuxtApp

# 或
yarn create nuxt-app <my-project>

3. 配置端口 ​

package.json:

json
"config": {
  "nuxt": {
    "port": "3002"
  }
}

提示:不能使用6666等特殊端口,否则无法打开网页

4. 运行 npm run dev ​

二、目录结构 ​

1. assets(资源目录) ​

存放需要webpack编译的静态资源如Less、SASS或JavaScript

引用地址:@/assets

2. static(静态文件目录) ​

存放不需要webpack编译的资源如图片

服务器启动的时候,该目录下的文件会映射到应用的根路径/下

引用地址:/

3. components(组件目录) ​

Nuxt不会扩展增强该目录下Vue.js组件,即这些组件不会像页面组件那样有asyncData方法等特性

4. layouts(布局目录) ​

用于组织应用的布局组件

5. middleware(中间件目录) ​

用于存放应用的中间件(会在页面渲染之前被调用,相当于导航守卫)

6. pages(页面目录) ​

Nuxt.js 框架读取该目录下所有的 .vue 文件并自动生成对应的路由配置

7. plugins(插件目录) ​

用于组织那些需要在 根vue.js应用 实例化之前需要运行的 Javascript 插件,如element-ui

8. store(目录) ​

在 store 目录下创建一个 index.js 文件可激活Vuex

9. nuxt.config.js(nuxt配置文件) ​

10. 别名 ​

@对应着文件根目录

三、页面组件 ​

在pages文件夹下定义的.vue文件,都是页面组件;页面组件相比于Vue组件,多了一些特殊的配置项

vue
<script>
export default {
  data() {
    return {
      name: "",
    };
  },
  /* 获取数据生命周期,在服务端被调用,返回值设置组件的数据*/
  asyncData() {
    return { name: "world" };
  },
  /* 用于在渲染页面之前填充状态树(store)数据,不会设置组件的数据*/
  fetch() {},
  /* 设置当前页面的Meta标签 */
  head() {},
  /* 指定当前页面使用的布局,默认使用layouts目录下的default.vue*/
  layout() {},
  loading() {},
  /* 指定页面切换的过渡动效 */
  transition() {},
  // 布尔值,默认false,用于判定渲染页面前是否需要将当前页面滚动至顶部。
  //用于嵌套路由的应用场景
  scrollToTop() {},
  /* 校验动态路由参数 */
  validate() {},
  /* 指定页面的中间件,中间件会在页面渲染之前被调用;相当于导航守卫*/
  middleware() {},
  //为查询字符串设置一个监视器
  //如果定义的字符串发生变化,所有的组件方法都会被调用(fetch除外)
  //默认情况下,返回值为false,以提高性能
  watchQuery() {
    return boolean;
  },
};
</script>

四、asyncData ​

用来获取接口数据。第一个参数获取当前页面的上下文对象(可获取路由参数)。asyncData返回的数据融合组件data方法返回的数据一并返回给当前组件

注意:由于asyncData方法是在组件初始化前被调用的,所以在方法内是没有办法通过 this 来引用组件的实例对象。

js
export default {
  async asyncData({ params }) {
    const { data } = await axios.get(`https://my-api/posts/${params.id}`);
    return { title: data.title };
  },
};
js
/* 并发请求 */
async asyncData() {
  let [a, b] = await Promise.all([API.getContactList(), API.getUser()]);
  return {
    contactList: a.data.data,
    user: b.data.data,
  };
},

五、路由 ​

路由

六、视图 ​

1. 默认布局 ​

@/layouts/default.vue。

vue
<template>
  <div>
    <p>文杰的博客</p>
    <nuxt />
  </div>
</template>

<nuxt/> 组件用于显示页面的主体内容

2. 自定义布局 ​

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

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

vue
<template>
  <div>
    <div>我的博客导航栏在这里</div>
    <nuxt />
  </div>
</template>

然后我们必须告诉页面 (即pages/posts.vue) 使用自定义布局:

vue
<script>
export default {
  layout: "blog",
};
</script>

3. 错误页面 ​

可以通过编辑 layouts/error.vue 文件来定制化错误页面

警告: 虽然此文件放在 layouts 文件夹中, 但应该将它看作是一个 页面(page).

vue
<template>
  <div>
    <h2 v-if="error.statusCode == 404">404页面不存在</h2>
    <h2 v-else>500服务器错误</h2>
    <ul>
      <li><nuxt-link to="/">HOME</nuxt-link></li>
    </ul>
  </div>
</template>

<script>
export default {
  props: ["error"],
};
</script>

七、baseURL多环境配置 ​

运行

bash
npm i @nuxtjs/dotenv cross-env -S

在nuxt.config.js中配置modules模块

js
module.exports = {
  modules: [
    ["@nuxtjs/dotenv"], //默认读取目录下的.env文件
  ],
};

新建.env文件

bash
DEV_BASE_URL = 'localhost:9000/api'
PROD_BASE_URL = 'https://www.example.com/api'

在nuxt.config.js中

js
require("dotenv").config();
let baseUrl = "";
switch (process.env.BASE) {
  case "dev":
    baseUrl = process.env.DEV_BASE_URL;
    break;
  case "pro":
    baseUrl = process.env.PROD_BASE_URL;
    break;
  default:
    baseUrl = process.env.PROD_BASE_URL;
    break;
}
export default {
  env: {
    baseUrl: baseUrl,
  },
};

在package.json中配置启动命令

json
"scripts": {
  "dev": "cross-env NODE_ENV=development BASE=dev nuxt",
  "build": "nuxt build",
  "start": "cross-env MODE=production BASE=pro nuxt start",
  "generate": "nuxt generate"
},

现在 process.env.baseUrl 可以在全局进行访问

八、axios ​

前提:在nuxt中使用axios中进行跨域代理时,发现寻常代理方式不起作用,只能使用官方推荐的工具

1. 安装 ​

bash
npm i @nuxtjs/axios @nuxtjs/proxy -D

2. 在nuxt.config.js中进行配置 ​

js
modules: [
  '@nuxtjs/axios',
  '@nuxtjs/proxy'
],
axios: {
  prefix: "/juhe",
  credentials: true,
  proxy: true
},
proxy: {
  '/juhe': {
    target: 'https://v.juhe.cn',
    changeOrigin: true,
    pathRewrite: {
      "^/juhe": ""
    }
  },
}

3. 使用 ​

html
<template>
  <article>
    <card :url="'/toutiao.png'" :source="List">今日头条</card>
  </article>
</template>
js
export default {
  data() {
    return {
      contactList: [],
    };
  },
  //请求数据的方法(相当于vue中的created)
  async asyncData({ app }) {
    let params = {
      type: "top",
      key: "your_api_key",
    };
    let { data } = await app.$axios("/toutiao/index", { params: params });
    return {
      contactList: data.data,
    };
  },
  //methods/created/mounted中使用
  mounted() {
    let params = {
      type: "guonei",
      key: this.$store.state.key,
    };
    this.$axios("/toutiao/index", { params: params });
  },
};

4. 配置拦截器 ​

nuxt.config.js

js
plugins: ["@/plugins/axios"];

plugins/axios.js

js
export default function ({ $axios, redirect }) {
  $axios.setHeader("Authorization", "123");
  $axios.onRequest((config) => {
    console.log("Making request to " + config.url);
  });

  $axios.onError((error) => {
    const code = parseInt(error.response && error.response.status);
    if (code === 400) {
      redirect("/400");
    }
  });
}

五种拦截情况:

  • onRequest(config)
  • onResponse(response)
  • onError(err)
  • onRequestError(err)
  • onResponseError(err)

参考网址:http://www.axios-js.com/zh-cn/docs/nuxtjs-axios.html

九、配置 ​

1. build ​

(1)analyze ​

例:使用webpack-bundle-analyzer来查看打包的大小

安装:

bash
npm install webpack-bundle-analyzer --save-dev

在nuxt.config.js文件build中增加配置

js
build: {
  analyze:true,
}

运行:

bash
npm run build --analyze

(2)babel ​

自定义babel配置

例:按需引入element-ui

1.安装

bash
npm install babel-plugin-component -D
  1. 在nuxt.config.js文件中build对象下添加配置
js
babel: {
  'plugins': [
    [
      'component',
      {
        'libraryName': 'element-ui',
        'styleLibraryName': 'theme-chalk'
      }
    ]
  ],
  'comments': true
}
  1. 修改plugins文件夹下element-ui.js
js
import { Pagination, Tag } from "element-ui";
Vue.use(Pagination, { locale });
Vue.use(Tag, { locale });

(3)crossorigin ​

类型:string

默认:undefined

在生成的 HTML 中的 <link rel="stylesheet"> 和 <script> 标记上配置 crossorigin 属性

(4)extractCSS ​

类型:Boolean

默认:false

使用extract-css-chunks-webpack-plugin将主块中的 CSS 提取到一个单独的 CSS 文件中(自动注入模板),该文件允许单独缓存文件。当有很多共用 CSS 时建议使用此方法,异步组件中的 CSS 将保持内联为 JavaScript 字符串并由 vue-style-loader 处理

(5)optimization ​

webpack代码压缩、拆包

(6)optimizeCSS ​

优化css,可与extractCSS配合使用

(7)plugins ​

配置webpack插件

例:配置编译后的全局变量

js
import webpack from 'webpack'

plugins: [
  new webpack.DefinePlugin({
    version: 'v1',
  }),
],

全局访问version可得到v1

(8)transpile ​

将库使用babel进行转换

例:element-ui

安装

bash
npm i element-ui -S

在plugins文件夹下面,新建element-ui.js文件

js
import Vue from "vue";
import Element from "element-ui";
import locale from "element-ui/lib/locale/lang/en";

Vue.use(Element, { locale });

在nuxt.config.js中增加配置

js
//全局css配置
css: [
  'element-ui/lib/theme-chalk/index.css'
],
//第三方插件配置
plugins: [
  '@/plugins/element-ui'
],
//打包配置
build: {
  //使用babel转换element-ui
  transpile: [/^element-ui/],
}

2. CSS ​

配置全局CSS,scss文件

js
// Global CSS (https://go.nuxtjs.dev/config-css)
css: [
  '@/assets/css/normalize.css',
  '@/assets/css/main.scss'
],

3. styleResources ​

配置全局scss、less变量

安装scss

bash
npm install sass sass-loader@^10.5.2 -D

运行:

bash
npm i @nuxtjs/style-resources --save--dev

在nuxt.config.js中配置:

js
modules: ['@nuxtjs/style-resources'],
styleResources: {
  scss: './assets/variables.scss',
  less: './assets/**/*.less'
  // sass: ...
}

4. generate ​

在 Nuxt.js 执行 generate 命令时,动态路由 会被忽略

通过此配置使静态页面生成动态路由

https://www.nuxtjs.cn/api/configuration-generate

5. head ​

配置应用默认的 meta 标签

6. loading ​

配置页面切换时,顶部加载进度条

https://www.nuxtjs.cn/api/configuration-loading

7. modules ​

扩展nuxt功能,比如axios,pwa等

8. plugins ​

该配置项用于配置那些需要在 根vue.js应用 实例化之前需要运行的 Javascript 插件

十、个性meta设置 ​

页面的Meta对于SEO的设置非常重要

vue
<script>
export default {
  //独立设置head信息
  head() {
    return {
      title: "",
      meta: [
        { hid: "description", name: "news", content: "This is news page" },
      ],
    };
  },
};
</script>

注意:为了避免子组件的meta标签不能正确覆盖父组件中相同的标签而产生重复的现象,建议使用hid键位meta标签配一个唯一的标识编号

十一、插件 ​

插件

十二、Vuex状态树 ​

Nuxt.js会根据store目录结构自动创建Vuex状态库

在store下新建index.js,Nuxt.js则会自动启用Vuex。

1. ~store/index.js ​

js
export const state = () => ({
  counter: 0,
});

export const mutations = {
  increment(state) {
    state.counter++;
  },
};

index.js则是主仓库,有state,mutations ,actions,getters

在store下再新建其他js文件,则会自动创建以该文件名为名称的模块,并且自动添加命名空间。

2. ~store/todo.js ​

在store下创建todos.js

js
export const state = () => ({
  list: [],
});

export const mutations = {
  add(state, text) {
    state.list.push({
      text,
      done: false,
    });
  },
  remove(state, { todo }) {
    state.list.splice(state.list.indexOf(todo), 1);
  },
  toggle(state, todo) {
    todo.done = !todo.done;
  },
};

Vuex将如下创建

js
new Vuex.Store({
  state: () => ({
    counter: 0,
  }),
  mutations: {
    increment(state) {
      state.counter++;
    },
  },
  modules: {
    todos: {
      namespaced: true,
      state: () => ({
        list: [],
      }),
      mutations: {
        add(state, { text }) {
          state.list.push({
            text,
            done: false,
          });
        },
        remove(state, { todo }) {
          state.list.splice(state.list.indexOf(todo), 1);
        },
        toggle(state, { todo }) {
          todo.done = !todo.done;
        },
      },
    },
  },
});

3. 在组件中使用 ​

vue
<template>
  <div>
    <h2>About Index page</h2>
      <li v-for="(todo, index) in list" :key="index">
        <input type="checkbox" :checked="todo.done" @change="toggle(todo)"/>
        <span :class="{ done: todo.done }">{{ todo.text }}</span>
      </li>
      <li>
        <input placeholder="What needs to be done?" @keyup.enter="addTodo"/>
      </li>
    </ul>
  </div>
</template>

<script>
import { mapMutations , mapState} from "vuex";

export default {
  computed: {
    ...mapState('todos',[
      'list'
    ])
    // list() {
    //   return this.$store.state.todos.list;
    // },
  },
  methods: {
    addTodo(e) {
      this.add(e.target.value);
      // this.$store.commit('todos/add',e.target.value)
      e.target.value = "";
    },
    ...mapMutations('todos',[
      'toggle',
      'add'
    ]),
  },
};
</script>

<style>
.done {
  text-decoration: line-through;
}
</style>

(1)取值 ​

在computed中,通过函数的方式取值

js
computed: {
  list() {
    return this.$store.state.todos.list;
  },
},

或利用mapState函数

js
import { mapState } from "vuex";
computed: {
  ...mapState('todos',[
    'list'
  ])
},

mapState第一个参数,是模块的名称,在nuxt.js中也就是创建的文件名。第二个参数可以是个数组,里面是String类型,放置属性名称

(2)改变state状态(mutations) ​

在methods中,通过this.$store.commit()方式改变state值

todos/add:第一个是模块名称,第二个是方法

js
import { mapMutations } from "vuex";
methods: {
  addTodo(e) {
    this.$store.commit('todos/add',e.target.value)
    e.target.value = "";
  },
},

或利用mapMutations函数

js
import { mapMutations } from "vuex";
methods: {
  addTodo(e) {
    this.add(e.target.value);
    e.target.value = "";
  },
  ...mapMutations('todos',[
    'toggle',
    'add'
  ]),
},

十三、fetch方法 ​

fetch方法会在渲染页面前被调用,作用是填充状态树(store)数据,与asyncData方法类似,不同的是它不会设置组件的数据。

fetch方法的第一个参数是页面组件的上下文对象context,可通过解构取得想要的值

警告: 您无法在内部使用this获取组件实例,fetch是在组件初始化之前被调用

1. 直接在fetch中调用接口 ​

例如 pages/index.vue:

vue
<template>
  <h1>Stars: {{ $store.state.stars }}</h1>
</template>

<script>
export default {
  async fetch({ store, params }) {
    let { data } = await axios.get("http://my-api/stars");
    store.commit("setStars", data);
  },
};
</script>

调用mutation方法需要同步,这里通过async与await方法,在调完接口之后再执行store.commit方法

2. 将接口定义在actions中 ​

store/index.js

js
export const actions = {
  async GET_STARS({ commit }) {
    const { data } = await axios.get("http://my-api/stars");
    commit("SET_STARS", data);
  },
};

调用

vue
<script>
export default {
  async fetch({ store, params }) {
    await store.dispatch("GET_STARS");
  },
};
</script>

3. 监听query字符串的改变 ​

默认情况下,不会在查询字符串更改时调用fetch方法。如果想更改此行为,例如,在编写分页组件时,您可以设置通过页面组件的watchQuery属性来监听参数的变化

类型: Boolean or Array (默认: [])

使用watchQuery属性可以监听参数字符串的更改。 如果定义的字符串发生变化,将调用所有组件方法(asyncData, fetch, validate, layout, ...)。 为了提高性能,默认情况下禁用。

如果您要为所有参数字符串设置监听, 请设置: watchQuery: true.

js
export default {
  watchQuery: ["page"],
};

十四、打包 ​

1. 打包静态资源 ​

步骤一:首先关闭其他环境的服务

步骤二:在nuxt.config.js下增加配置

js
export default {
  target: "static",
};

步骤三:运行npm run generate

步骤四:可以运行 npm start 起本地服务,查看页面效果

步骤五:配置Nginx

在Nginx服务下,创建static目录,并将dist文件拷贝到其中(dist文件是外部的,不是.next文件夹中的)

nginx
server {
    listen 9001;
    server_name localhost;

    location / {
        root   static/dist;
        try_files $uri $uri/ /index.html;
        index  index.html index.htm;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   html;
    }
}

步骤六:http://localhost:9001/#/ 通过地址查看演示效果

2. 打包SSR资源 ​

与PM2结合使用

(一)修改nuxt.config.js ​

删除打包静态资源配置的 target: 'static',

(二)在根目录下新建ecosystem.config.js ​

js
module.exports = {
  apps: [
    {
      name: "nuxtApp",
      exec_mode: "cluster",
      instances: "max", // Or a number of instances
      script: "./node_modules/nuxt/bin/nuxt.js",
      args: "start",
      env: {
        PORT: 3002,
        NODE_ENV: "production",
      },
    },
  ],
};

(三)项目运行:npm run build ​

(四)将.nuxt,static,nuxt.config.js,package.json,ecosystem.config.js五个文件压缩上传到服务器 ​

(五)在服务器上进行编译,在该目录下运行 npm i ​

(六)运行 ​

bash
pm2 start npm --name "nuxtApp" -- start

(七)配置Nginx ​

nginx
#node 服务
server {
    listen 443 ssl; #SSL协议访问端口号为443。此处如未添加ssl,可能会造成Nginx无法启动。
    server_name www.example.com; #将localhost修改为您证书绑定的域名,例如:www.example.com。
    #root html;

    # index.html index.htm;

    ssl_certificate domain/nuxt.pem; #将domain name.pem替换成您证书的文件名。
    ssl_certificate_key domain/nuxt.key; #将domain name.key替换成您证书的密钥文件名。
    ssl_session_timeout 5m;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4; #使用此加密套件。
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2; #使用该协议进行配置。
    ssl_prefer_server_ciphers on;
    location / {
        proxy_pass http://localhost:3002/;
    }

    # etag on;

    # expires 7d;

}

注意点:之前一直想找HTML静态文件,但这是SSR渲染,是node服务,所以只需配置代理路径即可 http://127.0.0.1:3002;

提示:之前写的代理路径为http://127.0.0.1:3002,但是网页报502,查看运行日志发现,启动改为了http://localhost:3002/

(八)访问:https://www.example.com/ ​

(九)如果在服务器上更改ecosystem.config.js中的端口号,需重新运行npm run build 与 pm2 start(运行pm2 start前需先删除服务) ​