基本案例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <body> <div id="app"> <h1>hello {{ msg }}</h1> </div> </body>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script type="application/javascript"> vm = new Vue({ el: '#app', data: { msg: "hello vue!" } }) </script>
|
Vue不同于直接使用js,js操作的对象是dom对象,而Vue则操作的是数据对象,因为都是将数据和dom进行绑定的,而我们想要改变值,直接通过改变数据即可。
挂载点,模板,实例之间的关系
id为app的div标签就是vue实例的挂载点,vue只会处理对应挂载点下的内容。在挂载点内部的内容都叫做模板,像上面的ID为app的div内的h1标签就是模板。
1 2 3 4 5 6 7 8 9 10 11 12
| <div id="app"></div> <script type="application/javascript"> vm = new Vue({ el: '#app', template: '<h1>hello {{ msg }}</h1>', data: { msg: "vue!" } }) </script>
|
所以模板可以写在挂载点内部,也可以写在vue实例内,使用template定义
注意使用template时,会将挂载点内的内容进行覆盖
指令
均采用 v-
开头
- v-text=‘data中的数据’:将标签内的显示内容同实例中的data中的值联系
- v-html=‘data中的数据’:作用和v-text一致,但是若data中的内容为html代码,则会渲染到页面,即不会对html代码进行转义
- v-on:绑定事件监听器,简写形式:@
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <div id="app"> <div v-on:click="handleClick">{{content}}</div> <div @click="handleClick">{{content}}</div> </div> <script type="application/javascript"> vm = new Vue({ el: '#app', data: { msg: "vue!", content: 'hello' }, methods: { handleClick: function(){ this.content = this.msg } } }) </script>
|
注意:不要在选项属性或回调上使用箭头函数,比如 created: () => console.log(this.a) 或 vm.$watch(‘a’, newValue => this.myMethod())。因为箭头函数并没有 this,this 会作为变量一直向上级词法作用域查找,直至找到为止,经常导致 Uncaught TypeError: Cannot read property of undefined 或 Uncaught TypeError: this.myMethod is not a function 之类的错误。
属性绑定和双向绑定
使用v-bind将某个标签属性和vue实例的data属性绑定,v-bind可以直接简写成 :
,使用冒号
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <body> <div id="app"> <div v-bind:title=" 'woshi:'+ title">hello</div> <div :title=" 'woshi:'+ title">hello</div> </div> </body>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script type="application/javascript"> vm = new Vue({ el: '#app', data: { title: "this is hello vue" } }) </script>
|
<div v-bind:title=" 'woshi:'+ title">hello</div>
绑定之后,是可以直接写js表达式的。
双向数据绑定:使用v-model模板指令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <body> <div id="app"> <div v-bind:title="'woshi '+ title">hello</div> <input v-model="content"/> <div>{{content}}</div> </div> </body>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script type="application/javascript"> vm = new Vue({ el: '#app', data: { title: "this is hello vue", content: "hello" } }) </script>
|
计算属性和侦听器
计算属性:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| <body> <div id="app"> 姓:<input v-model="firstName"/> 名:<input v-model="lastName"/> <div>{{fullName}}</div> <div>{{count}}</div>
</div> </body>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script type="application/javascript"> vm = new Vue({ el: '#app', data: { firstName: '', lastName:'', count: 0, }, computed: { fullName: function () { return this.firstName + this.lastName } }, watch:{ firstName:function(){ this.count++ }, lastName: function(){ this.count++ } } }) </script>
|
使用computed创建一个计算属性(注意:使用计算属性的时候,函数一定要return返回语句),绑定一个函数,计算属性也是属性,调用方式和插值操作一致。使用计算属性的好处是,计算属性的结果会被缓存,除非依赖的响应式属性变化才会重新计算,所以性能较高。
侦听器:
当侦听的属性发生变化的时候调用,Vue 实例将会在实例化时调用 $watch()
,遍历 watch 对象的每一个属性。上面的侦听器还可以侦听计算属性
1 2 3 4 5
| watch:{ funName:function(){ this.count++ } }
|
v-if、v-for、v-show
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| <body> <div id="app"> <div v-show="show">hello world</div> <div v-if="show">hello world</div> <button @click="handleClick">button</button> </div> </body>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script type="application/javascript"> vm = new Vue({ el: '#app', data:{ show: true, }, methods:{ handleClick: function(){ this.show = !this.show } } }) </script>
|
v-if和v-show的区别:
- 使用v-if时,当判断为false时,是将当前dom移除,而使用v-for则是该当前dom添加
sytle='display:none'
属性来控制显示和隐藏。
- 一般来说,
v-if
有更高的切换开销,而 v-show
有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show
较好;如果在运行时条件很少改变,则使用 v-if
较好。
小结:
v-if控制dom的存在与否,v-show控制dom的显示与否(内部实现是通过添加sytle='display:none'
属性来控制)
v-else
元素必须紧跟在带 v-if
或者 v-else-if
的元素的后面,否则它将不会被识别。
案例
TodoList功能开发
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| <body> <div id="app"> <div> <input v-model="inputValue"/> <button @click="handleSubmit">提交</button> </div> <ul> <li v-for="(item,index) of list" :key="index">{{item}}</li> </ul> </div> </body>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script type="application/javascript"> vm = new Vue({ el: '#app', data:{ inputValue: '', list: [ ] }, methods:{ handleSubmit: function(){ if (this.inputValue !=''){ this.list.push(this.inputValue) this.inputValue = '' } } } }) </script>
|
组件
组件是可复用的 Vue 实例,且带有一个名字
全局组件: 可以在任何挂载点内的任何位置使用
1 2 3 4
| Vue.component('todo-list',{ template: '<li>item</li>' })
|
局部组件:绑定好后可以在当前挂载点内使用
1 2 3 4 5 6 7 8 9 10
| var TodoItem = { template: '<li>item</li>' } vm = new Vue({ el: '#app', components:{ 'todo-list':TodoItem }, }
|
使用全局组件重新实现Todolist案例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| <body> <div id="app"> <div> <input v-model="inputValue"/> <button @click="handleSubmit">提交</button> </div> <ul> <todo-list v-for="(item, index) of list" :key="index" :content="item" // 组件中的props的值在这里进行赋值,此处想通过数据绑定的方式和根组件进行绑定,也可以不是用绑定,此时content的值就是由给的值决定 ></todo-list> </ul> </div> </body>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script type="application/javascript"> Vue.component('todo-list',{ props: ['content'], template: '<li>{{content}}</li>' })
vm = new Vue({ el: '#app', data:{ inputValue: '', list: [ ] }, methods:{ handleSubmit: function(){ if (this.inputValue !=''){ this.list.push(this.inputValue) this.inputValue = '' } } } }) </script>
|
在组件内,想要给template中的代码添加插值,需要在组件的props属性中添加对应的插值变量,在使用组件的时候,对插值变量进行赋值,赋值的方式有两种,第一是可以直接赋值,使用变量=
的方式,如content='hello'
,则所有的插值都是hello,也可以使用对象绑定v-bind:content='vue实例的data中的值'
进行数据绑定。
注意:props接受的类型是插值变量的列表形式,只有在进行组件校验的时候才使用字典类型,如
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| Vue.component('child',{ props:{ content: { type: [String, Number], required: true, default: 'default value', validator: function (value){ return (value.length > 5) } } }, template: '<div>{{ content }}</div>' })
|
其余时刻都是使用的插值列表的形式。
实现Tolist的删除功能
父组件通过绑定属性的方式向子组件传值,而子组件想改变父组件的值的时候可以通过发布事件的方式。
组件模板内使用的值以及方法都是在组件内定义的,模板值在props值中,使用的方法在methods中定义,props只是定义了可以使用的变量,对应的值在使用组件的使用通过数据绑定v-bind进行绑定,在组件中定义的方法是根实例进行监听的,组件自己的监听方法需要放在template中。
若要在组件中使用data,请使用以下的方式
1 2 3 4 5 6 7
| Vue.component('magic-eight-ball', { data: function () { return { possibleAdvice: ['Yes', 'No', 'Maybe'] } } }
|
自定义事件、监听
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| <body> <div id="app"> <div> <input v-model="inputValue"/> <button @click="handleSubmit">提交</button> </div> <ul> <todo-list v-for="(item, index) of list" :key="index" :content="item" :index="index" @delete_list="handleDelete" ></todo-list> </ul> </div> </body>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script type="application/javascript"> Vue.component('todo-list',{ props: ['content', 'index'], template: '<li @click="handleClick">{{content}}</li>', methods:{ handleClick: function (){ this.$emit('delete_list', this.index) } } })
vm = new Vue({ el: '#app', data:{ inputValue: '', list: [ ], }, methods:{ handleSubmit: function(){ if (this.inputValue !=''){ this.list.push(this.inputValue) this.inputValue = '' } }, handleDelete:function (index) { this.list.splice(index,1) }, } }) </script>
|
组件用到的属性以及方法都在组件内定义,在template中使用,组件的dom一定要定义在template属性中。组件若想用到根组件的内容的时候在调用组件中使用,通过数据绑定获取根vue的data值,通过事件监听,调用根vue的方法。
注意: HTML 中的 attribute 名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符。Prop 的大小写 (camelCase vs kebab-case)
组件与实例的关系
每个组件都是一个vue实例,都有vue实例的各种方法。
每个项目包含多个vue实例,但是只有一个根实例,根实例可以没有template属性,没有template属性的时候,会自动找挂载点dom,将挂载点下的dom作为template的内容。若使用了template属性,挂载元素的内容都将被忽略,除非模板的内容有分发插槽。