Having implemented virtual DOM natively in Sciter (1), here are my findings:
In conventional browsers the fastest DOM population method is element.innerHTML = ...
The reason is that element.innerHTML works transactionally:
Lock updates -> parse and populate DOM -> verify DOM integrity -> unlock updates and update rendering tree.
While any "manual" DOM population using Web DOM API methods like appendChild() must do such transaction for any such call, so steps above shall be repeated for each appendChild() - each such call shall left the DOM in correct state.
And virtual DOM reconciliation implementations in browsers can use only public APIs like appendChild().
So, indeed, vDOM is not that performant as it could be.
But that also applies to Svelte style of updates: it also uses public APIs for updating DOM.
Solution could be in native implementation of Element.patch(vDOM) method (as I did in Sciter) that can work on par with Element.innerHTML - transactionally but without "parse HTML" phase. Yes, there is still an overhead of diff operation but with proper use of key attributes it is O(N) operation in most cases.