新闻头条项目详细分析实现过程

196 阅读30分钟

新闻头条

  • 回顾vue基础知识
  • vant组件库的使用
  • 一步步分析需求,实现功能

1、项目初始化

  • 直接使用脚手架项目开发

    • vue这个命令是如何得到 的?答:因为我们之前全局安装了@vue/cli这个包,只需要安装了这个包,我们就能够得到vue这个命令,那么我也就能够通过这个命令来下载脚手架项目。

      • 注:这个@vue/cli只需要全局安装一次即可,以后都可以直接使用vue命令(除非你换了电脑或者重装了系统)
    • vue create hm-project
      
  • 清除脚手架项目默认的欢迎界面等信息

    • src/assets/所有文件
    • src/components/所有文件
    • src/App.vue 里面的代码清除
  • 引入初始化样式

    • 在mian.js中引入预习资料中准备好的reset.less文件

    • import "./style/reset.less"
      
    • 下载less less-laoder

    • npm i less less-loader@5.0.0 -D
      有些下载可能出现报错,报错的怎么办呢?
      答:重新下载最新的less-loader的版本,执行下面命令即可
      npm i less less-loader -D
      
    • 引入全局图标库(app.vue中添加)

    • @import "http://at.alicdn.com/t/font_1426139_h6vn3jbl5q.css";
      
  • 下载必要的依赖包

    • npm i vant@2 axios vue-router@3
      
  • 关闭eslint,在vue.config.js中配置

module.exports = {
    lintOnSave: false
}

注意:写完配置之后,一定一定要重启项目才生效

  • 启动接口服务器

    • 直接双击“启动服务器.bat”文件即可

image-20220524100624827-16533579864991.png

  • 注意:这个启动“接口服务器”的小黑窗是不能关闭,因为关闭的小黑窗,那么服务器也关闭了,也就请求不到数据了

  • 使用mac系统的同学,不能通过bat文件启动接口服务器的,需要自己打开终端,执行npm run start命令启动

  • 接口服务器地址:

    • http://192.168.77.34:3000 // 大家都可以访问
      http://127.0.0.1:3000	 // 本机才可以访问
      

2、登录(login.vue)

1、创建login.vue组件

2、配置路由

3、布局

  • 整体布局,直接从上课资料中复制过来

  • 输入框和按钮:直接使用vant组件中的组件来实现

    • 1、下载插件: npm i babel-plugin-import -D

    • 2、在babel.config.js添加中配置

      • plugins: [
          ['import', {
            libraryName: 'vant',
            libraryDirectory: 'es',
            style: true
          }, 'vant']
        ]
        
    • 3、配置完成后重启项目,我们的配置才生效

    • 按钮组件

    • <van-button type="danger" icon="star-o" round block>登录</van-button>
      

4、实现功能

分析功能:

普通用户角度分析:进入网站,输入账号密码,点击登录按钮,登录成功跳转到首页。

程序员角度分析:获取用户输入的账号密码,给登录按钮绑定点击事件,并声明事件处理函数,事件处理函数中调用登录接口,并给接口传入账号密码,接口返回登录成功,跳转到首页,接口返回失败则弹出提示框。

根据分析拆解步骤:

  • 1、获取用户输入的账号密码,声明变量v-model双向绑定输入框

  • 2、给登录按钮绑定点击事件,并声明事件处理函数

  • 3、在函数中调用登录接口

    • 3-1、封装请求的基础路径,src/utils/reques.js
    • 3-2、封装接口函数,src/api/xxx.js
    • 3-3、在登录页面,引入接口函数,在点击登录的事件处理函数中调用。
  • 4、登录成功跳转首页,失败则弹出提示框。

  • 5、优化,调用接口之前,添加校验,校验账号密码是否为空。

    • 我们直接使用vant组件库中提供的校验

3、注册(register.vue)

1、创建register.vue组件

2、配置路由

3、布局

4、实现功能

用户角度分析:进入页面,输入手机号码,昵称和密码,然后点击注册按钮,实现注册,跳转登录页面。

‘程序员角度分析:获取用户输入信息,绑定点击事件,调用注册接口,实现注册功能,注册成功跳转到登录页面,失败提示用户。

拆解步骤:

  • 1、获取用户输入信息,声明变量,v-model双向绑定输入框。
  • 2、给注册按钮,绑定点击事件,并声明事件处理函数
  • 3、封装接口函数
  • 4、在注册页面引入接口函数,然后事件处理函数中调用接口,完成注册功能
  • 5、判断注册成功跳转到登录页面,否则弹出提示框
  • 6、优化:调用注册接口之前,添加校验用户信息是否为空,只有校验通过才能进行注册

4、个人中心(personal.vue)

1、创建personal.vue组件

2、配置路由

3、布局

4、实现功能

1、个人信息的渲染
2、退出
1、个人信息的渲染

用户角度分析:用户进入个人中心页面,立刻看到个人信息。

程序员角度分析:在created中调用“获取用户信息”的接口,请求并保存数据,然后根据数据进行渲染。

拆解步骤:

  • 1、封装 “获取用户信息” 的接口函数

  • 2、在个人中心页面吗引入接口函数,然后在created中调用接口函数,请求并保存数据

  • 3、根据数据进行渲染

2、退出

分析:用户点击退出按钮,跳转到登录页面

拆解步骤:

  • 1、给按钮绑定点击事件,并声明事件处理函数
  • 2、在事件处理函数中清除登录时保存的token和用户ID
  • 3、最后跳转到登录页面

页面权限-添加导航守卫

导航守卫(路由守卫)是什么?答:就是路由跳转之前会触发的一个函数,这个函数就是beforeEach。

黑马头条项目中:个人中心必须要登录后才能访问,就是可以使用到导航守卫来实现此功能

在src/router/index.js添加如下代码即可

router.beforeEach((to, from ,next)=>{
    let token = localStorage.getItem("75-token")
    // 当用户跳转的页面为“个人中心”且 没有登录的时候 ,跳转到登录页面,否则直接放行
    if(to.path == "/personal" && !token){
        next("/login")
    }else{
        next()
    }
})

优化:添加白名单

// 白名单,所有在白名单中的路径都表示需要登录后才能访问的页面路径
let writeArr = [ "/personal", "/edit_profile" ] 
router.beforeEach((to, from ,next)=>{
    let token = localStorage.getItem("75-token")
    // 当用户跳转的页面为“个人中心”且 没有登录的时候 ,跳转到登录页面,否则直接放行
    // if(to.path == "/personal" && !token || to.path === "/edit_profile"&&!token){
    // 判断到to.path的值在writeArr数组中的时候,说明跳转的目标路由路径必须登录才能访问的。
    if(writeArr.indexOf(to.path) != -1 && !token ){
        next("/login")
    }else{
        next()
    }
})

5、个人信息编辑(edit_profile.vue)

1、创建组件

2、配置路由

3、布局

4、实现功能

1、完成个人信息渲染
2、完成头像修改
3、完成昵称修改
4、完成密码修改
5、完成性别修改
1、完成个人信息渲染

用户角度分析:进入页面,就能马上看到个人 信息

程序员角度分析:进入页面,在created中调用接口,请求数据,渲染数据

拆解步骤:

  • 1、封装“用户详情”接口函数 ---- 在个人中心中已经完成了
  • 2、在页面引入接口函数,并在created中调用接口函数,请求并保存数据
  • 3、根据数据渲染即可
2、完成头像修改

用户角度分析:用户点击头像,显示弹窗用于选择图片,选中图片后修改头像。

程序员角度分析:给图片绑定点击事件,然后显示弹窗用于选择图片,用户选中调用接口上传图片,然后修改图片成功。

image-20220526163931945-16535543732531.png

拆分步骤:

  • 1、实现显示弹窗,用于选择图片
  • 2、实现修改头像功能
    • 1、在成功获取图片对象的函数中,调换用“文件上传”接口,获取图片在线地址
      • 封装“文件上传” 接口函数
      • 在页面中引入接口函数,并在成功获取图片对象的函数中调用,并保存图片在线地址
    • 2、在成功获取到图片在线地址的地方,调用“编辑用户信息”接口,实现修改头像
      • 封装“编辑用户信息”接口函数
      • 在引入接口函数,并在在成功获取到图片在线地址的地方调用函数,实现修改头像
3、完成昵称修改

用户角度分析:点击昵称单元格的时候,显示编辑昵称弹窗,用户输入新的昵称,点击确认按钮,进行修改。

程序员角度分析:给单元格绑定点击事件,并在事件处理函数中实现“编辑昵称”弹窗的显示,收集用户输入的新昵称,给确认按钮绑定点击事件,调用修改昵称接口,实现修改功能。

拆解步骤:

  • 1、给单元格绑定点击事件,并声明事件处理函数

  • 2、准备好“编辑昵称”弹窗组件

  • 3、在点击事件处理函数中控制弹窗的显示

  • 4、收集昵称,声明变量双向绑定弹窗中的输入框

  • 5、给确认按钮绑定点击事件,并声明事件处理函数

  • 6、在事件处理函数中直接调用“编辑用户信息”接口,实现修改昵称。

  • 7、成功关闭弹窗,失败提示用户。

  • 8、优化:当用户输入的昵称为空的时候,且用户点击了确认按钮,此时阻止弹窗关闭。

    • 如何实现阻止弹窗关闭呢?答:看文档组件有没有提供这样的方法来阻止弹窗关闭

image-20220527104450803-16536194922041.png

4、完成密码修改

用户角度分析:点击密码单元格,显示编辑密码弹窗,然后输入原密码和新密码,点击确认按钮实现修改。

程序员角度分析:给单元格添加点击事件,并声明事件处理函数,在事件处理函数中控制编辑密码弹窗的显示,收集用户输入的新旧密码,给确认按钮绑定事件,并在事件处理函数中调用接口实现修改密码。

拆解步骤:

  • 1、给单元格绑定点击事件,声明事件处理函数

  • 2、准备好“编辑密码”弹窗

  • 3、在事件处理函数中控制 "编辑密码弹窗"的显示

  • 4、收集新旧密码,声明变量,双向绑定输入框

  • 5、给“确认”按钮绑定confirm事件,并声明事件处理函数

  • 7、在事件处理函数中调用接口实现修改密码

    • 在调用接口前要添加判断
      • 判断一:原密码输入是否正确
      • 判断二:新密码和原密码不能相同
      • 判断三:新密码不能为空
    • 条件判断都通过后,再调用接口实现修改密码
  • 8、修改成功关闭弹窗,更新原密码,失败提示用户

  • 9、优化:当用户输入的内容不符合要求的时候,且点击了确认按钮,需要阻止弹窗关闭。

5、完成性别修改

用户角度分析:点击单元格,弹出性别框,点击选项实现修改性别功能。

程序员角度分析:给单元格绑定点击事件,声明事件处理函数,显示性别修改框,获取用户点击的性别,调用接口实现修改。

拆解步骤:

  • 1、给单元格绑定点击事件,声明事件处理函数
  • 2、准备好性别框组件
  • 3、在事件处理函数中控制性别框的显示
  • 4、给性别框组件绑定点击事件,并在事件处理函数中获取点击的性别
  • 5、调用接口实现修改
6、头部:点击左箭头回到上一页,点击右侧图标回到首页

image-20220527150436447-16536350776762.png

统一设置token - 请求拦截器的使用

什么是请求拦截器?答:请求发送出去之前会触发的一个函数。

因为项目中很多接口都需要传token给后端,因此我们可以把传递token的代码统一封装起来,那么我就可以利用请求拦截器来完成这个功能。

在src/utils/request.js中添加如下代码即可

// 添加请求拦截器
axios.interceptors.request.use(function (config) {
    // 在发送请求之前做些什么
    // 统一设置token 传递给后端
    // console.log(10, config);
    let token = localStorage.getItem("75-token")
    // 当token存在的时候 ,才把token传递给后端
    if(token){
        config.headers.Authorization = token
    }
    return config;
  }, function (error) {
    // 对请求错误做些什么
    return Promise.reject(error);
});

6、首页(index.vue)

1、创建组件

2、配置路由

3、布局

  • 头部:使用navBar组件完成
  • 栏目列表:使用tab组件完成
  • 新闻列表
    • 封装src/components/newsItem.vue组件,结构和样式从上课资料中获取,然后再首页中引入注册使用
    • 有3种结构,并且一篇文只可能是其中的一种结构。
    • 那么,数据中肯定存在某个条件用来判断文章属于哪种结构。

4、实现功能

1、栏目列表渲染
2、文章列表渲染
3、下拉刷新
4、上拉加载
5、完成点击栏目,切换不同新闻列表数据
1、栏目列表渲染

用户角度分析:点击 登录,进入首页,直接就可以看到栏目列表数据

程序员角度分析:在created钩子函数中,发送请求,获取数据,然后根据数据渲染列表

拆解步骤:

  • 1、封装获取栏目列表的接口函数
  • 2、首页引入接口函数,把并在created中调用接口,请求并保存数据
  • 3、根据数据渲染
2、新闻(文章)列表渲染

用户角度分析:点击 登录,进入首页,直接就可以看到新闻列表数据

程序员角度分析:在created钩子函数中,发送请求,获取数据,然后根据数据渲染列表

拆解步骤:

  • 1、封装获取新闻列表的接口函数
  • 2、首页引入接口函数,把并在created中调用接口,请求并保存数据
  • 3、根据数据渲染
    • 每一篇文章都需要渲染自己的结构,这个结构如何判断呢?
    • 答:文章详情数据中有相应的属性进行判断
      • 视频结构:type值为2
      • 上下结构:type值为1且 cover数组长度大于等于3
      • 左右结构:type值为1且cover数组长度小于3
3、下拉刷新

用户角度分析:用户按住屏幕,往下拖动,会出现“刷新”的一样提示,然后释放手指,开始刷新页面数据。

程序员角度分析:用户按住屏幕,往下拖动,会出现“刷新”的一样提示,然后释放手指,再次调用获取“新闻列表”接口,拿到最新的数据,更新到浏览器中。

拆解:

  • 实现用户按住屏幕往下拖动,出现刷新提示的效果
  • 实现刷新功能 --- 在下拉刷新的时候调用“新闻列表”接口
4、上拉加载

通过分页实现上拉加载功能的,接口提供了2个参数来实现,pageIndex: 表示当前的页数,pageSize:表示数据的条数。

实现上拉加载功能之前,先为我们之前封装的接口添加这个两个参数。

用户角度分析:用户按住屏幕,往上拖动,拖到底部显示“加载中”效果,然后开始加载更多数据。

程序角度分析:用户按住屏幕,往上拖动,拖到底部显示“加载中”效果, 然后开始调用接口加载下一页的数据。

拆解步骤:

  • 1、用户按住屏幕,往上拖动,拖到底部显示“加载中”效果
    • 使用vant组件库实现
  • 2、在上拉加载的回调函数中,调用接口实现加载下一页的数据
5、完成点击栏目,切换不同新闻列表数据

完成,栏目列表,自动选中“头条”的效果,只需要给tabs标签添加v-model="curIndex"即可

用户角度分析:点击不同的栏目,我们要获取到对应栏目的新闻列表展示出来。

程序员角度分析:给栏目绑定点击事件,然后再事件处理函数中获取到栏目的下标值,然后修改curIndex为当前栏目下标值即可,然后调用getNews即可获取到最新的数据。

拆解步骤:

  • 1、给栏目绑定事件,并声明事件处理函数。
  • 2、在函数中获取栏目对应的下标值
  • 3、调用getNews获取栏目对应的新闻列表数据
    • 因为点击栏目切换显示不同数据之前,也需要清空数组和finished等等数据,这些代码我都在下拉刷新函数中存在了,所以最终,我们只需要调用下拉刷新的函数即可。

image-20220529115833044-16537967140201.png

7、文章详情(articleDetail.vue)

1、创建组件

2、配置路由

3、布局

​ 主要结构:直接从上课资料中复制过来使用即可。

4、实现功能

1、根据新闻id渲染新闻详情数据
2、完成关注用户功能
3、完成点赞文章功能
4、完成收藏文章功能

完成从首页跳转到新闻详情页面中,并且传递id到详情页面

image-20220529150752374-16538080735411.png

1、根据新闻id渲染新闻详情数据

用户角度分析:点击首页新闻,跳转到对应的详情页面,然后就能够看到详情内容

程序员角度分析:进入页面立刻调用接口,请求文章详情数据,然后根据详情数据进行渲染。

拆解步骤:

  • 1、封装“文章详情”接口函数
  • 2、在详情页面引入接口函数,并在created中调用接口函数,请求并保存数据
  • 3、根据数据渲染
2、完成关注用户功能

实现功能

用户角度分析:点击关注按钮,实现关注用户功能

程序员角度分析:给关注按钮绑定点击事件,并声明事件处理函数,然后再事件处理函数中调用“关注”接口。

拆解步骤:

  • 1、给关注按钮绑定点击事件,并声明事件处理函数
  • 2、封装“关注”,"取消关注"用户的接口函数
  • 3、在事件处理函数中调用,实现关注功能
    • 文章详情接口返回了一个字段has_follow属性给我们判断当前是否已经关注了该用户,true表示已关注,false表示未关注

实现按钮效果

image-20220529173301467-16538167829002.png 已关注-效果:黑色边框和字体,白色背景

未关注-效果:白色字体,红色背景

需要根据当前作者是否已经关注,然后动态设置样式,动态设置样式有2种方式,动态类名和动态行内样式,这里我们选用动态类名来实现,动态判断的条件就是文章详情中的has_follow属性进行判断,true表示已关注,false表示未关注。

  • 1、准备好类名active,设置已关注样式
  • 2、根据has_follow属性动态设置class即可
3、完成点赞文章功能
  • 功能一:实现点赞按钮的效果

image-20220529174945703-16538177876913.png

  • 已点赞效果:红色字体
  • 未点赞效果:黑色边框黑色字体
  • 我们需要动态设置点赞按钮的效果,那么需要一个判断条件,文章详情接口返回了一个has_like的属性给我们判断,当这个属性的值为true表示已点赞,否则未点赞。
    • 1、准备已点赞的类名active,写上对应的样式
    • 2、根据has_like动态设置类名active即可
  • 动态设置按钮的文本,当已登录显示点赞数量,点赞数量我们的文章详情接口通过like_length属性给我们,未登录显示“点赞”两个字
    • 如何判断用户是否已登录?答:我们可以通过token来判断是否已登录,从动太设置按钮文本。
      • 1、在文章详情页面获取token
      • 2、动态设置按钮的文本
  • 功能二:实现点赞功能
    • 用户角度分析:点击点赞按钮,实现点赞功能
    • 程序员角度分析:给点赞按钮绑定点击事件,声明事件处理函数,然后再函数中调用接口实现点赞
      • 1、给点赞按钮绑定点击事件,声明事件处理函数
      • 2、封装“点赞”的接口函数
      • 3、在详情页面引入接口函数,并在点击事件的处理函数中调用接口,实现功能
4、完成收藏文章功能
  • 功能一:实现收藏按钮的效果
    • 已收藏:显示红色
    • 未收藏:显示黑马
    • 这里我们使用动态行内样式来完成这个功能,然后我们还需要知道如何来判断当前文章是否已收藏,那如何判断呢?答:后端文章详情接口中返回了一个has_star属性,当这个属性值为true表示已收藏,false表示未收藏。 +
  • 功能二:实现收藏功能
    • 用户角度分析:点击收藏按钮,实现收藏效果
    • 程序员角度分析:给收藏按钮绑定点击事件 声明事件处理函数,然后再事件处理函数中调用收藏接口实现收藏功能
    • 拆解步骤:
      • 1、给收藏按钮绑定点击事件 声明事件处理函数
      • 2、封装“收藏”接口函数
      • 3、引入接口函数,并在事件处理函数中调用接口实现收藏功能

8、评论列表

1、创建commentList.vue组件

2、配置路由

3、布局

4、实现功能

1、渲染评论列表

用户角度分析:用户点击文章详情页面中的 “评论数量”或者“更多跟帖”按钮,跳转到评论列表页面,进入页面就可以立刻看到评论的数据。

程序员角度分析:在文章详情页面,给“更多跟帖”和“评论数量”绑定点击事件,并通过事件处理函数跳转到评论列表页面,然后在created中调用接口,请请并保存数据,渲染数据。

拆解步骤:

  • 1、在文章详情页面,给“更多跟帖”和“评论数量”绑定点击事件,并通过事件处理函数跳转到评论列表页面
  • 2、封装 “获取评论列表”的接口函数
  • 3、评论列表页面引入接口函数,并在created中调用 接口,请求并保存数据
  • 4、根据数据渲染
2、渲染评论中的嵌套评论内容

问题1:嵌套评论的内容是如何产生的?答:是通过回复评论产生的,那么数据中如何体现出来呢?答:通过parent属性,当parent为null的时候表示改评论为直接评论,当parent为对象的时候,表示存在嵌套评论。

  • 1、把嵌套评论的结构实现出来
  • 2、根据parent判断,然后进行渲染
3、递归组件的使用

问题:评论内容有多层嵌套,且第二层以及第三层之后的所有嵌套都是一样的结构和样式,因此我们可以把嵌套评论内容部分封装起来,然后使用递归组件渲染内容。

封装src/commentItem.vue递归组件

<template>
    <div class="commentItem">
        <div class="top">
            <div class="left">
                <span>{{item.user.nickname}}</span> &nbsp;&nbsp;&nbsp;
                <span>2分钟前</span>
            </div>
            <span>回复</span>
        </div>
        <!-- 第二层嵌套 start
        递归组件:自己调用了自己,但是一定要有结束的判断
         -->
        <commentItem v-if="item.parent" :item="item.parent"></commentItem>
        <!-- 第二层嵌套 end -->
        <div class="bottom">{{item.content}}</div>
    </div>
</template>

<script>
export default {
    // props: ["item"]
    // name属性相当于通过components注册了该组件,name的值就是注册的组件名
    name: "commentItem", 
    props: {
        item: {
            type: Object, // 规定传入的item的数据类型
            required: true // 规定使用组件时,item为必传
        }
    }
}
</script>
<style lang="less" scoped>
.commentItem {
  border: 1px solid #ccc;
  padding: 5px;
  margin-top: 10px;
  .top {
    font-size: 12px;
    color: #aaa;
    display: flex;
    justify-content: space-between;
  }
  .bottom {
    font-size: 13px;
    line-height: 40px;
  }
}
</style>

image-20220530122750260-16538848714311.png

2、发表评论
  • 1、点击底部输入框,显示文本域。
发表评论有以下几种情况:
1、直接对文章发表评论
2、点击最外层的回复按钮,进行评论
3、点击第一层嵌套的回复按钮,进行评论
4、点击递归组件生成的嵌套的回复按钮,进行评论
  • 1、直接对文章发表评论

    • 用户角度分析:点击底部输入,显示文本域,输入评论内容,点击发送按钮,进行评论。
    • 程序员角度分析:给底部输入框绑定事件,控制文本域的显示,收集用户输入的评论内容,给“发送”绑定点击事件,在事件处理函数中调用接口进行发表评论。
    • 拆解步骤:
      • 1、给底部输入框绑定事件,控制文本域的显示,
      • 2、声明变量,v-model双向绑定文本域
      • 3、给“发送”按钮绑定点击事件,并声明事件处理函数
      • 4、封装“发表评论”的接口函数
      • 5、在事件处理函数中调用“发表评论”接口,实现发表功能。
  • 2、点击最外层的回复按钮,进行评论

    • 用户角度分析:用户点击回复按钮,显示底部文本域,输入评论内容,点击发送按钮,发表成功了。
    • 程序员角度分析:给回复按钮绑定点击事件,并声明事件处理函数,控制底部文本域的显示,收集用户输入的评论内容,给发送按钮绑定点击事件,调用接口实现发表评论。
    • 拆解步骤:
      • 1、给回复按钮绑定点击事件,并声明事件处理函数
      • 2、事件处理函中控制底部文本域的显示
      • 3、收集用户输入的评论内容 ---- 上一个功能已经实现了
      • 4、给发送按钮绑定点击事件,并声明事件处理函数。 --- 上一个功能已经实现了
      • 5、封装接口函数,调用接口函数。 ---- 上一个功能已经实现了
        • 注意:此刻我们是回复评论,因此接口需要添加多一个参数,这个参数就是parent_id。
  • 3、点击第一层嵌套的回复按钮,进行评论

    • 用户角度分析:用户点击回复按钮,显示底部文本域,输入评论内容,点击发送按钮,发表成功了。
    • 程序员角度分析:给回复按钮绑定点击事件,在事件处理函数中控制底部组件文本域的显示,收集用户输入的评论内容,给发送按钮绑定点击事件,调用接口实现发表评论。
    • 拆解步骤:
      • 1、给回复按钮绑定点击事件,并声明事件处理函数
      • 2、在函数中控制底部组件文本域的显示,以及把回复评论的id传递给子组件
        • 因为控制底部组件文本域的显示已经在父组件中实现了,因此我们可以在commentItem组件中通过子传父,通知父组件控制底部组件文本域的显示即可,
        • 因为id传递给底部组件也已经在父组件中实现了,因为,我们可以在commentItem组件中通过子传父,把id传递给父组件,父组件再传递底部组件。
  • 4、点击递归组件生成的嵌套的回复按钮,进行评论

    • 用户角度分析:用户点击回复按钮,显示底部文本域,输入评论内容,点击发送按钮,发表成功了。

    • 程序员角度分析:给回复按钮绑定点击事件,在事件处理函数中控制底部组件文本域的显示,收集用户输入的评论内容,给发送按钮绑定点击事件,调用接口实现发表评论。

    • 因为递归组件生成的回复按钮,已经在上一个功能中绑定了自定义事件,并且进行了父传子,因此我们只需在父组件commentItem组件中绑定自定义事件,并在事件处理函数中进行子传父即可

    • 在commentItem组件中添加如下代码即可

      <template>
          <div class="commentItem">
      .....
              <!-- 第二层嵌套 start
              递归组件:自己调用了自己,但是一定要有结束的判断
      			添加 @replyEvent="replyFn"事件完成,递归组件生成的回复按钮功能
               -->
              <commentItem v-if="item.parent" :item="item.parent" 
                           @replyEvent="replyFn"></commentItem>
      ....
      </template>
      

image-20220530175801010-16539046824602.png

9、我的关注(myFollow.vue)

1、创建组件

2、配置路由

3、布局

4、实现功能

1、完成关注列表渲染
2、完成取消关注功能
1、完成关注列表渲染

用户角度分析:进入“我的关注”页面,立刻看到关注列表

程序员角度分析:进入页面,立刻获取到关注列表数据,然后根据数据进行渲染。

拆解步骤:

  • 1、封装获取“关注列表”的接口函数
  • 2、引入接口函数,在created钩子函数中调用接口函数,请求并保存数据
  • 3、根据数据渲染页面
2、完成取消关注功能

用户角度分析:点击“取消关注”按钮,实现取消功能,并将取消的用户从列表中移除

程序员角度分析:给“取消关注”按钮绑定点击事件,在事件处理函数中调用接口实现取消功能,取消成功后把用户从列表中移除(重新获取一下“关注列表数据即可”)

拆解步骤:

  • 1、给“取消关注”按钮绑定点击事件,并声明事件处理函数
  • 2、引入“取消关注”接口,并在点击事件的处理函数中调用,实现取消功能
  • 3、取消成功,更新关注列表,失败弹出提示。

10、我的收藏(myStar.vue)

1、创建组件

2、配置路由

3、布局

4、实现功能

1、完成我的收藏列表渲染
2、取消收藏
3、点击收藏列表项,跳转到文章详情
1、完成我的收藏列表渲染

用户角度分析:进入页面,立刻看到收藏列表

程序员角度分析:进入页面,获取列表数据(调用接口函数),根据数据进行渲染

拆解步骤:

  • 1、封装接口函数
  • 2、引入接口函数,在created中调用接口,请求并保存数据
  • 3、根据数据进行渲染
2、取消收藏
  • 功能一:使用vant组件库的SwipeCell实现滑动效果

  • 功能二:实现取消收藏功能

  • 用户角度分析:点击“删除”按钮,实现取消功能,并将取消的文章从列表中移除

    程序员角度分析:给“删除”按钮绑定点击事件,在事件处理函数中调用接口实现取消功能,取消成功后把用户从列表中移除(重新调用接口获取一下“列表数据即可”)

  • 拆解步骤:

    • 1、给“删除”按钮绑定点击事件,声明事件处理函数
    • 2、引入接口,并在事件处理函数中调用 接口
    • 3、判断取消成功,更新收藏列表,失败,提示用户
  • 3、点击收藏列表项,跳转到文章详情

    • 给收藏列表绑定点击事件,在事件处理函数中跳转到、文章详情页面

11、频道管理(category.vue)

1、创建组件

2、配置路由

3、布局

4、实现功能

1、完成频道管理列表渲染(不包括“关注”和“头条”频道)
2、点击上方列表实现删除频道,将删除的频道添加到下面列表中
3、点击下方列表实现删除下方频道,将删除的频道添加到上方列表中
4、缓存列表数据
1、完成频道管理列表渲染(不包括“关注”和“头条”频道)

用户角度分析:用户进入频道管理页面,立刻看到数据。

程序员角度分析:用户进入页面,立刻通过接口获取数据,然后根据数据渲染

拆解步骤:

  • 1、引入“栏目列表”接口函数,并在created中调用,请求并保存数据
  • 2、根据数据进行渲染
2、点击上方列表实现删除频道,将删除的频道添加到下面列表中

用户角度分析:点击栏目,把栏目删除,并把被删除的栏目添加到下面的列表中。

程序员角度分析:给栏目绑定点击事件,并在事件处理函数中实现删除点击的那个栏目,然后删除的栏目添加到下面列表中。

拆解步骤:

  • 给栏目绑定点击事件,给事件处理函数传递id或者下标值
  • 在事件处理函数中把点击的栏目删除掉(根据id或者下标值删除)
  • 把被删除的栏目添加到下方列表中
4、缓存列表数据

程序员角度分析:当点击删除或者是添加的时候都需要缓存数据(两个数组都需要缓存)

12、搜索(search.vue)

1、创建组件

2、配置路由

3、布局

4、实现功能

1、完成点击搜索按钮实现搜索功能,并展示搜索结果列表
2、在搜索结果列表中将关键词标红
3、点击搜索结果实现跳转文章详情
4、缓存搜索历史记录,并渲染
5、点击搜索记录,实现搜索
1、完成点击搜索按钮实现搜索功能,并展示搜索结果列表

用户角度分析:在输入框中输入关键词,然后点击搜索按钮,把搜索结果展示出来。

程序员角度分析:获取用户输入的关键词,给搜索按钮绑定点击事件,在事件处理函数中调用接口,获取数据,最后根据数据渲染出来。

拆解步骤:

  • 1、声明变量,用v-model双向绑定输入框
  • 2、给搜索按钮绑定点击事件,并声明事件处理函数
  • 3、封装接口函数
  • 4、引入接口函数,并在事件处理函数中调用,请求并保存结果。
  • 5、根据数据渲染
2、在搜索结果列表中将关键词标红

分析:我需要把一行文字中的某几个文字设置独立样式,如何才能做到呢?答:我们必须给某几个字符串用一个标签包裹起来,然后再标签设置样式,这样才能够做到设置独立样式。

用代码如何实现呢?答:可以使用字符串replace方法实现

3、点击搜索结果实现跳转文章详情
4、缓存搜索历史记录,并渲染

分析:我们可以直接在点击搜索的事件处理函数中,保存搜索关键词。

image-20220602113552515-16541409536431.png

5、点击搜索记录,实现搜索

image-20220602114235693-16541413587232.png

响应拦截器

什么是响应拦截器?答:响应拦截器就是请求的接口,数据返回给我们的时候触发的一个函数,而且这个函数是被then触发之前触发。

黑马头条项目中,因为文章详情页面是运行用户不登录的情况下访问的,但是里面的“关注”“点赞”“收藏”等等操作是必须通过登录后才能操作的,因此当用户未登录情况下进行操作的时候,我们需要做判断,判断到没有登录直接跳转到登录页面,但是又因为有很多未登录的操作都需要添加这样的判断,因此我们可以通过响应拦截器统一进行判断。

在src/utils/request.js中添加如下代码即可

// 添加响应拦截器
axios.interceptors.response.use(function (response) {
  // 对响应数据做点什么
  // console.log(25,response);
  // 验证用户是否登录,未登录,则跳转到登录页面
  // 未登录的情况下,后端会返回statusCode属性,当这个属性的值为401说用户信息验证失败
  // 也就是没有登录
  if(response.data.statusCode === 401){
    // 没有登录跳转到登录页面
    router.push("/login")
  }

  return response;
}, function (error) {
  // 对响应错误做点什么
  return Promise.reject(error);
});

登录跳转优化

image-20220602150133469-16541532949033.png

统一设置-登录状态下不可以访问登录页面

image-20220602154123069-16541556841394.png

解决重复跳转同一路由报错问题

在src/router/index.js添加如下代码即可

const originalPush = VueRouter.prototype.push
VueRouter.prototype.push = function push(location) {
	return originalPush.call(this, location).catch(err => err);
}

打包上线

为什么要打包?答:压缩代码,减少项目体积,减少文件数量,提供浏览器访问的速度

打包命令:

npm run build

image-20220602180757007-16541644780575.png

项目上线:只需把打包好的dist文件夹,整个放到后端指定的位置上即可。(ftp上传,或者使用git 或者SVN上传)。