我最近试图为 d3.js 编写一个插件,但对一些可能微不足道的事情感到困惑。 d3's website上有解释关于如何创建可重用图表。该模式看起来像这样(只有最重要的细节,完整代码是 here ):
设计模式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();
我已经使用设计模式2一段时间了,没有任何问题(我同意它不是 super 干净,但我正在努力)。看完设计模式1后,我只是觉得它冗余太多了。例如,查看使内部变量可访问的最后代码块(chart.margin = function(_) {}
等)。
也许这是一个很好的做法,但它使维护变得麻烦,因为必须对每种不同的图表类型重复此操作(如在当前正在开发的名为 NVD3 的库中看到的),并且增加了开发工作量和错误风险。
我想知道如果我继续使用我的模式,我会面临什么样的严重问题,或者如何改进我的模式或使其更接近设计模式1的精神。我现在试图避免改变模式,因为这将需要完全重写,并且会在一个稍微稳定的迷你库中引入新的错误。有什么建议么?
最佳答案
事实上,您可以在 d3 源代码中找到第二个模式。
(function(){
...
d3 = {version: "2.9.6"}; // semver
...
d3.svg = {};
d3.svg.arc = function() {
var innerRadius = d3_svg_arcInnerRadius,
outerRadius = d3_svg_arcOuterRadius,
...
但是组件和生成器,如比例、轴、区域和布局,倾向于使用我们可以称之为“使用 getter-setter 方法作为闭包的图表”或“高阶编程”的模式 通过可配置的功能”。您可以关注 Google Group thread 上的讨论以了解基本原理。
就我个人而言,我也不喜欢这种冗余,即使它很有用并且具有相当的可读性。因此,我使用自定义函数自动生成这些 getter 和 setter:
d3.helper.createAccessors = function(visExport) {
for (var n in visExport.opts) {
if (!visExport.opts.hasOwnProperty(n)) continue;
visExport[n] = (function(n) {
return function(v) {
return arguments.length ? (visExport.opts[n] = v, this) : visExport.opts[n];
}
})(n);
}
};
我在图表模块的末尾使用这样的:
d3.helper.createAccessors(chart, opts);
其中 opts 是所有公共(public)函数的名称:
var opts = {
width: 200,
margin: [5, 0, 20, 0],
height: 200,
showPoints: true,
showAreas: false,
enableTooltips: true,
dotSize: 4
};
关于javascript - 这种设计模式什么时候会被打破?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11568751/