Не удается заставить D3.js работать внутри компонента Svelte (с Rollup) - PullRequest
0 голосов
/ 20 июня 2019

Я пытался вставить самый простой пример D3 в приложение Svelte и не могу заставить его работать.Сначала я попытался установить D3 в качестве модуля узла: npm install d3, но это дает тот же результат (отсутствие результата), что и импорт D3 в качестве внешнего скрипта из CDN внутри index.html: <script src="https://d3js.org/d3.v5.min.js"></script>.Используя любой из этих методов, я получаю несколько предупреждений о циклических зависимостях при запуске приложения:

(!) 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

Но приложение запускается без ошибок, динамическое форматирование D3 не происходит, и в консоли DevTools внутри Chrome не появляются какие-либо ошибки..

Компонент 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>

Помещение приведенного выше кода в статический файл HTML приводит к появлению гистограммы, как и ожидалось.Но при запуске в качестве компонента Svelte ничего не отображается.

Мой 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
    }
};

... и 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>

Я подозреваю, что Rollup неправильно связывает модуль D3, но как внешний скрипт в <body> теоретически он должен работать, но это не так.Пожалуйста, направьте меня в правильном направлении, я потратил слишком много времени, пытаясь заставить его работать, и, как новичок JS, у меня нет выбора.Спасибо!

1 Ответ

7 голосов
/ 21 июня 2019

Элемент <div class="chart"></div> не существует при первом запуске кода - содержимое <script> запускается при создании экземпляра компонента.Если вам нужен доступ к элементам DOM внутри компонента, он сначала будет доступен внутри onMount :

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

  // other code...

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

Использование селектора, например .chart, опасно, потому что, если у вас было большечем один компонент на странице D3 выбрал бы неправильную вещь.Лучше использовать bind: это вместо:

<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>

Теперь все, что вам нужно изменить, - это CSS - потому что Svelte откажется от селекторов, которые, по его мнению, не используются, и потому что он может 'Не знаю, что собирается делать D3, он удалит .chart div {...}.Вместо этого используйте модификатор : global (...) для нацеливания элементов div внутри элемента верхнего уровня:

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

С этими изменениями он работает отлично:

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

Но в этом случае D3 не приносит ничего на вечеринку.Вы можете достичь того же результата гораздо проще:

<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>

Помимо того, что вы пишете меньше кода, ваше приложение теперь содержит намного меньше JavaScript.Демо здесь: https://svelte.dev/repl/be5cac1695554b8e9ee6d0bc14b9dff1?version=3.5.3

...