This Baidu Tieba project is a project of Vue + KOa + Sequelize.

Because there is no Baidu post bar API interface, so write their own backend

Screenshot of part of the project (GIF)

Project dependencies and preparation

Front-end: Vue + vuex + AXIos + better-Scroll + iView + stylus (there are also odd dependencies, to complete some small modules, such as moment to complete the project time requirements)

Front-end dependencies

"Dependencies" : {" axios ":" ^ 0.19.0 ", "better - scroll" : "^ 1.15.2", "iview" : "^ 3.4.2", "js - cookies" : "^ 2.2.0 jsonwebtoken", ""," ^ 8.5.1 ", "" :" ^ 2.24.0 ", "vue" : "^ 2.5.2", "vue - photo - preview" : "^ 1.1.3", "vue - the router" : "^ 3.0.1 vuex", ""," ^ 3.1.1 "},Copy the code

Backend: KOa + koa-router + mysql2 + sequelize (these are the main ones, and there are many middleware)

Back-end dependencies

"Dependencies" : {" bcrypt ":" ^ 3.0.6 ", "env2" : "^ 2.2.2", "jsonwebtoken" : "^ 8.5.1", "koa" : "^ 2.7.0", "koa - body" : "^ 4.1.0 koa - bodyparser", "" :" ^ 2 ", "koa - JWT" : "^ 3.5.1 track of", "koa - the router" : "^ 7.4.0", "koa - session" : "^ 5.12.0 koa -", "static", "^ 5.0.0", "koa2 - cors" : "^ 2.0.6", "mysql2" : "^ 1.6.5", "sequelize" : "^ 5.8.12"},Copy the code

Front-end code initialization is used as scaffolding for VUE-CLI3.

Icon is from Alibaba vector icon library, find the icon as far as possible and Baidu post bar consistent

I chose iView for the front-end UI framework (some mistakes were made, because baidu Tieba’s project is mobile terminal, and it would be more appropriate to use an excellent mobile terminal UI framework like Vant)

To prepare, add some path items. In the alias option of the webpakc.base.conf.js file in the build directory, add some common paths for the next project

There are a few points to mention

  1. The permission control of the project only uses a token, which is relatively simple, but not very secure. Take a look at permission controls
  2. Because the code written by the front and back ends is separated, the cookie set during login authentication cannot be retrieved from the front end (it is supposed to be the domain name access problem, so the token information can only be passed to the front end, and the front end can set the cookie by itself (for interests, it is not recommended).
  3. There is no API and no crawler, so the data is handwritten and the amount of data is very small, so some effects cannot be implemented.
  4. Recently seen posts and recently visited bar, the data of these two modules is to use localStorage to store data, other data are from the back end
  5. I’m going to skip the back end because it’s not very well written

Project start

Tabbar

This is the finished product

The effect of router-link to attribute configuration in vue-router is adopted

Clicking on the item gives you two more class attributes

  1. Router-link-exact-active (The exact matching rule exists when the route path is exactly the same)
  2. Router-link-active (including all matching rules, that is, parent routes)

Adding linkActiveClass: ‘active’ to the Router configuration replaces router-link-active with active

At this point, you can also customize the active style

Page structure

There are roughly five sections and scattered pages, four of which (except info) correspond to the four tabbar options that separate the page.

Routing and permission control

The route defines the five parts separately and then summarizes them

Because the login authentication of this project changes the page greatly, the login status is determined in the beforeEach hook function of the route, and the login judgment is enforced for the page that must be logged in to access. If the login fails, the login page is redirected.

router.beforeEach((to, from, next) = > {
  // You must login to a URL without authentication information
  if (routerLoginRole.some(route= >to.path === route) && ! Cookies.get('username')) {
    next('/login')
    return}}Copy the code

You must log in to the following page

export const routerLoginRole = [
  '/release'.'/message'.'/user'.'/focuslist'.'/fanslist'.'/focusbalist'.'/tielist'.'/setting'.'/userhome'.'/useredit'.'/browsehistory'.'/collection'.'/release'.'/like'
]
Copy the code

Also, tabbar display and disappear state are different for different pages. Put tabbar display state into vuEX and manage it in the beforeEach hook function

if (TabbarRoutes.some(route= > (to.path.indexOf(route) === 0))) {
    store.dispatch('hiddenTabbar')}else {
    store.dispatch('showTabbar')}Copy the code

The pages that need to hide tabbar are as follows

export const TabbarRoutes = [
  '/login'.'/register'.'/search'.'/focuslist'.'/fanslist'.'/focusbalist'.'/tielist'.'/setting'.'/userhome'.'/useredit'.'/userinfo/'.'/tieinfo/'.'/bainfo/'.'/browsehistory'.'/release'.'/collection'.'/like'
]
Copy the code

Remove the base configuration and plug-ins

  • Iview component is introduced on demand. Create an iView. js in utils directory to manage the code introduced by iView.
import Vue from 'vue'
import {
  Button,
  Input,
  Switch,
  Progress,
  Form,
  FormItem,
  Icon,
  Upload,
  Dropdown,
  DropdownMenu,
  DropdownItem,
  Message,
  Spin
} from 'iview'
import 'iview/dist/styles/iview.css'

Vue.component('Button', Button)
Vue.component('i-input', Input)
Vue.component('i-switch', Switch)
Vue.component('Progress', Progress)
Vue.component('Form', Form)
Vue.component('FormItem', FormItem)
Vue.component('Icon', Icon)
Vue.component('Upload', Upload)
Vue.component('Dropdown', Dropdown)
Vue.component('DropdownMenu', DropdownMenu)
Vue.component('DropdownItem', DropdownItem)
Vue.component('Spin', Spin)

Vue.prototype.$Message = Message

export default Vue

Copy the code
  • Filter Global filter, using moment to achieve time processing, number processing, unified management

  • Set an interceptor for Axios, which has three functions:

    1. Set the default path for the AXIOS request and run it carrying cookie information

    2. If login authentication is required for some API interfaces, cookie credentials including tokens are sent to the server

    3. Loading animation when requesting data

axios.defaults.baseURL = 'http://192.168.1.4:3000/'
axios.defaults.withCredentials = true
axios.defaults.timeout = 5000

axios.interceptors.request.use(
  config= > {
    // Set the cookie header for the $HTTP request
    store.dispatch('showLoading')
    const token = Cookies.get('username')
    constisTokenRight = !! (token && JsonWebToken.decode(token))if (isTokenRight) {
      config.headers.common['Authorization'] = 'Bearer ' + token
    }
    return config
  },
  error => {
    Message.error('Network exception, please try again later')
    store.dispatch('hiddenLoading')
    return Promise.reject(error)
  }
)
axios.interceptors.response.use(
  response= > {
    store.dispatch('hiddenLoading')
    if(response.data.statusCode ! = =200) {
      Message.error(response.data.message)
      return Promise.reject(response)
    }
    return response
  },
  error => {
    Message.error('Network exception, please try again later')
    store.dispatch('hiddenLoading')
    return Promise.reject(error)
  }
)
Copy the code

Specific realization of some functions

Homepage refresh function

rendering

  1. The first is to listen for events that scroll past a certain value (500 for personal Settings) and trigger events that replace the front page in tabbar with a refresh button.
  2. Click the Refresh button to set the refreshData property in vuex to true. Then listen for this property in the home page, reload the data and trigger the Better-Scroll event to scroll to the top.

There are a few hiccups:

  1. Tabbar is to use the router – link, in the event bubbling phase stop event bubbling, with its own routing jump events, to trigger a custom event, the solution is in its internal dispose click event (or click event bubbling to the rendering of the elements before dispose click event, and stop the click event bubbling)

The HTML structure of the home page options on the Tabbar page

<router-link tag="span"
             class="tabbar-item"
             to="/home">
  <div class="tabbar-item-icon icon-home"
       v-show=! "" this.$store.getters.isRefresh">
  </div>
  <div class="tabbar-item-icon icon-home refresh"
       v-show="this.$store.getters.isRefresh"
       @click.stop.prevent="refresh">
  </div>
  <span class="tabbar-item-label"
        v-show=! "" this.$store.getters.isRefresh">Home page</span>
  <span class="tabbar-item-label refresh"
        v-show="this.$store.getters.isRefresh"
        @click.stop.prevent="refresh">The refresh</span>
</router-link>
Copy the code

The refresh method that it triggers

refresh () {
  if (this.$store.getters.isRefresh) {
    this.$store.commit('updateRefreshData'.true)}},Copy the code
  1. In order to avoid redundant loading data, the “keep alive” tag is used. When the “Better Scroll” scrollTo method is triggered, the method is invalid. I don’t know the specific reason, but LATER I find that the version of my “Better Scroll” is basic. It can be used normally (friends who know the specific reason can talk about it)

Achieve good left and right rolling

rendering

The top TAB animation is CSS transition: All 0.3s ease

The page uses better Scroll and performs related operations in the scrollEnd hook function.

The top marks the slide event

romve (index) {
      let name = 'message-title-' + index
      this.index = index
      this.oldX = -index * this.$refs['message-content'].offsetWidth
      this.$refs.unline.style.left = this.$refs[name].offsetLeft + 'px'
      this.contentScroll.scrollTo(-this.$refs['message-content'].offsetWidth * index, 0.300)},Copy the code

Initialization of left and right scrolling

    initScroll () {
      this.$nextTick((a)= > {
        if (!this.contentScroll) {
          this.contentScroll = new BScroll(this.$refs['message-content'] and {startX: 0.click: true.tap: true.scrollX: true.scrollY: false.momentum: false // Don't let it generate a scrolling slide animation})}else {
          this.contentScroll.refresh()
        }
        this.contentScroll.on('scrollEnd', ({ x }) => {
          let width = this.$refs['message-content'].offsetWidth // Get the width of a single page
          if(x ! == -width && x ! = =2 -* width && x ! = =0) { // Avoid auto-scrolling
            if (Math.abs(x - this.oldX) < width / 4) { // The scroll volume is less than 1/4 of the page
              this.contentScroll.scrollTo(-this.index * width, 0.300)}else if (this.oldX > x) { // Swipe left to automatically roll to the right page
              this.contentScroll.scrollTo(-(++this.index) * width, 0.300)}else { // Swipe right to automatically roll to the left page
              this.contentScroll.scrollTo(-(--this.index) * width, 0.300)}this.oldX = -this.index * width // recalculate the value
            this.romve(this.index) // Slide the top marker to the corresponding position}})this.romve(0) // The top tag initializes to the first one})}Copy the code

The page layout

  • In HTML
    • The main use of div, SPAN, IMG, P and other tags
  • CSS aspects
    • The class selector used by the selector
    • The layout mainly uses Flex layout
    • The z-index level is 1-9, the text level is low, and the animation mask level is slightly higher.
    • Icon ICONS are defined in the form of background
background-size: 30px 30px background-repeat: no-repeat background-position: center center background-image: url('.. /.. /assets/icon/left.png')Copy the code

Issues to watch out for:

  1. Z-index hierarchy problem, child element inherits parent element’s hierarchy.
  2. After setting the hierarchy, there will still be the problem of the lower element floating up. It may be that the upper element is transparent, so just add the background color.
  3. Frame animations using JS setInterval should be reduced in favor of CSS tween animations.

conclusion

The small function of each page is not much, but many modules are not difficult, there is no need to write in detail, specific can see the source code.

Writing down the whole project, there are many problems. Basically there is lack on integrity, componentization is not thorough enough. UI style is still not consistent with Baidu post bar (there is a certain deviation).

If you like this article or can help you, give the author a pat on the back and give him a thumbs-up. At the same time, I also hope that you can express some opinions on this article!

Front-end source back-end source