d3 doesn't work in vue3 composition mode in single file component (SFC) written by typescript

here is a vue3 single file component (SFC) in composition mode written by typescript.

here is a snippet of simplified code.

There are 2 sets of [y, 0, x, 1] points data, plot lineData1 as smooth line, and plot lineData2 as symbols.

In template, the DOM tree is defined. In script, the 1st step is to import definitions, the 2nd step is to define the sample data, the 3rd step is to define the d3.scale for x and y, the 4th step is to plot the smooth line, the 5th step is to plot the symbols with transform.

I save the code as a .vue file which is a vue3 SFC, and integrate it into a vue3 project.

<template>
    <div>
        <svg xmlns="http://www.w3.org/2000/svg" ref="svgElement" class="center-plot-graph">
            <g ref="lineElement" class="lineElements">
                <path class="splineElement" />
                <g class="symbolElement" />
            </g>
        </svg>
    </div>
</template>

<script setup lang="ts">
import {
    select,
    Selection,
    scaleLinear,
    ScaleLinear,
    symbol,
    symbolCircle,
    line,
    Line,
    curveLinear,
    ValueFn,
    BaseType,
} from "d3";
// import * as d3 from "d3"
import { ref, Ref, inject, onMounted } from "vue";

type Vector1d = Array<number>;
type Vector2d = Array<Vector1d>;

const svgElement: Ref<SVGSVGElement | null> = ref<SVGSVGElement | null>(null);
const lineElement: Ref<SVGGElement | null> = ref<SVGGElement | null>(null);

let lineData1: Vector2d = [
    [10.5, 0, -26.5, 1],
    [10.515418023668452, 0, -25.98615240945993, 0.9999999999999999],
    [10.54673612522916, 0, -25.4242010779106, 0.9999999999999998],
    [10.599262000092798, 0, -24.79828284532514, 0.9999999999999998],
    [10.682246533999162, 0, -24.080779736377877, 0.9999999999999999],
    [10.814880527142483, 0, -23.214463145842437, 0.9999999999999998],
    [11.054647461420542, 0, -22.028119146822945, 0.9999999999999999],
    [11.671811400028488, 0, -19.86283988201097, 0.9999999999999998],
    [12.613086386434938, 0, -17.545566003251505, 0.9999999999999999],
    [13.813926262485333, 0, -15.363942341711933, 1],
    [15.263842352648265, 0, -13.35830776532418, 1.0000000000000004],
    [16.93791350677736, 0, -11.540160490114632, 1.0000000000000007],
    [18.800820600423645, 0, -9.900220288676735, 1.0000000000000004],
    [20.817015127495804, 0, -8.428748556487237, 1.0000000000000004],
    [22.9509485819025, 0, -7.116006689022895, 1.0000000000000002],
    [25.16710035838422, 0, -5.9522535772986, 1],
    [27.437849524060642, 0, -4.927039014178517, 0.9999999999999999],
    [29.7542920863866, 0, -4.028232704145715, 0.9999999999999999],
    [32.11023490194244, 0, -3.243461017781614, 0.9999999999999998],
    [34.499484827308535, 0, -2.56035032566763, 0.9999999999999997],
    [36.91584871906518, 0, -1.9665269983851892, 0.9999999999999996],
    [39.35328336132695, 0, -1.4504051633262336, 0.9999999999999997],
    [41.8071311282343, 0, -1.0076791848683684, 0.9999999999999999],
    [44.273483525476955, 0, -0.6379795521514762, 1],
    [46.499497521985354, 0, -0.3675404908536285, 0.9999999999999998],
    [47.70003443876498, 0, -0.24619654497075863, 1],
    [48.571814387462624, 0, -0.16874721429387524, 0.9999999999999999],
    [49.292111379398605, 0, -0.11149361650523448, 1],
    [49.91998747855483, 0, -0.06654412557289584, 1],
    [50.483859148305946, 0, -0.03010369508701564, 1.0000000000000002],
    [51, 0, 0, 1],
];
let lineData2: Vector2d = [
    [10.5, 0, -26.5, 1],
    [10.573820291076342, 0, -22.314239546778368, 0.9999999999999995],
    [13.359019071367536, 0, -13.451591871333579, 1.0000000000000007],
    [24.504724058628779, 0, -5.452588866468975, 1.0000000000000003],
    [37.471579656269877, 0, -1.4571818281579628, 0.9999999999999992],
    [46.497084577993259, 0, -0.24908378543712748, 1.0000000000000003],
    [51, 0, 0, 1],
];

let xScale: ScaleLinear<number, number, never> = scaleLinear()
    .domain([-26, 0])
    .range([10, 920]);
let yScale: ScaleLinear<number, number, never> = scaleLinear()
    .domain([10, 52])
    .range([10, 950]);

// let rootNode: Selection<SVGSVGElement, Vector2d, null, undefined> = select<SVGSVGElement, Vector2d>(svgElement.value as SVGSVGElement)
let splineElement: Selection<SVGPathElement, Vector2d, null, undefined> =
    select(lineElement.value as SVGGElement)
        .select<SVGPathElement>(".splineElement")
        .datum<Vector2d>(lineData1)
        .attr(
            "d",
            (
                d: Vector2d,
                i: number,
                a: Array<SVGPathElement> | ArrayLike<SVGPathElement>
            ) => {
                return line<Vector1d>()
                    .x((di: Vector1d, ii: number, ao: Vector2d) => xScale(di[2]))
                    .y((di: Vector1d, ii: number, ao: Vector2d) => yScale(di[0]))
                    .curve(curveLinear)(d);
            }
        )
        .style("stroke", "black")
        .style("fill", "none");

let SymbolTransform: ValueFn<SVGPathElement | BaseType, Vector1d, string> =
    function (
        d: Vector1d,
        i: number,
        a: Array<BaseType | SVGPathElement> | ArrayLike<BaseType | SVGPathElement>
    ): string {
        const x = xScale(d[2]);
        const y = yScale(d[0]);
        return `translate(${x},${y})`;
    };

let symbolNode: Selection<SVGPathElement, Vector1d, SVGGElement, Vector2d> =
    select(lineElement.value as SVGGElement)
        .select<SVGPathElement>(".symbolElement")
        .selectAll<SVGPathElement, Vector1d>("path")
        .data<Vector1d>(lineData2)
        .join("path")
        .attr("d", symbol<Vector1d>(symbolCircle, 20))
        .style("stroke", "red")
        .style("fill", "none")
        .attr("transform", SymbolTransform);
</script>

<style scoped lang="less">
.center-plot-graph {
    position: absolute;
    width: 936px;
    height: 960px;
}
</style>

the dependencies are:

"dependencies": {
    "d3": "^7.9.0",
    "lodash-es": "^4.17.21",
    "vue": "^3.4.21"
  },
  "devDependencies": {
    "@types/d3": "^7.4.3",
    "@types/lodash-es": "^4.17.12",
    "@types/node": "^20.12.7",
    "@vitejs/plugin-vue": "^5.0.4",
    "eslint": "^9.1.1",
    "eslint-plugin-vue": "^9.25.0",
    "less": "^4.2.0",
    "typescript": "^5.4.5",
    "vite": "^5.2.10",
    "vite-plugin-top-level-await": "^1.4.1",
    "vitest": "^1.5.2",
    "vue-tsc": "^2.0.6"
  }

it’s very easy to reproduce the bug:

  1. create a new project
npm init vite@latest
  1. copy the dependencies to package.json, then run
npm install
  1. create a new .vue file, copy the code into the file and save
  2. import and insert the .vue file into app.vue file
  3. compile and test
npm run dev

the command would tell you an address, access that address by your browser.

Then you will see: the plot doesn’t appear. Click F12 in the browser and check the elements, you will find there is nothing in or

I guess the problem is in the line generator and symbol generator, maybe type definition is not correct.

I tried to debug line by line, but d3 doesn’t allow me to check what happens inside attr(“d”, (d, i, a)=>{})

can anybody help me?

Repeating my earlier response since you posted the same question in the Observable Slack:

There’s a far amount still going on in the simplified code. I would try to either reduce it further until you understand better where it’s going wrong, or use the debugger to step through the code and see where it’s not behaving as you expect.

To your last comment, D3 won’t prevent you from debugging anything. You just need to use the “step in” command to step into the code as it runs.