我最近试图为 d3.js 编写一个插件,并且被一些可能微不足道的东西弄糊涂了。d3 的网站上有关于如何创建可重用图表的解释。模式看起来像这样(只有最重要的细节,完整的代码在这里):
设计模式 1:来自 d3 网站
function timeSeriesChart() {
var margin = {top: 20, right: 20, bottom: 20, left: 20},
...
area = d3.svg.area().x(X).y1(Y),
line = d3.svg.line().x(X).y(Y);
function chart(selection) {
selection.each(function(data) {
// Convert data to standard representation greedily;
// this is needed for nondeterministic accessors.
data = data.map(function(d, i) {
return [xValue.call(data, d, i), yValue.call(data, d, i)];
});
// Update the x-scale.
...
// Update the y-scale.
...
// Select the svg element, if it exists.
var svg = d3.select(this).selectAll("svg").data([data]);
...
// Otherwise, create the skeletal chart.
var gEnter = svg.enter().append("svg").append("g");
...
}
// The x-accessor for the path generator; xScale ∘ xValue.
function X(d) { }
// The x-accessor for the path generator; yScale ∘ yValue.
function Y(d) { }
chart.margin = function(_) {
if (!arguments.length) return margin;
margin = _;
return chart;
};
chart.width = function(_) {
if (!arguments.length) return width;
width = _;
return chart;
};
chart.height = function(_) {
if (!arguments.length) return height;
height = _;
return chart;
};
chart.x = function(_) {
if (!arguments.length) return xValue;
xValue = _;
return chart;
};
chart.y = function(_) {
if (!arguments.length) return yValue;
yValue = _;
return chart;
};
return chart;
}
我毫不怀疑这种模式是健壮的,尤其是因为它是由 d3 的创建者本人提出的。但是,在我遇到该帖子之前,我已经使用以下模式一段时间没有问题(类似于一般如何创建插件):
设计模式 2:创建插件的一般方法
(function() {
var kg = {
version: '0.1a'
};
window.kg = kg;
kg.chart = {};
// ==========================================================
// CHART::SAMPLE CHART TYPE
// ==========================================================
kg.chart.samplechart = {
// ----------------------------------------------------------
// CONFIGURATION PARAMETERS
// ----------------------------------------------------------
WIDTH: 900,
HEIGHT: 500,
MARGINS: {
top: 20,
right: 20,
bottom: 20,
left: 60,
padding: 40
},
xRange: d3.time.scale(),
yRange: d3.scale.linear(),
xAxis: d3.svg.axis(),
yAxis: d3.svg.axis(),
data: {},
// ----------------------------------------------------------
// INIT FUNCTION
// ----------------------------------------------------------
init: function() {
// Initialize and add graph to the given div
this.update();
},
// ----------------------------------------------------------
// Redraws the graph
// ----------------------------------------------------------
update: function() {
var parentThis = this;
var newData = parentThis.data;
// Continue with adding points/lines to the chart
},
// ----------------------------------------------------------
// Gets random data for graph demonstration
// ----------------------------------------------------------
getRandomData: function() {
// Useful for demo purposes
}
};
// ==========================================================
// HELPER FUNCTIONS
// ==========================================================
}());
// EXAMPLE: This renders the chart.
kg.chart.samplechart.vis = d3.select("#visualization");
kg.chart.samplechart.data = kg.chart.samplechart.getRandomData();
kg.chart.samplechart.init();
我已经使用Design Pattern 2一段时间了,没有任何问题(我同意它不是超级干净,但我正在努力)。看了Design Pattern 1之后,我只是觉得它有太多的冗余。例如,查看使内部变量可访问(chart.margin = function(_) {}
等)的最后代码块。
也许这是一种很好的做法,但它使维护变得很麻烦,因为必须为每种不同的图表类型重复此操作(如这里在一个名为NVD3的库中看到的,目前正在开发中)并且增加了开发工作和错误风险。
我想知道如果我继续我的模式会面临什么样的严重问题,或者我的模式如何可以改进或更接近设计模式 1的精神。在这一点上,我试图避免改变模式,因为这将需要完全重写,并将新的错误引入到一个有点稳定的迷你库中。有什么建议么?