Note: this blog does not talk about the principle. If you don't understand it, please supplement the relevant contents such as policy mode, state mode, closure, lazy evaluation, functional programming, etc
1, Effect
2, Why use policy and state patterns
vue ordinary tab switching is very simple. You can do it with the following code. I'm afraid of everything. However, if you use the following code to draw ecarts, when switching the graph, because ecarts can't obtain the width and height, the graph can't be rendered to the page. Some people will say you can add this.$nexttick, yes. You can add this function to delay the ecarts to obtain the dom width and height for re rendering, but you need to have several switches and several this.$nexttick. Moreover, several switches need to declare several variables that are the same as the stored ecarts instance, which increases the number of variable maintenance in the future (some people will say that it's not enough to use a variable to store ehcarts instances. If it's a convenient point of view, it's true. If it's a later maintenance point of view, it's recommended to be consistent with the number of li and content switched by tab, because they correspond one by one at a glance).
<ul> <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> <div v-show="curIndex === 0"> tab1 </div> <div v-show="curIndex === 1"> tab2 </div> </div> </div>
In the actual development of tab switching, I did not use the above form of code, but the following form of code
<template> <div> <ul> <li :class="['eofe-tab-title-item', curIndex === 0 ? 'title-item-active' : '']" @click="operCtx(tabStrategy,'domestic')" > Domestic financing </li> <li :class="['eofe-tab-title-item', curIndex === 1 ? 'title-item-active' : '']" @click="operCtx(tabStrategy,'abroad')" > Overseas financing </li> </ul> <div> <div> <div>{{ banner }}</div> <div ref="chart"></div> </div> </div> </div> </template>
Two tab switches correspond to one content content (because they both draw the eckarts diagram, so they are put in one). In this way, the variables declared to store the eckarts instance can share one. It is ok to maintain one variable later. It also solves the problem that eckarts obtains the dom width and height during tab switching (this. $nexttick is not used) Because I pay more attention to code coupling and don't like highly coupled code, I use more design patterns in the usual programming process. In this tab switching development, I use the policy mode and state mode. I use the policy mode mainly to solve the problem of if judgment. I don't like if else judgment very much, so I can't write if else judgment at ordinary times In addition, the policy mode also has the characteristics of 1. The algorithm can be switched freely. 2. Good scalability and reusability. In addition, because the tab switching is in the form of two-to-one, the content of a content chart corresponds to two states, one is domestic and the other is foreign. It conforms to the state mode. Using the design mode saves effort for future expansion and maintenance.
3, Policy mode write tab switching
Policy pattern definition: encapsulate some small algorithms so that they can be replaced with each other (separate the implementation and use of the code)
/** * operCtx Operation context * @param { any } strategyObj Policy object * @param { string } param * @param { any } arg * @return { void } */ operCtx(strategyObj: any, param: string, ...arg: any): void { strategyObj[param].apply(this, arg); } //operHandle pop-up layer policy method tabStrategy = { domestic: function (this:EoFinanceEcharts) { if(this.curIndex===0)return; console.log('domestic'); this.curIndex=0; this.banner='Domestic financing this week: 4 | Amount: 2.76 RMB 100 million'; this.stateCtx?.changeState('domestic').action(); }, abroad: function (this:EoFinanceEcharts) { if(this.curIndex===1)return; console.log('abroad'); this.curIndex=1; this.banner='Foreign financing this week: 5 | Amount: 5.76 RMB 100 million'; this.stateCtx?.changeState('abroad').action(); } };
Understanding of mutual substitution: use operCtx(tabStrategy, 'xxx') for click binding in different li's. when different li's are clicked, different functions are executed in the operCtx function, and there is a mutual substitution relationship before different functions are executed.
4, State mode write different states of drawing
State pattern definition: allows an object to change its behavior when its state changes. The object appears to modify its class (state driven behavior)
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 () { //Traversal parameter object without inheritance 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 domestic'); this.createEchart(); }, abroad: () => { console.log('abroad abroad'); this.createEchart(); } }; mounted() { this.stateCtx = this.createStateCtx(); this.stateCtx.setState(this.chartAction)?.changeState('domestic').action(); } destroyed() { this.stateCtx = null;//Because closures are used, it is best to destroy them at the end of the declaration cycle }
Overall code
<template> <div> <ul> <li :class="['eofe-tab-title-item', curIndex === 0 ? 'title-item-active' : '']" @click="operCtx(tabStrategy,'domestic')" > Domestic financing </li> <li :class="['eofe-tab-title-item', curIndex === 1 ? 'title-item-active' : '']" @click="operCtx(tabStrategy,'abroad')" > Overseas financing </li> </ul> <div> <div> <div>{{ banner }}</div> <div ref="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='Financing this week: 4 | Amount: 2.76 RMB 100 million'; instance:echarts.ECharts|undefined; //Stock seals instance object chartBaseConfig:echarts.EChartOption={ tooltip: { trigger: 'axis', //Coordinate axis trigger is mainly used in charts that use category axes such as bar charts and broken line charts 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-axis split line show: true, lineStyle: { color: [ '#Eff4 '] / / split line color } }, }, yAxis: { type: 'value', axisLine: { lineStyle: { color: '#EFEFF4' } }, axisLabel: { color: '#999', margin: 5 }, axisTick: { alignWithLabel: true, show: false }, splitLine: { //x-axis split line show: true, lineStyle: { color: [ '#Eff4 '] / / split line color } }, splitNumber: 5 }, grid: { // show:true, top: 7, right: 5, bottom: 20 }, series: [ { type: 'line', smooth: true } ] } /** * @description createEchart Drawing * @param { any } arg * @returns { void } */ createEchart(this: EoFinanceEcharts,...arg:any) { if ( this.instance !== null && this.instance !== undefined ) { this.instance.dispose(); //Destroy instance } this.instance = echarts.init(this.$refs.chart as HTMLCanvasElement); //Create instance this.instance.setOption(this.chartBaseConfig); //Basic configuration //X axis this.instance.setOption({ xAxis: [ { data: arg[0].xAxis } ] }); //data 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 () { //Traversal parameter object without inheritance 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 domestic'); this.createEchart(); }, abroad: () => { console.log('abroad abroad'); this.createEchart(); } }; /** * operCtx Operation context * @param { any } strategyObj Policy object * @param { string } param * @param { any } arg * @return { void } */ operCtx(strategyObj: any, param: string, ...arg: any): void { strategyObj[param].apply(this, arg); } //operHandle pop-up layer policy method tabStrategy = { domestic: function (this:EoFinanceEcharts) { if(this.curIndex===0)return; console.log('domestic'); this.curIndex=0; this.banner='Domestic financing this week: 4 | Amount: 2.76 RMB 100 million'; this.stateCtx?.changeState('domestic').action(); }, abroad: function (this:EoFinanceEcharts) { if(this.curIndex===1)return; console.log('abroad'); this.curIndex=1; this.banner='Foreign financing this week: 5 | Amount: 5.76 RMB 100 million'; 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>