Barca fell behind when Denis Cheryshev diverted a shot home but Neymar levelled from a rebound.
Luciano Vietto slotted home to put the visitors ahead with the first of three goals in the space of four minutes.
Rafinha equalised from close range before Messi curled home to cut Real Madrid’s lead at the top to one point.
Barca made the the early running, with former Liverpool striker Luis Suarez denied by Sergio Asenjo following Dani Alves’ cross.
But Villarreal went in front when Cheryshev stuck out a leg and diverted Mario’s off-target shot into the net.
A fine move led to Barca equalising as Rafinha’s shot following Messi’s cross was saved by Asenjo only for Neymar to strike home the rebound.
The visitors regained the lead early in the second half as Gerard Pique lost the ball to Giovani, who ran on and crossed for Vietto to net his 10th goal of the season.
Villarreal’s lead lasted just two minutes, though, with Rafinha netting after Victor Ruiz blocked Messi’s header following Suarez’s cross.
And Barca grabbed the winner as Messi’s right-foot curler took him to 32 goals for the season and kept Real Madrid well in sight.