Honestly, if manually replacing pieces of DOM works for you, then by all means, keep doing that. I have no horse in the race.
In my experience, the approach you're describing doesn't really scale and only works for the most simplistic use cases. In a real world scenario you would have dozens if not hundreds or thousands of arrays/vars that are related to the view.
Even only considering we're talking about rendering blocks of dynamic HTML: How are you going to keep track which state changes affect which parts of the DOM? Are you going to have hundreds/thousands of innerHTML code all over the place? What is going to happen when (not if) you change your template?
Then you need to start considering, event handlers, initialization code for every car view, etc. Imagine those event handlers modify the state of a single car in your array. Are you going to repaint and re-initialize the events of all your cars on every mouse event? Again, that would probably work on very simplistic use cases.
I'm not describing anything new. These problems are what motivated people to create Ember, Backbone, Angular 1 and other frameworks some 10 years ago. But the JS world has moved on from that paradigm into the reactivity+components thing which is a much better abstraction in terms of developer experience, performance, memory consumption, etc [1].
I use this approach too but it fails whenever there's any state in the elements. An example is input elements:
function update() {
let value = document.querySelector("input")?.valueAsNumber;
document.querySelector("main").innerHTML = `
Enter a price: <input type="number" oninput="update()">
<br>
Discounted price is ${value * 0.8}
`;
}
update();
What will happen is each time you enter a character, it will create a new input element, which loses the focus and cursor position.
To solve this we can split it up so that the inputs are created once and the output is updated with innerHTML. No problem, right? But it's hard to scale up because now you have to split the html up into small segments based on whether there's internal state or not. If you want to update the 'max' of an input type=range, you aren't doing it with handlebars anymore but you have to use setAttribute instead.
What react, vue, etc. do for you is handle the updates in a way that reuses the existing elements. If you changed any attributes of the <input type=number>, it will not lose the focus and cursor and any other state. If you change the max of an <input type=range>, it will update the existing element (using setAttribute) without losing any other state. If you change some text, it will update the existing text node using innerText/textContent.
You may not need this type of system. I think React, Vue, etc. are overused. But in some projects it's much cleaner to use a React/Vue/etc. style system rather than splitting it up where some segments use setAttribute, some use innerHtml, etc.
Now let's say your data (cars) changes. Due to some interaction, a car gets added to the list.
What the above code will do is completely replace the HTML for the entire list of cars, when really all that needs to happen is appending one to the end. If the list is big, your app is now slow.
If the user has something focused or edited in that list of cars (let's say they're editing the description of one), not only is focus lost on that field now (its DOM node was completely wiped out and replaced), but the user's text they were editing is also lost.
That's an imperative approach, rather than a declarative one. You can't track where the state is being changed if #cars is being changed in many places. Templates are fine for smaller apps but in larger apps it's more difficult, and there'll be more spaghetti code and bugs due to that.
Ok, now show the cars list in three places, and one of them is a subset of only BLUE cars.
Now you have to update the DOM 3x and remember that the filter you set in that one place over there means you need to only show BLUE cars in this one place over here.
When you start to build things where one piece of data is not just shown in multiple places, but is dependent on other pieces of data, managing your state starts to become more difficult than the methodology you're describing.
Say a function changes the cars array. All it has to do to update the DOM is:
document.querySelector('#cars').innerHTML=carsTemplate(cars);
Where carsTemplate is a HandleBars template that on page load has been initialized with the html to render the list of cars.