Перейти к деталям vue - абстрактный компонент фактических статей

Vue.js
Перейти к деталям vue - абстрактный компонент фактических статей

Эта статья является пятой статьей в серии vue.Содержание этой статьи не такое, как раньше.На этот раз она относится к настоящей боевой статье. Те, кто заинтересован в предыдущих статьях этой серии, могут щелкнуть ссылку ниже, чтобы отправить

В первых двух статьях мы анализировали<transition>а также<transition-group>Идеи дизайна компонентов.

<transition>Является абстрактным компонентом и работает только с одним элементом. а также<transition-group>Компонент реализует переход списка и отображает реальный узел элемента. Оба добавляют эффекты перехода к элементам

Сегодня я собираюсь подумать о некоторых вещах, которые я исследовал ранее, и объединить их с реальными бизнес-сценариями.

1. Предыстория бизнеса

Я в основном отвечаю за поддержку бизнеса операционного слоя в компании, и давно написал статью(«TypeScript + крупномасштабный бой проекта») был примерно введен. При разработке некоторых обычных проектов неизбежна проверка различных разрешений.

На сервисном уровне моего проекта у разных людей разные операции.Например 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')">确认权限1</el-button>
    <el-button @click="handleCheck('type2')">确认权限2</el-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Шерстяная ткань? Ответ положительный.然后我又开始将上面的操作抽离出来做成了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Логика внутри следующая: для одного и того же сервиса мы делаем только один раз публичную проверку, и ставим ключевые параметры сервиса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')">操作1</el-button>
    <el-button @click="handleCheck('type2')">操作2</el-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>

ОК, переходим к этому шагу, все это вроде хорошо, после использования этой практики управлять полномочиями тоже удобно.

2. Фактический бой TS

Тем не менее, я чувствую, что многие страницы упоминаютсяmixinsОчень хлопотно. Тогда я подумал дальше, есть ли лучший способ управлять этим? Ответ конечно да

в отношенииvueПосле идеи дизайна встроенных компонентов я подумал, почему бы мне не извлечь идею и не объединить ее со своим бизнесом?

Ключевые слова для этой статьи抽象组件Мое намерение также не рендерирует настоящий узел, использовать抽象组件Инкапсулируйте слой, поместите все операции разрешений в компонент, а затем выполните события его дочерних узлов после прохождения проверки. Однако, поскольку мой фактический бизнес развивается с TS, иvueКажется, что написание абстрактных компонентов с использованием TS не поддерживается, потому что его нельзя установить для компонентов.abstractАтрибуты. (Я искал информацию, но я действительно не мог найти, как ее поддержать. Если кто-то знает, пожалуйста, дайте мне знать, спасибо)

Сцена в какой-то момент очень смущала, и чтобы не смущать, я мог сделать только следующую лучшую вещь и отрендерить реальные узлы напрямую, то есть что-то вроде<transition-group>Как реализован компонент.

Идея очень проста, в основном разделена на несколько шагов

  • существуетrenderУзлы рендеринга сцены и события, связанные с привязкой
  • правильноchildrenДочерний узел для обработки определенных событий
  • Реализовано отдельно<permission>а также<permission-group>компоненты
  • Глобальная регистрация компонентов

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<VNode> = this.$slots.default
    if (children.length > 1) {
      console.warn(
        '<permission> can only be used on a single element. Use ' +
        '<permission-group> 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)

Конкретное использование заключается в следующем, если делается ссылка<permission>компонента, дочерние узлы, которые он обертывает, будутclickилиnative clickКогда проверка разрешений выполняется заранее и проверка пройдена, выполняется собственный метод.

<template>
  <div class="srv-page">
    <permission>
      <el-button @click.native="handleCheck('type1')">权限操作1</el-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>компоненты,<permission-group>компоненты, то проще говоряparamПривязка параметра на каждом дочернем узле может быть. Обе специфической реализации логика согласованы, просто путем изменения параметров можно запросить разрешения

// render 部分的不同
render (h): VNode {
  const tag = this.tag
  const rawChildren: Array<VNode> = this.$slots.default || []
  const children: Array<VNode> = []
  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')">权限操作1</el-button>
      <el-button @click.native="handleCheck('type2')">权限操作2</el-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Атрибуты. Однако после этого процесса управление операциями с разрешениями становится очень простым, а обслуживание также очень простым.

3. 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(
        '<permission> can only be used on a single element. Use ' +
        '<permission-group> for lists.'
      )
    }
    const rawChild = children[0]
    this.handleOverride(rawChild)
    return rawChild
  },

  methods: {
    handleOverride (c) {
      // ...
    },
    handlePreCheck (cb) {
      // ...
    },
    handlePermissionCheck (param) {
      // ...
    }
  }
}
</script>

<permission-group>В противном случае, я не буду вдаваться в подробности здесь.

Суммировать

Пока что наше собственное дело抽象组件Уже достигнуто завершено. В реальном бизнесе, который, на самом деле, есть много деловых ценностей, чтобы исследовать лучшие способы достижения, например, мы можем нарисовать防抖или节流компоненты, что также очень распространено в бизнесе.

Несколько слов о курином бульоне в конце статьи:

  1. Около 80 % нашего технологического роста определяется бизнесом, за который мы несем ответственность. То, насколько он может помочь вам, зависит от того, как много вы думаете о бизнесе.
  2. Не сетуйте на повторяющиеся и скучные проекты. На самом деле, куда бы вы ни пошли, если у вас нет капитала, просто смотреть на бизнес скучно.
  3. Попробуй сделать из себя хозяина, а потом подставить, много чего можно увидеть
  4. Единственный способ расти — это идти своим путем, потратить некоторое время на изучение чего-либо, а затем совместить его со своим бизнесом или учиться через свой бизнес.
  5. Только применяя то, что вы узнали, на практике в бизнесе, вы можете запомнить это более надежно, что должно быть правильным путем к росту.
  6. Когда вы освоите все это, возможно, вам стоит научиться писать межличностные навыки, такие как документы или PPT.

Наконец-то поставил волну своей же группы

Группа внешнего обмена: 731175396, добро пожаловать к нам