The lead pack stretched to 24 bikes in the first half of the race, before Avintia's Vinales, Ajo team-mates Danny Kent and Sandro Cortese, RW's Luis Salom and Team Germany rider Louis Rossi broke away.
Vinales and the Ajo duo were the main protagonists, swapping the lead countless times - including some unnerving near-misses between team-mates Kent and Cortese in which they were fortunate not to crash.
As the top three became ever more fraught over the final two laps, Salom was able to slip through and briefly take the lead.
It looked like he might snatch his maiden Moto3 win by opportunistic style, only to then run wide at the final chicane, allowing Vinales to sweep through and steal victory, while the Ajo duo went either side of Salom across the line. Video evidence had to be examined before the officials ruled that the order was Cortese, Kent (his first Moto3 podium), Salom.
Rossi completed the top five, with Monlau's Alex Rins recovering from a mid-race trip over the run-off to escape the next pack in sixth.
Alexis Masbou (Caretta) lead the following group in seventh, ahead of TT Motion rider Niklas Ajo, who was as high as second for a while before being edged back.
Reigning Italian 125cc champion Niccolo Antonelli (Gresini) was in the thick of the podium fight initially, but was elbowed down the order and later crashed.