Vue.js实战第九章笔记

Vue.js2.x使用了VirtualDom(虚拟DOM)来更新DOM节点,提升渲染性能。虽然前面章节我们的组件模板都是写在template选项里的,但是在Vue.js编译是,都会解析为VirtualDom
9.1什么是Virtual Dom
Virtual Dom并不是真正意义上的DOM,而是一个轻量级的JavaScript对象,在状态发生变化时,Virtual Dom会进行Diff运算,来更新只需要被替换的DOM,而不是全部重绘。
与DOM操作相比,Virtual Dom是基于JavaScript计算的,所以开销会小很多。

在Vue.js 2中,Virtual Dom就是通过一种VNode类表达的,每个DOM元素或组件都对应一个VNode对象,在Vue.js源码中是这样定义的:

export interface VNode {
    tag?: string;
    data?: VNodeData;
    children?: VNode[];
    text?: string;
    elm?: Node;
    ns?: string;
    context?: Vue;
    key?: string | number;
    componentOptions?: VNodeComponentOptions;
    componentInstance?: Vue;
    parent?: VNode;
    raw?: boolean;
    isStatic?: boolean;
    isRootInsert?: boolean;
    isComment?: boolean;
}

具体含义如下:
· tag 当前节点的标签名
· data 当前节点的数据对象
VNodeData代码如下

export interface VNodeData {
    key?: string | number;
    slot?: string;
    scopedSlots?: { [key: string]: ScopedSlot };
    ref?: string;
    tag?: string;
    staticClass?: string;
    class?: any;
    staticStyle?: { [key: string]: any };
    style?: Object[] | Object;
    props?: { [key: string]: any };
    attrs?: { [key: string]: any };
    domProps?: { [key: string]: any };
    hook?: { [key: string]: Function };
    on?: { [key: string]: Function | Function[] };
    nativeOn?: { [key: string]: Function | Function[] };
    transition?: Object;
    show?: boolean;
    inlineTemplate?: {
        render: Function;
        staticRenderFns: Function[];
    };
    directives?: VNodeDirective[];
    keepAlive?: boolean;
}

· children 子节点,数组,也是VNode类型。
· text 当前节点的文本,一般文本节点或注释节点会有该属性。
· elm 当前虚拟节点对应的真实的DOM节点。
· ns 节点的namespace。
· context 编译作用域。
· functionalContext 函数化组件的作用域。
· key 节点的key属性,用于作为节点的标识,有利于patch的优化。
· componentOptions 创建组件实例时会用到的信息选项。
· child 当前节点对应的组件实例。
· parent 组件的占位节点。
· raw 原始html。
· isStatic 静态节点的标识。
· isRootInsert 是否作为根节点插入,被包裹的节点,该属性的值为false。
· isComment 当前节点是否是注释节点。
· isCloned 当前节点是否为克隆节点。
· isOnce 当前节点是否有v-once指令。

使用Virtual Dom就可以完全发会JavaScript的编程能力。在多数场景中,我们使用template就足够了,但在一些特定的场景下,使用Virtual Dom会更简单。

· TextVNode 文本节点。
· ElementVNode 普通元素节点。
· ComponentVNode 组件节点。
· EmptyVNode 没有任何内容的注释节点。
· CloneVNode 克隆节点,可以是以上任意类型的节点,唯一的区别在于 isCloned 属性为true。
9.2什么是Render函数
Render函数通过createElement参数来创建VirtualDom,结构精简了很多。在第7章中介绍slot时,有提到过访问slot的方法,使用场景就是在Render函数。
9.3creatElement用法
9.3.1基本参数
creatElement构成了Vue Virtual Dom的模板,它有三个参数:

createElement(
	// {String | Object | Function}
	// 一个HTML标签,组件选项,或一个函数
	// 必须return上述其中一个
	'div',
	// {Object}
	// 一个对应属性的数据对象,可选
	// 可以在template中使用
	{
	// 稍后详细介绍
	},
	// {String | Array}
	// 子节点(VNodes),可选
	[
		createElement('h1', 'hello world'),
		createElement(MyComponent, {
			props: {
				someProp: 'foo'
			}
		}),
		'bar'
	]
}

第一个参数必选,可以是一个HTML标签,也可以是一个组件或函数;第二个是可选参数,数据对象,在template中使用。第三个是子节点,也是可选参数,用法一致。
对于第二个参数“数据对象”。具体的选项如下:

{
    //和v-bind:class一样的API
    'class': {
        foo: true,
        bar: false
    },
    //和v-bind:style一样的API
    'style': {
        color: 'red',
        fontSize: '14px'
    },
    //正常的HTML特性
    attrs {
        id: 'foo'
    },
    //组件props
    props: {
        myProp: 'bar'
    },
    //DOM属性
    domProps: {
        innerHTML: 'baz'
    },
    //自定义事件监听器"on"
    //不支持如v-on:keyup.enter的修饰器
    //需要手动匹配keyCode
    on: {
        click: this.clickHandler
    },
    //进对于组件,用于监听原生事件
    //而不是组件使用vm.$emit触发的自定义事件
    nativeOn: {
        click: this.nativeClickHandler
    },
    //自定义指令
    directives: [
        {
            name: 'my-custom-directive',
            value: '2',
            expression: '1+1',
            arg: 'foo',
            modifiers: {
                bar: true
            }
        }
    ],
    //作用域slot
    //{name: props => VNode | Array<Vnode> }
    scopedSlots: {
        default: props => h('span', props.text)
    },
    //如果子组件有定义slot的名称
    slot: 'name-of-slot',
    //其他特殊顶层属性
    key: 'myKey',
    ref: 'myRef'
}

以往在template里,我们都是在组件的标签上使用形容 v-bind:class、v-bind:style、v-on:click这样的指令,在render函数都将其写在了数据对象里,比如下面的组件,使用传统的template写法是:

<div id="app" > 
	<ele></ele> 
</div> 
<script >
 Vue.component ('ele', { 
	template :'\ 
		<div id="element" \ 
		:class=" {show : s how }" \ 
		@click="handleClick">文本内容</div> ',
	data: function () { 
		return { 
			show:true 
		}
	}
	methods: { 
		handleClick: function () { 
			console.log ('clicked!'); 
		} 
	}
})
var app =new Vue ( { 
	el :'#app'
}) 
</script> 

使用Render改写后的代码如下:

<div id="app" > 
	<ele></ele> 
</div> 
<script> 
Vue.component ('ele', { 
	render : function (createElement) { 
		return createElement( 
	'div', 
	{
		//动态绑定 class,同:class
		class:{ 
			'show': this.show 
		}
		//普通 html 特性
		attrs:{ 
			id:'element ' 
		}
		//给 div 绑定 click 事件
		on:{ 
			click:this.handleClick
		}
	},
	'文本内容'
	data:function () { 
		return { 
			show: true 
		}, 
		methods: { 
			handleClick: function () { 
				console.log ('clicked!');
			}
		}
	}
});
var app =new Vue ( { 
	el :'#app'
}) 
</script> 

9.3.2约束
所有的组件树中,如果VNode是组件或含有组件的slot,那么VNode必须是唯一的。
重复渲染多个组件的方法很多。重复子组件如下示例:

<div id="app">
	<ele></ele>
</div>
<script>
//局部生命组件
var Child = {
	render:function(createElement){
		return createElement('p','text');
	}
};
Vue.component('ele',{
	render:function(createElement){
		return createElement('div',
			Array.apply(null,{
				length:5
			}),map(function(){
				return createElement(Child);
			})
		)
	}
});
var app = new Vue({
	el:'#app'
})
</script>

含有组件的slot,需要将slot的妹子子节点都克隆一份。代码如下:

<div id="app">
	<ele>
		<div>
			<Child></Child>
		</div>
	</ele>
</div>
<script>
//全局注册组件
Vue.component('Child',{
	render:function(createElement){
		return createElement('p','text');
	}
});
Vue.component('ele',{
	render:function(createElement){
	//克隆slot节点的方法
		function cloneVNode(vnode){
			//递归所有子节点
			const cloneChildren = vnode.children &&
			vnode.children.map(function(vnode){
				return cloneVNode(vnode);
			});
			const cloned = createElement(
				vnode.tag,
				vnode.data,
				cloneChildren
			);
			cloned.text = vnode.text;
			cloned.isComment = vnode.isComment;
			cloned.componentOptions = vnode.componentOptions;
			cloned.elm = vnode.elm;
			cloned.context = vnode.context;
			cloned.ns = vnode.ns;
			cloned.isStatic = vnode.isStatic;
			cloned.key = vnode.key;
			
			return cloned;
		}
		const vNodes = this.$slots.default;
		const cloneVNodes = vNodes.map(function(vnode){
			return cloneVNode(vnode);
		});
		return createElement('div',
			vNodes,
			cloneVNodes,
		)
	}
});
var app = new Vue({
	el:'#app'
})
</script>

在 Render 函数里创建了一个 cloneVNode 的工厂函数,通过递归将 slot 所有子节点都克隆了一份,并对VNode 的关键属性也进行复制。
9.3.3使用JavaScript代替模板功能
在render函数中不能够使用Vue内置的指令,可以用原生的JavaScript实现。
比如
v-if、v-else:

<div id="app">
	<ele :show="show"></ele>
</div>
render:function(createElement){
	if(this.show){
		return createElement('p','show的值为true');
	}else{
		return createElement('p','show的值为false');
	}
}
props:{
	show:{
		type: Boolean,
		default: false
	}
}

v-for:

<div id="app">
	<ele :list="list"></ele>
</div>
render:function(createElement){
	var nodes = {};
	for(var i=0;i<this.list.length;i++){
		nodes.push(createElement('p',this.list[i]));
	}
	return createElement('div',nodes);
}
props:{
	list:{
		type: Array
	}
}

v-model的逻辑:

<div id="app">
	<ele></ele>
</div>
<script>
Vue.component('ele',{
	render:function(createElement){
		var _this = this;
		return createElement('div',{
			createElement('input',{
				domProps:{
					value:this.value
				},
				on:{
					input:function(event){
					_this.value = event.target.value;
				}
			}
		}),
		createElement('p','value:'+this.value)
	})
},
data:function(){
	return {
		value:''
	}
});
var app = new Vue({
	el:'#app'
})
</script>

对应template写法:

<div>
	<input v-model="value">
	<p> value:{{value}}</p>
</div>

9.4函数化组件
Vue.js提供了一个functional的布尔值选项,设置为true可以使组件无状态和无实例,也就是没有data和this上下文。这样更容易渲染,开销小很多。
使用函数化组件时,render函数提供第二个参数context来提供临时上下文组件需要的data、props、slots、children、parent都是通过上下文传递。This.level改写为context.props.level,this.$slots.default改写为context.children。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值