$nextTick和 X-Templates

$nextTick

先看一个场景:

有一个 div,默认用 v-if 将它隐藏,点击一个按钮后,改变 v-if 的值,让它显示出来,同时拿到这个 div 的文本内容。
如果 v-if 的值是 false,直接去获取 div 的内容是获取不到的,因为此时 div 还没有被创建出来,那么应该在点击按钮后,改变 v-if 的值为 true, div 才会被创建,此时再去获取。

示例代码如下:

<div id="app">
	<div id="div" v-if="showDiv">这是一段文本</div>
	<button @click="getText">获取 div 内容</button >
</div>
<script>
	var app = new Vue({
		el: '#app',
		data: {
			showDiv: false
		},
		methods: {
			getText: function () {
				this.showDiv = true;
				var text = document.getElementById('div').innerHTML;
				console.log(text);	
			}
		}
	})
</script>

这段代码不难理解,但是运行后在控制台会抛出一个错误: Cannot read property ‘innerHTML’ of null, 意思是获取不到 div 元素。这里涉及到 Vue 一个重要的概念:异步更新队列

vue 在观察到数据变化时,并不是直接更新 DOM,而是开启一个队列,并缓冲在同一事件循环中发生的所有数据改变。在缓冲时会去除重复数据,从而避免不必要的计算和 DOM 操作。然后,在下一个事件循环 tick 中,Vue 刷新队列并执行实际(已去重)工作。

所以如果你用一个 for 循环来改变数据 100 次,其实它只会应用最后一次改变,如果没有这种机制,DOM 就要重绘 100 次,产生很大的开销。

Vue 会根据当前浏览器优先使用原生的 Promise.then 和 MutationObserver, 如果都不支持,就会采用 setTimeout 代替。

明白了 Vue 异步更新的原理,上面示例的报错也就不难理解了。事实上,在执行 this.showDiv = true; 时,div 仍然还没有被创建出来,直到下一个 Vue 事件循环时,才开始创建。$nextTick 就是用来知道什么时候 DOM 更新完成,所以上面的示例代码需修改为:

<div id="app">
	<div id="div" v-if="showDiv">这是一段文本</div>
	<button @click="getText">获取 div 内容</button >
</div>
<script>
	var app = new Vue({
		el: '#app',
		data: {
			showDiv: false
		},
		methods: {
			getText: function () {
				this.showDiv = true;
				this.$nextTick(function () {//等到 dom 更新完成后,执行下面代码逻辑
					var text = document.getElementById('div').innerHEML;
					console.log(text);
				});
					
			}
		}
	})
</script>

此时点击按钮,控制台就打印出 div 的内容“这是一段文本”了。
理论上,我们不应该主动操作 DOM,因为 Vue 的核心思想就是 数据驱动 DOM, 但是在很多业务里,我们避免不了使用一些第三方库,比如 proper.js(https://proper.js.org/)、swiper(http://idangero.us/swiper)等,这些基于原生 JavaScript 的库都有创建、更新、销毁的完整生命周期,与 Vue 配合使用时,就要利用好 $nextTick.

X-Templates

如果你没有使用 webpack、gulp 等工具,试想一下,你的组件 template 的内容很冗长、复杂,如果都在 JavaScript 里拼接字符串,效率是很低的,因为不能像写 HTML 那样舒服。Vue 提供了另一种定义模板的方式,在

<div id="app">
	<my-component></my-component >
	<script type="text/x-template" id="my-component">
		<div>这是组件的内容</div>
	</script>
</div>
<script>
	Vue.component('my-component', {
		template: 'my-component'
	});

	var app = new Vue({
		el: '#app'
	})
</script>

在 script 标签中,你可以愉快的写 HTML 代码,不用考虑换行等问题。

很多刚接触 Vue 开发的新手会非常喜欢这个功能,因为用它和组件知识,就可以轻松地完成交互相对复杂的页面和应用了。再配合一些构建工具(gulp)组织好代码结构,开发一些中小型产品是没有问题的。

不过,Vue 的初衷并不是滥用它,因为它将模板和组件的其他定义隔离了。

进阶篇里,将会介绍如何使用 webpack 来编译 .vue 的单文件,从而优雅地解决 HTML 书写的问题。

手动挂载实例

前面创建的实例都是通过 new Vue() 的形式创建出来的。在一些非常特殊的情况下,我们需要动态地区创建 Vue 实例, Vue 提供了 Vue.extend 和 $mount 两个方法来手动挂载一个实例。

Vue.extend 是基础 Vue 构造器,创建一个“子类”,参数就是一个包含组件选项的对象。

如果 Vue 实例在实例化时没有收到 el 选项,它就处于未挂载状态,没有关联的 DOM 元素。可以使用 $mount 手动地去挂载一个未挂载的实例。这个方法返回实例自身,因而 可以链式调用其他实例方法。示例代码如下:

<div id="mount-div">

</div>
<script>
	var MyComponent = Vue.extend({
		template: '<div> Hello: {{ name }} </div>',
		data: function () {
			return {
				name: 'Aresn'	
			}
		}	
	});

	new MyComponent().$mount('#mount-div');
</script>

运行后,id 为 mount-div 的 div 元素会被替换为组件 MyComponent 的template 的内容:

<div>Hello: Aresn</div>

除了这种写法外,以下两种写法也是可以的:

new MyComponent({
	el: '#mount-div'
});

或者,在文档之外渲染并且随后挂载

var component = new MyComponent().$mount();
document.getElementById('mount-div').appendChild(component.$el);

手动挂载实例(组件),是一种比较极端的高级的用法,在业务中几乎用不到,只在开发一些复杂的独立组件可能会使用,所以只需了解即可。

参考图书:Vue.js 实战

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值