渲染函数

最基础的渲染函数用法

new Vue({
	el: '#app',
	render(h){
		return h('div', 'hello world!');
	}
});

new Vue({
    el: '#app',
    render(h){
        return h({template: '<div>hello world!</div>'});
    }
});

Vue官方文档介绍中,createElement方法(h)的第一个参数可选值为:html标签、Object组件对象或者一个resolve了任何以上两种对象的Function。

下面用同步的写法,同步resolve

///这种属于同步渲染,跟以上两种方法区别不大
new Vue({
    el: '#app',
    render(h){
        return h(function(resolve){
			resolve({template: '<div>hello world!</div>'})
		});
    }
});

页面可以正常渲染。

下面再用一种异步的写法,需要异步的resolve

new Vue({
    el: '#app',
    render(h){
        return h(function(resolve){
            setTimeout(function (){
                resolve({template: '<div>hello world!</div>'})
            }, 1000)
        });
    }
});

发现页面无法渲染,并且会循环调用render方法,立马想到的是循环渲染了,但是尤大大讲了啊,可以是一个resolve了上面两种类型数据的函数。
最后不得不去看createElement的源码,发现使用渲染函数的时候,会调用resolveAsyncComponent方法,这个方法中对第一个参数Function进行了很对的状态控制,loading,resolved等。
问题找到了,每次render的时候,上面的h方法接收的参数,永远是一个新的“Function”,无法保留住resolveAsyncComponent方法中设置的各种状态控制。

解决方法

把异步函数独立出来

const app = function(resolve){
    setTimeout(function (){
        resolve({template: '<div>hello world!</div>'})
    }, 1000)
};
new Vue({
    el: '#app',
    render(h){
        return h(app);
    }
});

这样下来,页面就会在1秒后,把数据顺利渲染到页面了。
首次调用render的时候,app会设置resolved属性,第二次渲染的时候,直接取resolved的值,不会再去调用异步方法重新resolve。
当然上面的setTimeout只是模拟了一个异步的过程,实际项目中可能是一个异步的http请求等。

多讲一点

异步函数可以返回一个Promise,而不用去调用resolve。源码中Vue对Promise作了单独处理,会设置Function的loading值为true,进行不同的逻辑处理。