十七、网上商城项目(3)

本章概要

  • 商品列表
    • 商品列表项组件
    • 商品列表组件
  • 分类商品和搜索结果页面
    • Loading 组件
    • Books 组件

17.4 商品列表

商品列表页面以列表形式显示所有商品,将商品列表和商品列表项分别定义为单独的组件,商品列表组件作为父组件在其内部循环渲染商品列表项子组件。

17.4.1 商品列表项组件

在 components 目录下新建 BookLisItem.vue。如下:

BookLisItem.vue




说明:
十七、网上商城项目(3)_第1张图片

to 属性使用了表达式,因此要用 v-bind 指令(这里使用的是简写语法)进行绑定。params 和 path 字段不能同时存在,如果使用了 path 字段,那么 params 将被忽略,所以这里使用命名路由。当然,也可以采用前面例子中拼接路径字符串的方式。
十七、网上商城项目(3)_第2张图片

router-link 默认渲染为 a 标签,所有路由的跳转都是在当前浏览器窗口中完成的,但有时希望在新的浏览器窗口中打开目标页面,那么可以使用 target=“_blank”。但要注意,如果使用 v-slot API 定制 router-link ,将其渲染为其它标签,那么就不能使用 a 标签的target 属性,只能编写单击事件响应代码,然后通过 window.open() 方法打开一个新的浏览器窗口。
十七、网上商城项目(3)_第3张图片

BookListItem 组件需要的商品数据是由父组件通过 prop 传进来的,所以这里定义了一个 item prop。
十七、网上商城项目(3)_第4张图片

单击“加入购物车”按钮时,会调用 addCartItem() 方法将该商品加入购物车中,由于购物车中的商品不需要商品的定价,所以这里先计算出商品的实际价格。
十七、网上商城项目(3)_第5张图片

购物车中保存的每种商品都有一个数量,通过 quantity 字段表示,在商品列表项页面中的“加入购物车”功能是一种便捷方式,商品的数量默认为 1,后面会看到商品详情页面中加入任意数量商品功能的实现。
十七、网上商城项目(3)_第6张图片

在添加商品到购物车中后,路由跳转到购物车页面,这也是电商网站通常采用的方式,可以刺激用户的冲动消费。

17.4.2 商品列表组件

商品列表组件作为商品列表项组件的父组件,负责为商品列表项组件提供商品数据,并通过 v-for 指令循环渲染商品列表项组件。
在 components 目录下新建 BookList.vue 。如下:

BookList.vue




BookList 组件的代码比较简单,主要就是通过 v-for 命令循环渲染 BookListItem 子组件。某些项目的实现是在列表组件中向服务端请求数据渲染列表项,但在本项目中,BookList 组件会被多个页面复用,并且请求的数据接口是不同的,因此 BookList 组件仅仅是定义了一个 list prop 用来接收父组件传递进来的商品列表数据。

17.5 分类商品和搜索结果页面

单击某个分类链接,将跳转到分类商品页面,在该页面下,将以列表形式列出该分类下的所有商品信息;当搜索框中输入某个关键字,单击“搜索”按钮后,将跳转到搜索结果页面,在该页面下,也是以列表形式列出匹配关键字的所有商品信息。
既然这两个页面都是以列表形式显示商品信息,那么可以将他们合并为一个页面组件来实现,在该页面中无非就是根据路由的路径来动态切换页面标题,以及向服务端请求不同的数据接口。
先给出这两个页面的路由配置,编辑 router 目录下的 index.js 文件。如下:

router/index.js

...
const routes = [
  {
    path: '/',
    redirect: {
      name: 'home'
    }
  },
  {
    path: '/home',
    name: 'home',
    meta: {
      title: '首页'
    },
    component: Home
  },
  {
    path: '/category/:id',
    name: 'category',
    meta: {
      title: '图书分类'
    },
    component: () => import('../views/Books.vue')
  },
  {
    path: '/search',
    name: 'search',
    meta: {
      title: '搜索结果'
    },
    component: () => import('../views/Books.vue')
  }
]

routes.afterEach((to) => {
  document.title = to.meta.title;
})
...

在路由配置中,采用的是延迟加载路由的方式,只有在路由到该组件时才加载。关于延时加载路由,可以参看 14.14 节。
将分类图书(/category/:id)和搜索结果(/search)的导航链接对应到同一个目标路由组件 Books 上,同时根据 14.10.1 小节介绍的知识,利用全局后置钩子来为路由跳转后的页面设置标题。

17.5.1 Loading 组件

考虑到图书列表的数据是从服务端去请求数据及网络状况的原因,图书列表的显示可能会有延迟,为此,决定编写一个 Loading 组件,在图书列表数据还没有渲染时,给用户一个提示,让用户稍安勿躁。
在 10.9 节中,已经给出了一个使用 loading 图片实现加载提示的示例,也可以沿用该示例实现加载提示。在这里换一种实现方式,考虑到图片本身加载也需要时间(虽然 loading 图片一般都很小),采用 CSS 实现 loading 加载的动画效果,这种实现在网上有很多,本项目从中找了一个实现,并将其封装为组件。
在 components 目录下新建 Loading.vue 。如下:

Loading.vue


  


主要代码就是 CSS 的样式规则,没必要去深究具体的实现细节,当然想研究 CSS 如何实现该种动画效果另当别论。

17.5.2 Books 组件

有了 Loading 组件,接下来就可以开始编写 Books 组件了。在 views 目录下新建 Books.vue 。如下:

views/Books.vue




说明:
十七、网上商城项目(3)_第7张图片

为了控制 Loading 组件的显示与删除,定义一个数据属性 loading ,其默认值为 true,然后使用 v-if 指令进行条件判断。当成功接收到服务端发回的数据时,将数据属性 loading 设置为 false,这样 v-if 指令就会删除 Loading 组件。
十七、网上商城项目(3)_第8张图片

因为分类商品和搜索结果使用的是同一个组件,但是向服务端请求的数据接口是不同的,分类商品请求的接口是 /book/category/6,而搜索请求的接口是 /search?wd=keyword,为此定义了 setRequestUrl 方法动态设置请求的接口 URL。
十七、网上商城项目(3)_第9张图片

判断目标路由有多种方式,可以在导航守卫中通过 to.path 或 to.fullPath 判断,也可以使用 this.route.path 和 this.$route.fullPath 判断,如果在路由配置中使用了命名路由,还可以使用 this.route.name 判断,如本例所示。
十七、网上商城项目(3)_第10张图片

在组件内导航守卫 beforeRouteEnter() 中请求初次渲染的数据,当然也可以利用 created 生命周期钩子完成相同的功能。
十七、网上商城项目(3)_第11张图片

由于搜索框是独立的,用户可能会多次进行搜索行为,所以使用组件内守卫 beforeRouteUpdate() ,在组件被复用的时候再次请求数据。
十七、网上商城项目(3)_第12张图片

BookList 组件所需要的数据是通过 list prop 传进去的,由于父子组件生命周期的调用时机问题,可能会出现子组件已经 mounted ,而父组件的数据才传过去,导致子组件不能正常渲染,为此可以添加一个 v-if 指令,使用列表数据的长度作为条件判断,确保子组件能正常接收到数据并渲染。在本项目使用的 Vue.js 版本和采用的实现方式下,不添加 v-if 指令也能正常工作,如果以后遇到子组件的列表数据不能正常渲染,可以试试这种解决方案。

Books 组件的渲染效果与 BookList 组件渲染的效果是类似的,只是多了一个标题,以及在没有请求到数据时给出的一个提示信息。

你可能感兴趣的:(Vue.js,3.0,从入门到实战,商品列表项组件,商品列表组件,分类商品和搜索结果页面,Loading,组件,Books,组件)