简体   繁体   English

无法使D3.js在Svelte组件内运行(使用汇总)

[英]Cannot get D3.js to work inside Svelte component (with Rollup)

I've been trying to put the most basic D3 example into a Svelte app and can't get it to work. 我一直试图将最基本的D3示例放到Svelte应用程序中,但无法使其正常工作。 At first I tried installing D3 as a node module: npm install d3 but this produces the same result (a lack of result) as importing D3 as an external script from CDN inside of index.html : <script src="https://d3js.org/d3.v5.min.js"></script> . 最初,我尝试将D3安装为节点模块: npm install d3但这产生了与从index.html内的CDN导入D3作为外部脚本相同的结果(缺少结果): <script src="https://d3js.org/d3.v5.min.js"></script> Using either method I get a bunch of circular dependency warnings on app start: 无论使用哪种方法,我都会在应用启动时收到一堆循环依赖警告:

(!) Circular dependency: node_modules\d3-selection\src\selection\index.js -> node_modules\d3-selection\src\selection\select.js -> node_modules\d3-selection\src\selection\index.js

But the app starts with no errors, and no D3 dynamic formatting occurs, nor any errors pop up in the DevTools console inside Chrome. 但是该应用程序启动时没有错误,也没有发生D3动态格式化,也没有任何错误在Chrome内的DevTools控制台中弹出。

The Svelte component looks like this: Svelte组件如下所示:

<script>
    import * as d3 from 'd3';
    var data = [30, 86, 168, 281, 303, 365];

    d3.select(".chart")
        .selectAll("div")
        .data(data)
        .enter()
        .append("div")
        .style("width", function(d) {
        return d + "px";
        })
        .text(function(d) {
        return d;
        });
</script>

<style>
      .chart div {
    font: 10px sans-serif;
    background-color: steelblue;
    text-align: right;
    padding: 3px;
    margin: 1px;
    color: white;
  }
</style>

<div class="chart"></div>

Putting the code above into a static HTML file produces a bar chart, as expected. 如预期的那样,将上面的代码放入静态HTML文件中会生成条形图。 But when run as a Svelte component nothing is displayed. 但是当作为Svelte组件运行时,不会显示任何内容。

My rollup.config.js is: 我的rollup.config.js是:

import svelte from 'rollup-plugin-svelte';
import resolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';
import livereload from 'rollup-plugin-livereload';
import { terser } from 'rollup-plugin-terser';

const production = !process.env.ROLLUP_WATCH;

export default {
    input: 'src/main.js',
    output: {
        sourcemap: true,
        format: 'iife',
        name: 'app',
        file: 'public/bundle.js',
        globals: { 'd3': 'd3' },
        external: [ 'd3' ]
    },
    plugins: [
        svelte({
            dev: !production,
            css: css => { css.write('public/bundle.css'); }
        }),
        resolve({ browser: true }),
        commonjs(),
        !production && livereload('public'),
        production && terser()
    ],
    watch: {
        clearScreen: false
    }
};

...and index.html is: ...和index.html是:

<!doctype html>
<html>
<head>
    <meta charset='utf8'>
    <meta name='viewport' content='width=device-width'>

    <title>Svelte app</title>

    <link rel='icon' type='image/png' href='favicon.png'>
    <link rel='stylesheet' href='global.css'>
    <link rel='stylesheet' href='bundle.css'>
</head>

<body>
    <script src="https://d3js.org/d3.v5.min.js"></script>
    <script src='bundle.js'></script>
</body>
</html>

I would suspect Rollup not bundling D3 module correctly, but as an external script in <body> it should in theory work, but it doesn't. 我怀疑Rollup不能正确捆绑D3模块,但作为<body>的外部脚本,理论上应该可以,但事实并非如此。 Please point me in the right direction, I've spent way too much time trying to get it to work, and as a JS noob am out of options. 请为我指出正确的方向,我花了太多时间尝试使其正常工作,而且作为JS noob,我已经选择不了。 Thanks! 谢谢!

The <div class="chart"></div> element doesn't exist when your code first runs — the contents of <script> run when the component is instantiated. 第一次运行代码时, <div class="chart"></div>元素不存在,而<script>的内容在实例化组件时运行。 If you need to access DOM elements inside the component, it will first be available inside onMount : 如果您需要访问组件内部的DOM元素,它将首先在onMount内部可用

<script>
  import { onMount } from 'svelte';

  // other code...

  onMount(() => {
    d3.select('.chart')
      // ...
  });
</script>

Using a selector like .chart is dangerous though, because if you had more than one component on the page D3 would select the wrong thing. 但是,使用.chart类的选择器很危险,因为如果在D3页上有多个组件,则会选择错误的内容。 It's better to use bind:this instead: 最好使用bind:this代替:

<script>
  import { onMount } from 'svelte';

  // other code...

  let el;

  onMount(() => {
    d3.select(el) // no danger of selecting the wrong element
      // ...
  });
</script>

<div class="chart" bind:this={el}></div>

Now all you need to change is the CSS — because Svelte will discard selectors that it thinks are unused, and because it can't know what D3 is going to do, it will remove .chart div {...} . 现在,您需要更改的只是CSS —因为Svelte会丢弃它认为未使用的选择器,并且因为它不知道D3将要做什么,所以它将删除.chart div {...} Instead, use the :global(...) modifier to target divs inside your top-level element: 而是使用:global(...)修饰符将顶级元素内的div定位为目标:

<style>
  .chart :global(div) {
    /* styles */
  }
</style>

With those changes, it works perfectly: 通过这些更改,它可以完美运行:

https://svelte.dev/repl/8722c32f4e1a44a98e3a3fc8a095b2d7?version=3.5.3 https://svelte.dev/repl/8722c32f4e1a44a98e3a3fc8a095b2d7?version=3.5.3

But in this case, D3 isn't really bringing anything to the party. 但是在这种情况下,D3并没有真正为聚会带来任何好处。 You could achieve the same result much more simply: 您可以更简单地达到相同的结果:

<script>
  var data = [30, 86, 168, 281, 303, 365];
</script>

<style>
  .chart div {
    font: 10px sans-serif;
    background-color: steelblue;
    text-align: right;
    padding: 3px;
    margin: 1px;
    color: white;
  }
</style>

<div class="chart">
  {#each data as d}
    <div style="width: {d}px">
      {d}
    </div>
  {/each}
</div>

As well as being less code for you to write, your app now contains a lot less JavaScript. 现在,您的应用程序包含的JavaScript数量大大减少,而且您可以编写的代码更少了。 Demo here: https://svelte.dev/repl/be5cac1695554b8e9ee6d0bc14b9dff1?version=3.5.3 演示在这里: https : //svelte.dev/repl/be5cac1695554b8e9ee6d0bc14b9dff1?version=3.5.3

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM