Vue.js輕松實現(xiàn)頁面后退時畦粮,還原滾動位置

前言

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自身的滾動條把敢,這樣就能大大的提升了我們的用戶體驗

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末寄摆,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子修赞,更是在濱河造成了極大的恐慌婶恼,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件柏副,死亡現(xiàn)場離奇詭異勾邦,居然都是意外死亡,警方通過查閱死者的電腦和手機割择,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門眷篇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人锨推,你說我怎么就攤上這事铅歼」溃” “怎么了?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵椎椰,是天一觀的道長厦幅。 經(jīng)常有香客問我,道長慨飘,這世上最難降的妖魔是什么确憨? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮瓤的,結果婚禮上休弃,老公的妹妹穿的比我還像新娘。我一直安慰自己圈膏,他們只是感情好塔猾,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著稽坤,像睡著了一般丈甸。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上尿褪,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天睦擂,我揣著相機與錄音,去河邊找鬼杖玲。 笑死顿仇,一個胖子當著我的面吹牛,可吹牛的內容都是我干的摆马。 我是一名探鬼主播臼闻,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼今膊!你這毒婦竟也來了些阅?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤斑唬,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后黎泣,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體恕刘,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年抒倚,在試婚紗的時候發(fā)現(xiàn)自己被綠了褐着。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡托呕,死狀恐怖含蓉,靈堂內的尸體忽然破棺而出频敛,到底是詐尸還是另有隱情,我是刑警寧澤馅扣,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布斟赚,位于F島的核電站,受9級特大地震影響差油,放射性物質發(fā)生泄漏拗军。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一蓄喇、第九天 我趴在偏房一處隱蔽的房頂上張望发侵。 院中可真熱鬧,春花似錦妆偏、人聲如沸刃鳄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽叔锐。三九已至,卻和暖如春罐柳,著一層夾襖步出監(jiān)牢的瞬間掌腰,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工张吉, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留齿梁,地道東北人。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓肮蛹,卻偏偏與公主長得像勺择,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子伦忠,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內容