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
- The permission control of the project only uses a token, which is relatively simple, but not very secure. Take a look at permission controls
- 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).
- 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.
- 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
- 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
- Router-link-exact-active (The exact matching rule exists when the route path is exactly the same)
- 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
- 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.
- 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:
- 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
- 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:
- Z-index hierarchy problem, child element inherits parent element’s hierarchy.
- 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.
- 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