整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          Vue組件間通信幾種方式,你用哪種?「實踐」

          Vue組件間通信幾種方式,你用哪種?「實踐」



          者:Fundebug

          轉發鏈接:https://www.jianshu.com/p/7df2576a8a2b


          ue-element-admin 登陸

          引言

          vue-element-admin是vue生態圈中,最火的一個后臺管理框架。基于vue和element-ui實現。

          這篇文章主要會講解登陸的流程以及我認為這個框架的厲害的東西:動態路由,之前看代碼的時候,總想著一個登陸搞那么麻煩,后面仔細品味發現原來一個小小的登陸功能涉及到了這么多的東西。

          準備工作

          目錄結構

          了解一個框架之前,先要從目錄結構入手(這里直接引用花褲衩大佬的目錄結構)。目錄結構的了解能夠更加清楚模塊功能的劃分。

          ├── src                        # 源代碼
          │   ├── api                    # 所有的api請求
          │   ├── assets                 # 主題 字體等靜態資源
          │   ├── components             # 全局公用組件
          │   ├── directive              # 全局指令
          │   ├── filters                # 全局 filter過濾器
          │   ├── icons                  # 項目所有 svg icons
          │   ├── layout                 # 全局 layout
          │   ├── router                 # 路由
          │   ├── store                  # 全局 store管理
          │   ├── styles                 # 全局樣式
          │   ├── utils                  # 全局公用方法
          │   ├── vendor                 # 公用vendor
          │   ├── views                  # views 所有頁面
          │   ├── App.vue                # 入口頁面
          │   ├── main.js                # 入口文件 加載組件 初始化等
          │   └── permission.js          # 權限管理
          ├── tests                      # 測試
          ├── .env.xxx                   # 環境變量配置
          ├── .eslintrc.js               # eslint 配置項
          ├── .babelrc                   # babel-loader 配置
          ├── .travis.yml                # 自動化CI配置
          ├── vue.config.js              # vue-cli 配置
          ├── postcss.config.js          # postcss 配置
          └── package.json               # package.json
          復制代碼

          permission.js

          在登陸這個流程中,permission.js這個是最重要的一環,其實這個文件就是路由的全局鉤子(beforeEach和afterEach),全局鉤子的意思就是每次跳轉的時候可以根據情況進行攔截,不讓它進行跳轉。使用場景最常見的就是有些頁面需要用戶登陸之后才能訪問,就可以在beforeEach中校驗用戶是否登陸來進行相對應的攔截處理。下面會詳細的講解permission.js的內容。

          util / auth.js

          這個文件主要就是設置token到cookie中的操作封裝。

          router

          這個是路由中的一些設置,理解這個后面看組件Sidebar、TagViews將會事半功倍。

          /**
            hidden: true                      是否隱藏于Sidebar側邊欄       
            alwaysShow: true					是否顯示在根菜單
            redirect: noRedirect				Breadcrumb中重定向的path
            name: 'router-name'				用于keep-alive的Name
            meta: {
          	roles: ['admin', 'editor'],		當前路由的訪問所需要權限
          	title: 'title',					Sidebar和Breadcrumb的title
          	icon: 'svg-name',				Sibebar的icon
          	noCache: true					是否設置不緩存
          	breadcrumb: true				是否顯示在Breadcrumb上
          	activeMenu: '/example/list'		Sidebar高亮時的顯示path
            }
          **/
          復制代碼

          其他的一些我就沒有介紹了,比如說封裝好axios的request.js,還有把請求封裝成api,這些可以自行去了解。

          view / login / index.vue

          省略了一些的細枝末節,直接從點擊登陸之后發生了一系列事情開始講起,第一個就是handleLogin方法。

          handleLogin() {
            this.$refs.loginForm.validate(valid=> {
              if (valid) {
                this.loading=true
                this.$store.dispatch('user/login', this.loginForm)
                  .then(()=> {
                    this.$router.push({
                      path: this.redirect || '/',
                      query: this.otherQuery
                    })
                    this.loading=false
                  })
                  .catch(()=> {
                    this.loading=false
                  })
              } else {
                console.log('error submit!!')
                return false
              }
            })
          }
          復制代碼

          可以看到這個方法很簡單,就是利用validate方法進行表單驗證,驗證通過則使用this.$store.dispatch調用user/login方法并傳遞這個表單的數據,然后有一個.then()方法,方法里面有this.$router.push(...),可能有同學就會有疑惑了,this.redirect和this.otherQuery是啥,用一句話來概括就是:我從哪里跳到/login頁面,登陸之后我就返回到哪里。

          這個user/login是什么呢?一起來揭開它神秘的面紗。

          user / login

          先解析這個之前,先來補充一點vuex基礎知識:

          vuex中使用namespaced:true開啟命名空間,調用mutations或者調用actions,則是模塊名 + 相對應的方法名。

          另外actions是異步的,action處理函數之后返回的Promise進行相對應的處理。

          // user.js
          // 不開啟命名空間
          const actions={
              login(){}
          }
          export default { actions };
          
          this.$store.dispatch('login');
          
          // user.js
          // 開啟命名空間
          const actions={
              login(){}
          }
          export default { actions, namespaced: true }; // 注意!開啟命名空間
          
          this.$store.dispatch('user/login'); // 模塊名user + 方法名 login
          復制代碼

          上面所說的user/login,則就是user模塊中的login方法,核心代碼就如下:

          login({ commit }, userInfo) {
              const { username, password }=userInfo
              return new Promise((resolve, reject)=> {
                login({ username: username.trim(), password: password }).then(response=> {
                  const { data }=response // 解構出data
                  commit('SET_TOKEN', data.token) // 更新store里面的token
                  setToken(data.token) // token保存到cookie
                  resolve()
                }).catch(error=> {
                  reject(error)
                })
              })
           }
          復制代碼

          一句話來概括:登陸驗證,登陸成功之后,分別保存token到vuex、cookie中。

          這里完成之后就會回到之前的view/login/index.vue,當user/login調用完成之后,則會進行.then(...)方法,就是一個路由跳轉的過程(this.$router.push(...))。

          Permission.js

          這個文件就如同前面介紹所說,路由的全局鉤子,動態路由的實現這里相當于是一個入口。來看核心的實現代碼。

          router.beforeEach(async(to, from, next)=> {
            // 從cookie中取得token
            const hasToken=getToken()
            
            // 如果有token 也就是已經登陸的情況下
            if (hasToken) {
              // 并且要前往的路徑是'/login'  則返回 '/' 
              if (to.path==='/login') {
                next({ path: '/' })
              } else {
                // 從store中取得用戶的 roles, 也就是用戶的權限 并且用戶的權限數組必須有一個以上
                const hasRoles=store.getters.roles && store.getters.roles.length > 0
                // 有權限則直接進入
                if (hasRoles) {
                  next()
                } else {
                  // 沒有權限的話
                  try {
                    // 獲取用戶信息
                    const { roles }=await store.dispatch('user/getInfo')
          		  // 生成可訪問路由
                    const accessRoutes=await store.dispatch('permission/generateRoutes', 			  														roles)
                    // 將可訪問路由添加到路由上
                    router.addRoutes(accessRoutes)
                    // 進入路由
                    next({ ...to, replace: true })
                  } catch (error) {
                    // 如果出現異常  清空路由 
                    await store.dispatch('user/resetToken')
                    // Message提示錯誤
                    Message.error(error || 'Has Error')
                    // 跳到login頁面重新登陸
                    next(`/login?redirect=${to.path}`)
                  }
                }
              }
            } else {
              // 沒有token 也就是沒有登陸的情況下  
              // 判斷是否是白名單(也就是說不需要登陸就可以訪問的路由)
              if (whiteList.indexOf(to.path) !==-1) {
                next()
              } else {
                // 其他的一路給我跳到login頁面 老老實實的進行登陸
                next(`/login?redirect=${to.path}`)
                NProgress.done()
              }
            }
          })
          復制代碼

          注釋已經寫的明明白白了,這個思路其實使用的特別多,就是利用全局鉤子進行訪問的攔截,如果沒有登陸的話,跳轉到登陸頁面進行登陸。

          但是花褲衩大佬的這個有一點點不同,可以看到他將登陸和獲取用戶信息分成了兩步,原因就是保證用戶信息是最新的。第二個是在獲取用戶信息之后返回的roles生成可訪問的路由,也就是這兩步實現了動態路由。

          用一句話來概括:是否登陸?沒有就給我老老實實登陸。是否有用戶信息?沒有就給我獲取用戶信息,并且生成可訪問路由然后利用addRoutes進行添加。

          這兩步都是actions:user/getInfo,permission/generateRoutes。

          可以看到這是vuex中的actions提交,可能有些小伙伴會有點困惑,為什么有的請求直接寫在api文件夾,有些就放在actions里面?這個問題:我的理解就是,如果返回的數據要保存在vuex中的話,可以直接使用actions,原因是action里面可以提交mutation改變store的狀態,可以少寫一些代碼,同時思路更加清晰,如果返回的數據只需要在當前頁面使用的話,并不需要保存到vuex中,那就直接用寫在api文件夾中引用即可。當然這個是我的個人理解,如有錯誤請望指出。

          user / getInfo

          首先我們看第一個user/getInfo。

          如上面所說,這是user模塊中的getInfo方法,來看核心代碼。

          getInfo({ commit , state }) {
            return new Promise((resolve, reject)=> {
              // 調用getInfo接口
              getInfo(state.token).then(response=> {
                const { data }=response // 解構出data
                if (!data) { // 進行數據校驗
                  reject('Verification failed, please Login again.')
                }
                // 解構出需要保存的值  
                const { roles, name, avatar, introduction }=data
                // roles權限數組至少有一個權限
                if (!roles || roles.length <=0) {
                  reject('getInfo: roles must be a non-null array!')
                }
                // 保存到vuex
                commit('SET_ROLES', roles)
                commit('SET_NAME', name)
                commit('SET_AVATAR', avatar)
                commit('SET_INTRODUCTION', introduction)
                // 將 data 返回
                resolve(data)
              }).catch(error=> {
                reject(error)
              })
            })
          }
          復制代碼

          這個就是調用getInfo接口獲取用戶信息并且保存到vuex中,為了嚴謹性,進行相對應的數據校驗,然后把data返回。

          用一句話來概括:獲取用戶信息,并保存用戶信息。

          permission/generateRoutes

          我們來看第二個actions,這個是動態路由中的重要一步,生成可訪問路由,根據當前用戶的權限數組,和路由中可訪問的權限數組,進行匹配生成。

          // 判斷是否有權限
          function hasPermission(roles, route) {
            if (route.meta && route.meta.roles) {
              // roles.some=> Array.some 相當于是只要有一個滿足就為true 
                
              // 判斷用戶的權限于當前路由訪問所需要的權限是否有一個滿足
              // 比如說用戶權限為 ['one','two']  當前路由訪問所需要權限為 ['two','three']  那么就說明當前用戶可以訪問這個路由
              return roles.some(role=> route.meta.roles.includes(role))
            } else {
              // 默認是可訪問的
              return true
            }
          }
          // 生成可訪問路由
          export function filterAsyncRoutes(routes, roles) {
            const res=[]
          
            routes.forEach(route=> {
              const tmp={ ...route }
              // 判斷當前路由是否可以訪問
              if (hasPermission(roles, tmp)) {
                // 如果當前路由還有子路由
                if (tmp.children) {
                  // 進行遞歸處理
                  tmp.children=filterAsyncRoutes(tmp.children, roles)
                }
                // 將可訪問路由放入數組中
                res.push(tmp)
              }
            })  
            // 返回
            return res
          }
          
          // 為什么要寫這里呢,因為后面的Sidebar組件與這個環環相扣
          const mutations={
            SET_ROUTES: (state, routes)=> {
              // 添加的路由
              state.addRoutes=routes
              // 將vuex中的路由進行更新
              state.routes=constantRoutes.concat(routes)
            }
          }
          
          const actions={
            generateRoutes({ commit }, roles) {
              return new Promise(resolve=> {
                let accessedRoutes
                // 如果roles包含 'admin' 直接可以全部訪問
                if (roles.includes('admin')) {
                  accessedRoutes=asyncRoutes || []
                } else {
                  // 利用 filterAsyncRoutes 過濾出可訪問的路由
                  accessedRoutes=filterAsyncRoutes(asyncRoutes, roles)
                }
                // 保存可訪問的路由到store中
                commit('SET_ROUTES', accessedRoutes)
                // 將可訪問路由返回
                resolve(accessedRoutes)
              })
            }
          }
          復制代碼

          歐克,注釋已經全部寫好了,這個就是動態路由中重要一步,慢慢品味發現也不難,越看越覺得666。

          用一句話來概括:根據得到的用戶權限生成可以訪問的路由。

          動態路由的實現

          router.addRoutes(accessRoutes)
          
          復制代碼

          ???,沒呢?

          對的!動態路由的核心代碼就這一句話,短小精悍,其他的都是為了完成動態路由做的一些 "準備工作" ,user/getInfo獲取用戶信息得到用戶的roles權限數組,返回user/generateRoutes生成可訪問路由,就是這么神奇的一步,實現了動態路由。

          寫到這里基本流程就走完了,但是還有兩個注意點。

          注意

          為什么項目開啟默認就是登陸頁面

          想必通過之前的講解也應該知道了permission.js的作用了,全局路由鉤子,每次路由跳轉都要調用全局路由鉤子(誰讓它是全局的呢),原因就是當頁面加載首頁時也會經過全局路由鉤子,而permission.js判斷當前用戶有沒有登陸,沒有登陸就直接跳轉到/login頁面進行登陸把,就是這么任性,啦啦啦。

          動態路由顯示不出來

          有些小伙伴可能會改這個框架的代碼,但是發現顯示不出來?這也是我遇到過的一個坑,需要注意的是permission.js里面有兩個條件:第一個是否登陸?第二個是否獲取用戶信息(判斷是否獲取用戶信息是根據roles)?有些小伙伴可能是登陸的時候就把用戶信息獲取了,但是!!沒有更新用戶roles權限數組,所以就一直會獲取用戶信息,從而導致顯示不出來。

          完結,撒花

          其實到這里整個登陸流程就已經結束了,可以看到花褲衩大佬想的很多,用戶信息的獲取和用戶登陸的分離,根據用戶權限生成可以訪問的路由并添加,這些思路我覺得很厲害,還有這么多組件可以用,哈哈哈哈。

          好的,我們來總結一下:

          1. Login/index.vue點擊登陸 提交user/login的actions。
          2. user/login進行登陸驗證,登陸成功之后保存token到vuex和cookie中。
          3. 回到Login/index.vue跳轉路由,這時就到了permission.js
          4. permission.js進行判斷是否登陸,是否有用戶信息?沒有用戶信息就獲取用戶信息,并且保存到vuex,然后根據用戶信息中的roles生成可訪問路由,并通過addRoutes進行添加。

          看到這里,相信你對這個框架的登陸流程已經有一定的了解了,自己再去理一遍把,啦啦啦啦,那下面這一張流程圖相信你也可以看懂。

          天是2021.7.14,是個好日子.好久沒發布文章了.今天發布下如何在在html頁面中使用vue3.義縣游學電子科技一直以技術文章為主.以下是h5的頁面源碼:

          <html>

          <script src="https://unpkg.com/vue@next"></script>


          <body>

          <div id="vue">

          <div v-html="rhtml"></div>

          <props-demo-simple></props-demo-simple>


          </div>


          </body>

          <script>

          const htmls={

          data(){

          return{

          rhtml:"<h1>html頁面中引用VUE3的演示頁面</h1>",

          }

          }

          }


          const app=Vue.createApp(htmls)


          // 簡單語法注冊或獲取全局組件.注冊還會自動使用給定的 id 設置組件的名稱

          app.component('props-demo-simple', { data() {

          return {

          count: 0

          }

          },

          props: ['size', 'myMessage'],template: `

          <button v-on:click="count++">

          You clicked me {{ count }} times.

          </button>`

          }



          )

          app.mount("#vue")

          </script>

          </html>

          分析下:首先<script src="https://unpkg.com/vue@next"></script>,引入vue3的腳本地址. 然后在body下的<script>中書寫vue3的代碼即可. 下圖是運行的結果效果


          主站蜘蛛池模板: 国产精品高清视亚洲一区二区 | 亚洲国产精品一区二区三区久久| 在线精品亚洲一区二区小说| 欧洲精品码一区二区三区免费看| 国产成人AV区一区二区三 | 在线视频一区二区三区四区| eeuss鲁片一区二区三区| 国产免费av一区二区三区| 久久亚洲中文字幕精品一区四| 视频一区二区三区人妻系列| 久久无码人妻精品一区二区三区 | 亚洲AV日韩AV天堂一区二区三区 | 日韩一区精品视频一区二区| 3D动漫精品啪啪一区二区下载| 天天躁日日躁狠狠躁一区| 中文字幕一区在线| 亚洲国产成人一区二区三区| 国产精品视频免费一区二区| 国产精品一区二区久久精品无码| 国99精品无码一区二区三区| 日本高清一区二区三区 | 中文字幕一区二区在线播放| 人妻aⅴ无码一区二区三区| 精品国产日韩亚洲一区在线 | 无码精品国产一区二区三区免费| 日韩一区二区视频| 国产在线精品一区二区高清不卡| 色窝窝无码一区二区三区 | 蜜臀AV免费一区二区三区| 日本一区二区三区久久| 性色AV一区二区三区| 国产91精品一区| 久久精品国产第一区二区三区| 国产一区二区影院| 亚洲午夜一区二区三区| 国产日韩AV免费无码一区二区三区| 无码国产精品一区二区免费式影视| 丰满人妻一区二区三区视频| 中文字幕精品一区二区| 亚洲国产成人一区二区精品区 | 无码人妻精品一区二区三区夜夜嗨 |