阅读 90

策略和状态模式应用——tab切换时,图表内容变化

注意:本篇博客不讲原理,如果看不懂请先补充策略模式、状态模式、闭包、惰性求值、函数式编程等相关内容

一、效果

请添加图片描述

二、为什么使用策略和状态模式

vue普通的tab切换很简单,使用下面代码就可以搞定。凡事就怕但是,如果使用下面的代码形式画echarts的话,切换图的时候,因为echarts获取不到宽高,导致图渲染不到页面上去。有人会说可以加this.$nexttick,是的。可以通过加入该函数做到延迟的echarts获取到dom宽高再渲染,但是需要有几个切换加几个this.$nexttick。而且有几个切换需要声明几个同于存储的echarts实例的变量,增加了以后的变量维护数量(有人会说,用一个变量存储ehcarts实例不就行了,如果站在方便的角度说的话,确实如此。如果是以后期维护角度的话,建议要和tab切换的li和content数量一致,因为它们是一一对应的一目了然)。

<div>     <ul class="eofe-tab-title eo-flex">       <li         :class="['eofe-tab-title-item', curIndex === 0 ? 'title-item-active' : '']"         @click="operCtx(tabStrategy,'domestic')"       >         tab1       </li>       <li         :class="['eofe-tab-title-item', curIndex === 1 ? 'title-item-active' : '']"         @click="operCtx(tabStrategy,'abroad')"       >         tab2       </li>     </ul>     <div class="eofe-tab-content">       <div v-show="curIndex === 0" class="eofe-tab-content-item">        tab1       </div>       <div v-show="curIndex === 1" class="eofe-tab-content-item">        tab2       </div>     </div> </div> 复制代码

在实际开发tab切换的时候,我没有使用上面形式的代码,而是使用下面形式的代码

<template>   <div class="eo-finance-echarts">     <ul class="eofe-tab-title eo-flex">       <li         :class="['eofe-tab-title-item', curIndex === 0 ? 'title-item-active' : '']"         @click="operCtx(tabStrategy,'domestic')"       >         国内融资       </li>       <li         :class="['eofe-tab-title-item', curIndex === 1 ? 'title-item-active' : '']"         @click="operCtx(tabStrategy,'abroad')"       >         海外融资       </li>     </ul>     <div class="eofe-tab-content">       <div class="eofe-tab-content-item">         <div class="banner">{{ banner }}</div>         <div ref="chart" class="chart"></div>       </div>     </div>   </div> </template> 复制代码

两个tab切换对应一个content内容(因为它们两个都是画echarts图的,所以就放到一个里面了)。这样的话声明用来存储echarts实例的变量就可以两个共用一个了,以后就维护一个变量就ok了,也解决了tab切换时echarts获取dom宽高的问题(不使用this.$nexttick)。因为本人比较注重代码耦性问题,不喜欢高耦合代码,所以在平时编程过程中使用设计模式比较多。在本次tab切换开发中使用了策略模式和状态模式。我使用策略模式主要是为了解决if判断问题,本人非常不喜欢if else判断,所以平时能不写if else判断的地方都不会写,除此之外策略模式还具有1、算法可以自由切换。2、扩展性、复用性良好等特性。又因为tab切换是二对一形式的,一个content 图表内容对应两种状态一个是国内,一个是国外。符合状态模式。使用设计模式对以后拓展和维护都是省力。

三、策略模式写tab切换

策略模式定义:把一些小的算法,封装起来,使他们之间可以相互替换(把代码的实现和使用分离开来)

/**    * operCtx 操作上下文    * @param { any } strategyObj 策略对象    * @param { string } param    * @param { any } arg    * @return { void }    */   operCtx(strategyObj: any, param: string, ...arg: any): void {     strategyObj[param].apply(this, arg);   }   //operHandle 弹出层策略方法   tabStrategy = {     domestic: function (this:EoFinanceEcharts) {       if(this.curIndex===0)return;       console.log('国内');       this.curIndex=0;       this.banner='国内本周融资:4家 | 金额:2.76亿人民币';       this.stateCtx?.changeState('domestic').action();     },     abroad: function (this:EoFinanceEcharts) {       if(this.curIndex===1)return;       console.log('国外');       this.curIndex=1;       this.banner='国外本周融资:5家 | 金额:5.76亿人民币';       this.stateCtx?.changeState('abroad').action();     }   }; 复制代码

对相互替换的理解:在不同li中使用operCtx(tabStrategy,'xxx')进行click绑定,当进行不同li点击时operCtx函数中执行不同的函数,执行不同的函数之间互为替换关系。

四、状态模式写画图的不同状态

状态模式定义:允许一个对象在其状态改变时改变他的行为,对象看起来视乎修改了他的类(状态驱动行为)

  createStateCtx() {     let currentState: any = {};     let action: any = {};     const Ctx = {       changeState: function (...arg: any) {         currentState={};         arg.map((item: any) => {           currentState[item] = true;         });         return this;       },       action: function () {         //遍历参数对象,不含继承         Object.keys(currentState).forEach((k) => action[k] && action[k].call(this, this));         return this;       },       setState: function (setAction: any) {         action = setAction;         return this;       }     };     return Ctx;   }   chartAction = {     domestic: () => {       console.log('国内domestic');       this.createEchart({xAxis:[ 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun' ],series:[ 820, 932, 901, 934, 1290, 1330, 1320 ]});     },     abroad: () => {       console.log('国外abroad');       this.createEchart({xAxis:[ 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun' ],series:[ 800, 732, 901, 932, 1090, 1430, 1320 ]});     }   };   mounted() {     this.stateCtx = this.createStateCtx();     this.stateCtx.setState(this.chartAction)?.changeState('domestic').action();   }   destroyed() {     this.stateCtx = null;//因为使用到了闭包,所以声明周期最后,最好销毁   } 复制代码

总体代码

<template>   <div class="eo-finance-echarts">     <ul class="eofe-tab-title eo-flex">       <li         :class="['eofe-tab-title-item', curIndex === 0 ? 'title-item-active' : '']"         @click="operCtx(tabStrategy,'domestic')"       >         国内融资       </li>       <li         :class="['eofe-tab-title-item', curIndex === 1 ? 'title-item-active' : '']"         @click="operCtx(tabStrategy,'abroad')"       >         海外融资       </li>     </ul>     <div class="eofe-tab-content">       <div class="eofe-tab-content-item">         <div class="banner">{{ banner }}</div>         <div ref="chart" class="chart"></div>       </div>     </div>   </div> </template> <script lang="ts"> import Vue from 'vue'; import Component from 'vue-class-component'; import * as echarts from 'echarts'; @Component({   components: {} }) export default class EoFinanceEcharts extends Vue {   curIndex = 0;   stateCtx: { changeState: any; action: any; setState: any }|null;   banner='本周融资:4家 | 金额:2.76亿人民币';   instance:echarts.ECharts|undefined; //stock echarts 实例对象   chartBaseConfig:echarts.EChartOption={     tooltip: {       trigger: 'axis', //坐标轴触发,主要在柱状图,折线图等会使用类目轴的图表中使用       backgroundColor: '#fff',       padding: 12,       textStyle: { color: '#666666' },       transitionDuration: 0,       confine: true,       extraCssText: 'border-radius: 3px;box-shadow: 0 0 3px #E9EBF1;width:76px;'     },     xAxis: {       type: 'category',       axisLine: {         lineStyle: {           color: '#EFEFF4'         }       },       axisLabel: {         color: '#999'       },       axisTick: {         alignWithLabel: true,         show: false       },       splitLine: {         //x轴分割线         show: true,         lineStyle: {           color: [ '#EFEFF4' ] //分割线颜色         }       },     },     yAxis: {       type: 'value',       axisLine: {         lineStyle: {           color: '#EFEFF4'         }       },       axisLabel: {         color: '#999',         margin: 5       },       axisTick: {         alignWithLabel: true,         show: false       },       splitLine: {         //x轴分割线         show: true,         lineStyle: {           color: [ '#EFEFF4' ] //分割线颜色         }       },       splitNumber: 5     },     grid: {       // show:true,       top: 7,       right: 5,       bottom: 20     },     series: [       {         type: 'line',         smooth: true       }     ]   }   /**    * @description  createEchart 画图    * @param { any } arg    * @returns { void }    */   createEchart(this: EoFinanceEcharts,...arg:any) {     if (       this.instance !== null &&       this.instance !== undefined     ) {       this.instance.dispose(); //销毁实例     }     this.instance = echarts.init(this.$refs.chart as HTMLCanvasElement); //创建实例     this.instance.setOption(this.chartBaseConfig); //基本配置     //X轴     this.instance.setOption({       xAxis: [ { data: arg[0].xAxis } ]     });     //数据     this.instance.setOption({       series: [ { data: arg[0].series } ]     });   }   createStateCtx() {     let currentState: any = {};     let action: any = {};     const Ctx = {       changeState: function (...arg: any) {         currentState={};         arg.map((item: any) => {           currentState[item] = true;         });         return this;       },       action: function () {         //遍历参数对象,不含继承         Object.keys(currentState).forEach((k) => action[k] && action[k].call(this, this));         return this;       },       setState: function (setAction: any) {         action = setAction;         return this;       }     };     return Ctx;   }   chartAction = {     domestic: () => {       console.log('国内domestic');       this.createEchart({xAxis:[ 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun' ],series:[ 820, 932, 901, 934, 1290, 1330, 1320 ]});     },     abroad: () => {       console.log('国外abroad');       this.createEchart({xAxis:[ 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun' ],series:[ 800, 732, 901, 932, 1090, 1430, 1320 ]});     }   };   /**    * operCtx 操作上下文    * @param { any } strategyObj 策略对象    * @param { string } param    * @param { any } arg    * @return { void }    */   operCtx(strategyObj: any, param: string, ...arg: any): void {     strategyObj[param].apply(this, arg);   }   //tabStrategy tab策略方法   tabStrategy = {     domestic: function (this:EoFinanceEcharts) {       if(this.curIndex===0)return;       console.log('国内');       this.curIndex=0;       this.banner='国内本周融资:4家 | 金额:2.76亿人民币';       this.stateCtx?.changeState('domestic').action();     },     abroad: function (this:EoFinanceEcharts) {       if(this.curIndex===1)return;       console.log('国外');       this.curIndex=1;       this.banner='国外本周融资:5家 | 金额:5.76亿人民币';       this.stateCtx?.changeState('abroad').action();     }   };   mounted() {     this.stateCtx = this.createStateCtx();     this.stateCtx.setState(this.chartAction)?.changeState('domestic').action();   }   destroyed() {     this.stateCtx = null;   } } </script> <style lang="less" scoped> .eo-finance-echarts {   // border: 1px solid red;   width: 296px;   height: 380px;   padding: 16px;   background-color: #fff; } .eofe-tab-title {   color: #333;   font-size: 18px;   cursor: pointer;   height: 33px;   position: relative;   &::before {     content: '';     width: 100%;     border-bottom: 1px dotted #333333;     position: absolute;     bottom: 0;   } } .title-item-active {   &::before {     content: '';     width: 38px;     height: 4px;     position: absolute;     bottom: 0;     left: 0 !important;     background-color: #333333;   } } .eofe-tab-title-item {   margin-right: 14px;   font-weight: bold;   overflow: hidden;   position: relative;   &:last-of-type {     margin-right: 0;   }   &::before {     content: '';     width: 38px;     height: 4px;     position: absolute;     bottom: 0;     left: -100%;     background-color: #333333;     transition: all 0.2s;   }   &:hover {     &::before {       left: 0;     }   } } .eofe-tab-content {   margin-top: 16px; } .eofe-tab-content-item {   // border: 1px solid lightblue;   position: relative;   .banner {     width: 264px;     height: 33px;     line-height: 33px;     text-align: center;     color: #0086f0;     background-color: rgba(0, 134, 240, 0.08);     font-size: 12px;   }   .chart {     // border: 1px solid lawngreen;     height: 250px;     margin-top: 16px;   } } // .tip-outer{ //     border-radius: 3px; //     background-color: rgba(255, 255, 255, 1); //     border: 0.79px solid red; // } </style>


作者:k_
链接:https://juejin.cn/post/7027753086827364360

文章分类
代码人生
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐