[英]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.