First you should use $this->widget instead of $this->beginWidget for CTreeView because it doesn't need body content.
Because you mainly want to save the time generating $data, you can write a getter method for it in the controller. Calling this getter method will be expensive, but because it is called inside the fragment cache, it will only be called when the cache is invalidated.
Use $this. $this always refers to the controller object, unless the view belongs to a widget. In that case, $this refers to the widget object. Accessing data via $this is known as pull, while your earlier approach is push.
Hmm, so to leverage the view cache appropriately, we have to change the way we initialize $data objects in view files? The standard way seems to be initializing $data vars in controllers, and then passing it to the view file.
But now, we have to setup getter methods for each piece of data, then calling it in view file? Is this the best way?