1. 做了什么:登录模块、频道模块、搜索模块、文章模块、评论模块、个人信息模块

2.具体使用的技术栈和操作步骤和问题解决:

(1)vue技术

创建vue项目

vue create toutiao-m

进行一些基本的配置,选择Manually select features

Babel、Router、Vuex、CSS Pre-processors、Linter / Formatter 
n
Less
ESLint + Standard config
Lint on save、Lint and fix on commit
In dedicated config files
N

然后进入项目运行项目(yarn serve)

(2)vue-cli 利用脚手架创建项目

npm install --global @vue/cli

(3)vue-Router 配置路由

app.vue中写<router-view />、router.js中写

import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter)

const routes = [
]

const router = new VueRouter({
  routes
})

export default router

 layout页面采用路由懒加载

{
  path: '/',
  component: () => import('@/views/layout')
}

(4)iconfont(iconfont-阿里巴巴矢量图标库

创建一个新项目,点击添加‘上传图标到项目’

点击去除颜色并提交,生成在线链接,引入到项目中

(5)less   创建style文件夹,将图标的less文件写入到该文件中

(6)vant组件库

下载、这里用全局导入方式

button、cell、icon、image、popup、toast、field、grid、navbar、pullRefresh

(7)rem适配 postcss-pxtorem (将单位转化为rem)、lib-flexible 设置rem的基准值

)postcss-pxtorem 插件的配置

module.exports = {
    'postcss-pxtorem': {
      rootValue: 37.5,
      propList: ['*']
    }
  }
}
  • rootValue:表示根元素字体大小,它会根据根元素大小进行单位转换
  • propList 用来设定可以从 px 转为 rem 的属性

 (8)axios 

安装 axios


npm i axios


创建 src/utils/request.js


/** * 封装 axios 请求模块 */ import axios from "axios" const request = axios.create({  baseURL: "http://ttapi.research.itcast.cn/" // 基础路径 }) export default request


如何使用

  • 方式一(简单方便,但是不利于接口维护):我们可以把请求对象挂载到 Vue.prototype 原型对象中,然后在组件中通过 this.xxx 直接访问
  • 方式二(推荐):我们把每一个请求都封装成每个独立的功能函数,在需要的时候加载调用,这种做法更便于接口的管理和维护

(9) 本地存储localstroage

(10)返回的有token和refresh_token,

  • token:访问令牌,有效期2小时
  • refresh_token:刷新令牌,有效期14天,用于访问令牌过期之后重新获取新的访问令牌

 token存在vuex容器中(比起子父组件传值,它更加方便),为了持久化,还需要本地储存

(11)接口封装请求方式:

import store from '@/store'

/**
 * 获取用户自己的信息
 */
export const getUserInfo = () => {
  return request({
    method: 'GET',
    url: '/app/v1_0/user',
    // 发送请求头数据
    headers: {
      // 注意:该接口需要授权才能访问
      //       token的数据格式:Bearer token数据,注意 Bearer 后面有个空格
      Authorization: `Bearer ${store.state.user.token}`
    }
  })
}
// vue文件中
import { getUserInfo } from '@/api/user'

(12)使用请求拦截器统一给Authorization 添加`Bearer token `

// 请求拦截器
// Add a request interceptor
request.interceptors.request.use(function (config) {
  // Do something before request is sent
  // config :本次请求的配置对象
  // config 里面有一个属性:headers
  const { user } = store.state
  if (user && user.token) {
    config.headers.Authorization = `Bearer ${user.token}`
  }
  return config
}, function (error) {
  // Do something with request error
  return Promise.reject(error)
})

(13)loading和finished两个变量控制加载状态:组件初始化或滚动到到底部时,会触发 load 事件并将 loading 设置成 true,此时可以发起异步操作并更新数据,数据更新完毕后,将 loading 设置成 false 即可。若数据已全部加载完毕,则直接将 finished 设置成 true 即可。

(14) vuex

将用户的登录信息储存进store里面的state中,在mutation中用setUser将数据本地储存

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
  state: {
    // 用户的登录状态信息
    user: JSON.parse(window.localStorage.getItem('TOUTIAO_USER'))
    // user: null
  },
  mutations: {
    setUser (state, user) {
      state.user = user
      window.localStorage.setItem('TOUTIAO_USER', JSON.stringify(user))
    }
  },
  actions: {
  },
  modules: {
  }
})
async onLogin () {
  // const loginToast = this.$toast.loading({
  this.$toast.loading({
    duration: 0, // 持续时间,0表示持续展示不停止
    forbidClick: true, // 是否禁止背景点击
    message: '登录中...' // 提示消息
  })
  try {
    const res = await login(this.user)

    // res.data.data => { token: 'xxx', refresh_token: 'xxx' }
+    this.$store.commit('setUser', res.data.data)

    // 提示 success 或者 fail 的时候,会先把其它的 toast 先清除
    this.$toast.success('登录成功')
  } catch (err) {
    console.log('登录失败', err)
    this.$toast.fail('登录失败,手机号或验证码错误')
  }

  // 停止 loading,它会把当前页面中所有的 toast 都给清除
  // loginToast.clear()
}

(15)403问题的解决

403:我们项目的接口数据是后端通过爬虫抓取的第三方平台内容,而第三方平台对图片资源做了防盗链保护处理。

在index.html中添加

<meta name="referrer" content="no-referrer" />

不发送referrer就能解决图片拒绝访问的问题(即403问题)

(16)用dayjs或者moment处理时间

参考官网进行配置

设置过滤器

import Vue from 'vue'
import dayjs from 'dayjs'

// 加载中文语言包
import 'dayjs/locale/zh-cn'

import relativeTime from 'dayjs/plugin/relativeTime'

// 配置使用处理相对时间的插件
dayjs.extend(relativeTime)

// 配置使用中文语言包
dayjs.locale('zh-cn')

// 全局过滤器:处理相对时间
Vue.filter('relativeTime', value => {
    return dayjs().to(dayjs(value))
})

使用

<p>{{ 日期数据 | relativeTime }}</p>

(17)防抖技术lodash,使用方法参照官网

(18)搜索关键词高亮

highlightText(text) {
      const highlightStr = `<span class="active">${this.searchText}</span>`;
      // 正则表达式 // 中间的内容都会当作匹配字符来使用,而不是数据变量
      // 如果需要根据数据变量动态的创建正则表达式,则手动 new RegExp
      // RegExp 正则表达式构造函数
      //    参数1:匹配模式字符串,它会根据这个字符串创建正则对象
      //    参数2:匹配模式,要写到字符串中
      const reg = new RegExp(this.searchText, 'gi');
      // text.replace(/匹配的内容/gi, highlightStr)
      return text.replace(reg, highlightStr);
    }

(19)使用json-bigint处理大数字问题

之所以请求文章详情返回 404 是因为我们请求发送的文章 ID (article.art_id)不正确。

JavaScript 能够准确表示的整数范围在-2^532^53之间(不含两个端点),超过这个范围,无法精确表示这个值,这使得 JavaScript 不适合进行科学和金融方面的精确计算。

const jsonStr = '{ "art_id": 1245953273786007552 }'

console.log(JSON.parse(jsonStr)) // 1245953273786007600
// JSON.stringify()

// JSONBig 可以处理数据中超出 JavaScript 安全整数范围的问题
console.log(JSONBig.parse(jsonStr)) // 把 JSON 格式的字符串转为 JavaScript 对象

// 使用的时候需要把 BigNumber 类型的数据转为字符串来使用
console.log(JSONBig.parse(jsonStr).art_id.toString()) // 1245953273786007552

console.log(JSON.stringify(JSONBig.parse(jsonStr)))

console.log(JSONBig.stringify(JSONBig.parse(jsonStr))) // 把 JavaScript 对象 转为 JSON 格式的字符串转

用法试例如上

(20)409     昵称重复

(21)cropperjs获取blob对象

安装后引入

import 'cropperjs/dist/cropper.css'
import Cropper from 'cropperjs'

在mounted里配置

const cropper = new Cropper(image, {
    viewMode: 1, // 只能在裁剪的图片范围内移动
    dragMode: 'move', // 画布和图片都可以移动
    aspectRatio: 1, // 裁剪区默认正方形
    autoCropArea: 1, // 自动调整裁剪图片
    cropBoxMovable: false, // 禁止裁剪区移动
    cropBoxResizable: false, // 禁止裁剪区缩放
    background: false // 关闭默认背景
})

获取blob

confirm () {
      // console.log(this.cropper.getData())
      this.cropper.getCroppedCanvas().toBlob(blob => {
        console.log(blob)
      })
    }

3.总结

零散的知识汇编成项目,需要做到两点:1.优化代码(模块化,相同的方法先定义成函数,方法的封装),2.打印错误信息,在控制台查看方便找错

最后,要时时刻刻复制变量名,避免因为变量名引发的错误