[TOC]
vue教程
1. Prop
整理自:https://cn.vuejs.org/v2/guide/components-props.html
1.1 Prop 的大小写 (camelCase vs kebab-case)
HTML 中的 attribute 名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符。这意味着当你使用 DOM 中的模板时,camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名) 命名:
Vue.component('blog-post', {
// 在 JavaScript 中是 camelCase 的
props: ['postTitle'],
template: '<h3>{{ postTitle }}</h3>'
})
<!-- 在 HTML 中是 kebab-case 的 -->
<blog-post post-title="hello!"></blog-post>
如果你使用字符串模板,那么这个限制就不存在了。
字符串模板:就是用在组件选项里用template: ""
指定的模板,对应地还有一种是在单文件组件里用<template><template/>
指定的模板。
1.2 Prop 类型
如果不需要类型检查,则以字符串数组形式列出prop:
props: ['title', 'likes', 'isPublished', 'commentIds', 'author']
如果需要prop指定类型,可以以对象形式列出 prop,这些 property 的名称和值分别是 prop 各自的名称和类型:String、Number、Boolean、Array、Object、Function、Promise。
props: {
title: String,
contactsPromise: Promise // or any other constructor
}
1.3 Prop 验证
当设置 prop 验证后,如果有一个需求没有被满足,则 Vue 会在浏览器控制台中警告你。
为了定制 prop 的验证方式,你可以为 props
中的值提供一个带有验证需求的对象,而不是一个字符串数组。例如:
props: {
propA: Number, // 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
propB: [String, Number], // 多个可能的类型
propC: { type: String, required: true }, // 必填的字符串
propD: { type: Number, default: 100 }, // 带有默认值的数字
propE: { // 带有默认值的对象
type: Object,
// 对象或数组默认值必须从一个工厂函数获取
default: function () { return { message: 'hello' } }
},
propF: { // 自定义验证函数
validator: function (value) {
// 这个值必须匹配下列字符串中的一个
return ['success', 'warning', 'danger'].indexOf(value) !== -1
}
}
}
注意那些 prop 会在一个组件实例创建之前进行验证,所以实例的 property (如
data
、computed
等) 在default
或validator
函数中是不可用的。
type 的值可以是下列原生构造函数中的一个:
String
,Number
,Boolean
,Array
,Object
,Date
,Function
,Symbol
额外的,
type
还可以是一个自定义的构造函数,并且通过instanceof
来进行检查确认。例如:function Person (firstName) { this.firstName = firstName } Vue.component('blog-post', { // 验证 author prop 的值是否是通过 new Person 创建的 props: { author: Person } })
1.4 Prop的传值:静态或动态
静态赋值:
<blog-post title="My journey with Vue"></blog-post>
prop 可以通过 v-bind
动态赋值:
<!-- 动态赋予一个变量的值 -->
<blog-post v-bind:title="post.title"></blog-post>
<!-- 动态赋予一个复杂表达式的值 -->
<blog-post
v-bind:title="post.title + ' by ' + post.author.name"
></blog-post>
传入JS数据类型时,即使是静态的,我们仍然需要 v-bind
来告诉 Vue,这是一个 JavaScript 表达式而不是一个字符串。
<!-- 传入一个数字。-->
<blog-post v-bind:likes="42"></blog-post>
<!-- 传入一个布尔值: 包含该 prop 没有值的情况在内,都意味着 `true`。-->
<blog-post is-published></blog-post>
<blog-post v-bind:is-published="false"></blog-post>
<!-- 传入一个数组。-->
<blog-post v-bind:comment-ids="[234, 266, 273]"></blog-post>
<!-- 传入一个对象 -->
<blog-post v-bind:author="{ name: 'Veronica', company: 'Veridian Dynamics' }"></blog-post>
传入一个对象的所有 property:
如果你想要将一个对象的所有 property 都作为 prop 传入,你可以使用不带参数的 v-bind
(取代 v-bind:prop-name
)。例如,对于一个给定的对象 post
:
post: { id: 1, title: 'My Journey with Vue' }
<blog-post v-bind="post"></blog-post>
<!-- 上面等价于: -->
<blog-post v-bind:id="post.id" v-bind:title="post.title" ></blog-post>
1.5非 Prop 的 Attribute
一个非 prop 的 attribute 是指传向一个组件,但是该组件并没有相应 prop 定义的 attribute。
显示定义 prop 适用于向一个子组件传入信息,然后并不能确保组件会被用于怎样的场景。因此,组件可以接受任意的Attribute,而这些 属性 会被添加到这个组件的跟元素上。
- 替换/合并已有的 Attribute
假设一个bootstrap-date-input
组件的模板:
<input type="date" class="form-control">
为了给我们的日期选择器插件定制一个主题,我们可能需要像这样添加一个特别的类名:
<bootstrap-date-input data-date-picker="activated" class="date-picker-theme-dark"
/>
在这种情况下,我们定义了两个不同的 class
的值:
form-control
,这是在组件的模板内设置好的
date-picker-theme-dark
,这是从组件的父级传入的
对于绝大多数 attribute 来说,从外部提供给组件的值会替换掉组件内部设置好的值。所以如果传入 type="text"
就会替换掉 type="date"
并把它破坏!庆幸的是,class
和 style
attribute 会稍微智能一些,即两边的值会被合并起来,从而得到最终的值:form-control date-picker-theme-dark
。
- 禁用 Attribute 继承
如果你不希望组件的根元素继承 attribute,你可以在组件的选项中设置 inheritAttrs: false
。例如:
Vue.component('my-component', {
inheritAttrs: false,
})
2. 过滤器
转载自:https://segmentfault.com/a/1190000023149254
在 vue2.0 之前,是有内置过滤器的,在2.0中已经没有内置的过滤器了,但我们可以自定义过滤器。
关于 vue 过滤器,在官方文档中是这样说明的:
Vue.js 允许你自定义过滤器,可被用于一些常见的文本格式化。过滤器可以用在两个地方:双花括号插值和
v-bind
表达式 (后者从 2.1.0+ 开始支持)。过滤器应该被添加在 JavaScript 表达式的尾部,由“管道”符号指示。
即过滤器是用来格式化数据的一个函数。过滤器不会修改原始数据,它的作用是过滤数据,就是对数据进行加工处理并返回处理后的数据,比如做一些数据格式上的修改,状态转换等。
过滤器分为两种
- 组件内的过滤器(组件内有效)
- 全局过滤器(所有组件共享)
定义过滤器
第一个参数是过滤器的名字
第二个参数是过滤器的功能函数 (若不定义vue就不知道这个字符串是什么,有什么作用)。
过滤器的功能函数
- 声明
function(data,argv1,argv2...){}
- 第一个参数是传入的要过滤的数据,即调用时管道符左边的内容。
- 第二个参数开始往后就是调用过滤器的时候传入的参数。
- 声明
过滤器的使用
- 先注册,后使用
- 组件内
filters:{ 过滤器名: fn }
fn 内通过 return 返回最终的数据 - 全局
Vue.filter('过滤器名',fn)
fn 内通过 return 返回最终的数据 - 使用
// 使用过滤器时需要添加管道符号( | )作为分隔,管道符 | 右边是过滤器名称,即文本的功能函数
<!-- 在双花括号中 -->
{{ message | 过滤器名称 }}
<!-- 在 `v-bind` 中 -->
<div v-bind:id="id | 过滤器名称"></div>
自定义全局过滤器
Vue.filter('过滤器名称', function(val) { // val表示要被处理的数据
// 过滤器业务逻辑,要有返回值
})
<div>{{ msg | 过滤器名称 }}</div>
<div v-bind="msg | 过滤器名称"></div>
局部过滤器
data () {
return {
msg: 'hello world'
}
},
//定义私用局部过滤器。只能在当前 vue 对象中使用
filters: {
dataFormat: (msg, a) => { // msg表示要过滤的数据,a表示传入的参数
return msg + a;
}
}
<p>{{ msg | dataFormat('!')}}</p> // 结果: hello world!
注意:
- 全局注册时是 filter 没有 s , 而组件过滤器是 filters,是有 s 的,虽然写的时候没有 s 也不报错,但是过滤器是没有效果的。
- 当全局过滤器和局部过滤器名字重复的时候,会以就近原则进行调用,即:局部过滤器优先于全局过滤器被调用
- 一个表达式可以使用多个过滤器,其执行顺序从左往右,前一个过滤器的结果作为后一个过滤器的被处理数据,所以要注意使用顺序
熟悉 vue 的童鞋会知道,过滤器有时候同methods、computed、watch一样可以达到处理数据的目的,但又不能替代它们,因为它不能改变原始值。如果一个过滤器的内部特别复杂,可以考虑把它写成一个计算属性,因为计算属性本身带有缓存,可复用性强,而过滤器一般用来做一些简单的操作。
在实际开发中,全局的过滤器要比局部过滤器使用的更广泛一些,说白了我们为什么要使用过滤器,其实就跟使用函数是一样,把一些方法封装,供其它组件使用,这样调用起来更方便也更快捷。
大家知道全局过滤器是在 main.js 中定义的,但万一项目过大,有多个过滤器,那 main.js 就一堆代码,为了项目模块化,最好是有专门的目录来统一存放这些过滤器,然后把处理函数给抽离出去,放在一个.js文件中,下边通过实例代码展示。
过滤器可以串联:
{{ message | filterA | filterB }}
在这个例子中,filterA
被定义为接收单个参数的过滤器函数,表达式 message
的值将作为参数传入到函数中。然后继续调用同样被定义为接收单个参数的过滤器函数 filterB
,将 filterA
的结果传递到 filterB
中。
过滤器是 JavaScript 函数,因此可以接收参数:
{{ message | filterA('arg1', arg2) }}
这里,filterA
被定义为接收三个参数的过滤器函数。其中 message
的值作为第一个参数,普通字符串 'arg1'
作为第二个参数,表达式 arg2
的值作为第三个参数。
2.1 全局过滤器使用示例
filter/dict.js 文件
// filter/dict.js 文件
import Dict from '../constants/dictionary'
export const genderMenu = {
func: value => {
const target = Dict.GENDER_MENU.filter(item => {
return item.code = value;
})
return target.length ? target[0].label : value;
}
}
export const commonMenu = {
func: value => {
const target = Dict.COMMON_MENU.filter(item => {
return item.code = value;
})
return target.length ? target[0].label : value;
}
}
filter/index.js 文件
// filter/index.js 文件
import * as filters from './dict' // 导入过滤函数
const Install = Vue => {
// 导入的 filters 是一个对象,使用Object.keys()方法,得到一个由key组成的数组,遍历数据,让key作为全局过滤器的名字,后边的是key对应的处理函数,这样在任何一个组件中都可以使用全局过滤器了
Object.keys(filters).forEach(key => {
Vue.filter(key, filters[key].func)
})
/*
for(const _filter in filters) {
Vue.filter(`${_filter}`, filters[_filter].func)
} */
}
export default Install
main.js 文件
// main.js 文件
import filters from './filter/index'
Vue.use(filters)
在.vue 文件中使用全局过滤器
// .vue 文件中使用全局过滤器
<p>性别:{{ gender | genderMenu }}</p>
其他参考:
https://blog.csdn.net/kekeChris/article/details/104758106
3. 自定义组件的 v-model
版本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)"
>
`
})
复杂的:
import CKEditor from '@ckeditor/ckeditor5-build-decoupled-document';
export default {
model: {
prop: 'fatherData',// 指定v-model属性接受数值
event: 'changeData'// 指定事件回送数据给父组件
},
props: { fatherData: { type: String, default: '' }, },
data() { return { data: '', editor: null, } }
mounted() { this.initCKEditor(); },
methods: {
initCKEditor() {
CKEditor.create(editorDom, config).then(editor => {
this.editor = editor;
editor.model.document.on('change:data', () => {
this.data = editor.getData();
// 把富文本编辑器获取到的内容传给父组件
this.$emit('changeData', this.data);
});
}
}
},
watch: {
// 监听prop传的value,如果父级有变化了,将子组件的value也跟着变,达到父变子变的效果
fatherData(newVal) {
if (this.editor && this.data !== newVal) {
// 设置内容
this.editor.setData(newVal);
}
},
}
}
4. this.$nextTick():获取到异步更新的dom元素中数据
Vue 在更新 DOM 时是异步执行的。只要侦听到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。如果同一个 watcher 被多次触发,只会被推入到队列中一次。然后,在下一个的事件循环“tick”中,Vue 刷新队列并执行实际 (已去重的) 工作。
如果你想基于更新后的 DOM 状态来做点什么,这就可能会有些棘手。为了在数据变化之后等待 Vue 完成更新 DOM,可以在数据变化之后立即使用 Vue.nextTick(callback)
。这样回调函数将在 DOM 更新完成后被调用。例如:
var vm = new Vue({
el: '#example',
data: {
message: '123'
},
template: '<div id="example">{{message}}</div>'
})
vm.message = 'new message' // 更改数据
vm.$el.textContent === 'new message' // false
Vue.nextTick(function () {
vm.$el.textContent === 'new message' // true
})
在组件内使用 vm.$nextTick()
实例方法特别方便,因为它不需要全局 Vue
,并且回调函数中的 this
将自动绑定到当前的 Vue 实例上:
this.$nextTick(function () {
console.log(this.$el.textContent) // => '已更新'
})
因为 $nextTick()
返回一个 Promise
对象,所以你可以使用新的 ES2017 async/await 语法完成相同的事情:
methods: {
updateMessage: async function () {
this.message = '已更新'
console.log(this.$el.textContent) // => '未更新'
await this.$nextTick()
console.log(this.$el.textContent) // => '已更新'
}
}