Vant:轻量、可靠的移动端组件库
Vant官网
# Vue2 项目,安装 Vant 2:
npm i vant -S
Vant 支持一次性导入所有组件,引入所有组件会增加代码包体积,因此不推荐这种做法。
但是在开发阶段,我们不用考虑体积的问题,怎么快怎么来。在发布的时候,可以对项目体积进行优化,可以直接把vant从项目中抽出去。
输入网址:http://toutiao.liulongbin.top/report.html 可以看到黑马头条项目(该项目已经发布)打包之后生成的结果,如下:
import Vue from 'vue';
import Vant from 'vant';
import 'vant/lib/index.css';
Vue.use(Vant); // use函数是来安装插件的
之后参考官方文档,根据需求进行使用,看到页面效果出现就成功了。
<template>
<div>
<router-view>router-view>
<van-tabbar route>
<van-tabbar-item icon="home-o" to="/home">首页van-tabbar-item>
<van-tabbar-item icon="user-o" to="/user">我的van-tabbar-item>
van-tabbar>
div>
template>
在router/index.js 中配置路由规则:
<template>
<div class="home-container">
<van-nav-bar title="黑马头条" fixed />
div>
template>
<script>
export default {
name: 'Home'
}
script>
<style lang="less" scoped>
.home-container {
padding-top:46px;
padding-bottom: 50px;
.van-nav-bar {
background-color: #007bff;
}
/deep/.van-nav-bar__title {
color:#fff;
}
}
style>
参数名 | 数据类型 | 说明 |
---|---|---|
_page | Number | 页码值。从 1 开始 |
_limit | Number | 每页展示的数据条数。 |
[
{
"art_id": "8163",
"title": "iOS原生混合RN开发最佳实践",
"aut_id": "1111",
"comm_count": "254",
"pubdate": "2019-03-11 09:00:00",
"aut_name": "黑马先锋",
"is_top": 0,
"cover": {
"type": 3,
"images": [
"http://www.liulongbin.top:8000/resources/images/32.jpg",
"http://www.liulongbin.top:8000/resources/images/80.jpg",
"http://www.liulongbin.top:8000/resources/images/32.jpg"
]
}
},
{
"art_id": "8089",
"title": "Typescript玩转设计模式 之 创建型模式",
"aut_id": "1111",
"comm_count": "24",
"pubdate": "2019-03-11 09:00:00",
"aut_name": "黑马先锋",
"is_top": 0,
"cover": {
"type": 1,
"images": [
"http://www.liulongbin.top:8000/resources/images/11.jpg"
]
}
},
{
"art_id": "8145",
"title": "JAVA消息确认机制之ACK模式",
"aut_id": "1111",
"comm_count": "99",
"pubdate": "2019-03-11 09:00:00",
"aut_name": "黑马先锋",
"is_top": 0,
"cover": {
"type": 0
}
}
]
参数名 | 类型 | 说明 |
---|---|---|
art_id | string | 文章 id |
title | string | 文章标题 |
aut_id | string | 作者的 id |
comm_count | string | 评论数 |
pubdate | string | 发布日期 |
aut_name | string | 作者名字 |
|- cover | object | 文章封面 |
|---- type | number | 封面的数量,可选值:0、1、3 |
|---- images | array | 文章封面图片的 URL 数组 |
npm i axios -S
import axios from 'axios'
const request = axios.create({
// 指定请求的根路径
baseURL: 'https://www.escook.cn'
})
export default request
一进入Home组件就要发请求,来拿第一页数据,一般来说是在created里面来调函数,发起请求。
但是,我们先不要写created,先封装一个methods来调接口拿数据:
// 导入 request.js
import request from '@/utils/request.js'
export default {
name: 'Home',
data () {
return {
// 页码值
page: 1,
// 每页显示多少条数据
limit: 10
}
},
methods: {
// 封装获取文章列表数据的方法
async initArticleList () {
// 发起 GET请求,获取文章的列表数据
const { data: res } = await request.get('/articles', {
// 请求参数
params: {
_page: this.page,
_limit: this.limit
}
})
console.log(res)
}
},
created () {
this.initArticleList()
}
}
现有这样一个需求:我希望一进入User组件时,也会发起axios请求来获取图书列表,但我希望一次只请求5条数据,即把limit改成5即可
// 文章相关的 API 接口,都封装到这个模块中
import request from '@/utils/request.js'
// 向外按需导出一个 API 函数
export const getArticleListAPI = function (_page, _limit) {
return request.get('/articles', {
// 请求参数
params: {
_page,
_limit
}
})
}
// 按需导入 API 接口
import { getArticleListAPI } from '@/api/articleAPI.js'
export default {
name: 'Home',
data () {
return {
// 页码值
page: 1,
// 每页显示多少条数据
limit: 10
}
},
methods: {
async initArticleList () {
const { data: res } = await getArticleListAPI(this.page, this.limit)
console.log(res)
}
},
created () {
this.initArticleList()
}
}
今后,如果需要调接口来获取用户相关的信息,则在src/api文件夹下新建一个模块专门来定义用户的api如userAPI.js
ArticleInfo组件是可复用的组件,放在components文件夹中
<ArticleInfo v-for="item in artlist" :key="item.id">ArticleInfo>
<ArticleInfo v-for="item in artlist" :key="item.id" :title="item.title" :author="item.aut_name"
:cmt-count="item.comm_count" :time="item.pubdate">ArticleInfo>
export default {
name: 'ArticleInfo',
// 自定义属性
props: {
// 标题
title: {
type: String,
default: null
},
// 作者名字
author: {
type: String,
default: null
},
// 评论数
cmtCount: {
// 通过数组形式, 为当前属性定义多个可能的类型
type: [Number, String],
default: 0
},
time: {
type: String,
default: null
}
}
}
// 封面的信息对象
cover: {
type: Object,
// 通过default函数返回cover属性的默认值
default: function () {
return { type: 0 }
}
}
需要用到 Vant 提供的组件 — List
List 组件通过 loading 和 finished 两个变量控制加载状态,当组件滚动到底部时,会触发 load 事件并将 loading 设置成 true。此时可以发起异步操作并更新数据,数据更新完毕后,将 loading 设置成 false 即可。若数据已全部加载完毕,则直接将 finished 设置成 true 即可。
export default {
name: 'Home',
components: {
ArticleInfo
},
data () {
return {
// 页码值
page: 1,
// 每页显示多少条数据
limit: 10,
// 文章列表数组
artlist: [],
// 是否正在下一页数据,如果loading为true,则不会反复触发onload事件
// 每当下一页数据请求回来之后,千万要记得 ,把loading 从 true 改为 false
loading: true,
// 所有数据是否加载完毕,如果没有更多数据了,要把finished改成true
finished: false
}
},
methods: {
async initArticleList () {
// 返回值 res 就是一个数组
const { data: res } = await getArticleListAPI(this.page, this.limit)
// 如果是上拉加载更多,那么应该是:
// this.artlist=[旧数据在前,新数据在后]
this.artlist = [...this.artlist, ...res]
// 当第一页数据请求回来之后把 loading 改为 false
this.loading = false
if (res.length === 0) {
// 证明没有下一页数据了,直接把 finished 改为true,表示数据加载完了!
this.finished = true
}
},
// 只要onload 被调用,就应该请求下一页数据
onLoad () {
// 1.让页码值 +1
this.page++
// 2. 重新请求接口获取数据
this.initArticleList()
}
},
created () {
this.initArticleList()
}
}
下拉刷新时会触发 refresh 事件,在事件的回调函数中可以进行同步或异步操作,操作完成后将 v-model 设置为 false,表示加载完成。
export default {
name: 'Home',
components: {
ArticleInfo
},
data () {
return {
// 页码值
page: 1,
// 每页显示多少条数据
limit: 10,
// 文章列表数组
artlist: [],
// 是否正在下一页数据,如果loading为true,则不会反复触发load事件
// 每当下一页数据请求回来之后,千万要记得 ,把loading 从 true 改为 false
loading: true,
// 所有数据是否加载完毕,如果没有更多数据了,要把finished改成true
finished: false,
// 是否正在下拉刷新
isLoading: false
}
},
created () {
this.initArticleList()
},
methods: {
async initArticleList (isRefresh) {
// 返回值 res 就是一个数组
const { data: res } = await getArticleListAPI(this.page, this.limit)
if (isRefresh) {
// 下拉刷新: 新数据在前,新数据在后
this.artlist = [...res, ...this.artlist]
this.isLoading = false
// 当
} else {
// 上拉加载更多:旧数据在前,新数据在后
this.artlist = [...this.artlist, ...res]
// 当第一页数据请求回来之后把 loading 改为 false
this.loading = false
}
if (res.length === 0) {
// 证明没有下一页数据了,直接把 finished 改为true,表示数据加载完了!
this.finished = true
}
},
// 只要onload 被调用,就应该请求下一页数据
onLoad () {
// 1.让页码值 +1
this.page++
// 2. 重新请求接口获取数据
this.initArticleList()
},
// 下拉刷新的处理函数
onRefresh () {
// 1. 要页码值 +1
this.page++
// 2. 重新请求接口,获取数据
this.initArticleList(true)
}
}
}
该项目的标题“黑马头条" 默认是白色背景,我们是通过 /deep/ 深度选择器再结合审查元素找到了类名进行覆盖
这个方法有一个缺点:如果另一个页面,如用户页面,也有这个标题,那么需要重复这种方法,不然样式仍然是默认的。
因此我们使用 定制主题 这个方法
// 这个文件是 vue-cli 创建出来的 项目的 配置文件
// 在 vue.config.js 这个配置文件中,可以对整个项目的打包、构建进行全局性的配置
// webpack 在进行打包的时候,底层用到的是 node.js
// 因此,在 vue.config.js 配置文件中,可以导入并使用 node.js 中的核心模块
const path = require('path')
const themePath = path.join(__dirname, './src/theme.less')
module.exports = {
css: {
loaderOptions: {
less: {
// 若 less-loader 版本小于 6.0,请移除 lessOptions 这一级,直接配置选项。
modifyVars: {
// 可以通过 less 文件覆盖(文件路径为绝对路径)
// 从 盘符 开始的路径叫做绝对路径
hack: `true; @import "${themePath}";`
}
}
}
}
}
下面介绍打包完成后能双击 dist/index.html 来打开项目,需要配置 vue.config.js:
然后在终端运行:
npm run build