With GNU tools (your --color=always is a GNU extension already) and a shell with support for Ksh-style process substitution:
xargs -r0a <(grep -rlZ pattern .) vim
Or:
xargs -r0a <(find . ... -exec grep -lZ pattern {} +) vim
With zsh:
vim ${(0)"$(find . ... -exec grep -lZ pattern {} +)"}
With bash 4.4+:
readarray -td '' files < <(find . ... -exec grep -lZ pattern {} +)
vim "${files[@]}"
Those minimise the number of grep invocations that are performed. The point is to tell grep to output the file names NUL-delimited, so they can be reliably split into separate arguments for vim by GNU xargs -0 or zsh's 0 parameter expansion flag or bash's readarray -td ''.
In zsh, you could also do:
vim ./**/*(...e['grep -q pattern $REPLY'])
(where ... stands in for further qualifiers you may want to add, like for the find approach).
That means however that like the approaches that use find -exec grep -q pattern {} ';', one grep invocation would be run per file which would make it significantly slower.
Your first approach would work in zsh provided you replaced --color=always with -Z and changed the value of IFS to IFS=$'\0' from the default of IFS=$' \r\n\0'. I wouldn't work in other shells, as they don't support storing NULs in variables, let alone $IFS and would also perform filename generation on the words resulting of the splitting which you'd need to disable with set -o noglob or set -f.
Why is looping over find's output bad practice? will give you more tips on how to process the list of files found by find reliably.
--color=always? With-lgrep only lists matched files anyway, so you really shouldn't need colors. – Quasímodo Sep 07 '20 at 15:26-land removing--color=alwaystakes more or less the same fraction of time. And changing the latter to the former takes even less time. I've modified the question to get some more knowledge about the command substitution approach vs thexargsapproach. – Enlico Sep 07 '20 at 15:37