基于 prop 动态扩展 VueJs 组件

Dynamically Extend VueJs Component based on prop

首先我想说这个问题不是一成不变的,因为我非常愿意尝试其他建议,因为我可能完全错过了合适的方法。

我在 JSX 中使用 Vue-ChartJs,但我想在应用程序开始时不必扩展我需要的每个图表组件,例如:

    // Line bar component.
Vue.component('line-chart', {
    extends: VueChartJs.Line,

    props: ['data', 'options'],

    mounted() {
        this.renderChart(this.data, this.options)
    }
});

相反,我想动态扩展组件基于,也许,将决定扩展什么的道具:

const getChart = (type) => {
    if (type === 'bar') {
        return VueChartJs.Bar;
    };
};

Vue.component('chart', {
    props: ['data', 'options', 'type'],
    extends: () => {
        getChart(this.type)
    },
    mounted() {
        this.renderChart(this.data, this.options)
    }
})

此方法目前不起作用,因为在 extends 中:它 returns 未定义 t 的错误,未设置 "this"。

对于可能的解决方案有什么建议吗? 提前致谢。

在您的示例中,type 属性 有效地决定了使用哪个组件。内置的 属性 is 更适合这项工作。请注意,这里我仍然注册所有组件,但是使用 util 函数使代码非常简短。

<div id="app">
    <div>
        <!-- think of :is like the :type property in your example, it serves the same purpose -- deciding which component to use -->
        <component :is="chartTypeLine" :data="chartData" :options="chartOptions"></component>
        <component :is="chartTypeBar" :data="chartData" :options="chartOptions"></component>
    </div>

</div>

<script src="https://vuejs.org/js/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.1/Chart.min.js"></script>
<script src="https://unpkg.com/vue-chartjs/dist/vue-chartjs.min.js"></script>
<script>

    const getChart = (type) => {
        switch (type) {
            case 'line-chart':
                return VueChartJs.Line;
            case 'bar-chart':
                return VueChartJs.Bar;
            default:
                throw new Error("wrong chart type");
        }
    };

    const chartTypes = ['line-chart', 'bar-chart'];

    //this makes registering all those components easier.
    chartTypes.forEach(type => {
        Vue.component(type, {
            extends: getChart(type),
            props: ['data', 'options'],
            mounted () {
                this.renderChart(this.data, this.options)
            }
        })
    });

    new Vue({
        el: '#app',
        data: {
            chartData: {
                labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
                datasets: [
                    {
                        backgroundColor: '#f87979',
                        data: [40, 39, 10, 40, 39, 80, 40]
                    }
                ]
            },
            chartOptions: {responsive: true, maintainAspectRatio: false},
            chartTypeLine: 'line-chart',
            chartTypeBar: 'bar-chart'
        }
    })
</script>

https://codepen.io/anon/pen/MBxRpP

除了之前的回答,今天早上我还发现了另一种方法。当 JSX 文件是第一个 运行 时,它通过一个对象使用相同的 foreach 循环实例化每个图表类型,这会将它们全部注册在准备好使用的范围内。

const chartNames = [
    {
        name: 'line-chart',
        type: VueChartJs.Line
    }, {
        name: 'bar-chart',
        type: VueChartJs.Bar
    },
    {
        name: 'horizontal-bar-chart',
        type: VueChartJs.HorizontalBar
    },
    {
        name: 'doughnut-chart',
        type: VueChartJs.Doughnut
    },
    {
        name: 'pie-chart',
        type: VueChartJs.Pie
    },
    {
        name: 'radar-chart',
        type: VueChartJs.Radar
    },
    {
        name: 'polar-area-chart',
        type: VueChartJs.PolarArea
    },
    {
        name: 'bubble-chart',
        type: VueChartJs.Bubble
    },
    {
        name: 'scatter-chart',
        type: VueChartJs.Scatter
    }];
const registerCharts = (chartNames) => {
    chartNames.forEach((chartName) => {
        Vue.component(chartName.name, {
            extends: chartName.type,
            mixins: [VueChartJs.mixins.reactiveProp],
            props: {
                options: Object
            },
            methods: {
                // Used in the alternate render version, this updates the chart in question.
                update() {
                    this.$data._chart.update()
                }
            },
            mounted() {
                this.renderChart(this.chartData, this.options)
            }
        });
    })
};
registerCharts(chartNames);

我喜欢这个

Chart.js

import VueChartJs from 'vue-chartjs'
import Vue from 'vue'

const getChart = (type) => {
  switch (type) {
    case 'LineChart':
      return VueChartJs.Line
    case 'BarChart':
      return VueChartJs.Bar
    case 'PieChart':
      return VueChartJs.Pie
    default:
      throw new Error('wrong chart type')
  }
}

// generate component via fun
// it mean that you will generate as many components as you need

export default (type) => {
  return Vue.component(type, {
    extends: getChart(type),
    name: type,
    props: ['data', 'options'],
    mounted () {
      this.renderChart(this.data, this.options)
    }
  })
}

index.vue

<template> 
..
        <PieChart
          :data="testData"
          :options="options"
        />
..
</template>

<script>
import Chart from '~/components/UI/Chart/index'
..
export default {
  components: {
    PieChart: Chart('PieChart')
  }

}
</script>