前言
從Vue.js 2.x
發(fā)布之后,陸陸續(xù)續(xù)做了七八個項目返弹,摸索出來了一套自己的狀態(tài)管理模式锈玉,我將之稱為Vuet。它以規(guī)則來驅動狀態(tài)更新义起,它帶來的是開發(fā)效率上的飆升拉背,它就像草原,而你是野馬默终,任你隨意馳騁椅棺,總之它是為敏捷開發(fā)而誕生犁罩。
緣由
在大型的Vue應用程序開發(fā)中,多組件通信两疚、多頁面通信床估,往往是跨不過的坎,一個頁面組件中往往參雜著頁面獲取數(shù)據(jù)的代碼和響應用戶操作的代碼诱渤,稍有不慎丐巫,就使得代碼混亂不堪。A勺美、B递胧、C三個頁面中,都需要同樣的數(shù)據(jù)赡茸,然后每一個頁面都寫一次缎脾、發(fā)送一次請求,不久之后占卧,代碼就十分臃腫了遗菠。因此我們就需要vuex
這樣的第三方庫來管理狀態(tài)了
Vuet誕生初衷
從列表點擊進去到詳情,從詳情返回后华蜒,我們期望能顯示回原來的位置辙纬,而不是整個頁面重新初始化,重新請求數(shù)據(jù)友多,這樣帶來的是用戶體驗的極度糟糕的牲平,我們期望能有一種規(guī)則來定義狀態(tài)應該如何更新,這便是Vuet.js誕生的初衷域滥。它以規(guī)則來定義狀態(tài)的更新,它也是一種Vue.js全新的狀態(tài)管理模式蜈抓。天生的規(guī)則驅動启绰,使得本次教程的主題,也將變得異常簡單沟使,因為我們只需要定義好頁面更新的規(guī)則即可實現(xiàn)委可。
有了Vuex還需要Vuet做什么?
Vuex和Vuet的出發(fā)點不一樣腊嗡,Vuex不建議直接更新狀態(tài)着倾,而是通過提交mutation來更新狀態(tài),而Vuet則是允許的燕少。因此Vuex和Vuet是可以配合使用的卡者,并且有著不同的應用場景,該用
Vuex的地方就用Vuex客们,可用
Vuet的地方崇决,就可以使用Vuet
開始
上面廢話了那么久材诽,也是因為Vuet.js才剛剛誕生,急需大家的支持恒傻。嗯脸侥,接下來我們開始本次的主題!
目錄結構
|-- pages // 頁面組件
| |-- topic // 主題模塊
| |-- Detail.vue // 主題詳情
| |-- List.vue // 主題列表
|-- router // router相關
| |-- index.js // 入口文件
| |-- router.js // 實例化VueRouter
|-- vuet // vuet相關
| |-- index.js // 入口文件
| |-- topic-detail.js // 主題詳情的狀態(tài)
| |-- topic-list.js // 主題列表的狀態(tài)
| |-- vuet.js // 實例化Vuet
|- index.html // 程序頁面入口文件
|- main.js // Vue實例化入口文件
上面是我們本次項目的基本目錄結構
安裝模塊
npm install vue vue-router vuet --save
這些都是基本的模塊盈厘,想必不用多說睁枕,大家都知道的。
route規(guī)則
先給出官方文檔地址
本章的主題沸手,核心就是在route
規(guī)則身上譬重,它能幫你獲取、更新罐氨、重置頁面的狀態(tài)臀规,配合v-vuet-scroll指令就能幫你處理頁面的全局滾動條和div元素自身的滾動條
code社區(qū)api為例子
- main.js
import Vue from 'vue' import router from './router/' import vuet from './vuet/' export default new Vue({ el: '#app', vuet, router, render (h) { return h('router-view') } })
- vuet/index.js
import vuet from './vuet' export default vuet
- vuet/vuet.js
import Vue from 'vue' import Vuet from 'vuet' import topicList from './topic-list' import topicDetail from './topic-detail' Vue.use(Vuet) const vuet = new Vuet({ data () { return { loading: true, // 請求中 loaderr: false // 請求失敗 } }, pathJoin: '-', // 父子模塊的連接路徑 modules: { topic: { list: topicList, detail: topicDetail } } }) vuet.beforeEach(({ path, params, state }) => { state.loading = true state.loaderr = false }) vuet.afterEach((err, { path, params, state }) => { state.loading = false state.loaderr = !!err }) export default vuet
- vuet/topic-list.js
export default { routeWatch: 'query', // 定義頁面的更新規(guī)則 data () { return { data: [], tabs: [ { label: '全部', value: 'all' }, { label: '精華', value: 'good' }, { label: '分享', value: 'share' }, { label: '問答', value: 'ask' }, { label: '招聘', value: 'job' } ] } }, async fetch ({ route }) { const { tab = '' } = route.query const { data } = await window.fetch(`https://cnodejs.org/api/v1/topics?mdrender=false&tab=${tab}`).then(response => response.json()) return { data } } }
- vuet/topic-detail.js
export default { routeWatch: 'params.id', // 定義頁面的更新規(guī)則 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 } } }, async fetch ({ route }) { const { data } = await window.fetch(`https://cnodejs.org/api/v1/topic/${route.params.id}`).then(response => response.json()) return { data } } }
- router/index.js
import router from './router' export default router
- router/router.js
import Vue from 'vue' import VueRouter from 'vue-router' import TopicList from '../pages/topic/List' import TopicDetail from '../pages/topic/Detail' Vue.use(VueRouter) const RouterView = { render (h) { return h('router-view') } } const router = new VueRouter({ routes: [ { path: '/', component: RouterView, children: [ { path: '', name: 'topic-list', component: TopicList }, { path: '/:id', name: 'topic-detail', component: TopicDetail } ] } ] }) export default router
- pages/topic/List.vue
<template>
<!--
設置指令監(jiān)聽全局滾動條,
注意了栅隐,光是設置指令可不行塔嬉,還需要在組件中使用route規(guī)則,
來處理頁面滾動的操作租悄,
局部滾動條直接去掉.window即可
如果需要同時記錄全局滾動條和div滾動條直接設置.window.self即可
它能做到N多個滾動位置記錄谨究,具體看官方文檔喔!
注:記錄div滾動的話泣棋,需要設置一個name來識別
v-vuet-scroll="{ path: 'topic-detail', name: 'xxx' }"
-->
<div v-vuet-scroll.window="{ path: 'topic-list' }">
<header>
<ul>
<li v-for="item in list.tabs">
<router-link :to="{ name: 'topic-list', query: { tab: item.value } }">{{ item.label }}</router-link>
</li>
</ul>
</header>
<ul class="list">
<li v-for="item in list.data">
<router-link :to="{ name: 'topic-detail', params: { id: item.id } }">{{ item.title }}</router-link>
</li>
</ul>
</div>
</template>
<script>
import { mapRules, mapModules } from 'vuet'
export default {
mixins: [
// 設置模塊的更新規(guī)則
mapRules({
route: 'topic-list'
}),
// 連接模塊的狀態(tài)
mapModules({
list: 'topic-list'
})
]
}
</script>
<style scoped>
</style>
- pages/topic/Detail.vue
<template>
<div v-vuet-scroll.window="{ path: 'topic-detail' }">
<h3>{{ detail.data.title }}</h3>
<div v-html="detail.data.content"></div>
</div>
</template>
<script>
import { mapRules, mapModules } from 'vuet'
export default {
mixins: [
// 設置模塊的更新規(guī)則
mapRules({
route: 'topic-detail'
}),
// 連接模塊的狀態(tài)
mapModules({
detail: 'topic-detail'
})
]
}
</script>
<style scoped>
</style>
總結
咋的一看胶哲,Vuet看起來也不是很復雜,只需要定義好模塊狀態(tài)潭辈,然后在組件中設置對應的規(guī)則來更新模塊的狀態(tài)即可鸯屿。其實vuet自帶的route規(guī)則能夠支持同時記錄全局滾動條、div自身的滾動條把敢,這樣就能大大的提升了我們的用戶體驗