黑马头条案例(基于vue2)

一、目标:

       目的:自己做记录,记录制作过程以及遇到的问题。内容来自b站的up主:黑马程序员(up主名字)的黑马程序员Vue全套视频教程,从vue2.0到vue3.0一套全覆盖,前端学习....(视频名称部分)。

       效果展示:

 二、开始制作

(一)、初始化操作:

 (1)创建项目

1.  打开一个文件夹,在所处文件夹中打开终端(将鼠标放在文件夹名字那一栏的空白处(如图所示),点击一下,输入cmd回车,再按回车打开终端)

2.  在终端中输入:vue create toutiao,开始进行项目的创建,选择如下选项(多选选项:点击空格添加“*”号是选择,回车进入下一步,通过下上键进行选项切换)

History路由需要和后端配合使用,兼容性较差。最后一个的意思是存不存为一个预设,存不存都无所谓。等它加载成(如图)这样就是创建好了。然后关闭终端,用vscode打开我们创建的文件。在vscode中打开终端。

3.  图上这两个文件夹的区别:它们两个都是放组件的,如果某个组件是通过路由来进行动态切换的,这种组件放在views里面;如果不是通过路由来切换的,是一个可复用的组件,放在components里面

因为它会自动生成代码,打开app.vue,ctrl+A全选删除。Views文件夹中自动生成了两个组件,不需要,直接右键删除,components下面的helloworld也不需要,删除。将router文件夹里面的index.js里routes的数组清空,然后把导入的home组件也删除

更改以后的index.js:

import Vue from 'vue'
import VueRouter from 'vue-router'
// 把VueRoter安装为Vue的插件
Vue.use(VueRouter)
// 路由规则的数组
const routes = []
// 创建路由实例对象
const router = new VueRouter({
  routes
})
export default router

打开终端,npm run serve,运行,按住ctrl点击local对应的网址,打开网页,F12进入,以移动设备展示

4.  安装和配置vant组件:Vant 2 - 轻量、可靠的移动端组件库 (gitee.io) (如果直接在浏览器输入的,要记得选择适用于vue2的那个)它提供了很多组件,很适合移动端的开发

4.1 进行安装

选择命令进行安装。点击加号新建一个终端,在新的终端里面复制命令(鼠标右键即可在终端中复制)。如果出现了以下情况的报错:

输入 npm i vant@latest-v2 -S --legacy-peer-deps (legacy的意思:遗产/(软件或硬件)已过时但因使用范围广而难以替代的;而npm install xxxx --legacy-peer-deps命令用于绕过peerDependency里依赖的自动安装;它告诉npm忽略项目中引入的各个依赖模块之间依赖相同但版本不同的问题,以npm v3-v6的方式去继续执行安装操作。所以其实该命令并没有真的解决冲突,而是忽略了冲突,以“过时”(v3-v6)的方式进行下载操作。)

4.2 进行配置   安装了之后继续看文档,看如何进行配置。

因为组件很多,但不是每一个我们都需要,如果我们都打包体积就会很大,出于性能上考虑,方法一更好。但由于方法一比较麻烦复杂,所以在开发阶段,由于不需要考虑体积的问题,我们通常采用方法三。在发布阶段,可以直接把vant抽出去(将vant优化掉)。

注意:在同一个文件中,不要重复导入,复制没有的粘贴即可(import必须放在头部)

更改之后的main.js:

import Vue from 'vue'
import App from './App.vue'
import router from './router'
// 导入并安装Vant组件库
import Vant from 'vant';
import 'vant/lib/index.css';
Vue.use(Vant);
Vue.config.productionTip = false
new Vue({
  router,
  render: h => h(App)
}).$mount('#app')

5.  实现底部Tabbar的切换:

要实现这两个组件的切换,首先我们需要这两个组件。所以(1)创建两个组件的页面,那这两个组件我们应该放在哪个目录(components或views)下呢?放在views下,因为这两个页面都是通过路由来加载的。所以在views文件夹下新建User文件夹(在文件夹里面创建User.vue)和Home文件夹(在文件夹里面创建Home.vue),注意等级关系,不要放错位置将两个的基础部分都写好。

(2)组件页面写好了,来到vant寻找合适的组件,在“导航组件”里面有个“Tabbar标签栏”。思考:假设我们要使用它,应该将它放在何处展示?放在根组件的底部,点击即切换,底下我们可以认为是两个路由链接,它的上面就是一个路由占位符,下面就是vant的组件。打开app根组件。

v-model:双向数据绑定指令。此处我们不需要使用v-model="active",保存查看效果

 

效果图

删掉保留两个,切换第二个的图标(如何换图标?点开vant文档,点击基础组件,点开icon图标,找到自己喜欢的,将图标名字替换成自己挑的那个)

(选择的图标)

(更改后的)

此时点击它们两个还不会切换,所以我们要想办法把底下两个改造成路由链接进行跳转。(3)来到vant文档的Tabbar标签栏,来到“路由模式”

它是给容器加route属性,里面每一个可点的项目加“to”属性,指向我们要跳转的hash地址。

首页就是“/”。打开页面,点击两个图标,看见头部的文件地址会随之切换。

注意:此时底部不会高亮,之后加上路由规则就可以高亮了,试过了配置了之后就可以高亮了。

将占位符写上:

<template>
  <div>
    <!-- 路由占位符 -->
    <router-view></router-view>
    <!-- Tabbar区域 -->
    <van-tabbar route>
  <van-tabbar-item icon="home-o" to="/">首页</van-tabbar-item>
  <van-tabbar-item icon="user-o" to="/user">我的</van-tabbar-item>
</van-tabbar>
  </div>
</template>

6.  来到index.js。进行如下配置:

import Vue from 'vue'
import VueRouter from 'vue-router'
// 导入需要的组件
import Home from '@/views/Home/Home.vue'
import User from '@/views/Users/User.vue'
// 把VueRoter安装为Vue的插件
Vue.use(VueRouter)
// 路由规则的数组
const routes = [
  // 定义首页的路由规则
  { path:'/',component:Home },
  // 定义我的路由规则
  { path:'/user',component:User }
]
// 创建路由实例对象
const router = new VueRouter({
  routes
})
export default router

7.  制作“我的”页面

这个页面只设计,不做功能。在User.vue组件中,声明如下内容:

<template>
  <div class="user-container">
    <!-- 用户基本信息面板 -->
    <div class="user-card">
      <!-- 用户头像、姓名 -->
      <van-cell>
        <!-- 使用 title 插槽来自定义标题 -->
        <template #icon>
          <img src="../../assets/logo.png" alt="" class="avatar" />
        </template>
        <template #title>
          <span class="username">用户名</span>
        </template>
        <template #label>
          <van-tag color="#fff" text-color="#007bff">申请认证</van-tag>
        </template>
      </van-cell>
      <!-- 动态、关注、粉丝 -->
      <div class="user-data">
        <div class="user-data-item">
          <span>0</span>
          <span>动态</span>
        </div>
        <div class="user-data-item">
          <span>0</span>
          <span>关注</span>
        </div>
        <div class="user-data-item">
          <span>0</span>
          <span>粉丝</span>
        </div>
      </div>
    </div>

    <!-- 操作面板 -->
    <van-cell-group class="action-card">
      <van-cell icon="edit" title="编辑资料" is-link />
      <van-cell icon="chat-o" title="小思同学" is-link />
      <van-cell icon="warning-o" title="退出登录" is-link />
    </van-cell-group>
  </div>
</template>

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

<style lang="less" scoped>
.user-container {
  .user-card {
    background-color: #007bff;
    color: white;
    padding-top: 20px;
    .van-cell {
      background: #007bff;
      color: white;
      &::after {
        display: none;
      }
      .avatar {
        width: 60px;
        height: 60px;
        background-color: #fff;
        border-radius: 50%;
        margin-right: 10px;
      }
      .username {
        font-size: 14px;
        font-weight: bold;
      }
    }
  }
  .user-data {
    display: flex;
    justify-content: space-evenly;
    align-items: center;
    font-size: 14px;
    padding: 30px 0;
    .user-data-item {
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-items: center;
      width: 33.33%;
    }
  }
}
</style>

(二)实现首页

1.  开始实现首页。(1)首先实现头部,①来到vant文档,找到导航组件的Navbar导航栏,复制粘贴,保留自己需要的部分。放在home组件下。留下标题后,我们要注意,此时如果内容过多,在滚动时标题也会随之消失,如何让标题一直留在头部?

点开vant文档,继续往下翻,来到“api”,看到如下:

更改为如下:(因为是布尔值,所以需要加“:”进行属性绑定

(简写形式)

因为在使用vant组件的时候,如果某个组件的某个属性默认值是false,想要改成true,只需要直接写属性名称即可。

输入一点内容,如下图:

我们可以看到,第一行的内容被遮住了,为什么呢?因为标题固定定位了,脱离了标准文档流,所以覆盖掉了。同样,当内容过长时,底部的内容也会被遮盖住,因为底部导航栏也是固定定位。如何解决?给home组件的盒子加个样式,打开我们编写的页面测量出上面标题栏高度为46,下面导航栏是50,使用样式如图(此处的46与50是通过检查盒子的大小得到的)

  

②完善样式:点开我们写的页面,检查代码,点击头部区域,找到背景颜色所在的位置,复制样式的名称:.van-nav-bar,放在home组件中。找到题目文字,改他的颜色,发现直接改不好使,在前面加/deep/,就可以了。为什么呢?因为加上deep之后她就不只是类名[ ]选择器了,而是变成后代选择器

 

 

(加上deep之后变成这样)

 (改标题)

注意:这种改法有一个缺点,就是只改了当前页面的样式,假设我们还有一个页面也用到了这个头部组件,在那个页面他的颜色并不会改变。(vant有定制主题,可以使用定制主题进行统一更改)

(1)文章列表功能

(cover是文章的封面)

  1.  用axios调接口,拿数据。在终端装包,npm i axios -S. 装完包之后,就要开始配置,先在main.js里面导入axios

1.1 在src文件夹下创建文件夹utils(代表工具,存放工具模块),在里面封装一个request.js(因为我们要调接口,所以先找到我们的小axios,因此先创建一个request模块,这个模块就是一个工具模块)。

request.js代码:

import axios from 'axios'
const requset = axios.create({
  // 指定请求的根路径
  baseURL: 'https://applet-base-api-t.itheima.net'
})
export default requset

1.2 在home组件里面一进来就要发请求,拿数据。所以在home组件里面封装一个methods

Home.vue(script部分)代码:

<script>
import request from '@/utils/request';
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);
   }
 }
}
</script>

图为控制台(拿到的数据)展示内容:

1.3 封装articleAPI模块。假如我们希望一点开“我的”便获取到数据,应该怎么办呢?先点开user.vue,在里面封装方法,步骤跟上面一样。但是这样写完了代码会重复,我们希望能够复用,应该怎么做呢?为什么加await,因为调用的返回值是params,将来我们如果能拿到一个params的实例对象,就可以了。放哪去呢?

所以怎么把代码抽走呢?在src目录下,新建接口api文件夹,创建获取文件数据的api(articleAPI.js)

  (在App.vue里)(得到的结果)

传什么值,就请求谁的数据。所以在其他组件导入使用它时,需要传参

articleAPI.js:

将Home.vue里面改成如下:

 

这样,如果我们要调接口获取用户的信息,新建api专门用来获取用户的。首先要导入request,向外导出一个方法,需要传参就用params,不需要传参直接get,将结果return出去。

1.4 渲染列表的数据,封装ArticleInfo组件。ArticleInfo组件放在components文件夹下,在components文件夹下创建一个Article文件夹,在Article文件夹下存放ArticleInfo组件

(ArticleInfo.vue里面)

如果某一个数据要渲染到页面上使用,要把数据放在data里面。此处,res是数据

 此时Home.vue的代码:

<template>
  <!-- <h1>Home组件</h1> -->
  <div class="home-container">
    <van-nav-bar title="黑马头条" fixed/>
    <p>{
   
   { artlist.length }}</p>
    <ArticleInfo v-for="item in artlist" :key="item.id"></ArticleInfo>
  </div>
</template>

<script>
import { getArticleListAPI } from '@/api/articleAPI.js';
import ArticleInfo from '@/components/Article/ArticleInfo.vue'
export default {
 name:'Home',
 data(){
  return {
    page:1,
    limit:10,
    // 文章的数组
    artlist:[]
  }
 },
 created(){
  this.initArticleList()
 },
 methods:{
  async initArticleList(){
    const { data:res } =await getArticleListAPI(this.page,this.limit) 
    // console.log(res);
    this.artlist=res
   }
 },
 components:{
  ArticleInfo
 }
}
</script>

<style lang="less" scoped>
.home-container {
  padding: 46px 0 50px 0;

  .van-nav-bar {
  background-color: #007bf0;
}

/deep/ .van-nav-bar__title {
  color: #fff;
}
}
</style>

打开页面显示显示的效果:

1.4.1 在 ‘ArticleInfo.vue’组件中,声明如下的模板结构:

<template>
  <div>
    <van-cell>
      <!-- 标题区域的插槽 -->
      <template #title>
        <div class="title-box">
          <!-- 标题 -->
          <span>文章的标题噢</span>
          <!-- 单张图片 -->
          <img src="https://www.escook.cn/vuebase/pics/1.png" alt="" class="thumb">
        </div>
        <!-- 三张图片 -->
        <div class="thumb-box">
          <img src="https://www.escook.cn/vuebase/pics/2.png" alt="" class="thumb">
          <img src="https://www.escook.cn/vuebase/pics/2.png" alt="" class="thumb">
          <img src="https://www.escook.cn/vuebase/pics/2.png" alt="" class="thumb">
        </div>
      </template>
      <!-- label 区域的插槽 -->
      <template #label>
        <div class="label-box">
          <span>作者 &nbsp;&nbsp; 0评论 &nbsp;&nbsp; 发布日期</span>
          <!-- 关闭按钮 -->
          <van-icon name="cross" />
        </div>
      </template>
    </van-cell>
  </div>
</template>

<script>
export default {
  name:'ArticleInfo',
}
</script>

<style lang="less" scoped>
.label-box {
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: medium;
}
.thumb {
  // 矩形黄金比例:0.618
  width: 150x;
  height: 90px;
  background-color: #f8f8f8;
  object-fit: cover;
}

.title-box {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  font-size: large;
}

.thumb-box {
  display: flex;
  justify-content: space-between;
}
</style>

将home组件里面刚刚p标签注释掉,页面展示效果如下:

此时内容都是写死的,所以需要我们自定义属性

自定义属性运用差值表达式

(效果展示图)

此时会没有内容先显示,所以应该在使用文章组件的时候传一个title进去(:title【属性名叫title】,值等于item项.标题,查看上面的表)。

(home.vue)(效果展示)

然后就一个一个的传过去

为ArticleInfo组件封装cover属性。

因为有一张图片的和三张图片的,所以需要v-if的判断,由于他们两个不是平级,所以不能用v-else

此时图片地址还是写死的,用cover.image

一张图片的索引为0,三张的采用循环,key后面绑定的值无意义(直接拿索引当做key)

(自定义属性里面)

(home组件里面) 

(2)上拉加载更多功能

  1. 1.了解list组件。打开vant文档,找到list列表。我们在这里是希望ArticleInfo上拉加载更多
  2. loading=true,代表当前正在请求下一页数据,防止用户多次请求(比如反复上拉加载),只有为false时才会触发loading事件。因为loading为true的时候,不会反复触发load事件
  3. 先将它添加进代码(home组件中)

(写在方法methods里)

(效果图)

但是一进来就会触发一次,这并不是我们想要的效果。因为一进来我们会触发created生命周期,在created中调用了函数拿到了第一页的数据。将loading值改为true就不会触发了,但是这样到底部本来应该触发也没有触发,所以应该在第一页数据请求回来以后改成false

让它请求新数据

同时,当前是把最新的数据覆盖掉上一页的数据,这样不对,应该在覆盖之前先进行新旧数据的拼接。

使用push不行,用…;如果把两个数组合成一个大数组,用…

当res等于空数组,证明没有下一页了,此时将finish改为true,同时解决了刚才一过快就会卡死的现象

当前Home.vue的(script部分)代码:

<script>
import { getArticleListAPI } from '@/api/articleAPI.js';
import ArticleInfo from '@/components/Article/ArticleInfo.vue'
export default {
 name:'Home',
 data(){
  return {
    page:1,
    limit:10,
    // 文章的数组
    artlist:[],
    loading:true,
    finished:false
  }
 },
 created(){
  this.initArticleList()
 },
 methods:{
  async initArticleList(){
    const { data:res } =await getArticleListAPI(this.page,this.limit) 
    // console.log(res);
    this.artlist=[...this.artlist,...res]
    this.loading=false
    if(res.length===0){
      this.finished=true
    }
   },
   onLoad(){
    console.log('触发了load事件');
    this.page++
    this.initArticleList()
   }
 },
 components:{
  ArticleInfo
 }
}
</script>

(3)下拉刷新功能

1. 同样在vant文档的list组件中,将其包裹在外面,

(写在method里面)

此时它会一直显示“加载中”这样。只要我们下拉刷新一松手就应该发起请求去拿数据,当数据拿回来以后,我们应该把refreshing重置成false

注意:此时是全部都是尾部拼接,这样就只能实现上拉加载更多,所以我们应该进行判断。加个布尔值isRefresh。什么时候会传一个true进来,设置下拉的时候会传一个true;如果什么都不传,就是undefined。给下拉刷新传一个true进来,上拉加载没有传true,默认为undefined

当前Home.vue的(script部分)代码:

<script>
import { getArticleListAPI } from '@/api/articleAPI.js';
import ArticleInfo from '@/components/Article/ArticleInfo.vue'
export default {
 name:'Home',
 data(){
  return {
    page:1,
    limit:10,
    // 文章的数组
    artlist:[],
    loading:true,
    finished:false,
    refreshing:false
  }
 },
 created(){
  this.initArticleList()
 },
 methods:{
  async initArticleList(isRefresh){
    const { data:res } =await getArticleListAPI(this.page,this.limit) 
    if(isRefresh){
      this.artlist=[...res,...this.artlist]
      this.refreshing=false
    }else{
      this.artlist=[...this.artlist,...res]
      this.loading=false
    }
    if(res.length===0){
      this.finished=true
    }
   },
   onLoad(){
    console.log('触发了load事件');
    this.page++
    this.initArticleList()
   },
   onRefresh(){
    console.log('触发了下拉刷新事件');
    this.page++
    this.initArticleList(true)
   }
 },
 components:{
  ArticleInfo
 }
}
</script>

这样就能实现了下拉刷新了。同时,当它全部刷新出来没有数据之后,我们应该把下拉刷新禁用掉。来到vant文档,找到pullrefresh,查看是否有禁用:

完成啦!!

猜你喜欢

转载自blog.csdn.net/qq_53767416/article/details/128143297