The answer is actually a bit common-sense after a moment: the difference between injecting a <script> and a eval() is that in the former case the code is in a file on some server, whereas in the latter case it's in a string. To hack up a solution with eval requires converting the file into a string, but to do that you need an XMLHttpRequest and to deal with origin policies and it rapidly explodes into a pointless mess because you're explicitly requesting that the loaded script have the power to interact with the context you're in, even if you're not using this power by boxing it into a context. (For example when you eval() in `function () { var secret = "this string is totally private!"; eval(code) }` that code has a power which no script tag has.)
[1] The sins of eval include ugly metaprogramming where it's not needed like `eval("someObject." + property + ".otherThing")` rather than `someObject[property].otherThing`, and accidental remote code exploits where none was intended like `var json = eval(thing_i_dont_actually_trust)`: whoops, someone passed `(function() { ... }())` instead of JSON and it went straight through my parser.
Furthermore, if you cache said scripts in, say, IndexedDB you can just perform out-of-date checks via the websocket and only re-sync those scripts that have changed on the server resulting in even faster load times.
This works and it's great. How do I know? I implemented such a solution in Gate One a few years ago:
https://github.com/liftoff/GateOne
It also has the added benefit of making embedding a web application into another so much easier. Because you can just load a tiny little script on a page that makes just one connection to an external websocket and can load everything from that in an instant.
My guess is it's something to do with cross site scripting. I can see advantages to using XMLHttpRequest in conjuction with eval in some cases, such as being able to monitor script download progress with the onprogress event.
Also, if you use a websocket you can pretty much forget about the content security restrictions. Websockets have no origin restrictions so you can load whatever you want from anywhere.