记录我的第一个移动端项目(更新中)

273 阅读4分钟

头条APP

1.项目介绍:

  • 头条(极客园)移动端是一个IT资讯移动web应用,有着和今日头条类似的资讯浏览体验。
  • 主要功能:资讯列表、标签页切换,文章举报,频道管理、文章详情、关注功能、点赞功能、评论功能、搜索功能、登录功能、个人中心、编辑资料、小思同学
  • 头条可打包成一款移动APP,后期结合H5+可在Dcloud打包成一款体验较好的手机应用。

2.项目技术点介绍

  • vuejs中

    • $nextTick使用 (vue更新DOM是异步的)
    • 组件通信 (父, 子传递)
    • async和await用法
    • keep-alive 组件缓存
  • vuex

    • actions中发ajax请求
    • actions的返回值问题
  • vue-router

    • 懒加载 (对打包以后首页加载速度有提高)
  • axios

    • 请求和响应拦截器
    • 封装请求工具方法 (代码分层)
  • vant

    • 对组件使用更熟悉
    • 掌握使用和查阅文档的能力
  • socket.io-client

    • http协议复习 (请求报文和响应报文)
    • 与ajax区别
    • ajax和socket之间的选择
  • amfe-flexible

    • 移动端rem适配

3.项目分析

  • 1首先开发登录注册页面
  • 2登录后首页,以及默认展示的页面,组件间通信,数据获取,day.js的使用(多翻文档,很多方法都封装好了)
  • 3遇到一个大坑,父子间通信props接收的名字一定不要错!!!!!!
  • 4 vue中use的用法--封装一个自动聚焦方法 [Vue.use相关文档: cn.vuejs.org/v2/api/#Vue…]
  • 搜索关键字高亮的方法----注意:模板字符串中修改样式要用v-html
// 搜索关键字高亮
    lightFn (originStr, target) {
      // originStr:原来字符串
      // target:关键字
      return originStr.replace(target, `<span style="color: red;">${target}</span>`)
    }
<!-- 搜索建议列表 -->
    <div class="sugg-list">
        <div class="sugg-item"  v-for="(str, index) in searchList" :key="index" v-html="lightFn(str, value)" >
                搜索建议
        </div>
    </div>
  • 保存搜索历史
 watch: {
    // 因为跳转页面, 原来页面被释放了, 回来后数组重新创建
    //  解决: 本地缓存起来
    //  问题2: 但是发现跳转后, 并未保存到本地(==原因: 先跳转了, watch还未来的及执行==)
    // 解决: 给路由跳转加个定时器(最后执行)
    history () {
      localStorage.setItem('his', JSON.stringify(this.history))
    }
  },
  methods: {
    // 跳转到搜索结果页
    onSearch (val) {
      this.history.push(val) // 保存搜索关键字
      setTimeout(() => {
        this.$router.push(`/search/:${val}`)
      })
    }
  • 首页文章列表有X符号,搜索结果页没有
// 在结果页组件中
:showX="false"

// 在首页组件中
props: {
     showX: {
          type: Boolean,
          default: true
    }
 }
  • 关于关注和取消关注followedFn
<van-button type="info" plain size="mini" v-if="artObj.is_followed" @click="followedFn(false)">已关注</van-button>
<van-button icon="plus" plain type="info" size="mini" v-else  @click="followedFn(true)">关注</van-button> 
 methods: {
    // 作者关注/取关
    async followedFn (bool) {
      if (bool === true) {
        // 业务: 关注用户
        // 显示: 已关注按钮
        this.artObj.is_followed = true
        await followedUserAPI({
          target: this.artObj.aut_id
        })
      } else {
        // 业务: 不关注用户
        // 显示: 已取消关注按钮
        this.artObj.is_followed = false
        await unFollowedUserAPI({
          uid: this.artObj.aut_id
        })
      }
    }
  }
  • 不需要循环的接口数据,在data接收后直接用就好啦
 data () {
    return {
      // 请求数据, 保存到data变量上, 才能在vue模板标签上直接用
      user: {}
    }
  }
  • 更新用户头像
  • 1点击图片,触发选择文件按钮(移花接木)
<van-image round class="avatar" :src="profile.photo" @click="$refs.iptFile.click()"/>
<input type="file" ref="iptFile" v-show="false" accept="image/*" @change="onFileChange">
  • 调用api接口
// 用户- 更新头像
// 注意: formObj的值必须是一个表单对象
// '{"a": 10, "b": 20}' // 对象格式的JSON字符串
// new FormData() // 表单对象
export const updatePhotoAPI = (formObj) => {
  return request({
    url: '/v1_0/user/photo',
    method: 'PATCH',
    data: formObj
    // 如果你的请求体内容是表单对象, 浏览器会自动携带请求头Content-Type为multipart/form-data
  })
}
  • 3更新头像--前端如何创建表单对象?
    • new FormData
 methods: {
    async onFileChange (e) {
    //   console.log(e.target.files[0])
      if (e.target.files[0].length === 0) return // 防止用户未选择图片
      const fd = new FormData()
      fd.append('photo', e.target.files[0]) // photo在表单里参数名携带
      const res = await updatePhotoAPI(fd)
      console.log(res)
      this.profile.photo = res.data.data.photo
    }

websocket实现聊天机器人

步骤:

1.安装客户端socket的包(支持websocket) - 内部对websocket进行了封装

yarn add socket.io-client@4.0.0

2.在Chat/index.vue引入包

// 导入 socket.io-client 包
import { io } from 'socket.io-client'

// 定义变量,存储 websocket 实例
let socket = null

3.创建socket服务

created() {
    // ...
    
    // 创建客户端 websocket 的实例
    socket = io('http://toutiao.itheima.net', {
        query: {
            token: this.token
        },
        transports: ['websocket']
    })
}

4.监听是否连接成功--只有连接内置事件执行了, 才能进行后续操作

created() {
  // 建立连接的事件
  socket.on('connect', () => {
    console.log('与服务器建立了连接')
  })
}

5在组件销毁前, 关闭服务

// 组件被销毁之前,清空 sock 对象
beforeDestroy() {
  // 关闭连接
  socket.close()

  // 销毁 websocket 实例对象
  socket = null
},

6在created监听socket的消息

created() {
    // ...

    // 接收到消息的事件
    socket.on('message', data => {
      // 把服务器发送过来的消息,存储到 list 数组中
      this.list.push({
        name: 'xs',
        msg: data.msg
      })
    })
},

7在 send事件中, 把服务器发来的数据装到数组里

sendFn () {
    // 判断内容是否为空
    if (!this.word) return

    // 添加聊天消息到 list 列表中
    this.list.push({
        name: 'me',
        msg: this.word
    })
}

8客户端调用 socket.emit('message', 消息内容) 方法把消息发送给 websocket 服务器:

// 向服务端发送消息
sendFn () {
    // 判断内容是否为空
    if (!this.word) return

    // 添加聊天消息到 list 列表中
    this.list.push({
        name: 'me',
        msg: this.word
    })

    // 把消息发送给 websocket 服务器
    socket.emit('message', {
        msg: this.word,
        timestamp: new Date().getTime()
    })

    // 清空文本框的内容
    this.word = ''
}
  • 路由懒加载--使用
component: Login
// 改成这个写法
component: () => import('@/views/Login.vue')

路由拦截器, 可以让页面不上来都被引入到webpack里一起打包插入到浏览器运行

等待使用时, 再去循环对应页面组件

  1. 为何使用路由拦截器?

    • 为了让首页渲染更快
  2. 什么是路由懒加载?

    • 路由规则匹配后, 再加载对应js代码
  • 路由懒加载后,编辑个人资料不会立即刷新。解决办法,将更新后的数据存到vuex中,映射更新

路有缓存(大坑)

实现部分路由缓存,部分路由不缓存(打开刷新),在一级路由中使用keep-alive,使用include包含或者exlude去除来决定需要或不需要缓存的组件!!!

<!-- exclude 的值要与组件的name一致,而不是路由的名字!!! -->
    <keep-alive :exclude="['ArticleDetail', 'MyLogin', 'MySearch', 'SearchResult']">
    <router-view/>
    </keep-alive>