Every page has a “main query” which is run before the template is loaded. The results of the main query are how WordPress determines what template to load. The standard Loop operates on the data contained in the main query, this is why it seems to just magically “work” without you having to explicitly query something yourself.
query_posts
overwrites the contents of that main query. This is why you shouldn’t use query_posts
– you’re changing the query results after the template is loaded, which can have unexpected consequences. pre_get_posts
is the preferred way to modify the main query, it runs before the main query happens, so that everything that follows is operating on the correct set of data.
get_posts
and WP_Query
are for running additional queries, separate from the main query. You have to assign the results to a variable, and run some sort of loop on those results. Your use of get_posts
is doing nothing, because you’ve done nothing with the results of that query.