一、自定义组件

在做页面开发的时候经常会遇到相同的功能会在多个页面中用到,在每个页面中都加上相同的代码显然是不明智的,这么做会有两个问题:

  • 代码冗余重复
  • 如果这块功能发生改变,那么所有页面都得修改,不便维护

所以这里就需要用到组件,将相同功能封装到同一个组件中,页面中只需要引用该组件即可,如果功能发生变动也只需要修改该组件。

接下来通过一个实例来了解组件如何使用,例如下面的页面中有顶部导航和底部页脚,这两部分是在所有页面都需要用到的,所以我们将他们封装成组件。

未使用组件前的pages/index.vue页面代码如下:
<template>
  <section>
    <nav class="navbar is-light" role="navigation" aria-label="main navigation">
      <div class="container">
        <div class="navbar-brand">
          <a class="navbar-item" href="https://mlog.club">
            <img src="https://i.loli.net/2019/10/11/aAiXVBCNknJeYGD.png" />
          </a>
        </div>

        <div id="navbarBasicExample" class="navbar-menu">
          <div class="navbar-start">
            <a class="navbar-item">Home</a>
            <a class="navbar-item">Documentation</a>
          </div>

          <div class="navbar-end">
            <div class="navbar-item">
              <div class="buttons">
                <a class="button is-primary"><strong>Sign up</strong></a>
                <a class="button is-light">Log in</a>
              </div>
            </div>
          </div>
        </div>
      </div>
    </nav>
    <div class="container">
      <div class="notification">正文内容</div>
    </div>
    <footer class="footer">
      <div class="content has-text-centered">
        <p>
          Powered by <a href="https://mlog.club"><strong>bbs-go</strong></a>
        </p>
      </div>
    </footer>
  </section>
</template>

<script>
export default {
  components: {},
};
</script>

<style>
.container {
  min-height: 300px;
}
</style>

首先我们先将导航条封装成组件,在项目的components目录中新建文件MyNav.vue内容如下:

<template>
  <nav class="navbar is-light" role="navigation" aria-label="main navigation">
    <div class="container">
      <div class="navbar-brand">
        <a class="navbar-item" href="https://mlog.club">
          <img src="https://i.loli.net/2019/10/11/aAiXVBCNknJeYGD.png" />
        </a>
      </div>

      <div id="navbarBasicExample" class="navbar-menu">
        <div class="navbar-start">
          <a class="navbar-item">Home</a>
          <a class="navbar-item">Documentation</a>
        </div>

        <div class="navbar-end">
          <div class="navbar-item">
            <div class="buttons">
              <a class="button is-primary"><strong>Sign up</strong></a>
              <a class="button is-light">Log in</a>
            </div>
          </div>
        </div>
      </div>
    </div>
  </nav>
</template>

<style></style>

这样我们就新建了一个组件,接下来再修改pages/index.vue文件,将组件引入进来并使用,修改后的pages/index.vue页面代码如下:

<template>
  <section>
    <my-nav />
    <div class="container">
      <div class="notification">正文内容</div>
    </div>
    <footer class="footer">
      <div class="content has-text-centered">
        <p>
          Powered by <a href="https://mlog.club"><strong>bbs-go</strong></a>
        </p>
      </div>
    </footer>
  </section>
</template>

<script>
import MyNav from '~/components/MyNav';

export default {
  components: {
    MyNav,
  },
};
</script>

<style>
.container {
  min-height: 300px;
}
</style>

然后刷新页面会发现我们看到的效果和使用组件之前看到的效果是一样的。总结一下组件的使用步骤,主要为以下三步:

  • 新建组件
  • 在页面中通过import引入组件,例如:import MyNav from '~/components/MyNav'
  • 以 html 标签形式使用组件,例如:<my-nav />

以此类推我们也可以将页脚部分封装成组件。

Vue.js 的组件还有很多的用法,例如组件参数、时间监听等等,详情可以查看这里:https://cn.vuejs.org/v2/guide/components.html

二、使用 Nuxt.js 请求接口数据并渲染

bbs-go 是一个动态网页应用,页面内容都是需要从数据库查询数据然后渲染出来的,接下来我们讲解一下如何通过Nuxt.js请求接口获取数据。

使用 Go 语言提供接口

首先创建一个 Go 语言项目server,在该项目中我们利用 Go 语言提供json api接口来返回数据,如何创建 Go 语言项目在前几个实验中有详细介绍,如有疑问可温习下前面几个实验。

接下来新建server/main.go文件,在该文件代码中我们启动 http 服务,并提供一个返回当前时间的接口。完整代码如下:

package main

import (
    "time"

    "github.com/iris-contrib/middleware/cors"
    "github.com/kataras/iris"
)

func main() {
    app := iris.New()

    // 跨域配置
    app.Use(cors.New(cors.Options{
        AllowedOrigins:   []string{"*"}, // allows everything, use that to change the hosts.
        AllowCredentials: true,
        MaxAge:           600,
        AllowedMethods:   []string{iris.MethodGet, iris.MethodPost, iris.MethodOptions, iris.MethodHead, iris.MethodDelete, iris.MethodPut},
        AllowedHeaders:   []string{"*"},
    }))
    app.AllowMethods(iris.MethodOptions)

    app.Get("/api/json", func(ctx iris.Context) {
        ctx.JSON(iris.Map{"curTime": time.Now()})
    })

    app.Run(iris.Addr(":8081"), iris.WithoutServerError(iris.ErrServerClosed))
}
copy

然后执行go run main.go启动服务,端口为:8081,接口/api/json返回数据格式如下:

{
  "curTime": "2019-09-29T18:24:03.4427+08:00"
}

使用 Axios 请求接口数据

Nuxt.js 扩展了 Vue.js,增加了一个叫asyncData的方法,使得我们可以在设置组件的数据之前能异步获取或处理数据。asyncData方法会在页面每次加载之前调用,你可以利用asyncData方法来获取数据,Nuxt.js 会将asyncData返回的数据融合组件 data 方法返回的数据一并返回给当前组件。

bbs-go 是由 Go 语言提供jsonapi,然后由 Nuxt.js 通过asyncData调用jsonapi进行页面渲染的,这里我们使用 Nuxt.js的 axios 模块。在上一个实验中我们创建Nuxt.js项目的时候选择了安装axios模块,所以这里只需要做简单的配置即可。

Axios 配置代理默认

如果我们希望 Go 语言提供的所有接口都可以从 8080 端口访问,这里就可以使用axios的代理功能。axios代理的配置需要我们修改文件site/nuxt.config.js,修改如下:

  ...

  /*
  ** Axios module configuration
  ** See https://axios.nuxtjs.org/options
  */
  axios: {
    proxy: true
  },
  proxy: {
    '/api/': 'http://localhost:8081/' // 要求axios代理8081端口
  }

  ...

代理功能的应用场景有很多,例如我的界面渲染需要从多个服务去拉去数据:

axios: {
    proxy: true
  },

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

Axios 请求接口

接下来我们改造页面,新增asyncData方法,在该方法中调用http://localhost:8081/json接口获取数据然后渲染到页面,改造后页面代码如下:

<template>
  <section>
    <my-nav />
    <div class="container">
      <div class="notification">当前时间:{{ curTime }}</div>
    </div>
    <my-footer />
  </section>
</template>

<script>
import MyNav from '~/components/MyNav';
import MyFooter from '~/components/MyFooter';

export default {
  components: {
    MyNav,
    MyFooter,
  },
  async asyncData({ $axios }) {
    const resp = await $axios.get('/api/json');
    return {
      curTime: resp.data.curTime,
    };
  },
};
</script>

<style>
.container {
  min-height: 300px;
}
</style>

然后我们可以看到页面效果如下:

动态路由

Nuxt.js路径结构是 restful 风格的,我们可以根据页面文件路径来定义带参数的路由。路由参数需要将文件夹文件名称以下划线开头,即表示为动态参数。例如:

路由目录结构
/user/123/pages/user/_id.vue
/user/123/info/pages/user/_id/info.vue

在页面中我们可以通过Nuxt.js context中的params对象来获取路由参数。下面我们通过一个实例来学习和验证效果:

新增/pages/user/_id.vue文件,文件内容如下:

<template>
  <section>
    <my-nav />
    <div class="container">
      <div class="notification">当前数据编号:{{ curId }}</div>
    </div>
    <my-footer />
  </section>
</template>

<script>
import MyNav from '~/components/MyNav';
import MyFooter from '~/components/MyFooter';

export default {
  components: {
    MyNav,
    MyFooter,
  },
  async asyncData({ params }) {
    return {
      curId: params.id,
    };
  },
};
</script>

<style>
.container {
  min-height: 300px;
}
</style>

然后启动服务,并访问服务,路径为:/user/123,可以看到页面效果如下,页面中输出的当前数据编号:123即是从我们的路由参数中获取的。

三、页面布局

同一个网站,一般情况下顶部导航和底部等在所有页面中都是一样的,为避免重复代码, Nuxt.js 提供了布局功能,默认布局为default,我们也可以自定义布局文件,下面我们打开项目的layouts,去新建一个自定义布局:bbs。在layouts文件夹下新建文件bbs.vue,内容如下:

<template>
  <div>
    <div class="header">
      我是顶部
    </div>

    <nuxt />

    <div class="footer">
      我是底部
    </div>
  </div>
</template>

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

<style scoped>
  .header,
  .footer {
    font-size: 24px;
    color: red;
  }
</style>

上面我们定义了一个名为bbs的布局,下面我们将首页修改为该布局,编辑页面文件pages/index.vue,在代码中加上一行:layout: 'bbs',代码如下:

export default {
  layout: 'bbs',
  components: {
    Logo,
  },
};

接下来我们刷新下页面,就能看到效果了。

四、总结

通过本章实验内容,我们就能够掌握Nuxt.js的日常使用,如果想更深入地了解它,可以去Nuxt.js官网( https://nuxtjs.org/ )查看更详细的文档。下面我们来看下本章节完整实验的目录结构。

.
├── server
│   ├── go.mod
│   ├── go.sum
│   └── main.go
└── site
    ├── README.md
    ├── assets
    │   └── README.md
    ├── components
    │   ├── Logo.vue
    │   ├── MyFooter.vue
    │   ├── MyNav.vue
    │   └── README.md
    ├── jsconfig.json
    ├── layouts
    │   ├── README.md
    │   └── default.vue
    ├── middleware
    │   └── README.md
    ├── nuxt.config.js
    ├── package-lock.json
    ├── package.json
    ├── pages
    │   ├── README.md
    │   ├── index.vue
    │   └── user
    │       └── _id.vue
    ├── plugins
    │   └── README.md
    ├── static
    │   ├── README.md
    │   └── favicon.ico
    └── store
        └── README.md