如何在Node.js中正确使用D3?

46

我一直在尝试在Node.js中调用D3。我首先尝试使用脚本标签从D3的网站导入d3.v2.js,但是后来读到了这个帖子:

我想在Cakefile中运行d3

D3的作者建议应该'npm install d3'...我按照这个方法做了,并且我可以在node控制台成功调用它:

dpc@ananda:$ node
> var d3 = require("d3");
undefined
> d3.version;
'2.8.1' 

然而,当我尝试从app.js中使用"node app.js"调用它时,我会得到以下错误信息:
node.js:201
    throw e; // process.nextTick error, or 'error' event on first tick
          ^
TypeError: Cannot read property 'BSON' of undefined
at     /Users/dpc/Dropbox/sync/Business/MindfulSound/Development/nad.am/nadam/node_modules/mongoose/node_modules/mongodb/lib/mongodb/index.js:45:44

我意识到在其他地方,D3的作者已经明确指出应该要求canvas:

https://github.com/mbostock/d3/blob/master/examples/node-canvas/us-counties.js

如下:

var Canvas = require("canvas");

但即使如此,(并且在app.js的require语句中特别要求使用index.js而不是d3.v2.js),我仍然无法在Jade模板中使下面的内容正常工作:
- script('/javascripts/d3.v2.js')
h1 Dashboard
  section.css-table
    section.two-column
      section.cell
        hr.grey
        h2 Statistics
        #mainGraph
            script(type="text/javascript") 
              var Canvas = require("canvas");
              var w = 400,
                  h = 400,
                  r = Math.min(w, h) / 2,
                  data = d3.range(10).map(Math.random).sort(d3.descending),
                  color = d3.scale.category20(),
                  arc = d3.svg.arc().outerRadius(r),
                  donut = d3.layout.pie();
              var vis = d3.select("body").append("svg")
                  .data([data])
                  .attr("width", w)
                  .attr("height", h);
              var arcs = vis.selectAll("g.arc")
                  .data(donut)
                  .enter().append("g")
                  .attr("class", "arc")
                  .attr("transform", "translate(" + r + "," + r + ")");
              var paths = arcs.append("path")
                  .attr("fill", function(d, i) { return color(i); });
              paths.transition()
                  .ease("bounce")
                  .duration(2000)
                  .attrTween("d", tweenPie);
              paths.transition()
                  .ease("elastic")
                  .delay(function(d, i) { return 2000 + i * 50; })
                  .duration(750)
                  .attrTween("d", tweenDonut);

              function tweenPie(b) {
                b.innerRadius = 0;
                var i = d3.interpolate({startAngle: 0, endAngle: 0}, b);
                return function(t) {
                  return arc(i(t));
                };
              }

              function tweenDonut(b) {
                b.innerRadius = r * .6;
                var i = d3.interpolate({innerRadius: 0}, b);
                return function(t) {
                  return arc(i(t));
                };

      section.cell
        hr.grey
        h2 Achievements
3个回答

78

在Node中正确使用D3的方法是使用NPM安装d3,然后使用require引入它。您可以使用npm install d3或使用package.json文件,然后使用npm install命令:

{
  "name": "my-awesome-package",
  "version": "0.0.1",
  "dependencies": {
    "d3": "3"
  }
}

一旦你的node_modules目录中有了d3,通过require将其加载:

var d3 = require("d3");

就是这样了。

至于你的其他问题:使用D3并不需要Canvas。你链接的node-canvas示例需要Canvas,因为它渲染到Canvas上。TypeError(无法读取未定义属性'BSON')似乎与您使用mongoose / mongodb有关,而不是D3。


谢谢您的回复!这很奇怪:d3确实在node_modules中,因为我使用“npm install d3”安装了它...但是在app.js中使用“var d3 = require("d3");”会导致BSON错误...删除该行,BSON错误消失。这是使用d3更简单的示例之一,来自此处的SVG圆形绘制:http://christopheviau.com/d3_tutorial/ - pland
1
记录一下,发现在mongoose和d3之间使用全局变量存在问题,因此在引入mongoose之前先调用var d3 = require("d3");可以解决BSON错误问题。 - pland
这个问题已在BSON v0.1.5中得到修复(以及依赖于正确BSON版本的mongodb v2.0.0)。 - Jason Davies
2
现在我已经将d3作为一个模块引入到node.js中,那么如何使用它来选择元素呢?我想在服务器上创建一个svg并将其返回给客户端。d3.select()能否接受像'<div></div>'这样的html片段? - arunkjn
3
这是一个容易混淆的话题,让我来帮助澄清一下 - npm install d3 加载 d3 节点模块,使你可以在服务器内存中构建数据驱动文档,然后将它们作为预先构建(渲染)的 html/svg 等传递到浏览器。与传统 d3 的用法相比,在 HTML 中包括库并在浏览器中呈现元素。客户端与服务器端呈现之间有微妙但重要的差异。参见:http://mango-is.com/blog/engineering/pre-render-d3-js-charts-at-server-side.html - Brad Hein
1
我的理解是服务器端的d3需要一个伪dom来进行选择(select())。看一下JSDOM。我发现这篇文章非常有启发性http://mango-is.com/blog/engineering/pre-render-d3-js-charts-at-server-side.html - Colin

16

为了在ES6中使用import而不是require

import * as d3 from 'd3';

对于有经验的babel/ES6用户而言,这可能是显而易见的。我知道这是个老问题,但我来这里试图找出答案。希望对某些人有所帮助。

有关importrequire的更多信息,请访问此处


4

您需要使用yarn或npm安装jsdom。 默认情况下,Node.js不支持dom,因此需要使用jsdom来创建dom元素。除了fetch操作外,使用d3进行所有其他操作。即 d3.xml、d3.tsv

import jsdom from 'jsdom';
import * as d3 from 'd3';
const { JSDOM } = jsdom;
JSDOM.fromURL(
    'your resource url',
  ).then((dom) => {
    const doc = dom.window.document;
    const states = d3
      .select(doc)
      .select('path')
      .attr(':fme:ID');
    console.log(states);
  });

从文件创建DOM
JSDOM.fromURL(
    'your resource url',
  ).then((dom) => {
    const doc = dom.window.document;
}

从 HTML 字符串创建 DOM

const dom = new JSDOM(
  `<p>Hello
    <img src="foo.jpg">
  </p>`,
  { includeNodeLocations: true }
);


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接