[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 (如 datacomputed 等) 在 defaultvalidator 函数中是不可用的。

  • type 的值可以是下列原生构造函数中的一个:

    StringNumberBooleanArrayObjectDateFunctionSymbol

    额外的,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" 并把它破坏!庆幸的是,classstyle 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!

注意:

  1. 全局注册时是 filter 没有 s , 而组件过滤器是 filters,是有 s 的,虽然写的时候没有 s 也不报错,但是过滤器是没有效果的。
  2. 当全局过滤器和局部过滤器名字重复的时候,会以就近原则进行调用,即:局部过滤器优先于全局过滤器被调用
  3. 一个表达式可以使用多个过滤器,其执行顺序从左往右,前一个过滤器的结果作为后一个过滤器的被处理数据,所以要注意使用顺序

熟悉 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

自定义组件的 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) // => '已更新'
  }
}
Last Updated: 4/25/2021, 11:07:27 AM