Vue毕竟也是对JS的封装,只要是JS,免不了对DOM的操作。老外的教程和官网Vue的教程分类很不同,先来看看DOM操作吧。

  1. 双花括号 {{}} 叫做Interpolation或者 String Interpolation
  2. v-bind 绑定属性
  3. v-once 仅渲染一次
  4. v-html 输出原始HTML代码
  5. v-on 监听事件 给函数传入自定义参数
  6. 事件修饰符 event modifier – 阻止冒泡
  7. 事件修饰符 监听特定的键
  8. 在Vue表达式中直接使用JS代码
  9. v-model 双向绑定
  10. computed 计算属性
  11. watch 侦听属性
  12. 简化写法
  13. 更改CSS类
  14. 更改CSS样式
  15. 总结

双花括号 {{}}

双花括号就像是后端渲染的模板标签一样,对于其中的变量取值并且转换成字符串后进行显示,变量肯定也是有预先确定的命名空间。

由于类似于表达式求值,在Vue中双花括号的内容除了可以是data中的数据属性之外,还可以是methods中的方法名加上括号执行的结果,比如:

<div id="app">
    <p>{{ title }}</p>
    <p>{{ test1() }}</p>
</div>

<script>
    new Vue({
        el:"#app",
        data:{
            title:"Hello world!"
        },
        methods:{
            test1:function () {
                return "gugugugu"
            }
        }
    })
</script>

花括号里边的内容写成this.titlethis.test1()也是可以的,但不能写成data.title,因为Vue会把属性和方法都设置到Vue实例上。

methods中的方法也可以返回data中的数据,比如:

<script>
    new Vue({
        el:"#app",
        data:{
            title:"Hello world!"
        },
        methods:{
            test1:function () {
                return this.title
            }
        }
    })
</script>

只要函数的返回值可以转换成字符串就可以,比如显示刷新页面时候的时间:

<script>
    new Vue({
        el:"#app",
        data:{
            title:"Hello world!"
        },
        methods:{
            test1:function () {
                return new Date();
            }
        }
    })
</script>

注意,花括号只能用在原本是HTML的标签内容的部分,而不能应用在属性上。

v-bind 绑定属性

指令v-bind加上HTML的标准属性,然后设置值,可以将HTML的属性值与Vue实例的数据绑定。

比如想将一个链接的值设置为Vue实例内的一个URL字符串值:

<div id="app">
    <p>{{ title }} - <a v-bind:href="url">{{ showUrl() }}</a></p>
</div>

<script>
    new Vue({
        el:"#app",
        data:{
            title:"conyli.cc",
            url:"http://conyli.cc"
        },
        methods:{
            showUrl:function () {
                return this.title;
            }
        }
    })
</script>

使用了v-bind之后,虽然属性还是等于了一个字符串"url",但这个字符串此时已经不是字符串,而是表示Vue实例中的url属性。

有了绑定之后,不仅能使用双花括号操作标签的内容,还能使用绑定操作任意的标签属性。

v-once 仅渲染一次

这是一条单独的指令,对应的标签在生成页面的时候仅使用对应数据渲染一次,之后不再变动。

<div id="app">
    <h1 style="text-align: center;" v-once>{{ title }}</h1>
    <input type="text" v-on:input="changeTitle">
    <p>{{ title }}</p>
</div>

<script>
    new Vue({
        el:"#app",
        data:{
            title:"conyli.cc",
            url:"http://conyli.cc"
        },
        methods:{
            changeTitle:function (event) {
                this.title = event.target.value;
            }
        }
    })
</script>

这里双向绑定了INPUT元素和P元素,输入之后P元素的内容会发生变化,但标题不会变动,只用最初数据渲染并且固定。可以发现双向绑定是自动去掉两端空格,中间重复空格只保留一个。

v-html 输出原始HTML代码而不转义

正常情况下花括号的内容会被转义,以避免恶意脚本。如果想输出HTML代码,则要使用指令v-html,比较下边两个P元素:

<div id="app">
    <p>{{ link }}</p>
    <p v-html="link"></p>
</div>

<script>
    new Vue({
        el:"#app",
        data:{
            title:"conyli.cc",
            url:"http://conyli.cc",
            link: "<a href='http://conyli.cc'>conyli.cc</a>"
        }
    })
</script>

第一个P元素显示的就是转义后的字符串,而第二个P元素中就是HTML超链接,这说明v-html可以不转义直接输出HTML代码。在使用的时候要当心。

v-on 监听事件

这个指令在最早的例子中使用过,监听input事件。v-on之后可以监听任何JS原生的事件名称,然后值可以设置为methods中的处理方法,可以传入event对象。

做一个简单的计数器:

<div id="app">
    <button v-on:click="add">Click me</button>
    <p>{{ counter }}</p>
    <p v-on:mousemove="update" style="height:100px;width: 200px;border: 1px solid red" >{{ x }} / {{ y }}</p>
</div>

<script>
    new Vue({
        el:"#app",
        data:{
            counter: 0,
            x:0,
            y:0
        },
        methods: {
            add: function () {
                this.counter++
            },
            update: function (event) {
                this.x = event.clientX;
                this.y = event.clientY;
            }
        }
    })
</script>

这里给BUTTON元素设置了监听事件的add方法,每点一次按钮,就调用add方法让counter变量自增1,由于P元素的内容和counter变量绑定,所以会看到不断增加的数字。其实和v-bind一样,一旦对html属性应用指令,其字符串就变成了变量或者函数名称。

还给P元素设置了监听鼠标在其上移动的事件,动态的显示在框里鼠标的坐标。

牛逼之处还在于还可以自定义参数。如果想每次增加2,可以这么写:

<div id="app">
    <button v-on:click="add(2)">Click me</button>
    <p>{{ counter }}</p>
</div>

<script>
    new Vue({
        el:"#app",
        data:{
            counter: 0,
            x:0,
            y:0
        },
        methods: {
            add: function (step) {
                this.counter = this.counter + step;
            }
        }
    })
</script>

在methods中的方法中也增加对应的参数,这样就传入了2当做步长,每一次增加2。如果在使用自定义参数的情况下还想使用event参数,需要在v-on的部分传入固定的一个变量叫做$event,例子如下:

<div id="app">
    <button v-on:click="add(2, $event)">Click me</button>
    <p>{{ counter }}</p>
</div>

<script>
    new Vue({
        el:"#app",
        data:{
            counter: 0,
            x:0,
            y:0
        },
        methods: {
            add: function (step, event) {
                this.counter = this.counter + step;
                console.log(event);
            }
        }
    })
</script>

这样就能正确处理传入的事件了。对于methods中的所有方法,都可以如此来设置自定义参数。

控制事件 – event modifier

像刚才的例子改进一下,用一个大的Div套一个小的Div,希望小Div内部不发生事件。

代码如果用比较笨拙的方法实现,那么可以在小Div上监听同样的事件,然后阻止事件冒泡,这样外层就收不到事件:

<div id="app">
    <div v-on:mousemove="move" class="outer">
        <div v-on:mousemove="dummy" class="inner">
        </div>
    </div>
    <p>{{x }} / {{ y }}</p>
</div>

<script>
    new Vue({
        el:"#app",
        data:{
            x:0,
            y:0
        },
        methods:{
            move: function (event) {
                this.x = event.clientX;
                this.y = event.clientY;
            },
            dummy: function (event) {
                event.stopPropagation();

            }
        }
    })
</script>

可以用更简单的方法做到,给指令加上一个.stop修饰符即可:

<div id="app">
    <div v-on:mousemove="move" class="outer">
        <div v-on:mousemove.stop class="inner">
        </div>
    </div>
    <p>{{x }} / {{ y }}</p>
</div>

<script>
    new Vue({
        el:"#app",
        data:{
            x:0,
            y:0
        },
        methods:{
            move: function (event) {
                this.x = event.clientX;
                this.y = event.clientY;
            }
        }
    })
</script>

这样就会在对应的元素上阻止该事件的发生。

监听特定的键,使用事件修饰符

<div id="app">
    <div v-on:mousemove="move" class="outer">
        <div v-on:mousemove.stop class="inner">
        </div>
    </div>
    <p>{{x }} / {{ y }}</p>
    <input type="text" v-on:keyup.enter.space="alertMe">
</div>

<script>
    new Vue({
        el:"#app",
        data:{
            x:0,
            y:0
        },
        methods:{
            move: function (event) {
                this.x = event.clientX;
                this.y = event.clientY;
            },
            alertMe: function (event) {
                alert("Input end");

            }
        }
    })
</script>

这里监听了keyup事件,然后指定了enter.space表示输入空格,只要监听到了,就触发事件。

使用原生JS代码

在所有被当做表达式解析的地方,可以直接写JS代码,其中的函数和变量名称,就相当于在Vue的实例一样,可以直接使用。

<div id="app">
    <button v-on:click="count++">Click ++</button>
    <button v-on:click="count--">Click --</button>
    <p>{{ count }}</p>
    <p>{{ count * 2 }}</p>
    <p>{{ count < 10 ? "Less than 10": "Greater than 10" }}</p>
</div>

<script>
    new Vue({
        el: "#app",
        data: {
            count: 0
        },
        methods: {
            move: function (event) {
                this.x = event.clientX;
                this.y = event.clientY;
            }
        }
    });
</script>

v-model 双向绑定

第一个例子中的绑定input事件还不是双向绑定,双向绑定使用v-model:

<div id="app">
    <input type="text" v-model="name">
    <p>{{ name }}</p>
</div>

<script>
    new Vue({
        el: "#app",
        data: {
            name: "Saner"
        },
        methods: {
            move: function (event) {
                this.x = event.clientX;
                this.y = event.clientY;
            }
        }
    });
</script>

双向绑定是一个单独指令,将当前的元素的值直接绑定给data中的一个属性,同步变化。如果使用程序修改name的值,input框中的值也会变化。

computed 计算属性

考虑如下的情况:

<div id="app">
    <button v-on:click="count++">Count++</button>
    <button v-on:click="count--">Count--</button>
    <button v-on:click="another++">Another++</button>
    <button v-on:click="another--">Another--</button>
    <p>{{ count }} | {{ another }}</p>
    <p>{{ display() }} | {{computed_display }}</p>
</div>

<script>
    new Vue({
        el: "#app",
        data: {
            count: 0,
            another: 0
        },
        computed: {
          computed_display:function () {
              console.log("Computed value computed");
              return this.count >= 5 ? "Count > 5" : "Count < 5";
          }
        },
        methods: {
            display: function () {
                console.log("display() is executed");
                return this.count >= 5 ? "Count > 5" : "Count < 5";
            }
        }
    });
</script>

computed中的属性可以对应一个函数,但是不用像methods中的方法一样需要加括号调用,而是直接可以使用属性。

这两个有什么不一样的,答案是methods中的结果每次刷新dom或者刷新Vue实例的状态,都会执行,因为Vue不知道函数其中的内容是什么,只好每次求值并渲染。

但computed中的内容,会检查其内部和什么关联,当没有关联的时候,这个computed属性不会重新计算。

所以点击更改count属性的按钮时,两个方法都会执行,而点击更改another属性的按钮时,只会对display()求值并渲染,Vue知道computed中的内容与another没有关系,因此不会执行。

watch 侦听属性

<div id="app">
    <button v-on:click="count++">Count++</button>
    <button v-on:click="count--">Count--</button>
    <button v-on:click="another++">Another++</button>
    <button v-on:click="another--">Another--</button>
    <p>{{ count }} | {{ another }}</p>
    <p>{{ display() }} | {{computed_display }}</p>
</div>

<script>
    new Vue({
        el: "#app",
        data: {
            count: 0,
            another: 0
        },
        computed: {
          computed_display:function () {
              console.log("Computed value computed");
              return this.count >= 5 ? "Count > 5" : "Count < 5";
          }
        },
        watch:{
            count: function () {
                let vm = this;
                setTimeout(function () {
                    vm.count = 0;

                }, 2000);
            }
        },
        methods: {
            display: function () {
                console.log("display() is executed");
                return this.count >= 5 ? "Count > 5" : "Count < 5";
            }
        }
    });
</script>

与computed属性有些类似,watch是一个异步的侦听器。watch部分中的键名是已经存在的data中的属性名,只要这个属性一发生变化,对应的函数就会执行。

简化写法

经常使用的v-bind:可以简写成:;而v-on:可以简写成@

<div id="app">
    <p>{{ count }}</p>
    <input type="text" :value="count">
    <button @click="count++">Click</button>
</div>
<script>
    new Vue({
        el: "#app",
        data:{
            count:0
        }
    })
</script>

今后都采用简化的写法。

更改CSS类

更改CSS类有好几种做法:

<style>
	.demo {
		width: 100px;
		height: 100px;
		margin: 10px;
		background-color: #dddddd;
	}

	.green {
		background-color:green;
	}

	.blue {
		background-color: blue;
	}

	.red {
		background-color: red;
	}
</style>

<div id="app">
    <div class="demo" :class="{red:changeClass,blue:!changeClass}" @click="changeClass = !changeClass"></div>
    <div class="demo" :class="divClass" @click="changeClass = !changeClass"></div>
    <div class="demo" :class="color" @click="changeClass = !changeClass"></div>
    <div class="demo" :class="[color,{green:changeClass}]" @click="changeClass = !changeClass"></div>
    <input type="text" v-model="color">
</div>

<script>
    new Vue({
        el: "#app",
        data:{
            changeClass: false,
            color: "red"
        },
        computed:{
            divClass:function () {
                return {
                    red:this.changeClass,
                    green:!this.changeClass
                }
            }
        }

    })
</script>

这里首先要知道的是可以有一个属性,然后再有一个v-bind的同名属性,对于css类来说,不会覆盖原来的类,只会控制新的类的增加和去除。

在第一个DIV中,绑定了一个对象,要始终记得,花括号内和绑定的属性内,都是相当于在Vue实例内部执行的表达式。第一个div中用red和blue名称对应了两个布尔值,在改变的时候,red或者blue类名就会应用到原来的类上,不会覆盖demo类。

第二个DIV中,是改用计算属性传递了一个对象,本质上和第一种方法类似

第三个DIV是比较直观,直接绑定color属性,输入color的名称就可以增加指定的类名上去。

第四个DIV是还可以传入一个数组,其中既可以有字符串名称,又可以有对象,都会被拆解应用到类名上,这里设置的意思是:默认就是red红色,不管如何点击。如果在input里去掉red字样,那么点击的时候就会在绿色和无色之间切换。

更改CSS样式

更改CSS样式靠的是绑定style属性,然后给这个属性传入对象,对象中的键是css属性名称,而值是css属性的值。

<div id="app">
    <div class="demo" :style="styles"></div>
    <input type="text" v-model="color">
    <input type="text" v-model="width">
</div>

<script>
    new Vue({
        el: "#app",
        data:{
            changeClass: false,
            color: "red",
            width:200
        },
        computed:{
            styles:function () {
                return {
                    backgroundColor:this.color,
                    width:this.width+"px"
                }
            }
        }

    })
</script>

想修改何种属性,只要在对象中设置好键值对,然后传给绑定的style属性即可。在这个程序里,就可以通过输入颜色和宽度来控制Div元素的样式。

与类名一样,也可以通过数组的形式来修改css样式:

<div id="app">
    <div class="demo" :style="styles"></div>
    <div class="demo" :style="[styles, style2, {margin: this.height/5 + 'px'}]"></div>
    <input type="text" v-model="color">
    <input type="text" v-model="width">
    <input type="text" v-model="height">
</div>

<script>
    new Vue({
        el: "#app",
        data:{
            changeClass: false,
            color: "red",
            width:200,
            height:200
        },
        computed:{
            styles:function () {
                return {
                    backgroundColor:this.color,
                    width:this.width+"px"
                }
            },
            style2:function () {
                return {
                    height:this.height+"px"
                }
            }
        }

    })
</script>

第二个Div元素的样式就同时通过两个对象和一个等于高度五分之一的margin来动态控制。

总结

初看此处的部分有些凌乱,后来我总结了一下,由于是操作DOM,不外乎针对每个元素的所有能控制的属性进行控制,有如下这么几方面:

  1. 元素的内容,由花括号的表达式来修改。花括号普通情况下输出转义后的字符串,还可以使用v-html输入不转义的HTML代码,v-once仅渲染一次
  2. 元素的属性,由v-bind:来绑定修改,绑定后的属性值双引号里边的内容不再是字符串,而是当做Vue内部的表达式解析。这里边又分为单一的值属性和类似于CSS类和Style的多个值属性或者需要对象类别的属性,可以传入对象
  3. 事件,由v-on:来绑定修改,还可以使用事件修饰符来进行具体控制
  4. 双向绑定,Vue中的属性变化,对应的值也变化;值变化,Vue实例中的属性也变化,常用于input框,用一个单独的v-model指令即可。
  5. computed计算属性和watch侦听属性,有些类似于事件。computed属性能减少不必要的调用方法,watch是异步的,比较方便。
  6. 更改CSS类和CSS样式,Vue这里特别做了增强,除了绑定单一属性,还可以绑定数组和对象,便于传递多个设置。

通过这些方法,就可以操控Dom上任何一个元素的内容,各种属性,类和样式,以及事件,这就是Vue操作Dom的方式,也是Vue使用的基础。