赞
踩
Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
中文官方文档:
https://vuejs.bootcss.com/guide/
介绍
Vue.js是一个MVVM框架,即数据双向绑定,当数据发生变化时,视图也就发生变化,当视图发生变化时,数据也会跟着同步发生变化,这也是Vue.js的精髓之处。
注意:我们说的数据双向绑定,一定是对于UI控件来说的,非UI控件不会涉及到数据双向绑定,单向数据绑定是使用 状态管理工具的前提。如果我们使用vuex,那么数据流也是单项的,这时就会和双向绑定有冲突
为什么要实现数据的双向绑定
在Vue.js中,如果使用vuex,实际上数据还是单向的,之所以说是数据双向绑定,这是用的UI控件来说,对于我们处理表单,Vue.js的双向数据绑定用起来就特别舒服,即两者并不互斥,在全局性数据流使用单项,方便跟踪,局部性数据流使用双向,简单易操作
表单使用双向绑定
你可以用v-model指令在表单**、、** 元素上创建双向数据绑定,它会根据控件类型自动选取正确的方法来更新元素,尽管有些神奇,但v-model本质上不过是语糖法。它负责监听用户的输入事件以更新数据,并对一些极端场景进行一些特殊处理
注意:v-model会忽略所有表单元素的value、checked、selected特性的初始值而总是将Vue实例的数据作为数据来源,你应该通过JavaScript在组件的data选项中声明初始值!
代码体验:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <p>普通文本</p> <div id="input"> <input type="text" name="name" v-model="message"> 数据:{{message}} </div> <p>文本域</p> <div id="textarea"> <textarea name="" id="" cols="30" rows="10" v-model="TextArea_data"></textarea> 文本域中的数据: {{TextArea_data}} </div> <p>单选框</p> <div id="radio"> <input type="radio" name="sex" value="男" v-model="sexData">男 <input type="radio" name="sex" value="女" v-model="sexData">女 性别:{{sexData}} </div> <p>复选框</p> <div id="select"> <select name="" v-model="selectData"> <option value="" disabled>--请选择---</option> <option value="select1">选择一</option> <option value="select2">选择二</option> <option value="select3">选择三</option> </select> 你选择了:{{selectData}} </div> </body> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <script> var vm1 = new Vue({ el: "#input", data: { message: "data" } }); var vm2 = new Vue({ el: "#textarea", data: { TextArea_data: "ffffdffff" } }); var vm3 = new Vue({ el: "#radio", data: { sexData: "" } }); var vm4 = new Vue({ el: "#select", data: { selectData: "" } }); </script> </html>
注意:如果v-model表达式的初始值未能匹配任何选项,select元素将被渲染为 “未选择中” 状态,在IOS中,这会使用户无法选择第一个选项,因为这样的情况下,IOS不会触发change事件,因此,更推荐像上面这样提供一个值为空的禁用选项。
学习vue我们必须知道它的7个属性,8个 方法,以及7个指令。787原则
<div id="app"> {{msg}} <div>这是模板的第一种使用方法1</div> </div> <template id="bot">这是模板的第三种使用方法,不常用3</template> <script> <div id="bot">模板的第四种创建方法4</div> </script> <script> var vm1 = new Vue({ data: { msg: "data属性" }, methods: { Personal:function () { console.log("methods方法存放方法") } }, template: '<div>模板的第二种使用方法2</div>', //template:"#bot", render: (createElement) => { return createElement("h1",{"class":"类名","style":"color: red"},"这一个render属性创建的虚拟dom") }, }) </script>
methods和computed其中都可以写算法,有什么区别呢?
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="javascript/vue.min.js"></script> </head> <body> <div id="app"> <p>{{message}}</p> //直接在模板中绑定表达式 <p>{{message.split('').reverse().join('')}}</p> //运用计算属性 <p>{{reverseMessage}}</p> //运用methods方式 <p>{{methodMessage()}}</p> </div> <script> var vm=new Vue({ el:"#app", data:{ message:"hello" }, computed:{ reverseMessage:function(){ return this.message.split('').reverse().join(''); } }, methods:{ methodMessage:function () { return this.message.split('').reverse().join(''); } } }) </script> </body> </html>
我在此将三种方式进行了比较。返回的结果是一样的,但在写法上computed计算属性的方式在用属性时不用加(),而methods方式在使用时要像方法一样去用,必须加().
两种方式在缓存上也大有不同,利用computed计算属性是将 reverseMessage与message绑定,只有当message发生变化时才会触发reverseMessage,而methods方式是每次进入页面都要执行该方法,但是在利用实时信息时,比如显示当前进入页面的时间,必须用methods方式
条件:
v-if v-else
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <p>条件判断的结果是:</p> <div id="if_label"> <span v-if="type===A">文本一</span> <span v-else-if="type===B">文本二</span> <span v-else="type===C">文本三</span> </div> <p>循环得到信息:</p> <div id="for_message"> <li v-for="(item,index) in items"> {{item.message}}-----{{index}} </li> </div> </body> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <script> var vm = new Vue({ el: "#if_label", data: { type: "C" } }) var vm = new Vue({ el: "#for_message", data: { items: [ { message: "信息一" }, { message: "信息二" }, { message: "信息三" }, { message: "信息四" } ] } }) </script> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <div id="el_bing"> <p>元素绑定</p> <span v-bind:title="message">鼠标悬停几秒查看此处动态绑定的提示信息</span> </div> </body> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <script> var vm = new Vue({ el: "#el_bing", data: { message: "页面加载于" + new Date().toLocaleString() } }) </script> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <div id="app1"> <button v-on:click="click_bing1">请点击按钮</button> </div> <div id="app2"> <p>{{message}}</p> <button v-on:click="click_bing2">反转消息</button> </div> </body> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <script> var vm = new Vue({ el: "#app1", data: { message: "已点击" }, methods: { click_bing1: function () { alert(this.message); } } }); var vm1 = new Vue({ el: "#app2", data: { message: "已点击" }, methods: { click_bing2: function () { this.message = this.message.split('').reverse().join('') } } }) </script> </html>
Vue组件即为自定义标签,
组件系统是 Vue 的另一个重要概念,因为它是一种抽象,允许我们使用小型、独立和通常可复用的组件构建大型应用。仔细想想,几乎任意类型的应用界面都可以抽象为一个组件树:
在 Vue 里,一个组件本质上是一个拥有预定义选项的一个 Vue 实例。在 Vue 中注册组件很简单:
步骤:
定义组件 Vue.component(‘组件名’, { template: ‘模板’ } )
构建模板 <组件名></组件名>
接收参数 props: [‘todo’]
注:自定义的标签中不能有大写
// 定义名为 todo-item 的新组件
Vue.component('todo-item', {
template: '<li>这是个待办项</li>'
})
var app = new Vue(...)
现在你可以用它构建另一个组件模板:
<ol>
<!-- 创建一个 todo-item 组件的实例 -->
<todo-item></todo-item>
</ol>
但是这样会为每个待办项渲染同样的文本,这看起来并不炫酷。我们应该能从父作用域将数据传到子组件才对。让我们来修改一下组件的定义,使之能够接受一个 prop:
Vue.component('todo-item', {
// todo-item 组件现在接受一个
// "prop",类似于一个自定义 attribute。
// 这个 prop 名为 todo。
props: ['todo'],
template: '<li>{{ todo.text }}</li>'
})
现在,我们可以使用 v-bind
指令将待办项传到循环输出的每个组件中:
<div id="app-7"> <ol> <!-- 现在我们为每个 todo-item 提供 todo 对象 todo 对象是变量,即其内容可以是动态的。 我们也需要为每个组件提供一个“key”,稍后再 作详细解释。 --> <todo-item v-for="item in groceryList" v-bind:todo="item" v-bind:key="item.id" ></todo-item> </ol> </div> Vue.component('todo-item', { props: ['todo'], template: '<li>{{ todo.text }}</li>' }) var app7 = new Vue({ el: '#app-7', data: { groceryList: [ { id: 0, text: '蔬菜' }, { id: 1, text: '奶酪' }, { id: 2, text: '随便其它什么人吃的东西' } ] } })
例2
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <div id="app"> <wangping v-for="hobby in hobbies" v-bind:wang="hobby"></wangping> </div> </body> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <script> //定义组件 Vue.component("wangping", { props: ['wang'], //props用于接收参数 template: '<li>{{wang}}</li>' }) var vm = new Vue({ el: '#app', data: { hobbies: ["Spring", "SpringMVC", "SpringBoot", "Mybatis", "Maven", "Vue", "Redis", "Mysql", "Oracle"] } }) </script> </html>
尽管这只是一个刻意设计的例子,但是我们已经设法将应用分割成了两个更小的单元。子单元通过 prop 接口与父单元进行了良好的解耦。我们现在可以进一步改进 <todo-item>
组件,提供更为复杂的模板和逻辑,而不会影响到父单元。
在一个大型应用中,有必要将整个应用程序划分为组件,以使开发更易管理。在后续教程中我们将详述组件,不过这里有一个 (假想的) 例子,以展示使用了组件的应用模板是什么样的:
<div id="app">
<app-nav></app-nav>
<app-view>
<app-sidebar></app-sidebar>
<app-content></app-content>
</app-view>
</div>
你可能已经注意到 Vue 组件非常类似于自定义元素——它是 Web 组件规范的一部分,这是因为 Vue 的组件语法部分参考了该规范。例如 Vue 组件实现了 Slot API 与 is
attribute。但是,还是有几个关键差别:
虽然 Vue 内部没有使用自定义元素,不过在应用使用自定义元素、或以自定义元素形式发布时,依然有很好的互操作性。Vue CLI 也支持将 Vue 组件构建成为原生的自定义元素。
如果只是单纯地知道vue里面的简单语法,你是可以实现对应的一些项目和对应的想法,但是如果一旦发生问题,我们就需要借助生命周期去寻找问题,甚至说有一些需求的话,你也通过生命周期的情况来定我们该把这个东西写在哪里。所以理解vue的生命周期还是很有必要的。
vue的生命周期就是每个vue实例被创建之前一直到结束经历的一系列的初始化过程。
下面是官网关于vue生命周期的图解,
可以看出在vue整个生命周期中会有很多钩子函数(图中标红的部分),虽然我们用到的就那么几个,但我们都来了解一下。首先先明白一个概念:Vue的实例。它是Vue框架的入口,可以理解为前端的ViewModel,它包含它包含了页面中的业务逻辑处理、数据模型等,它也有自己的一系列的生命周期的函数钩子,辅助我们进行对整个Vue实例生成、编译、挂着、销毁等过程进行js控制。
1、beforeCreated()
在实例初始化之后,数据观测(data observer) 和 event/watcher 事件配置之前被调用。(这里是在index.html里做的测试)
2、created()
实例已经创建完成之后被调用。在这一步,实例已完成以下的配置:数据观测(data observer),属性和方法的运算, watch/event 事件回调。然而,挂载阶段还没开始,$el 属性目前不可见。
3、beforeMount()
在挂载开始之前被调用:相关的 render 函数首次被调用。
4、mounted()(钩子函数)
el 被新创建的 vm. e l 替 换 , 并 挂 载 到 实 例 上 去 之 后 调 用 该 钩 子 。 如 果 r o o t 实 例 挂 载 了 一 个 文 档 内 元 素 , 当 m o u n t e d 被 调 用 时 v m . el 替换,并挂载到实例上去之后调用该钩子。如果 root 实例挂载了一个文档内元素,当 mounted 被调用时 vm. el替换,并挂载到实例上去之后调用该钩子。如果root实例挂载了一个文档内元素,当mounted被调用时vm.el 也在文档内。
5、beforeUpdate()
数据更新时调用,发生在虚拟 DOM 重新渲染和打补丁之前。 你可以在这个钩子中进一步地更改状态,这不会触发附加的重渲染过程。
6、updated()
由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。
当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。然而在大多数情况下,你应该避免在此期间更改状态,因为这可能会导致更新无限循环。
该钩子在服务器端渲染期间不被调用。
7、beforeDestroy()
实例销毁之前调用。在这一步,实例仍然完全可用。
8、destroyed()
Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。 该钩子在服务器端渲染期间不被调用。
由于Vue.js是一个视图层框架,并且作者严格遵守SoC(关注度分离原则),所以Vue.js并不包括AJAX的通信功能,为了解决通信问题而推出Axios框架(可以去该官网学习),少用jQuery,因为它操作Dome太频繁了
引入JS文件:
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <div id="vue"> <div>{{info.datail.name}}</div> </div> </body> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <script src="https://unpkg.com/axios/dist/axios.min.js"></script> <script> var vm = new Vue({ el: '#vue', data() { //区分 data方法和data:属性 return { info: { //摆格式 name: null } } }, mounted() { //钩子函数 axios.get('data.json').then(response => (this.info = response.data)) //axios.get('data.json').then(response => (console.log(response.data))) //axios完成通信 } }); </script> </html>
是一个将计算结果缓存起来的属性(将行为转化成了静态的属性),可以想象成为缓存
计算属性的特点就是为了将不经常计算的结果进行缓存,以节约我们的系统开销
有时候我们可能需要在{{}}
里添加一些需要计算再展示出来数据
例如:在页面中展示学生的成绩总分和平均分:
<div id="app"> <table border="1"> <thead> <th>学科</th> <th>分数</th> </thead> <tbody> <tr> <td>数学</td> <td><input type="text" v-model="Math"></td> </tr> <tr> <td>英语</td> <td><input type="text" v-model="English"></td> </tr> <tr> <td>化学</td> <td><input type="text" v-model="chemistry"></td> </tr> <tr> <td>总分</td> <td>{{Math + English + chemistry}}</td> </tr> <tr> <td>平均分</td> <td>{{(Math + English + chemistry)/3}}</td> </tr> </tbody> </table> </div>
new Vue({
el:'#app',
data:{
Math:88,
English:77,
chemistry:99,
}
})
以上是通过在{{}}
里运算,得出总分和平均分
虽然也能解决问题,但是特别不清晰,特别是当运算比较复杂的时候
那怎么办呢?
了解一点的,应该会想到methods
,
没错,确实methods
也可以!但事实上,vue
给我们提供了一个更好的解决方案叫计算属性
计算属性是vue
实例中的一个配置选项:computed
通常里面都是一个个计算相关的函数,函数里头可以写大量的逻辑,最后返回计算出来的值
即我们可以把这些计算的过程写到一个计算属性中去,然后让它动态的计算。
<div class="app"> <table border="1"> <thead> <th>学科</th> <th>成绩</th> </thead> <tbody> <tr> <td>数学</td> <td><input type="text" v-model.number="Math"></td> </tr> <tr> <td>英语</td> <td><input type="text" v-model.number="English"></td> </tr> <tr> <td>化学</td> <td><input type="text" v-model.number="chemistry"></td> </tr> <tr> <td>总分</td> <td>{{sum}}</td> </tr> <tr> <td>平均分</td> <td>{{average}}</td> </tr> </tbody> </table> </div>
var vm = new Vue({ el:'.app', data:{ Math:88, English: 77, chemistry:99, }, computed:{ sum:function(){ return this.Math+ this.English+this.chemistry; }, average:function(){ return Math.round(this.sum/3); } } });
计算属性一般就是用来通过其他的数据算出一个新数据,而且它有一个好处就是,它把新的数据缓存下来了,当其他的依赖数据没有发生改变,它调用的是缓存的数据,这就极大的提高了我们程序的性能。而如果写在methods
里,数据根本没有缓存的概念,所以每次都会重新计算。这也是为什么这里我们没用methods
的原因
以上就是Vue
的计算属性的基本用法!
v-
前缀作为一种视觉提示,用来识别模板中 Vue 特定的 attribute。当你在使用 Vue.js 为现有标签添加动态行为 (dynamic behavior) 时,v-
前缀很有帮助,然而,对于一些频繁用到的指令来说,就会感到使用繁琐。同时,在构建由 Vue 管理所有模板的单页面应用程序 (SPA - single page application) 时,v-
前缀也变得没那么重要了。因此,Vue 为 v-bind
和 v-on
这两个最常用的指令,提供了特定简写:
<!-- 完整语法 -->
<a v-bind:href="url">...</a>
<!-- 缩写 -->
<a :href="url">...</a>
<!-- 动态参数的缩写 (2.6.0+) -->
<a :[key]="url"> ... </a>
<!-- 完整语法 -->
<a v-on:click="doSomething">...</a>
<!-- 缩写 -->
<a @click="doSomething">...</a>
<!-- 动态参数的缩写 (2.6.0+) -->
<a @[event]="doSomething"> ... </a>
它们看起来可能与普通的 HTML 略有不同,但 :
与 @
对于 attribute 名来说都是合法字符,在所有支持 Vue 的浏览器都能被正确地解析。而且,它们不会出现在最终渲染的标记中。缩写语法是完全可选的,但随着你更深入地了解它们的作用,你会庆幸拥有它们。
在vue中,引入的子组件标签中间是不允许写内容的。为了解决这个问题,官方引入了插槽(slot)的概念。
插槽,其实就相当于占位符。它在组件中给你的HTML模板占了一个位置,让你来传入一些东西。插槽又分为匿名插槽、具名插槽以及作用域插槽。
你可能不太明白,为什么我要给子组件中传入HTML,而不直接写在子组件中呢?答案是这样的。你可以想象一个场景,你有五个页面,这五个页面中只有一个区域的内容不一样,你会怎么去写这五个页面呢?复制粘贴是一种办法,但在vue中,插槽(slot)是更好的做法。
初体验
<html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <div id="app"> <todo> <todo-title slot="todo-title" :title="title"></todo-title> <todo-items slot="todo-items" v-for="item in todoItems" :item="item"></todo-items> </todo> </div> </body> <script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.js"></script> <script> //定义组件在前,new Vue在后,否则报错 //插槽 Vue.component("todo", { template: '<div>\ <slot name="todo-title"></slot>\ <ul>\ <slot name="todo-items"></slot>\ </ul>\ </div>' }); //填插槽 Vue.component("todo-title", { props: ['title'], template: '<div>{{title}}</div>' }); Vue.component("todo-items", { props: ['item'], template: '<li>{{item}}</li>' }); var vm = new Vue({ el: "#app", data: { title: '书籍列表', todoItems: ['java', 'javascript', 'vue', 'Spring', 'SpringMVC', 'SpringBoot'] } }); </script> </html>
匿名插槽,我们又可以叫它单个插槽或者默认插槽。与具名插槽相对,它不需要设置name属性。(它隐藏的name属性为default。)
上面已经说过,插槽有一个name属性。与匿名插槽相对,加了name属性的匿名插槽就是具名插槽。
注意 v-slot 只能添加在template标签上 (只有一种例外情况)。
例外情况(被废弃的slot=‘name’)
带slot属性的具名插槽自 2.6.0 起被废弃,vue3.x被完全废弃。只有vue3之前的cli可以使用
具名插槽的小知识点
跟 v-on 和 v-bind 一样,v-slot 也有缩写,即把参数之前的所有内容 (v-slot:) 替换为字符 #。例如 v-slot:header 可以被重写为 #header。
作用域插槽
作用域插槽其实就是可以传递数据的插槽。子组件中的一些数据想在父组件中使用,必须通过规定的方法来传递。在官方文档中提出了一条规则,**父级模板里的所有内容都是在父级作用域中编译的。子模板里的所有内容都是在子作用域中编译的。**如果你在父组件直接使用子组件中的值,是会报错的。
匿名插槽的作用域插槽
为了让 子组件中的数据 在父级的插槽内容中可用,我们可以将 数据 作为 元素的一个特性绑定上去:
不同于组件和 prop,事件名不存在任何自动化的大小写转换。而是触发的事件名需要完全匹配监听这个事件所用的名称。举个例子,如果触发一个 camelCase 名字的事件:
this.$emit('myEvent')
则监听这个名字的 kebab-case 版本是不会有任何效果的:
<!-- 没有效果 -->
<my-component v-on:my-event="doSomething"></my-component>
不同于组件和 prop,事件名不会被用作一个 JavaScript 变量名或 property 名,所以就没有理由使用 camelCase 或 PascalCase 了。并且 v-on事件监听器在 DOM 模板中会被自动转换为全小写 (因为 HTML 是大小写不敏感的),所以 v-on:myEvent将会变成 v-on:myevent——导致 myEvent 不可能被监听到。
因此,推荐你始终使用 kebab-case 的事件名。
2.2.0+ 新增
一个组件上的 v-model 默认会利用名为 value
的 prop 和名为 input
的事件,但是像单选框、复选框等类型的输入控件可能会将 value
attribute 用于不同的目的。model
选项可以用来避免这样的冲突:
Vue.component('base-checkbox', { model: { prop: 'checked', event: 'change' }, props: { checked: Boolean }, template: ` <input type="checkbox" v-bind:checked="checked" v-on:change="$emit('change', $event.target.checked)" > ` })
现在在这个组件上使用 v-model
的时候:
<base-checkbox v-model="lovingVue"></base-checkbox>
这里的 lovingVue
的值将会传入这个名为 checked
的 prop。同时当 <base-checkbox>
触发一个 change
事件并附带一个新的值的时候,这个 lovingVue
的 property 将会被更新。
注意你仍然需要在组件的 props
选项里声明 checked
这个 prop。
你可能有很多次想要在一个组件的根元素上直接监听一个原生事件。这时,你可以使用 v-on
的 .native
修饰符:
<base-input v-on:focus.native="onFocus"></base-input>
在有的时候这是很有用的,不过在你尝试监听一个类似 <input>
的非常特定的元素时,这并不是个好主意。比如上述 <base-input>
组件可能做了如下重构,所以根元素实际上是一个 <label>
元素:
<label>
{{ label }}
<input
v-bind="$attrs"
v-bind:value="value"
v-on:input="$emit('input', $event.target.value)"
>
</label>
这时,父级的 .native
监听器将静默失败。它不会产生任何报错,但是 onFocus
处理函数不会如你预期地被调用。
为了解决这个问题,Vue 提供了一个 $listeners
property,它是一个对象,里面包含了作用在这个组件上的所有监听器。例如:
{
focus: function (event) { /* ... */ }
input: function (value) { /* ... */ },
}
有了这个 $listeners
property,你就可以配合 v-on="$listeners"
将所有的事件监听器指向这个组件的某个特定的子元素。对于类似 <input>
的你希望它也可以配合 v-model
工作的组件来说,为这些监听器创建一个类似下述 inputListeners
的计算属性通常是非常有用的:
Vue.component('base-input', { inheritAttrs: false, props: ['label', 'value'], computed: { inputListeners: function () { var vm = this // `Object.assign` 将所有的对象合并为一个新对象 return Object.assign({}, // 我们从父级添加所有的监听器 this.$listeners, // 然后我们添加自定义监听器, // 或覆写一些监听器的行为 { // 这里确保组件配合 `v-model` 的工作 input: function (event) { vm.$emit('input', event.target.value) } } ) } }, template: ` <label> {{ label }} <input v-bind="$attrs" v-bind:value="value" v-on="inputListeners" > </label> ` })
现在 <base-input>
组件是一个完全透明的包裹器了,也就是说它可以完全像一个普通的 <input>
元素一样使用了:所有跟它相同的 attribute 和监听器都可以工作,不必再使用 .native
监听器。
2.3.0+ 新增
在有些情况下,我们可能需要对一个 prop 进行“双向绑定”。不幸的是,真正的双向绑定会带来维护上的问题,因为子组件可以变更父组件,且在父组件和子组件都没有明显的变更来源。
这也是为什么我们推荐以 update:myPropName
的模式触发事件取而代之。举个例子,在一个包含 title
prop 的假设的组件中,我们可以用以下方法表达对其赋新值的意图:
this.$emit('update:title', newTitle)
然后父组件可以监听那个事件并根据需要更新一个本地的数据 property。例如:
<text-document
v-bind:title="doc.title"
v-on:update:title="doc.title = $event"
></text-document>
为了方便起见,我们为这种模式提供一个缩写,即 .sync
修饰符:
<text-document v-bind:title.sync="doc.title"></text-document>
注意带有 .sync
修饰符的 v-bind
不能和表达式一起使用 (例如 v-bind:title.sync=”doc.title + ‘!’”
是无效的)。取而代之的是,你只能提供你想要绑定的 property 名,类似 v-model
。
当我们用一个对象同时设置多个 prop 的时候,也可以将这个 .sync
修饰符和 v-bind
配合使用:
<text-document v-bind.sync="doc"></text-document>
这样会把 doc
对象中的每一个 property (如 title
) 都作为一个独立的 prop 传进去,然后各自添加用于更新的 v-on
监听器。
将 v-bind.sync
用在一个字面量的对象上,例如 v-bind.sync=”{ title: doc.title }”
,是无法正常工作的,因为在解析一个像这样的复杂表达式的时候,有很多边缘情况需要考虑。
splice()方法是修改Array的“万能方法”,它可以从指定的索引开始删除若干元素,然后再从该位置添加若干元素:
var arr = ['Nicrosoft " , ‘Apple ', 'vahoo ' , 'AoL ', 'Excite ' , 'oracle' ];
//[从索引2开始翻除3个元素,然后再添加两个元素:
arr.splice(2,3,'6oogle', 'Facebook');//返回剧除的元素[ 'vahoo', 'AOL ', 'Excite']
arr; // [ "Nicrosoft ', "Apple " , 'Google ',, "Facebook ' , "oracle']
//只删除,不添加:
arr.splice(2,2); //[ "Google ' , 'Facebook " ]
arr; //[" wicrosoft ' ," apple " , 'oracle']
//只添加i,不删除:
arr.splice(2,0, '6oogle ','Facebook ' );//返回[],因为没有删除任何元素
arr; // [ 'wicrosoft ', "Apple' , 'Google', 'Facebook ', 'oracle']
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。