preface
The last article “vue. js easy to achieve a page back, restore the scroll position” is just a simple implementation of the route switch when the scroll position restore, many friends will ask how to achieve pull loading ah! Then I remembered a previous project called VUE-cNode, so I spent two days to reconstruct it, completely removing Vuex and using Vuet as a state management tool. If you follow Vuet, you will find that the version update is so fast, it is like version emperor!! In fact, Vuet version upgrades are downward compatible. Each version release will go through complete unit testing and E2E testing, which greatly ensures the stability of the released version.
Program source code
- Vuet status management tool
- Vue-cnode Github and effect preview address
Demand analysis
- Records the number of pages when a pull-up request is made
- When the page rewinds, the state of the previous list page is restored
- The status of the list is reset when the list category is switched
- Click detail A from list A, back up the page, open detail A again, and restore the previous accessed state of detail A
- Click detail A from list A, back up the page, open detail B again, clear the state of detail A, and initialize the state of detail B
The installation
npm install --save vuetCopy the code
Vuet instance
import Vue from 'vue'
import Vuet from 'vuet'
import utils from 'utils'
import http from 'http'
Vue.use(Vuet)
export default new Vuet({
pathJoin: The '-'.// Define the module's connector
modules: {
topic: {
create: {
data () {
return {
title: ' './ / title
tab: ' '.// Published plate
content: ' ' // The content of the publication}},manuals: {
async create ({ state }) {
if(! state.title) {return utils.toast('Title cannot be empty')}else if(! state.tab) {return utils.toast('Option cannot be empty')}else if(! state.content) {return utils.toast('Content cannot be empty')}const res = await http.post(`/topics`, {
...state
})
if (res.success) {
this.reset()
} else {
utils.toast(res.error_msg)
}
return res
}
}
},
/********* Implement list pull up load scroll position restore core code start *************/
list: {
data () {
return {
data: [].// List stored data
loading: true.// Data is being loaded
done: false.// Whether all data has been loaded
page: 1 // The number of pages loaded}},async fetch ({ state, route, params, path }) {
// In vuet 0.1.2 and above, there is an extra params.routeWatch parameter, which we can use to determine whether the page has changed
if (params.routeWatch === true) { // Route changed, reset module state
this.reset(path)
} else if (params.routeWatch === false) { // Routing does not trigger requests that may be returned from details to the list
return{}}// params.routeWatch has no arguments, so it is a pull-up call
const { tab = ' ' } = route.query
const query = {
tab,
mdrender: false.limit: 20.page: state.page
}
const res = await http.get('/topics', query)
const data = params.routeWatch ? res.data : [...state.data, ...res.data]
return {
data, // Update module list data
page: ++state.page, // The number of pages +1 after each successful request
loading: false.// Data loading is complete
done: res.data.length < 20 // Check whether all the pages of the list are loaded}}},/********* implement list pull up load scroll position restore core code end *************/
detail: {
data () {
return {
data: {
id: null.author_id: null.tab: null.content: null.title: null.last_reply_at: null.good: false.top: false.reply_count: 0.visit_count: 0.create_at: null.author: {
loginname: null.avatar_url: null
},
replies: [].is_collect: false
},
existence: true.loading: true.commentId: null}},async fetch ({ route }) {
const { data } = await http.get(`/topic/${route.params.id}`)
if (data) {
return {
data,
loading: false}}return {
existence: false.loading: false}}}},user: { // Login user's module
self: {
data () {
return {
data: JSON.parse(localStorage.getItem('vue_cnode_self')) || {
avatar_url: null.id: null.loginname: null.success: false}}},manuals: {
async login ({ state }, accesstoken) { // User login method
const res = await http.post(`/accesstoken`, { accesstoken })
if (typeof res === 'object' && res.success) {
state.data = res
localStorage.setItem('vue_cnode_self'.JSON.stringify(res))
localStorage.setItem('vue_cnode_accesstoken', accesstoken)
}
return res
},
signout () { // User exit method
localStorage.removeItem('vue_cnode_self')
localStorage.removeItem('vue_cnode_accesstoken')
this.reset()
}
}
},
detail: {
data () {
return {
data: {
loginname: null.avatar_url: null.githubUsername: null.create_at: null.score: 0.recent_topics: [].recent_replies: []},existence: true.loading: true.tabIndex: 0}},async fetch ({ route }) {
const { data } = await http.get(`/user/${route.params.username}`)
if (data) {
return {
data,
loading: false}}return {
existence: false.loading: false}}},messages: {
data () {
return {
data: {
has_read_messages: [].hasnot_read_messages: []},loading: true}},async fetch () {
// The user is not logged in
if (!this.getState('user-self').data.id) return
const { data } = await http.get(`/messages`, { mdrender: true })
return {
data
}
},
count: {
data () {
return {
data: 0}},async fetch () {
// The user is not logged in
if (!this.getState('user-self').data.id) return
const res = await http.get('/message/count')
if(! res.data)return
return {
data: res.data
}
}
}
}
}
}
})Copy the code
After the Vuet instance is created, we can wire our Vuet in the component.
- Home page list
<template>
<div>
<nav class="nav">
<ul flex="box:mean">
<li v-for="item in tabs" :class="{ active: item.tab === ($route.query.tab || '') }">
<router-link :to="{ name: 'index', query: { tab: item.tab } }">{{ item.title }}</router-link>
</li>
</ul>
</nav>
<! If your page is a global scrollbar, set the command to V-vuet-scroll. window="{path: 'topic-list'}" -->
<v-content v-vuet-scroll="{ path: 'topic-list', name: 'content' }">
<ul class="list">
<li v-for="item in list.data" key="item.id">
<router-link :to="{ name: 'topic-detail', params: { id: item.id } }">
<div class="top" flex="box:first">
<div class="headimg" :style="{ backgroundImage: 'url(' + item.author.avatar_url + ')' }"></div>
<div class="box" flex="dir:top">
<strong>{{ item.author.loginname }}</strong>
<div flex>
<time>{{ item.create_at | formatDate }}</time>
<span class="tag">Share the # #</span>
</div>
</div>
</div>
<div class="common-typeicon" flex v-if="item.top || item.good">
<div class="icon" v-if="item.good">
<i class="iconfont icon-topic-good"></i>
</div>
<div class="icon" v-if="item.top">
<i class="iconfont icon-topic-top"></i>
</div>
</div>
<div class="tit">{{ item.title }}</div>
<div class="expand" flex="box:mean">
<div class="item click" flex="main:center cross:center">
<i class="iconfont icon-click"></i>
<div class="num">{{ item.visit_count > 0 ? Item.visit_count: 'Not read yet'}}</div>
</div>
<div class="item reply" flex="main:center cross:center">
<i class="iconfont icon-comment"></i>
<div class="num">{{ item.reply_count > 0 ? Item. reply_count: 'No comment yet'}}</div>
</div>
<div class="item last-reply" flex="main:center cross:center">
<time class="time">{{ item.last_reply_at | formatDate }}</time>
</div>
</div>
</router-link>
</li>
</ul>
<v-loading :done="list.done" :loading="list.loading" @seeing="$vuet.fetch('topic-list')"></v-loading>
</v-content>
<v-footer></v-footer>
</div>
</template>
<script>
import { mapModules, mapRules } from 'vuet'
export default {
mixins: [
mapModules({ list: 'topic-list' }), // Connect the state of the vuet.js we defined
mapRules({ route: 'topic-list' }) // Use vuet.js's built-in route rules to manage page data and scroll positions
],
data () {
return {
tabs: [{title: 'all'.tab: ' '
},
{
title: 'essence'.tab: 'good'
},
{
title: 'sharing'.tab: 'share'
},
{
title: 'q&a'.tab: 'ask'
},
{
title: 'recruitment'.tab: 'job'}]}}}</script>Copy the code
- Page for details
<template>
<div>
<v-header title="Theme">
<div slot="left" class="item" flex="main:center cross:center" v-on:click="$router.go(-1)">
<i class="iconfont icon-back"></i>
</div>
</v-header>
<! Set local scroll bar for details -->
<v-content style="bottom: 0;" v-vuet-scroll="{ path: 'topic-detail', name: 'content' }">
<v-loading v-if="detail.loading"></v-loading>
<v-data-null v-if=! "" detail.existence" msg="The topic doesn't exist."></v-data-null>
<template v-if=! "" detail.loading && detail.existence">
<div class="common-typeicon" flex v-if="data.top || data.good">
<div class="icon" v-if="data.good">
<i class="iconfont icon-topic-good"></i>
</div>
<div class="icon" v-if="data.top">
<i class="iconfont icon-topic-top"></i>
</div>
</div>
<ul class="re-list">
<! Start -->
<li flex="box:first">
<div class="headimg">
<router-link class="pic" :to="{ name: 'user-detail', params: { username: author.loginname } }" :style="{ backgroundImage: 'url(' + author.avatar_url + ')' }"></router-link>
</div>
<div class="bd">
<div flex>
<router-link flex-box="0" :to="{ name: 'user-detail', params: { username: author.loginname } }">{{ author.loginname }}</router-link>
<time flex-box="1">{{ data.create_at | formatDate }}</time>
<div flex-box="0" class="num"># the building</div>
</div>
</div>
</li>
<! End -->
<! Start -->
<li>
<div class="datas">
<div class="tit">{{ data.title }}</div>
<div class="bottom" flex="main:center">
<div class="item click" flex="main:center cross:center">
<i class="iconfont icon-click"></i>
<div class="num">{{ data.visit_count }}</div>
</div>
<div class="item reply" flex="main:center cross:center">
<i class="iconfont icon-comment"></i>
<div class="num">{{ data.reply_count }}</div>
</div>
</div>
</div>
<div class="markdown-body" v-html="data.content"></div>
</li>
<! End -->
<li class="replies-count" v-if="replies.length">A total of (<em>{{ replies.length }}</em>Article) reply</li>
<! Start -->
<li v-for="(item, $index) in replies">
<div flex="box:first">
<div class="headimg">
<router-link class="pic" :to="{ name: 'user-detail', params: { username: item.author.loginname } }" :style="{ backgroundImage: 'url(' + item.author.avatar_url + ')' }"></router-link>
</div>
<div class="bd">
<div flex>
<router-link flex-box="0" :to="{ name: 'user-detail', params: { username: item.author.loginname } }">{{ item.author.loginname }}</router-link>
<time flex-box="1">{{ item.create_at | formatDate }}</time>
<div flex-box="0" class="num">#{{ $index + 1 }}</div>
</div>
<div class="markdown-body" v-html="item.content"></div>
<div class="bottom" flex="dir:right cross:center">
<div class="icon" @click="commentShow(item, $index)">
<i class="iconfont icon-comment-topic"></i>
</div>
<div class="icon" :class="{ fabulous: testThing(item.ups) }" v-if="item.author.loginname ! == user.data.loginname" @click="fabulousItem(item)">
<i class="iconfont icon-comment-fabulous"></i>
<em v-if="item.ups.length">{{ item.ups.length }}</em>
</div>
</div>
</div>
</div>
<reply-box v-if="detail.commentId === item.id" :loginname="item.author.loginname" :replyId="item.id"></reply-box>
</li>
<! -- Theme comment end -->
</ul>
<div class="reply" v-if="user.data.id">
<reply-box @success="$vuet.fetch('topic-detail')"></reply-box>
</div>
<div class="tip-login" v-if=! "" user.data.id">You have not logged in yet, please first<router-link to="/login">The login</router-link>
</div>
</template>
</v-content>
</div>
</template>
<script>
import http from 'http'
import replyBox from './reply-box'
import { mapModules, mapRules } from 'vuet'
export default {
mixins: [
// Connection details and login user module
mapModules({ detail: 'topic-detail'.user: 'user-self' }),
// Use the route rule to manage the page data
mapRules({ route: 'topic-detail'})].components: { replyBox },
computed: {
data () {
return this.detail.data
},
author () {
return this.detail.data.author
},
replies () {
return this.detail.data.replies
}
},
methods: {
testThing (ups) { // Verify whether to like
return ups.indexOf(this.user.data.id || ' ') > - 1
},
fabulousItem ({ ups, id }) { / / thumb up
if (!this.user.data.id) return this.$router.push('/login')
var index = ups.indexOf(this.user.data.id)
if (index > - 1) {
ups.splice(index, 1)}else {
ups.push(this.user.data.id)
}
http.post(`/reply/${id}/ups`)
},
commentShow (item) { // Display the hidden reply box
if (!this.user.data.id) return this.$router.push('/login')
this.detail.commentId = this.detail.commentId === item.id ? null : item.id
}
}
}
</script>Copy the code
conclusion
Because space is limited, so only lists the list and details of the code, if you are interested in in-depth, you can take a look at vue-cNode code. This is a complete project of state management based on Vuet, including user login and exit, routing page, scrolling position restoration, post editing state preservation and so on. Although small, it has all the five organs.