VUE常用特性 (表单操作 // 自定义指令 // 计算属性 // 过滤器 // 侦听器 // 生命周期)
常用特性概览
// 表单操作 // 自定义指令 // 计算属性 // 过滤器 // 侦听器 // 生命周期
表单操作
基于vue的表单操作
-input -textarea -select -radio -checkbox
表单域修饰符
-.number // 自动将用户的输入值转为数值类型 -.trim. // 自动过滤用户输入的首尾空白字符 -.lazy // 将input事件切换为change事件
如果想自动将用户的输入值转为数值类型,可以给 v-model 添加 number 修饰符:
例如:
// template// 业务逻辑 data: { age: ' ' }, methods: { commit: function () { console.log(this.age + 13); } }
// 显示结果为:
错因:因为我们输入的是字符串类型的数字,需转换为数字。
解决办法:
// 显示结果为:
自定义指令directives
全局
// 自定义指令的语法规则:使用vue.directive();定义全局的指令
// 参数1:指令名称
// 参数2:对象,这个对象身上有一些跟指令相关的钩子函数(关于钩子函数,下面会讲到),这些函数可以在特定的阶段实行相关的操作
// 全局注册一个全局自定义指令 `v-focus` Vue.directive('focus', { // 当被绑定的元素插入到 DOM 中时…… inserted: function (el) { // 聚焦元素 el.focus() } })
局部(私有)
directives: { focus: { // 指令的定义 inserted: function (el) { el.focus() } } }
自定义指令的用法:在模板中任何元素上使用新的 v-focus property,如下:
注意:在定义的时候,指令的名称前面,不需要加 v- 前缀,在调用的时候必须在指令名称前加 v-
钩子函数
一个指令定义对象可以提供如下几个钩子函数 (均为可选):
bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。 inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。 update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。unbind:只调用一次,指令与元素解绑时调用。
简单分析一下钩子函数的执行顺序
// 自定义指令 Vue.directive('focus', { // 每当指令绑定到元素上的时候,会立即执行这个bind函数,并且只执行一次 bind: function(el) { // 注意:在下面每个函数中 ,第一个参数,永远是el,表示被绑定了指令的那个元素,这个el参数是一个原生的js对象 // 在元素刚绑定了指令的时候,还没有插入到DOM中去,这时候调用focus方法没有作用,因为一个元素只有插入DOM之后,才能获取焦点 }, // 表示元素插入到DOM中的时候会执行inserted函数,【触发一次】 inserted: function(el) { el.focus(); }, // 当VNode更新的时候,会执行Updated,可能会触发多次 updated: function(el) {} });
接下来我们来看一下钩子函数的参数 (即 el、binding、vnode 和 oldVnode)。
钩子函数参数
指令钩子函数会被传入以下参数:
el:指令所绑定的元素,可以用来直接操作 DOM。 binding:一个对象,包含以下 property name:指令名,不包括 v- 前缀。 value:指令的绑定值,例如:v-my-directive="1 + 1" 中,绑定值为 2。 expression:字符串形式的指令表达式。例如 v-my-directive="1 + 1" 中,表达式为 "1 + 1"。
我们来看一个例子,分别打印第二个参数binding的属性的值
// template模版 // 业务逻辑 // 自定义全局指令v-color Vue.directive('focus', { bind: function (el, binding) { console.log(binding.name); console.log(binding.value); console.log(binding.expression); }, // 当被绑定的元素插入到 DOM 中时…… inserted: function (el) { // 聚焦元素 el.focus(); }, updated: function () {} });
// focus。 // 指令名称
// value // 传递的值
// 'value' // 传递的表达式
了解钩子函数的参数以后,看下面例子,带参数的自定义指令(改变元素的背景色)
// template模版 // 业务逻辑 Vue.directive('color', { // 全局自定义指令 bind: function (el, binding) { el.style.backgroundColor = binding.value.color; }, }); data: { msg: { color: 'yellowgreen' } },
计算属性computed
为何需要计算属性?
模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护。
// template模版// 业务逻辑 data: { msg: 'hello vue' }{{ msg }}
{{ msg.split('').reverse().join('') }}
在这个地方,模板不再是简单的声明式逻辑。你必须看一段时间才能意识到,这里是想要显示变量 message 的翻转字符串。当你想要在模板中的多处包含此翻转字符串时,就会更加难以处理。
所以,对于任何复杂逻辑,你都应当使用计算属性。
// template模版// 业务逻辑 data: { msg: 'hello vue' }, computed: { reversedMessage: function () { return this.msg.split('').reverse().join(''); } }{{ msg }}
{{ reversedMessage }}
显示结果:
这里我们声明了一个计算属性 reversedMessage。我们提供的函数将用作 property vm.reversedMessage 的 getter 函数:
console.log(vm.reversedMessage) // => 'olleH' vm.message = 'Goodbye' console.log(vm.reversedMessage) // => 'eybdooG'
你可以打开浏览器的控制台,自行修改例子中的 vm。vm.reversedMessage 的值始终取决于 vm.message 的值。
你可以像绑定普通 property 一样在模板中绑定计算属性。Vue 知道 vm.reversedMessage 依赖于 vm.message,因此当 vm.message 发生改变时,所有依赖 vm.reversedMessage 的绑定也会更新。而且最妙的是我们已经以声明的方式创建了这种依赖关系:计算属性的 getter 函数是没有副作用 (side effect) 的,这使它更易于测试和理解。
计算属性缓存 vs 方法
你可能已经注意到我们可以通过在表达式中调用方法来达到同样的效果:
// template 模版// 业务逻辑 data: { msg: 'hello vue' }, methods: { reversedMessage: function () { return this.msg.split('').reverse().join(''); } } // computed: { // reversedMessage: function () { // return this.msg.split('').reverse().join(''); // } // }{{ msg }}
{{ reversedMessage() }}
我们可以将同一函数定义为一个方法而不是一个计算属性。两种方式的最终结果确实是完全相同的。
不同的是计算属性是基于它们的响应式依赖进行缓存的。
举个例子:
// template模版// 业务逻辑 // methods: { // reversedMessage: function () { // console.log('Hi'); // return this.msg.split('').reverse().join(''); // } // } computed: { reversedMessage: function () { console.log('Hi'); return this.msg.split('').reverse().join(''); } }{{ reversedMessage }}
{{ reversedMessage }}
上面代码,同样的逻辑,计算属性控制台只输出1次,而methods方法输出2次。因此说明了只在相关响应式依赖发生改变时它们才会重新求值。这就意味着只要 message 还没有发生改变,多次访问 reversedMessage 计算属性会立即返回之前的计算结果,而不必再次执行函数。提高一定的性能。
侦听属性watch
Vue 提供了一种更通用的方式来观察和响应 Vue 实例上的数据变动:侦听属性。
过滤器
Vue.js 允许你自定义过滤器,可被用于一些常见的文本格式化。过滤器可以用在两个地方:差值表达式和 v-bind 表达式
过滤器应该被添加在 JavaScript 表达式的尾部,由“管道”符号指示:
{{ message | capitalize }}
全局(任何组件都可用)
// 语法 参数1:过滤器名称,参数2:对管道符前面传过来的数据做相应处理,处理函数的第一个参数永远是过滤器管道符前面传递过来的数据
Vue.filter('过滤器的名称', function (data) {});
私有
filters: { dataFormat: function (value) { if (!value) return '' value = value.toString() return value.charAt(0).toUpperCase() + value.slice(1) } }
过滤器可以串联:
{{ message | filterA | filterB }}
在这个例子中,filterA 被定义为接收单个参数的过滤器函数,表达式 message 的值将作为参数传入到函数中。然后继续调用同样被定义为接收单个参数的过滤器函数 filterB,将 filterA 的结果传递到 filterB 中。
过滤器是 JavaScript 函数,因此可以接收参数:
{{ message | filterA('arg1', arg2) }}
这里,filterA 被定义为接收三个参数的过滤器函数。其中 message 的值作为第一个参数,普通字符串 'arg1' 作为第二个参数,表达式 arg2 的值作为第三个参数。
实例的生命周期钩子
概念:从vue实例创建、运行、销毁期间,总是伴随着各种各样的事件,这些事件统称为生命周期!
分类:创建期间的生命周期函数 / 运行期间的生命周期函数 / 销毁期间的生命周期函数
1、创建期间的生命周期函数 beforeCreate(){}
// 实例完全被创建出来之前会执行它 // template模版
{{ msg }}
// 业务逻辑 var vm = new Vue({ el: '#app', data: { msg: '你好,vue!', }, methods: { show() { console.log('执行了show方法'); } }, beforeCreate() { // 注意:在 beforeCreate 生命周期函数执行的时候,data 和 methods 中的数据都还没有被初始化 console.log(this.msg); this.show(); } })
// 在beforeCreate钩子函数中打印结果:
2、创建期间的生命周期函数 created(){}
created() { // 注意: 在 created 中,data 和 methods 都已经被初始化好了,如果要调用methods中的方法,或者操作data中的数据,最早只能在created 中操作 console.log(this.msg); this.show(); }
// 在created钩子函数中打印结果:
3、创建期间的生命周期函数 beforeMount(){}
// 接下来 Vue 开始编辑模版,把Vue代码中的那些指令进行执行,最终在内存中生成一个编译好的最终模版字符串,然后,把这个模版字符串,渲染为内存中的DOM。此时,只是在内存中渲染好了模版,并没有把模版挂载到真正的页面中去。
beforeMount() { console.log(document.getElementById('h3').innerText); // {{ msg }} }
4、创建期间的生命周期函数 mounted(){}
// 注意:mounted 是实例创建期间的最后一个生命周期函数,当执行完 mounted 就表示,实例已经被完全创建好了(初始化完毕了)。此时,组件已经脱离了创建阶段,进入到了运行阶段。mounted() { console.log(document.getElementById('h3').innerText); // 你好,vue! }1、实例运行期间生命周期函数 beforeUpdate(){}
(这两函数会根据data上数据的改变,有选择性的触发0次到多次) 这一步执行的是:先根据data中最新的数据,在内存中重新渲染出一份最新的内存DOM树,当最新的内存DOM树被更新之后,会把最新的内存DOM树,重新渲染到真实的页面中去,这时候就完成了数据从data(Model层)->view(视图层)的更新
// 这一步执行的是:先根据data中最新的数据,在内存中重新渲染出一份最新的内存DOM树,当最新的内存DOM树被更新之后,会把最新的内存DOM树,重新渲染到真实的页面中去,这时候就完成了数据从data(Model层)->view(视图层)的更新 beforeUpdate() { // 这时候,表示我们的界面还没有被更新(数据已经改变) console.log('界面上的内容是:' + document.getElementById('h3').innerText); // ok console.log('data 中的 msg 数据是:' + this.msg); // No } // 得出结论: 当执行 beforeUpdate 的时候,获得页面中显示的数据还是旧的,此时获得data上的数据是最新的,页面尚未和最新的数据保持同步。
2、实例运行期间生命周期函数 updated(){}
updated() {
console.log('界面上的内容是:' + document.getElementById('h3').innerText); // No
console.log('data 中的 msg 数据是:' + this.msg); // No
}
// 得出结论: 当执行 updated 的时候,获得页面中显示的数据是最新的,此时页面已和最新的数据保持同步
destroyed() {
} 3、实例销毁期间生命周期函数 beforeDestroy() {} 、destroyed() {}
// 当执行 beforeDestroy 钩子函数时,Vue实例就已经从运行阶段进入销毁阶段,实例身上的 data 和所有的 methods 以及 过滤器、指令等等,都处于可用状态,此时还没有真正进行销毁。 // 当执行 destroyed 钩子函数时,Vue实例(组件)就已经完全被销毁了,此时组件中所有的数据、方法、指令、过滤器等等都已经不可用。