[记录]基于vue3和vant3实现的移动端"知乎日报"

236 阅读4分钟

一、 起源

我平时喜欢刷刷知乎,看看每日推送的精选日报。但在手机上必须下载知乎日报app, 在PC端看网页版的知乎日报体验并不怎么好。找了一圈也没有发现移动端的网页版知乎日报。于是乎萌生出模仿“知乎日报”app做一个移动端项目的想法,说干就干。

二、 技术栈

本项目采用Vue3实现,搭配有赞前端团队开源的一套轻量、可靠的移动端组件库Vant3。

vue3+vue-router+axios+vant3

三、 项目演示&数据来源

因为目前没有找到登陆相关的api,因此需要登陆后才能进行的互动操作,如点赞、评论、收藏功能暂未实现。

四、 项目实现

项目目录结构:
image.png

1. 项目准备

1.1 数据接口准备

安装axios用于发送请求获取数据

npm install axios

在utils文件夹下创建request.js,用于封装axios

import ajax from 'axios'

const axios = ajax.create({
  timeout: 10000, // 超时时间
})

export default ({ url, method = 'GET', params = {}, data = {}, headers = {} }) => {
  return axios({
    url,
    method,
    params, // 即将与请求一起发送的 URL 参数
    data, // 发送给服务端的数据
    headers
  })
}

在api文件夹下创建index.js,统一管理请求接口

import request from "../utils/request";

// 获取轮播的消息(六条)
export const getLatestNewsAPI = () => request({
  url: '/api/news/latest'
})

// 获取回答详情
export const getAnswerDetailAPI =(id)=>request({
  url: `/api/news/${id}`
})

// 获取热门回答列表
export const getHotAnswerAPI = ()=>request({
  url: '/api/news/hot'
})

// 获取以往的回答
export const getBeforeAnswerAPI= (time)=>request({
  url: `/api/news/before/${time}`
})

// 获取回答的额外信息(点赞、评论数)
export const getExtraInfoAPI = (id)=>request({
  url: `/api/story-extra/${id}`
})

// 获取长评论
export const getLongCommentAPI = (id)=>request({
  url: `/api/story/${id}/long-comments`
})

// 获取短评论
export const getShortCommentAPI = (id)=>request({
  url: `/api/story/${id}/short-comments`
})

// 获取所有的合集
export const getAllSectionAPI = ()=>request({
  url: '/api/sections'
})

// 获取某个合集的详情列表
export const getSectionList = (id)=>request({
  url: `/api/section/${id}`
})

// 获取往前的合集
export const getSectionBefore = (id,timestamp)=>request({
  url: `/api/section/${id}/before/${timestamp}`
})

1.2 Vant组件库的使用

在项目中引入Vant可以直接参考 官方文档 , 这里就不再赘述。

1.3 开发环境下的代理配置

在根目录下创建vue.config.js文件,在文件中添加如下的配置用于开启代理:

let path = require('path')
module.exports = {
  // 开启代理服务器
  publicPath: './',
  devServer: {
    // 让代理服务器将请求转发到http://news-at.zhihu.com/api/4
    proxy: {
      '/api': {
        target: 'http://news-at.zhihu.com/api/4',
        pathRewrite: {
          '^/api': ''
        }, // 重写请求路径,因为带了前缀,还需要再把前缀去掉再转发到服务器
        changeOrigin: true //用于控制请求头中host值
      }
    }
  }
}

2. 首页部分实现

首页实现的效果图

image.png
首页主要分为: 头部区域、轮播区域和回答列表区域。
头部区域用于显示欢迎词以及日期时间, 时间格式处理使用的day.js。在utils文件夹下创建time专门用于处理时间格式:

import dayjs from 'dayjs'
import 'dayjs/locale/zh-cn'
import relativeTime from 'dayjs/plugin/relativeTime'
dayjs.extend(relativeTime)
dayjs.locale('zh-cn')

const month = ['一', '二', '三', '四', '五', '六', '七', '八', '九', '十', '十一', '十二']
// 获取当前的日期(天)
export const getDay = () => {
  return dayjs().date()
}
// 返回当前的月份
export const getMonth = () => {
  return `${month[dayjs().month()]}月`
}

// 根据当前的时间返回问候语
export const getTimeNow = () => {
  const now = dayjs().hour()
  if (now < 5) {
    return '夜深人静'
  } else if (now < 8) {
    return '早上好'
  } else if (now < 12) {
    return '上午好'
  } else if (now < 13) {
    return '中午好'
  } else if (now < 19) {
    return '下午好'
  } else {
    return '晚上好'
  }

}

// 返回当前时间的年月日: YYYYMMDD
export const getTimeFormat = () => {
  return dayjs().format('YYYYMMDD')
}
// 返回当前时间的月日: X月X日
export const getMonthAndDay = (date = '') => {
  const day =parseInt(date.slice(6)) 
  const month =parseInt(date.slice(4, 6)) 
  return `${month}${day}日`
}

// 减去一天的时间
export const getSubtractTime = (days = 1) => {
  return dayjs().subtract(`${days}`, 'day').format('YYYYMMDD')
}

// 距离现在多久
export const getFromNow = (time)=>{
  return dayjs(time*1000).fromNow()
}

2.轮播切换

轮播.gif
轮播使用的是Vant的轮播组件,轮播时间间隔为3s。

// 获取轮播数据
 onActivated(async () => {
      console.log('home actived')
      // 获取首页轮播
      const res = await getLatestNewsAPI()
      const { date, stories, top_stories } = res.data
      state.topAnswer.push(...top_stories) // 6条轮播数据
      state.newAnswerList.push({ // 回答列表的前6条回答和轮播是一样的
        date,
        stories,
      })
    })

3.回答列表以及滚动加载更多
首页初始化时会展示当天推荐的回答,这里使用的vant的list列表组件。
例如今天是2022年5月20日,查询 2 月 20 日的消息,请求的时间参数应为 20220521,需要在当前时间的基础上再+1。当滚动条距离底部30px会去请求前一天的回答。

加载.gif

// 在home组件中定义加载更多的回答方法
    const getMoreAnswer = async (num) => {
      const day = getSubtractTime(num) // 处理时间参数,当前日期 -1
      const res = await getBeforeAnswerAPI(day)
      const { date, stories } = res.data
      state.newAnswerList.push({
        date,
        stories,
      })
      // 根据时间先后进行排序,防止乱序
      state.newAnswerList.sort((a, b) => {
        return b.date - a.date
      })
   }

// 回答列表组件中通过inject接收父组件getMoreAnswer方法后调用
// 滚动时获取更多的回答
    const onLoad = () => {
      // 开始加载
      state.loading = true
      getMoreAnswer(num)
      // 结束加载
      state.loading = false
      // 往后的多少天
      num++
    }

4.回答详情页

image.png

5. 回答评论

评论.gif

总结

调用知乎api的跨域问题,这里是使用vue-cli配置代理

devServer: {
    // 让代理服务器将请求转发到http://news-at.zhihu.com/api/4
    proxy: {
      '/api': {
        target: 'http://news-at.zhihu.com/api/4',
        pathRewrite: {
          '^/api': ''
        }, // 重写请求路径,因为带了前缀,还需要再把前缀去掉再转发到服务器
        changeOrigin: true //用于控制请求头中host值
      }
    }
  },

本项目主要是本人在学习vue3过程中用于巩固知识点,因此难免会有许多的疏漏和不完善之处。

项目地址

gitee.com/wblues/zhih…