詳細vue-抽象コンポーネント実戦編

33590 ワード

この文章はvueシリーズの5編目を詳しく話していますが、この文章の内容は以前とは異なり、今回は実戦編です.このシリーズ以前の記事に興味がある方は以下のリンクをクリックして転送できます
  • 『細談vueコア-vdom編』
  • 『細談vue-slot編』
  • 『細談vue-transition編』
  • 『細談vue-transition-group編』
  • 前の2編では,コンポーネントの設計構想をそれぞれ解析した.は抽象的なコンポーネントであり、単一の要素にのみ有効です.コンポーネントは、リストの遷移を実現し、実際の要素ノードをレンダリングします.どちらも要素に遷移効果を加えます
    今日は、これまで研究したことのあることを考え、実際のビジネスのシーンと結びつけます.
    一、業務背景
    私は会社で主に末端業務のサポートを担当しています.ずいぶん前に文章を書いたことがあります(『Type Script+大型プロジェクト実戦』)大体紹介しました.通常のいくつかのプロジェクトの開発では、さまざまな権限の検証は避けられません.
    私のこちらのプロジェクトはサービスの面で、異なる人は異なる操作を持っていて、例えばSREはSREの対用の権限を持っていて、できることはたくさんあります;普通のRDはその対応する権限を持っていて、できることの大部分はいくつかの基本的な运维能力だけで、しかもこれらはすべて自分の担当するサービスの下で持つ権限です.これらの権限検査は本当に多すぎて、もしあなたが統一管理をしなければ、気が狂っていると思います.
    この文章は「抽象コンポーネントを使用して権限を統一して操作する方法」と名付けられるべきかもしれません.もし仲間たちが私のビジネス全体に対する思考過程を見たくないなら、この章をスキップして直接次の章に入ることができます.
    1、一般的なやり方
    このような状況に対応して、サービスの具体的な情報を取得する際に、バックエンドにフロントエンド権限に関するフィールドをインタフェースに投げ込ませ、その後、フロントエンドに権限値のグローバルsetを行うのが最初の方法である.具体的な操作は以下の通りです.
  • vuex
  • interface State {
      hasPermission: boolean
    }
    
    const state: State = {
      hasPermission: false
    }
    
    const getters = {
      hasPermisson: (state: State) => state.hasPermisson
    }
    
    const mutations = {
      SET_PERMISSON (state: State, hasPermisson: boolean) {
        state.hasPermisson = hasPermisson
      }
    }
    
    const actions = {
      async srvInfo (context: { commit: Commit }, params: { appkey: string }) {
        return request.get(`xxx/srv/${params.appkey}`)
      },
      //       (            )
      async checkPermisson (context: { commit: Commit }, params?: { [key: string]: string }) {
        return request.get('xxx/permission', { params: params })
      }
    }
    
    export default {
      state,
      getters,
      mutations,
      actions
    }
    
  • は、その後、ページにおいて、対応する操作
  • を行う.
    <template>
      <div class="srv-page">
        <el-button @click="handleCheck('type1')">    1el-button>
        <el-button @click="handleCheck('type2')">    2el-button>
      div>
    template>
    
    <script lang="ts">
    import { Component, Vue } from 'vue-property-decorator'
    import { Getter, Mutation, Action } from 'vuex-class'
    
    @Component
    export default class SrvPage extends Vue {
      appkey: string = 'common-appkey'
    
      @Getter('hasPermisson') hasPermisson: boolean
      @Mutation('SET_PERMISSON') SET_PERMISSON: Function
      @Action('srvInfo') srvInfo: Function
      @Action('checkPermisson') checkPermisson: Function
    
      getSrvInfo () {
        this.srvInfo({ appkey: this.appkey }).then((res: Ajax.AjaxResponse) => {
          if (res.data.code === 0) {
            this.SET_PERMISSON(true)
          } else {
            this.SET_PERMISSON(false)
          }
        })
      }
    
      handleCheck (type: string) {
        if (this.hasPermisson) {
          this.checkPermisson({ type: type }).then((res: Ajax.AjaxResponse) => {
            if (res.data.code !== 0) {
              this.notify('xxx')
            }
          })
        } else {
          this.notify('xxx')
        }
      }
    	
      notify (name?: string) {
        this.$notify({
          title: '  ',
          message: `       ,      ${name}    `,
          type: 'warning',
          duration: 5000
        })
      }
    }
    script>
    

    しかし、バックエンドでサービス情報を取得するインタフェースにはいくつかのサードパーティインタフェースが接続されているため、インタフェースの応答速度が少し遅くなります.これにより、特定のサービス情報を取得する操作に遅延が発生し、ユーザーはデフォルトの権限値を見ることができます.
    2、バージョンアップのやり方
    上記の方法で管理すると、ページが少なく、操作が少ない場合は適用可能かもしれませんが、これもプロジェクト初期のやり方で、その時はページ上の権限操作が少なかったので、何の問題も発見されませんでした.しかし,権限に関する操作が多くなるにつれて,上記のやり方はあまりにも過剰であることが分かった.自分の後にプロジェクトの開発とメンテナンスをよりよく行うために、ビジネスと結びつけて操作のアップグレードを行いました.
    多くのページに多くの権限操作がある場合、関連操作をmixinsに引き抜くことができますか?答えはyesです.そして私はまた上の操作を引き出してmixinsにしました
  • vuex既存部分は変わらず、新規部分操作
  • const state: State = {
      isAppkeyFirstCheck: false
    }
    
    const getters = {
      isAppkeyFirstCheck: (state: State) => state.isAppkeyFirstCheck
    }
    
    const mutations = {
      SET_APPKEY_FIRST_CHECK (state: State, firstCheck: boolean) {
        state.isAppkeyFirstCheck = firstCheck
      }
    }
    
  • それからmixins/check-permission.tsの中の論理は以下の通りです:同じサービスに対して私達はただ1回の共通の検査をして、そしてサービスの肝心なパラメータappkey$route.queryを使って保存して、毎回変更すると権限を初期化して、残りの操作は以前ととても似ています
  • import { Vue, Component, Watch } from 'vue-property-decorator'
    import { Action, Getter, Mutation } from 'vuex-class'
    
    declare module 'vue/types/vue' {
      interface Vue {
        handleCheckPermission (params?: { appkey?: string, message?: string }): Promise<any>
      }
    }
    
    @Component
    export default class CheckPermission extends Vue {
      @Getter('hasPermisson') hasPermisson: boolean
      @Getter('isAppkeyFirstCheck') isAppkeyFirstCheck: boolean
      @Mutation('SET_PERMISSON') SET_PERMISSON: Function
      @Mutation('SET_APPKEY_FIRST_CHECK') SET_APPKEY_FIRST_CHECK: Function
      @Action('checkPermisson') checkPermisson: Function
    
      @Watch('$route.query.appkey')
      onWatchAppkey (val: string) {
        if (val) {
          this.SET_APPKEY_FIRST_CHECK(true)
          this.SET_PERMISSON(false)
        }
      }
    
      handleCheckPermission (params?: { appkey?: string, message?: string }) {
        return new Promise((resolve: Function, reject: Function) => {
          if (!this.isAppkeyFirstCheck) {
            if (!this.hasPermisson) {
              this.notify('xxx')
            }
            resolve()
            return
          }
          const appkey = params && params.appkey || this.$route.query.appkey
          this.checkPermisson({ appkey: appkey }).then(res => {
            this.SET_APPKEY_FIRST_CHECK(false)
            if (res.data.code === 0) {
              this.SET_PERMISSON(true)
              resolve(res)
            } else {
              this.SET_PERMISSON(false)
              this.notify('xxx')
            }
          }).catch(error => {
            reject(error)
          })
        })
      }
    
      notify (name?: string) {
        this.$notify({
          title: '  ',
          message: `       ,      ${name}    `,
          type: 'warning',
          duration: 5000
        })
      }
    }
    
  • 最後にページで
  • を使用できます.
    <template>
      <div class="srv-page">
        <el-button @click="handleCheck('type1')">  1el-button>
        <el-button @click="handleCheck('type2')">  2el-button>
      div>
    template>
    
    <script lang="ts">
    import { Component, Vue } from 'vue-property-decorator'
    import CheckPermission from '@/mixins/check-permission'
    
    @Component({
      mixins: [ CheckPermission ]
    }}
    export default class SrvPage extends Vue {
      handleCheck (type: string) {
        this.handleCheckPermission().then(res => {
          console.log(type)
        })
      }
    }
    script>
    

    OK、ここまで来て、このすべては悪くないように見えますが、このようなやり方を使って権限操作を管理するのも確かに便利です.
    二、TS実戦
    しかし、多くのページがmixinsを引用するのは面倒だと思います.そして私はさらに考えて、もっと良い方法で管理していますか?答えはもちろんyesvueの内蔵コンポーネントの設計構想を参考にした後、なぜ私はその構想を引き抜いて自分の業務と結合しないのかと思っています.
    この文章のキーワードは で、私の本意も真実のノードをレンダリングしないで、 を使って1層をカプセル化して、権限操作をすべてこのコンポーネント内に置いて、それから検査を通じてそのサブノードのイベントを実行します.しかし、私の実際の業務はTSで開発されているため、vueはTS書き込み抽象コンポーネントの使用をサポートしていないようです.これは、コンポーネントにabstract属性を設定できないためです.(私は資料を探しましたが、本当にサポート方法が見つかりませんでした.もし仲間が知っていたら教えてください.ありがとうございます)
    場面は一時非常に気まずくて、気まずくなることを避けるために、私は引き下がって次を求めるしかなくて、直接真実のノードをレンダリングして、つまりコンポーネントのような実現方式です.
    考え方は簡単で,主にいくつかのステップに分かれている.
  • は、render段階でノードをレンダリングし、関連イベント
  • をバインドする.
  • childrenサブノードに対して具体的なイベント処理
  • を行う.
  • は、および組立体
  • をそれぞれ実現する.
  • グローバル登録コンポーネント
  • 1、permission
    まず、コンポーネントが実装され、主に単一要素の権限イベントバインドを担当します.
    <script lang="ts">
    import { Vue, Component, Watch, Prop } from 'vue-property-decorator'
    import { Action, Getter, Mutation } from 'vuex-class'
    import { VNode } from 'vue'
    
    @Component({
      name: 'permission'
    })
    export default class Permission extends Vue {
      @Prop({ default: 'span' }) tag: string
      @Prop() appkey: string
      @Prop() message: string
      @Prop({ default: null }) param: { template_name: string, appkey?: string, env?: string } | null
    
      @Getter('adminsName') adminsName: string
      @Getter('hasPermisson') hasPermisson: boolean
      @Getter('isAppkeyFirstCheck') isAppkeyFirstCheck: boolean
      @Mutation('SET_PERMISSON') SET_PERMISSON: Function
      @Mutation('SET_APPKEY_FIRST_CHECK') SET_APPKEY_FIRST_CHECK: Function
      @Action('checkPermisson') checkPermisson: Function
      @Action('isSlient') isSlient: Function
    
      @Watch('$route.query.appkey')
      onWatchAppkey (val: string) {
        if (val) {
          this.SET_APPKEY_FIRST_CHECK(true)
          this.SET_PERMISSON(false)
        }
      }
    
      render (h): VNode {
        const tag = this.tag
        const children: Array = this.$slots.default
        if (children.length > 1) {
          console.warn(
            ' can only be used on a single element. Use ' +
            ' for lists.'
          )
        }
        const rawChild: VNode = children[0]
        this.handleOverride(rawChild)
        return h(tag, null, [rawChild])
      }
    
      handleOverride (c: any) {
        if (!(c.data && (c.data.on || c.data.nativeOn))) {
          return console.warn('there is no permission callback')
        }
        const method = c.data.on ? c.data.on.click : c.data.nativeOn.click
        c.data.on && (c.data.on.click = this.handlePreCheck(method))
        c.data.nativeOn && (c.data.nativeOn.click = this.handlePreCheck(method))
      }
    
      handlePreCheck (cb: Function) {
        return () => {
          const {
            appkey = this.$route.query.appkey,
            message = ''
          } = this
          this.handlePermissionCheck({ appkey, message }).then(() => {
            cb && cb()
          })
        }
      }
    
      handlePermissionCheck (params: { [key: string]: string }) {
        return new Promise((resolve: Function, reject: Function) => {
          if (!this.isAppkeyFirstCheck) {
            if (!this.hasPermisson) {
              return this.$notify({
                title: '  ',
                message: `         ,          :${this.adminsName}`,
                type: 'warning',
                duration: 5000
              })
            }
            if (this.param) {
              return this.isSlient(this.param).then(res => {
                resolve(res)
              })
            }
            resolve()
            return
          }
          this.checkPermisson({ appkey: params.appkey || this.$route.query.appkey }).then(res => {
            this.SET_APPKEY_FIRST_CHECK(false)
            if (res.data.code === 0) {
              this.SET_PERMISSON(true)
              if (this.param) {
                return this.isSlient(this.param).then(slientRes => {
                  resolve(slientRes)
                })
              }
              resolve(res)
            } else {
              this.SET_PERMISSON(false)
              this.$notify({
                title: '  ',
                message: params.message || res.data.message,
                type: 'warning',
                duration: 5000
              })
            }
          }).catch(error => {
            reject(error)
          })
        })
      }
    }
    script>
    

    そしてグローバルに登録
    import Permission from 'components/permission.vue'
    Vue.component('Permission', Permission)
    

    具体的には、コンポーネントを引用すると、そのラップされたサブノードがclickまたはnative clickを行う場合、事前に権限チェックを行い、チェックを通過してこそ自分自身の方法を実行する
    <template>
      <div class="srv-page">
        <permission>
          <el-button @click.native="handleCheck('type1')">    1el-button>
        permission>
      div>
    template>
    
    <script lang="ts">
    import { Component, Vue } from 'vue-property-decorator'
    
    @Component
    export default class SrvPage extends Vue {
      handleCheck (type: string) {
        console.log(type)
      }
    }
    script>
    

    2、permission-group コンポーネント、コンポーネントと比較すると、paramパラメータを各サブノードにバインドするだけでよい.具体的な両者は論理的にほぼ一致し,権限要求のパラメータを変更するだけでよい.
    // render      
    render (h): VNode {
      const tag = this.tag
      const rawChildren: Array = this.$slots.default || []
      const children: Array = []
      for (let i = 0; i < rawChildren.length; i++) {
        const c: VNode = rawChildren[i]
        if (c.tag) {
          children.push(c)
        }
      }
      children.forEach(this.handleOverride)
      return h(tag, null, children)
    }
    //        
    const param = c.data.attrs ? c.data.attrs.param : null
    

    グローバル登録
    import PermissionGroup from 'components/permission-group.vue'
    Vue.component('PermissionGroup', PermissionGroup)
    

    ページの使用
    <template>
      <div class="srv-page">
        <permission-group>
          <el-button @click.native="handleCheck('type1')">    1el-button>
        	<el-button @click.native="handleCheck('type2')">    2el-button>
        permission-group>
      div>
    template>
    
    <script lang="ts">
    import { Component, Vue } from 'vue-property-decorator'
    
    @Component
    export default class SrvPage extends Vue {
      handleCheck (type: string) {
        console.log(type)
      }
    }
    script>
    

    これで我々の権限ブロックコンポーネントは既に実現しており,本来は を直接使用してこれを完成させたいと思っていたが,vueがTSを使用した後はabstract属性をサポートしない方法もない.しかし、このような処理を行うと、権限操作の管理が非常にeasyになり、メンテナンスが容易になります.
    三、JS実戦vueはTSを使って自分の を書くことができないということはすでに知っていますが、JSはいいでしょう.JS実現については、実は具体的な論理もほぼそっくりで、render段階の違いにほかならないので、私はすべてのコードをリストしません.同じコードを直接省略
    <script>
    export default {
      abstract: true
    
      props: {
        appkey: String,
        message: String,
        param: {
          type: Object,
          default: () => { return {} }
        }
      },
    
      render (h) {
        const children = this.$slots.default
        if (children.length > 1) {
          console.warn(
            ' can only be used on a single element. Use ' +
            ' for lists.'
          )
        }
        const rawChild = children[0]
        this.handleOverride(rawChild)
        return rawChild
      },
    
      methods: {
        handleOverride (c) {
          // ...
        },
        handlePreCheck (cb) {
          // ...
        },
        handlePermissionCheck (param) {
          // ...
        }
      }
    }
    script>
    
    則と同様に、ここでは後述しません.
    まとめ
    これまで、当社の業務に属する はすでに完成しています.実際の業務では、 または のコンポーネントを抽出することができます.これは業務でもよく見られます.
    文章の最後に鶏のスープについて話します.
  • 私たちの技術の成長は基本的に80%ぐらい私たちが担当している業務が駆動しています.具体的にはあなたを駆動することができます.本当にあなたの業務に対する思考が
  • あるかどうかを見なければなりません.
  • 自分が担当しているプロジェクトがどれだけ重複しているかを嘆かないでください.実はあなたがどこに行っても、あなた自身が株式を持っていない場合、業務だけを見ても、退屈極まりない
  • です.
  • 自分をownerにして代入してみると、いろいろなことがわかります.
  • 唯一の成長ルートは自分のこの道で、自分で時間をかけて何かを研究して、それから業務と結合します.あるいは業務を通じて
  • を学ぶ.
  • ビジネスで学んだ実戦は、よりしっかりと記憶することができます.それが正しい成長の道であるはずです.
  • これらをマスターするまで、ドキュメントやPPTのソフトスキルを学ぶべきかもしれません.
  • 最後に、自分で作ったグループを放送します.
    フロントエンド交流群:731175396、皆さん、いらっしゃいませ