(精华)2020年7月17日 vue mixins的使用

以echarts折线图为例

<template>
  <div :class="className" :style="{height:height,width:width}" />
</template>

<script>
import echarts from 'echarts'
require('echarts/theme/macarons') // echarts theme
import resize from './mixins/resize'

export default {
  mixins: [resize],
  props: {
    className: {
      type: String,
      default: 'chart'
    },
    width: {
      type: String,
      default: '100%'
    },
    height: {
      type: String,
      default: '350px'
    },
    autoResize: {
      type: Boolean,
      default: true
    },
    chartData: {
      type: Object,
      required: true
    }
  },
  data() {
    return {
      chart: null
    }
  },
  watch: {
    chartData: {
      deep: true,
      handler(val) {
        this.setOptions(val)
      }
    }
  },
  mounted() {
    this.$nextTick(() => {
      this.initChart()
    })
  },
  beforeDestroy() {
    if (!this.chart) {
      return
    }
    this.chart.dispose()
    this.chart = null
  },
  methods: {
    initChart() {
      this.chart = echarts.init(this.$el, 'macarons')
      this.setOptions(this.chartData)
    },
    setOptions({ expectedData, actualData } = {}) {
      this.chart.setOption({
        xAxis: {
          data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
          boundaryGap: false,
          axisTick: {
            show: false
          }
        },
        grid: {
          left: 10,
          right: 10,
          bottom: 20,
          top: 30,
          containLabel: true
        },
        tooltip: {
          trigger: 'axis',
          axisPointer: {
            type: 'cross'
          },
          padding: [5, 10]
        },
        yAxis: {
          axisTick: {
            show: false
          }
        },
        legend: {
          data: ['expected', 'actual']
        },
        series: [{
          name: 'expected', itemStyle: {
            normal: {
              color: '#FF005A',
              lineStyle: {
                color: '#FF005A',
                width: 2
              }
            }
          },
          smooth: true,
          type: 'line',
          data: expectedData,
          animationDuration: 2800,
          animationEasing: 'cubicInOut'
        },
        {
          name: 'actual',
          smooth: true,
          type: 'line',
          itemStyle: {
            normal: {
              color: '#3888fa',
              lineStyle: {
                color: '#3888fa',
                width: 2
              },
              areaStyle: {
                color: '#f3f8ff'
              }
            }
          },
          data: actualData,
          animationDuration: 2800,
          animationEasing: 'quadraticOut'
        }]
      })
    }
  }
}
</script>

父组件

<line-chart :chart-data="lineChartData" />
const lineChartData = {
  newVisitis: {
    expectedData: [100, 120, 161, 134, 105, 160, 165],
    actualData: [120, 82, 91, 154, 162, 140, 145]
  },
  messages: {
    expectedData: [200, 192, 120, 144, 160, 130, 140],
    actualData: [180, 160, 151, 106, 145, 150, 130]
  },
  purchases: {
    expectedData: [80, 100, 121, 104, 105, 90, 100],
    actualData: [120, 90, 100, 138, 142, 130, 130]
  },
  shoppings: {
    expectedData: [130, 140, 141, 142, 145, 150, 160],
    actualData: [120, 82, 91, 154, 162, 140, 130]
  }
}

resize.js

import { debounce } from '@/utils'

export default {
  data() {
    return {
      $_sidebarElm: null,
      $_resizeHandler: null
    }
  },
  mounted() {
    this.$_resizeHandler = debounce(() => {
      if (this.chart) {
        this.chart.resize()
      }
    }, 100)
    this.$_initResizeEvent()
    this.$_initSidebarResizeEvent()
  },
  beforeDestroy() {
    this.$_destroyResizeEvent()
    this.$_destroySidebarResizeEvent()
  },
  // to fixed bug when cached by keep-alive
  // https://github.com/PanJiaChen/vue-element-admin/issues/2116
  activated() {
    this.$_initResizeEvent()
    this.$_initSidebarResizeEvent()
  },
  deactivated() {
    this.$_destroyResizeEvent()
    this.$_destroySidebarResizeEvent()
  },
  methods: {
    // use $_ for mixins properties
    // https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential
    $_initResizeEvent() {
      window.addEventListener('resize', this.$_resizeHandler)
    },
    $_destroyResizeEvent() {
      window.removeEventListener('resize', this.$_resizeHandler)
    },
    $_sidebarResizeHandler(e) {
      if (e.propertyName === 'width') {
        this.$_resizeHandler()
      }
    },
    $_initSidebarResizeEvent() {
      this.$_sidebarElm = document.getElementsByClassName('sidebar-container')[0]
      this.$_sidebarElm && this.$_sidebarElm.addEventListener('transitionend', this.$_sidebarResizeHandler)
    },
    $_destroySidebarResizeEvent() {
      this.$_sidebarElm && this.$_sidebarElm.removeEventListener('transitionend', this.$_sidebarResizeHandler)
    }
  }
}

debounce.js

export function debounce(func, wait, immediate) {
  let timeout, args, context, timestamp, result

  const later = function() {
    // 据上一次触发时间间隔
    const last = +new Date() - timestamp

    // 上次被包装函数被调用时间间隔 last 小于设定时间间隔 wait
    if (last < wait && last > 0) {
      timeout = setTimeout(later, wait - last)
    } else {
      timeout = null
      // 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用
      if (!immediate) {
        result = func.apply(context, args)
        if (!timeout) context = args = null
      }
    }
  }

  return function(...args) {
    context = this
    timestamp = +new Date()
    const callNow = immediate && !timeout
    // 如果延时不存在,重新设定延时
    if (!timeout) timeout = setTimeout(later, wait)
    if (callNow) {
      result = func.apply(context, args)
      context = args = null
    }

    return result
  }
}
<div class="post-text" itemprop="text"> <p>I want to set an interface that enforces the following behavior. Structs that implement the Rules interface, implement a prepareData() function, they contain Conditions that Evaluate() the prepared data, and return a result. If the data passes all conditions, actions can be triggered.</p> <p>There may be different structs that implement this Rules interface e.g. a JSONRules object as in the example below, where the prepared data are in JSON format, and a JSON Condition that simply checks for a value of a field in the JSON object. Or it could be something completely different e.g. GET a URL and check if it is reachable with HTTP status is 200.</p> <p>There's 3 objectives in the design:</p> <ol> <li><p>The main program should be agnostic to the implementation e.g. whether it's JSONRules or a URLRules that it's executing. It should simply be able to say Do(Rules) e.g. do what you have to do and let me know the result.</p></li> <li><p>The sequence of execution e.g. prepareData(), evaluateConditions() should be abstracted since it is the same for all Rules and we shouldn't have to write it all over again.</p></li> <li><p>As part of the sequence of execution, actions will be triggered via a separate, specialized component which could be an external API, which is one more reason that this part should be abstracted and not re-implemented by each individual rules.</p></li> </ol> <p>Below is an example of how I went about implementing this. The problem that I face is that the Data and the way the data are evaluated by each Condition are different for every implementation, which means that the Condition interface cannot be aware of certain fields or functions, but yet, the implementations need to follow the interfaces. I therefore get a "data.actualValue undefined (type Data is interface with no methods)" error in the following example.</p> <p>The code should speak for itself, if I'm not explaining this well. Are there are any design pattern in Go that would help overcoming the limitations in interfaces in Go?</p> <p>Here is a link to the code at the Go Playground: <a href="https://play.golang.org/p/G9XnEZ74bM" rel="nofollow noreferrer">https://play.golang.org/p/G9XnEZ74bM</a></p> <pre><code>package main import ( "fmt" ) func main() { condition := JSONCondition{ "Expected Value" } rules := JSONRules{ RulesBase{ data : nil, conditions : []Condition{ condition }, }, } Do(rules) } type Rules interface { getData() Data getConditions() []Condition prepareData() evaluateConditions() bool } type RulesBase struct { data Data conditions []Condition } func (rules RulesBase) getData() Data { return rules.data } func (rules RulesBase) getConditions() []Condition { return rules.conditions } func (rules RulesBase) evaluateConditions() bool { allOk := true for _, condition := range rules.getConditions() { ok := condition.Evaluate(rules.getData()) if !ok { allOk = false break } } return allOk } func Do(rules Rules) { rules.prepareData() ok := rules.evaluateConditions() if ok { // Take some actions if the conditions evaluated successfully. fmt.Println("Conditions passed.") } } type Data interface {} type Condition interface { Evaluate(Data) bool } type JSONRules struct { RulesBase // Could have additional fields that would be used to prepare the data such as // a URL to fetch data from in the prepareData() function. } func (rules JSONRules) prepareData() { rules.data = JSONData { actualValue : "Expected Value", } } type JSONData struct { actualValue string } type JSONCondition struct { expectedValue string } func (condition JSONCondition) Evaluate(data Data) bool { if data.actualValue == condition.expectedValue { return true } return false } </code></pre> </div>
©️2020 CSDN 皮肤主题: 猿与汪的秘密 设计师:上身试试 返回首页