Vue(八):头条项目

一、新建项目

与之前的不同之处在于,本次选择自动创建Router,如下:

注:发现在src目录下新增views文件夹。views文件夹和components文件夹都是存放vue页面文件的。区别在于凡是通过router/index.js路由文件引入的.vue文件放入view文件夹,其余放入components文件夹。

二、开发

2.1 安装Vant组件库

组件库地址:vant-contrib.gitee.io/vant/#/zh-CN/theme

执行npm i vant -S

2.2 组件引入

推荐采用方式一引入组件,但是方式一过于麻烦,一般采用方式三。一次性引入vant的所有组件,导致打包的体积大,后期解决该问题。

 将上述代码添加至main.js中

2.3 测试组件库

 将下面代码复制至App.vue中,页面效果实现如上,说明组件引入成功。

<van-button type="primary">主要按钮</van-button>
<van-button type="info">信息按钮</van-button>
<van-button type="default">默认按钮</van-button>
<van-button type="warning">警告按钮</van-button>
<van-button type="danger">危险按钮</van-button>

 2.4 Tabbar和导航栏的创建

2.4.1创建主页Home和用户页User

分别在views文件夹下新建Home和User文件夹,并建立对应的vue文件。

 2.4.2 tabbar标签栏的创建

             

 如上,App.vue中添加占位符和Tabbar。

<template>
  <div>
    <router-view></router-view>
<van-tabbar route>
  <van-tabbar-item to="/" icon="home-o">首页</van-tabbar-item>
  <van-tabbar-item to="/user" icon="user-o">我的</van-tabbar-item>
</van-tabbar>
  </div>
</template>

<script>
export default {
  name: 'App'
}
</script>

<style lang="less" scoped>

</style>

并在index.js中添加路由:

import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '@/views/Home/Home.vue'
import User from '@/views/User/User.vue'

Vue.use(VueRouter)

const routes = [
  { path: '/', component: Home },
  { path: '/user', component: User }
]

const router = new VueRouter({
  routes
})

export default router

2.4.3 导航栏的创建

主要存在问题如下:

(1)导航栏固定,这里是通过fixed参数设置

(2)底下的h1字体会被顶部的导航栏和底部的Tabbar标签栏挡住,这里需要设置padding。padding的设置为顺时针,上,右,下,左

(3)样式设置采用审查元素查看,若不生效,可加/deep/

<template>
  <div class="home-container">
    <van-nav-bar title="头条" fixed/>
    <h1>aaa</h1>
    <h1>bbb</h1>
  </div>
</template>

<script>
export default {
  name: 'Home'
}
</script>

<style lang="less" scoped>
.home-container {
  padding: 46px 0 50px 0;
  .van-nav-bar {
    background-color: #007bff;
  }
  /deep/ .van-nav-bar__title {
    color: aliceblue;
  }
}
</style>

2.5 axios请求数据

npm i axios -S安装

src目录下新建utils文件夹,并在utils文件夹下新建request.js文件,代码如下:

import axios from 'axios'
const request = axios.create({
  baseURL: 'https://www.escook.cn'
})

export default request

Home.vue中引入request.js文件,并定义方法发起axios请求,并在created方法中调用axios请求的方法。

<template>
  <div class="home-container">
    <van-nav-bar title="头条" fixed/>
  </div>
</template>

<script>
import request from '@/utils/request.js'

export default {
  name: 'Home',
  data() {
    return {
      // 页码值
      page: 1,
      // 每页展示的数据条数
      limit: 10
    }
  },
  created() {
    this.initArticleList()
  },
  methods: {
    async initArticleList() {
      const { data: res } = await request.get('/articles', {
        params: {
          _page: this.page,
          _limit: this.limit
        }
      })
      console.log(res, res)
    }
  }
}
</script>

<style lang="less" scoped>
.home-container {
  padding: 46px 0 50px 0;
  .van-nav-bar {
    background-color: #007bff;
  }
  /deep/ .van-nav-bar__title {
    color: aliceblue;
  }
}
</style>

如上,若在我的页面也想调用axios请求'/articles'获取相关数据,则又需要在User.vue重新写一次axios.get....。为了提高可用性,创建API接口:

(1)在src下新建api文件夹,并在api文件夹下新建articleAPI.js文件。articleAPI.js代码如下:

// 文章相关的接口,都封装到这个模块中
import request from '@/utils/request.js'

// 向外导出一个API函数
export const getArticleListAPI = function(_page, _limit) {
  return request.get('/articles', {
    params: {
      _page,
      _limit
    }
  })
}

(2)在Home.vue中引入articleAPI.js中的getArticleListAPI方法,并调用,代码如下:

注:方法引入时两侧需要加大括号

 2.6 文章列表

(1)在src/components文件夹下新建Article文件夹,并在其下新建ArticleInfo.vue组件。

<template>
  <div>
    <van-cell>
      <!-- 标题区域的插槽 -->
      <template #title>
        <div class="title-box">
          <!-- 标题 -->
          <span>{{ title }}</span>
          <!-- 单张图片 -->
          <img :src="cover.images[0]" alt="" class="thumb" v-if="cover.type === 1">
        </div>
        <!-- 三张图片 -->
        <div class="thumb-box" v-if="cover.type === 3">
          <img :src="item" alt="" class="thumb" v-for="(item, index) in cover.images" :key="index">
        </div>
      </template>
      <!-- label区域的插槽 -->
      <template #label>
        <div class="label-box">
          <span>作者 {{ author }}&nbsp;&nbsp; {{ cmbCount }} 评论 &nbsp;&nbsp; 发布日期 {{ time }}</span>
          <!-- 关闭按钮 -->
          <van-icon name="cross"></van-icon>
        </div>
      </template>
    </van-cell>
  </div>
</template>

<script>
export default {
  name: 'ArticleInfo',
  props: {
    // 标题
    title: {
      type: String,
      default: ''
    },
    // 作者
    author: {
      type: String,
      default: ''
    },
    // 评论数
    cmbCount: {
      // 通过数组的形式,当前属性定义为多个可能的类型
      type: [String, Number],
      default: 0
    },
    // 发布时间
    time: {
      type: String,
      default: ''
    },
    // 封面的信息对象
    cover: {
      type: Object,
      default: function() {
        return { type: 0 }
      }
    }
  }
}
</script>

<style lang="less" scoped>
.label-box {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.thumb {
  width: 113px;
  height: 70px;
  background-color: #f8f8f8;
  object-fit: cover;
}
.title-box {
  display: flex;
  justify-content: space-between;
  align-items: flex-start
}
.thumb-box {
  display: flex;
  justify-content: space-between;
}
</style>

(2)Home.vue中引入并使用ArticleInfo.vue组件

 注:主要涉及的是props值的传递。

tips:(1)props值类型可设置为数组,定义为多个可能的类型

 (2)父组件在设置属性值时,若原变量名为小驼峰式,最好修改为全部小写,中间加-。如下:

 2.7 上拉加载更多

借助vant中展示组件中的List列表实现上拉加载更多的功能

(1)使用van-list包裹<ArticleInfo>

(2)data中声明:loading(默认true)、finished(默认false)两个属性

(3)onLoad方法定义(page++、请求数据)

(4)修改initArticleList方法(数据拼接、loading属性设置、finished属性设置)

修改后的Home.vue代码如下:

<template>
  <div class="home-container">
    <van-nav-bar title="头条" fixed/>
    <van-list
  v-model="loading"
  :finished="finished"
  finished-text="没有更多了"
  @load="onLoad"
>
    <ArticleInfo
    v-for="item in articleList"
    :key="item.art_id" :title="item.title"
    :author="item.aut_name"
    :cmbCount="item.comm_count"
    :time="item.pubdate"
    :cover="item.cover">
    </ArticleInfo>
</van-list>
  </div>
</template>

<script>
import { getArticleListAPI } from '@/api/articleAPI.js'
import ArticleInfo from '@/components/Article/ArticleInfo.vue'

export default {
  name: 'Home',
  components: {
    ArticleInfo
  },
  data() {
    return {
      // 页码值
      page: 1,
      // 每页展示的数据条数
      limit: 10,
      // 文章列表
      articleList: [],
      // 是否正在加载下一页数据,loadding为true,则不会反复触发load事件
      // 当下一页数据请求回来后,要记得把loading改为false
      // 初始值设为true,可以防止首次进入页面时调用load事件
      loading: true,
      // 所有数据是否加载完成
      finished: false
    }
  },
  created() {
    this.initArticleList()
  },
  methods: {
    async initArticleList() {
      const { data: res } = await getArticleListAPI(this.page, this.limit)
      // this.articleList=[旧数据,新数据]
      this.articleList = [...this.articleList, ...res]
      this.loading = false

      if (res.length === 0) {
        // 无下一页数据,finished设置为true
        this.finished = true
      }
    },
    onLoad() {
      console.log('ok')
      // 页码值加1
      this.page++
      // 重新请求数据
      this.initArticleList()
    }
  }
}
</script>
<style lang="less" scoped>
.home-container {
  padding: 46px 0 50px 0;
  .van-nav-bar {
    background-color: #007bff;
  }
  /deep/ .van-nav-bar__title {
    color: aliceblue;
  }
}
</style>

2.8 下拉刷新

借助vant中展示组件中的List列表实现下拉刷新更多的功能.

(1)使用van-pull-refresh包裹van-list。并将其disabled属性设置为finished。也就是说当加载完成时,设置下拉刷新不可用

(2)data中定义refreshing属性,初始值设置为false

(3)定义onRefresh方法(page++、请求数据)

(4)修改initArticleList方法。(添加isRefresh入参、判断是下拉刷新还是上拉加载更多。前者是新数据在前,后者是新数据在后)

修改后的Home.vue如下:

<template>
  <div class="home-container">
    <van-nav-bar title="头条" fixed/>
    <van-pull-refresh v-model="refreshing" :disabled="finished" @refresh="onRefresh">
    <van-list
  v-model="loading"
  :finished="finished"
  finished-text="没有更多了"
  @load="onLoad"
>
    <ArticleInfo
    v-for="item in articleList"
    :key="item.art_id" :title="item.title"
    :author="item.aut_name"
    :cmbCount="item.comm_count"
    :time="item.pubdate"
    :cover="item.cover">
    </ArticleInfo>
</van-list>
</van-pull-refresh>
  </div>
</template>

<script>
import { getArticleListAPI } from '@/api/articleAPI.js'
import ArticleInfo from '@/components/Article/ArticleInfo.vue'

export default {
  name: 'Home',
  components: {
    ArticleInfo
  },
  data() {
    return {
      // 页码值
      page: 1,
      // 每页展示的数据条数
      limit: 10,
      // 文章列表
      articleList: [],
      // 是否正在加载下一页数据,loading为true,则不会反复触发load事件
      // 当下一页数据请求回来后,要记得把loading改为false
      // 初始值设为true,可以防止首次进入页面时调用load事件
      loading: true,
      // 所有数据是否加载完成
      finished: false,
      // 是否正在加载下一页数据,refreshing为true,则不会反复触发onRefresh事件
      refreshing: false
    }
  },
  created() {
    this.initArticleList()
  },
  methods: {
    async initArticleList(isRefresh) {
      console.log('------')
      console.log(this.page)
      const { data: res } = await getArticleListAPI(this.page, this.limit)
      // isRefresh为true,证明是下拉刷新,新数据在前
      if (isRefresh) {
        // this.articleList=[新数据, 旧数据]
        this.articleList = [...res, ...this.articleList]
        this.loading = false
      } else {
      // this.articleList=[旧数据,新数据]
        this.articleList = [...this.articleList, ...res]
        this.loading = false
      }

      if (res.length === 0) {
        // 无下一页数据,finished设置为true
        this.finished = true
      }
    },
    onLoad() {
      // 页码值加1
      this.page++
      // 重新请求数据
      this.initArticleList(true)
    },
    onRefresh() {
      // 页码值加1
      this.page++
      // 重新请求数据
      this.initArticleList()
    }
  }
}
</script>
<style lang="less" scoped>
.home-container {
  padding: 46px 0 50px 0;
  .van-nav-bar {
    background-color: #007bff;
  }
  /deep/ .van-nav-bar__title {
    color: aliceblue;
  }
}
</style>

至此,购物车项目首页开发完成。

项目完整链接Headline | 黑马头条 - 移动端

三、 定制主题(参考vue官网)

背景:一个组件在多个页面需要引入,如NavBar 导航栏默认背景颜色为白色,如果要修改颜色,则每个页面都需要重复修改。为了解决该问题,提出定制主题的思路。

步骤一 引入样式源文件

定制主题时,需要引入组件对应的 Less 样式文件,修改的是main.js文件。注释原引入index.css文件的行,修改引入文件为index.less文件。

// import 'vant/lib/index.css'
// 引入全部样式
import 'vant/lib/index.less'

步骤二 修改样式变量

本项目为 vue-cli 搭建的项目,在 vue.config.js 中进行配置。根目录下新建 vue.config.js文件。

// vue.config.js
module.exports = {
  css: {
    loaderOptions: {
      less: {
        // 若 less-loader 版本小于 6.0,请移除 lessOptions 这一级,直接配置选项。
        lessOptions: {
          modifyVars: {
            // 直接覆盖变量
            'text-color': '#111',
            'border-color': '#eee',
            // 或者可以通过 less 文件覆盖(文件路径为绝对路径)
            hack: `true; @import "your-less-file-path.less";`,
          },
        },
      },
    },
  },
};

less版本号查看:package.json搜索less-loader.查看发现小于6.0,移除lessOptions 这一级

// vue.config.js
module.exports = {
  css: {
    loaderOptions: {
      less: {
        modifyVars: {
          // 直接覆盖变量
          // 'text-color': '#111',
          // 'border-color': '#eee',
          // 或者可以通过 less 文件覆盖(文件路径为绝对路径)
          // hack: 'true; @import "your-less-file-path.less";'
        }
      }
    }
  }
}

查看vue官网,发现NavBar样式变量中控制背景颜色的变量如下:

方式一:直接覆盖变量。 修改vue.config.js的样式变量:(不采用该方式)

// vue.config.js
module.exports = {
  css: {
    loaderOptions: {
      less: {
        modifyVars: {
          // 直接覆盖变量
          'nav-bar-background-color': 'red'
        }
      }
    }
  }
}

修改后生效,以后NavBar默认颜色为红色。该方法的缺陷在于每次修改完都需要执行npm run serve重启服务器。采用方式二避免该问题。

方式二:新建theme.less文件,并在vue.config.js 中引入。(采用)

(1)在src下新建theme.less文件,并在文件中设置背景颜色和字体颜色

@blue: #007bff;

@nav-bar-background-color: @blue;
@nav-bar-title-text-color: #ecf0f3;

(2)vue.config.js中引入theme.less文件,如下:

// vue.config.js
const path = require('path')
const themePath = path.join(__dirname, './src/theme.less')
module.exports = {
  css: {
    loaderOptions: {
      less: {
        modifyVars: {
          // 或者可以通过 less 文件覆盖(文件路径为绝对路径)
          hack: `true; @import "${themePath}";`
        }
      }
    }
  }
}

则通过修改theme.less文件保存后样式直接生效,不需再重启服务器。

四、vue.config.js 相关配置

(1)publicPath设置为'/'或者''。打包后(npm run build)的dist/index.html支持file协议打开,也就是双击本地文件打开。默认是仅支持http协议的。

浏览器输入:D:/vueworkspace/toutiao/dist/index.html可直接访问。

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值