Skip to content

Latest commit

 

History

History
113 lines (101 loc) · 6.26 KB

14 其他.md

File metadata and controls

113 lines (101 loc) · 6.26 KB

其他

1.1 $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 vm = new Vue({
        el: '#app',
        data: {
            showDiv: false
        },
        methods: {
            getText: function() {
                this.showDiv = true
                var divText = document.getElementById('div').innerHTML
                consloe.log(divText)
            }
        }
    })
</script>

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

Vue在观察到数据变化时并不是直接更新DOM,而是开启一个队列,并缓冲在同一事件循环 中发生的所有数据改变。在缓冲时会去除重复数据,从而避免不必要的计算和DOM操作。然后, 在下一个事件循环tick中,Vue刷新队列井执行实际(己去重的)工作。所以如果你用一个for循 环来动态改变数据100次,其实它只会应用最后一次改变,如果没有这种机制,DOM就要重绘100次,这固然是一个很大的开销

Vue会根据当前浏览器环境优先使用原生的Promise.thenMutationObserver,如果都不支持, 就会采用setTimeout代替。 知道了Vue异步更新DOM的原理,上面示例的报错也就不难理解了 。 事实上,在执行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 vm = new Vue({
        el: '#app',
        data: {
            showDiv: false
        },
        methods: {
            getText: function() {
                this.$nextTick(function() {
                    this.showDiv = true
                    var divText = document.getElementById('div').innerHTML
                    consloe.log(divText)
                })
            }
        }
    })
</script>

这时再点击按钮,控制台就打印出div的内容"这是一段文本"了。

理论上,我们应该不用去主动操作DOM,因为Vue的核心思想就是数据驱动 DOM,但在很 多业务里,我们避免不了会使用一些第三方库,比如 popperswiper 等,这些基于原生JavaScript的库都有创建和更新及销毁的完整生命周 期,与Vue配合使用时,就要利用好$nextTick

1.2 X-Templates

如果你没有使用webpackgulp等工具,试想一下你的组件template的内容很冗长、复杂,如果都在JavaScript里拼接字符串,效率是很低的,因为不能像写HTML那样舒服。Vue提供了另 一种定义模板的方式,在<script>标签里使用text/x-template类型,井且指定一个id, 将这个id赋 给template。示例代码如下:

<div id="app">
    <my-component></my-component>
    <script type="x-template" id="my-component">
    <div>这是组件的内容</div>
    </script>
</div>
<script>
    Vue.componennt('my-component', {
        template: '#my-component'
    })
    var vm = new Vue({
        el: '#app'
    })
</script>

<script>标签里,你可以愉快地写HTML代码,不用考虑换行等问题。 很多刚接触Vue开发的新手会非常喜欢这个功能,因为用它,再加上组件知识,就可以很轻松地完成交互相对复杂的页面和应用了。如果再配合一些构建工具(gulp)组织好代码结构,开发一些中小型产品是没有问题的。不过,Vue的初衷并不是滥用它,因为它将模板和组件的其他定义隔离了。在后面的文章中,我们会介绍如何使用webpack来编译.vue的单文件,从而优雅地解决HTML书写的问题。

1.3 手动挂载实例

我们现在所创建的实例都是通过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() {
        return {
            name: 'Aresn'
        }
    }
})
new MyComponent().$mount('#mount-div')
</script>

运行后,idmount-divdiv元素会被替换为组件MyComponenttemplate的内容:<div>hello: {{name}}</div>

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

new MyComponent().$mount('#mount-div')
//同上
new Mycomponent({
    el: '#mount-div'
})
    // 或者在文档之外渲染并且随后挂载
    var component = new MyComponent().$mount()
    document.getElelmentById('#mount-div').appendChild(component.$el)

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