资讯专栏INFORMATION COLUMN

Vue全栈开发之百度贴吧

stefanieliang / 522人阅读

这个百度贴吧的项目是 vue + koa + sequelize 的项目。

由于没有百度贴吧API接口,所以自己写了后端

项目部分截图(GIF)





项目的依赖以及准备工作

前端:vue + vuex + axios + better-scroll +iview + stylus (还有零散的依赖,完成某些小模块的,如 moment 完成项目内时间的需求)

前端依赖项

</>复制代码

  1. "dependencies": {
  2. "axios": "^0.19.0",
  3. "better-scroll": "^1.15.2",
  4. "iview": "^3.4.2",
  5. "js-cookie": "^2.2.0",
  6. "jsonwebtoken": "^8.5.1",
  7. "moment": "^2.24.0",
  8. "vue": "^2.5.2",
  9. "vue-photo-preview": "^1.1.3",
  10. "vue-router": "^3.0.1",
  11. "vuex": "^3.1.1"
  12. },

后端:koa + koa-router + mysql2 + sequelize (这些是主要的,还有很多的中间件)

后端依赖项

</>复制代码

  1. "dependencies": {
  2. "bcrypt": "^3.0.6",
  3. "env2": "^2.2.2",
  4. "jsonwebtoken": "^8.5.1",
  5. "koa": "^2.7.0",
  6. "koa-body": "^4.1.0",
  7. "koa-bodyparser": "^4.2.1",
  8. "koa-jwt": "^3.5.1",
  9. "koa-router": "^7.4.0",
  10. "koa-session": "^5.12.0",
  11. "koa-static": "^5.0.0",
  12. "koa2-cors": "^2.0.6",
  13. "mysql2": "^1.6.5",
  14. "sequelize": "^5.8.12"
  15. },

前端代码初始化是用来vue-cli的脚手架。

图标是来自阿里巴巴矢量图标库,找到的图标尽量和百度贴吧一致

前端的UI框架选择了iview(选择有些失误,因为百度贴吧的项目是移动端的,应该采用像vant这类优秀的移动端UI框架会更合适)

准备工作是添加一些路径项 在build目录下的webpakc.base.conf.js文件中的alias选项中添加一些接下来项目中常用的路径


有几点要前面说一下

该项目的权限控制只采用了一个token,比较简单,但不是很安全。有兴趣可以看看权限控制

由于前后端分离写的代码,所以导致登录认证时设置的cookie 不能在前端取到(应该是域名访问的问题,取出来是undefined),所以只能将token信息传给前端,让前端自己设置cookie(权益之计,不建议采用)

没有API,也没写爬虫,所以数据都是手写的,数据量很少,所以一些效果无法实现。

最近看过的帖子和最近访问的吧, 这两个模块的数据是采用localStorage存数据,其它的数据均来自后端

因为后端写的不是很好,就不讲后端的内容了

项目启动 Tabbar

这是成品效果

采用了vue-router 中 router-link to属性配置的效果

点击的该项会多出两个class属性

router-link-exact-active (精确匹配规则,路由路径完全一致时会有)

router-link-active (全包含匹配规则,也就是父路由也会有)

linkActiveClass: "active"添加至router 配置中会将router-link-active 替换成active

这时就也可自定义active的样式了

页面结构

大体分为5个部分,和零散的几个页面,5个部分中其中4个(除info)分别对应4个tabbar选项,将页面划分开来。


info中有三个page,分别是userInfo,baInfo,tieInfo

路由与权限控制

路由将5个部分分开定义,最后汇总

因为该项目的登录认证对页面变化很大,所以在路由的beforeEach钩子函数中进行登录状态的判断,对必须登录才能访问的页面进行强制的登录判断,不通过则跳转到登录页。

</>复制代码

  1. router.beforeEach((to, from, next) => {
  2. // 一定需要登录的url 没有认证信息 进行登录
  3. if (routerLoginRole.some(route => to.path === route) && !Cookies.get("username")) {
  4. next("/login")
  5. return
  6. }
  7. }

必须登录的页面如下

</>复制代码

  1. export const routerLoginRole = [
  2. "/release",
  3. "/message",
  4. "/user",
  5. "/focuslist",
  6. "/fanslist",
  7. "/focusbalist",
  8. "/tielist",
  9. "/setting",
  10. "/userhome",
  11. "/useredit",
  12. "/browsehistory",
  13. "/collection",
  14. "/release",
  15. "/like"
  16. ]

同时,不同的页面 tabbar显示和消失状态也不同,将tabbar的显示状态放入vuex 中,在beforeEach钩子函数中统一管理

</>复制代码

  1. if (TabbarRoutes.some(route => (to.path.indexOf(route) === 0))) {
  2. store.dispatch("hiddenTabbar")
  3. } else {
  4. store.dispatch("showTabbar")
  5. }

需要隐藏tabbar的页面如下

</>复制代码

  1. export const TabbarRoutes = [
  2. "/login",
  3. "/register",
  4. "/search",
  5. "/focuslist",
  6. "/fanslist",
  7. "/focusbalist",
  8. "/tielist",
  9. "/setting",
  10. "/userhome",
  11. "/useredit",
  12. "/userinfo/",
  13. "/tieinfo/",
  14. "/bainfo/",
  15. "/browsehistory",
  16. "/release",
  17. "/collection",
  18. "/like"
  19. ]
抽离基本配置以及插件

iview组件的引入方式采用按需引入,在utils目录下创建一个iview.js 将iview引入的代码进行统一管理。

</>复制代码

  1. import Vue from "vue"
  2. import {
  3. Button,
  4. Input,
  5. Switch,
  6. Progress,
  7. Form,
  8. FormItem,
  9. Icon,
  10. Upload,
  11. Dropdown,
  12. DropdownMenu,
  13. DropdownItem,
  14. Message,
  15. Spin
  16. } from "iview"
  17. import "iview/dist/styles/iview.css"
  18. Vue.component("Button", Button)
  19. Vue.component("i-input", Input)
  20. Vue.component("i-switch", Switch)
  21. Vue.component("Progress", Progress)
  22. Vue.component("Form", Form)
  23. Vue.component("FormItem", FormItem)
  24. Vue.component("Icon", Icon)
  25. Vue.component("Upload", Upload)
  26. Vue.component("Dropdown", Dropdown)
  27. Vue.component("DropdownMenu", DropdownMenu)
  28. Vue.component("DropdownItem", DropdownItem)
  29. Vue.component("Spin", Spin)
  30. Vue.prototype.$Message = Message
  31. export default Vue

filter 全局过滤器,使用moment实现对时间的处理,数字的处理,统一进行管理

对axios设置了拦截器,功能有三:

1.设置axios请求的默认路径以及运行其携带cookie信息

2.部分api接口需要登录验证 则需要传给服务器包括token的cookie凭证

3.请求数据时的加载动画

</>复制代码

  1. axios.defaults.baseURL = "http://192.168.1.4:3000/"
  2. axios.defaults.withCredentials = true
  3. axios.defaults.timeout = 5000
  4. axios.interceptors.request.use(
  5. config => {
  6. // 给$http请求设置cookie请求头
  7. store.dispatch("showLoading")
  8. const token = Cookies.get("username")
  9. const isTokenRight = !!(token && JsonWebToken.decode(token))
  10. if (isTokenRight) {
  11. config.headers.common["Authorization"] = "Bearer " + token
  12. }
  13. return config
  14. },
  15. error => {
  16. Message.error("网络异常,请稍后再试")
  17. store.dispatch("hiddenLoading")
  18. return Promise.reject(error)
  19. }
  20. )
  21. axios.interceptors.response.use(
  22. response => {
  23. store.dispatch("hiddenLoading")
  24. if (response.data.statusCode !== 200) {
  25. Message.error(response.data.message)
  26. return Promise.reject(response)
  27. }
  28. return response
  29. },
  30. error => {
  31. Message.error("网络异常,请稍后再试")
  32. store.dispatch("hiddenLoading")
  33. return Promise.reject(error)
  34. }
  35. )
部分功能具体实现 首页刷新功能

效果图

分为两个阶段

首先是监听滚动的事件,滚动值超过某个值(个人设置500),触发事件将tabbar中的首页替换成刷新按钮。

点击刷新按钮,将vuex中的refreshData属性变为true。然后在首页中监听这个属性,重新加载数据并且触发better-scroll的滚动事件,让其滚动到最上方。

其中还是有些小问题的:

tabbar是采用router-link, 会在其事件冒泡阶段阻止事件冒泡,进行其自身的路由跳转事件,从而无法触发自定义的事件,解决办法是在其内部处理掉点击事件(也就是点击事件冒泡到其渲染的元素之前处理掉点击事件,并阻止点击事件的冒泡)

tabbar页面中首页选项的html结构

</>复制代码

  1. 首页
  2. 刷新

其触发的刷新方法

</>复制代码

  1. refresh () {
  2. if (this.$store.getters.isRefresh) {
  3. this.$store.commit("updateRefreshData", true)
  4. }
  5. },

要避免多余的加载数据,所以采用了keep-alive标签,在触发better-scroll的scrollTo方法时,方法无效,具体原因不知,但后面发现自己的better-scroll的版本是基础型的,后面换成全能力滚动后,就可以正常使用了(有知道具体原因的朋友可以聊一下)

实现优质的左右滚动

效果图

html可分为两部分 顶部的三个tab 和 下面的三个页面

顶部的tab动画效果为css的transition: all 0.3s ease

页面采用了better-scroll,并在scrollEnd钩子函数中进行相关操作。

顶部的标记滑动事件

</>复制代码

  1. romve (index) {
  2. let name = "message-title-" + index
  3. this.index = index
  4. this.oldX = -index * this.$refs["message-content"].offsetWidth
  5. this.$refs.unline.style.left = this.$refs[name].offsetLeft + "px"
  6. this.contentScroll.scrollTo(-this.$refs["message-content"].offsetWidth * index, 0, 300)
  7. },

左右滚动的初始化

</>复制代码

  1. initScroll () {
  2. this.$nextTick(() => {
  3. if (!this.contentScroll) {
  4. this.contentScroll = new BScroll(this.$refs["message-content"], {
  5. startX: 0,
  6. click: true,
  7. tap: true,
  8. scrollX: true,
  9. scrollY: false,
  10. momentum: false // 不让其生成滚动的滑行动画
  11. })
  12. } else {
  13. this.contentScroll.refresh()
  14. }
  15. this.contentScroll.on("scrollEnd", ({ x }) => {
  16. let width = this.$refs["message-content"].offsetWidth // 获取单个页面的宽度
  17. if (x !== -width && x !== -2 * width && x !== 0) { // 避免自动滚动后继续滚动
  18. if (Math.abs(x - this.oldX) < width / 4) { // 滚动量小于整个页面的1/4时 自动复原
  19. this.contentScroll.scrollTo(-this.index * width, 0, 300)
  20. } else if (this.oldX > x) { // 向左滑动时 自动滚到到右边页面
  21. this.contentScroll.scrollTo(-(++this.index) * width, 0, 300)
  22. } else { // 向右滑动时 自动滚到到左边页面
  23. this.contentScroll.scrollTo(-(--this.index) * width, 0, 300)
  24. }
  25. this.oldX = -this.index * width // 重新计算值
  26. this.romve(this.index) // 使顶部的标记滑动到对应的位置
  27. }
  28. })
  29. this.romve(0) // 顶部的标记初始化到第一个
  30. })
  31. }
页面布局

html方面

主要采用div,span,img,p 等标签

css方面

选择器采用的class选择器

布局主要采用 flex 布局

z-index的层级上是 1-9 ,文字层级低,动画蒙版等层级略高。

icon图标的定义是采用了background 的形式

</>复制代码

  1. background-size: 30px 30px
  2. background-repeat: no-repeat
  3. background-position: center center
  4. background-image: url("../../assets/icon/left.png")

要注意的问题:

z-index的层级问题,子元素会继承父元素的层级。

设置了层级,还是会出现下层元素上浮的问题,有可能是上层元素是透明的,添加背景颜色就可以。

应减少使用js的setInterval形成的帧动画,而应该采用css形成的补间动画。

结语

各个页面的小功能写的不多,但很多模块难度问题不大,没有详细写的必要,具体可看源码。

整个项目写下来,也有不少的问题。主要还是整体性上有欠缺,组件化不够彻底。UI风格上还是没办法和百度贴吧一致(有一定的偏差)。

如果你喜欢这篇文章或者可以帮到你,给作者一点鼓励,点个赞在走吧!同时也非常希望看到这篇文章的你能发表一点见解!

前端源码
后端源码

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/105617.html

相关文章

  • 前端学习资源整理

    稍微整理了一下自己平时看到的前端学习资源,分享给大家。 html MDN:Mozilla开发者网络 SEO:前端开发中的SEO css 张鑫旭:张鑫旭的博客 css精灵图:css精灵图实践 栅格系统:详解CSS中的栅格系统 媒体查询:css媒体查询用法 rem布局:手机端页面自适应布局 移动前端开发之viewport的深入理解:深入理解viewport 淘宝前端布局:手机淘宝移动端布局 fl...

    siberiawolf 评论0 收藏0
  • 第三方库

    摘要:微信支付,支付宝支付,银联支付三大支付总结支付宝植入总结支付宝的植基于和百度地图的组件库基于百度地图封装的组件库,使用这个库最好需要先了解和百度地图。 Commento - 多说 & Disqus 开源替代品 Commento - 多说 & Disqus 开源替代品 anime.js 简单入门教程 强大轻量的动画库 anime.js 入门教程 来自B站的开源的MagicaSakura源...

    seanHai 评论0 收藏0
  • 第三方库

    摘要:微信支付,支付宝支付,银联支付三大支付总结支付宝植入总结支付宝的植基于和百度地图的组件库基于百度地图封装的组件库,使用这个库最好需要先了解和百度地图。 Commento - 多说 & Disqus 开源替代品 Commento - 多说 & Disqus 开源替代品 anime.js 简单入门教程 强大轻量的动画库 anime.js 入门教程 来自B站的开源的MagicaSakura源...

    gityuan 评论0 收藏0
  • “别更新了,学不动了” 全栈开发者 2019 应该学些什么?

    摘要:但是,有一件事是肯定的年对全栈开发者的需求量很大。有一些方法可以解决这个问题,例如模式,或者你可以这么想,其实谷歌机器人在抓取单页应用程序时没有那么糟糕。谷歌正在这方面努力推进,但不要指望在年会看到任何突破。 对于什么是全栈开发者并没有一个明确的定义。但是,有一件事是肯定的:2019 年对全栈开发者的需求量很大。在本文中,我将向你概述一些趋势,你可以尝试根据这些趋势来确定你可能要投入的...

    NervosNetwork 评论0 收藏0

发表评论

0条评论

stefanieliang

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<