③ vue 组件

vue组件

1 组件的定义及复用性 局部组件和全局组件

// 局部组件的定义:要注册之后才能使用,建议大写字母单词,驼峰命名
const Counter2 = {
  data() {
    return {
      count: 1
    }
  },
  template: `<div @click="count+=2">{{count}}</div>`
}
const app = Vue.createApp({
  components: { Counter2 },
  // 组件具有复用性
  template: `<div>
    <counter />
    <counter />
    <counter2 />
  </div>`
})
// 全局组件的定义:建议小写字母单词,中间横杆连接
app.component('counter', {
  data() {
    return {
      count: 1
    }
  },
  template: `<div @click="count++">{{count}}</div>`
})

const vm = app.mount('#root')

2 组件间传值及传值校验

2.1 组件间传值

  • 静态传值

  • 动态传值

const app = Vue.createApp({
  data() {
    return { num: 123 }
  },
  template: `<div> <test :content="num" /> </div>`
})
app.component('test', {
  props: [ 'content' ],
  template: `<div>{{content}}</div>`
})
const vm = app.mount('#root')

2.2 传值校验

类型 type

  • String

  • Boolean

  • Array

  • Object

  • Function

  • Symbol

深度校验 validator

const app = Vue.createApp({
  data() {
    return { num: 123 }
  },
  template: `<div> <test :content="num" /> </div>`
})
app.component('test', {
  props: {
    content: {
      type: Number,
      default: () => {
        return 789
      },
      validator: value => {
        return value > 1000
      },
      required: true
    }
  },
  template: `<div>{{content}}</div>`
})
const vm = app.mount('#root')

3 单向数据流的理解

3.1 多个字段传入子组件

  • v-bind="params"
const app = Vue.createApp({
  data() {
    return {
      params: {
        content: 1234,
        a: 123,
        b: 456,
        c: 789
      }
    }
  },
  /** <test v-bind="params" /> 相当于
   *  <test :content="params.content" :a="params.a" :b="params.b" :c="params.c" />
   */
  template: `<div>
    <test v-bind="params" />
  </div>`
})
app.component('test', {
  props: [ 'content', 'a', 'b', 'c' ],
  template: `<div>{{content}}-{{a}}-{{b}}-{{c}}</div>`
})
const vm = app.mount('#root')

3.2 大小写传参

  • 标签上用 :xxx-xxx 传递

  • props上用 xxxXxx 接收,插值表达式使用 xxxXxx

3.3 单向数据流

  • 子组件可以使用父组件传递过来的数据,但是不能修改传递过来的数据
const app = Vue.createApp({
  data() {
    return { num: 1 }
  },
  template: `<div>
      <counter :count="num" />
    </div>`
})
app.component('counter', {
  props: [ 'count' ],
  data() {
    return {
      myCount: this.count
    }
  },
  template: `<div @click="myCount+=1">{{myCount}}</div>`
})
const vm = app.mount('#root')

为什么要有“单向数据流”的限制

  • 避免组件之间数据耦合,子组件A修改了父组件的数据x,会导致B组件接收到的数据x也被修改

4 Non-Props 属性是什么

4.1 Non-Props 属性

  • 父组件向子组件传值,子组件不接收的情况下,底层会将父组件传递的内容添加到子组件的最外层 DOM 上作为其属性

4.2 Non-Props 属性应用场景

  • 给子组件添加类名

  • 修改子组件样子

4.3 子组件拒绝 Non-Props 属性

  • inheritAttrs: false
const app = Vue.createApp({
    template: `<div>
        <counter msg="hello" />
      </div>`
  })
  app.component('counter', {
    inheritAttrs: false,
    template: `<div>counter</div>`
  })
  const vm = app.mount('#root')

4.4 子组件获取 Non-Props 属性

4.4.1 标签上

  • 子组件多个根节点时,使用 v-bind="$attrs" 将父组件传递的 Non-Props 属性放置到指定元素上
const app = Vue.createApp({
    template: `<div>
        <counter msg="hello" msg1="hello1"/>
      </div>`
  })
  app.component('counter', {
    inheritAttrs: false,
    template: `
      <div>counter</div>
      <div v-bind="$attrs">counter</div>
      <div :msg="$attrs.msg">counter</div>
    `
  })
  const vm = app.mount('#root')

4.4.2 js上

  • this.$attrs.msg

5 父子组件间如何通过事件进行通信

5.1 props + this.$emit()

  • 父组件通过 v-bind 传递参数,子组件通过 props 接收父组件传递的参数

  • 父组件调用子组件时使用 v-on 绑定事件,子组件通过 this.$emit() 触发父组件事件

const app = Vue.createApp({
  data() {
    return { count: 1}
  },
  methods: {
    handleAdd(param) {
      this.count += param
    }
  },
  template: `<div> <counter :count="count" @add="handleAdd" /> </div>`
})
app.component('counter', {
  props: ['count'],
  methods: {
    handleClick() {
      this.$emit('add', 2)
    }
  },
  template: `
    <div @click="handleClick">{{count}}</div>
  `
})
const vm = app.mount('#root')

6 组件间双向绑定高级内容

6.1 v-model + props: ['modelValue']

  1. 父组件调用子组件时使用 v-model 绑定参数

  2. 子组件通过 props: ['modelValue'] 接收参数

  3. 子组件通过 this.$emit('update:modelValue', param) 修改 modelValue

const app = Vue.createApp({
 data() {
   return { count: 1}
 },
 template: `<div>
    <counter v-model="count" />
  </div>`
})
app.component('counter', {
 props: ['modelValue'],
 methods: {
   handleClick() {
     this.$emit('update:modelValue', this.modelValue + 3)
   }
 },
 template: `<div @click="handleClick">{{modelValue}}</div>`
})
const vm = app.mount('#root')

6.2 v-model:xxx + props: ['xxx']

  1. 父组件调用子组件时使用 v-model:xxx 绑定参数

  2. 子组件通过 props: ['xxx'] 接收参数

  3. 子组件通过 this.$emit('update:xxx', param) 修改 xxx

const app = Vue.createApp({
 data() {
   return { count: 1}
 },
 template: `<div>
    <counter v-model:app="count" />
  </div>`
})
app.component('counter', {
 props: ['app'],
 methods: {
   handleClick() {
     this.$emit('update:app', this.app + 3)
   }
 },
 template: `<div @click="handleClick">{{app}}</div>`
})
const vm = app.mount('#root')

6.3 使用 props:['modelModifiers'] 接收 v-model的修饰符

app.component('counter', {
  props: {
    modelValue: String,
    modelModifiers: {
      default: () => ({})
    },
  },
  methods: {
    handleClick() {
      let newVal = this.modelValue + 'b'
      if(this.modelModifiers.uppercase) {
        newVal = newVal.toUpperCase()
      }
      this.$emit('update:modelValue', newVal )
    } ,
  },
  template: `
    <div @click="handleClick">{{modelValue}}</div>
  `
})
const vm = app.mount('#root')

7 使用插槽和具名插槽解决组件内容传递问题

7.1 slot 中使用的数据作用域的问题

  • 父模板里调用的数据属性,使用的都是父模板里的数据

  • 子模板里调用的数据属性,使用的都是子模板里的数据

const app = Vue.createApp({
  data() {
    return {
      text: '提交'
    }
  },
  template: `<div>
      <myform>
        <div>{{text}}</div>  
      </myform>
      <myform>
        <button>{{text}}</button>
      </myform>
    </div>`
})
app.component('myform', {
  template: `
    <div>
      <input />
      <span><slot /></span>
    </div>
  `
})
const vm = app.mount('#root')

7.2 slot 默认值

app.component('myform', {
  template: `
    <div>
      <input />
      <span><slot>default value</slot></span>
    </div>
  `
})

7.3 具名插槽

const app = Vue.createApp({
  template: `<layout>
    <template v-slot:header>
      <div>header</div>
    </template>
    <template v-slot:footer>
      <div>footer</div>
    </template>
  </layout>`
})
app.component('layout', {
  template: `
    <div>
      <slot name="header" />
      <div>contain</div>
      <slot name="footer" />
    </div>
  `
})
const vm = app.mount('#root')

7.4 插槽简写

const app = Vue.createApp({
  template: `<layout>
    <template #header>
      <div>header</div>
    </template>
    <template #footer>
      <div>footer</div>
    </template>
  </layout>`
})

8 作用域插槽

const app = Vue.createApp({
  // template: `<list v-slot="slotProps">
  //   <div>{{slotProps.item}}</div>
  // </list>`
  template: `<list v-slot="{item}">
    <div>{{item}}</div>
  </list>`
})
app.component('list', {
  data() {
    return {
      list: [1, 2, 3]
    }
  },
  template: `
    <div>
      <slot v-for="item in list" :item="item" />
    </div>
  `
})
const vm = app.mount('#root')

9 动态组件和异步组件

9.1 动态组件

  • 根据数据变化,结合 component 标签 + :is 属性来随时动态切换组件的实现

<keep-alive> 包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们,主要用于保留组件状态或者避免重新渲染

const app = Vue.createApp({
  data() {
    return {
      currentItem: 'input-item'
    }
  },
  methods: {
    handleClick() {
      this.currentItem = this.currentItem === 'input-item' ? 'common-item' : 'input-item'
    }
  },
  template: `
  <keep-alive>
    <component :is="currentItem" />
  </keep-alive>
  <button @click="handleClick">切换</button>
  `
})
app.component('input-item', {
  template: `<input />`
})
app.component('common-item', {
  template: `<div>hello world</div>`
})
const vm = app.mount('#root')

9.2 异步组件

  • 异步执行某些组件的逻辑

Vue.defineAsyncComponent() + return Promise

const app = Vue.createApp({
  template: `
  <div>
    <common-item />
    <async-common-item />
  </div>
  `
})

app.component('common-item', {
  template: `<div>hello world</div>`
})
app.component('async-common-item', Vue.defineAsyncComponent(() => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve({
        template: `<div> this is an async component</div>`
      })
    }, 2000)
  })
}))
const vm = app.mount('#root')

10 基础语法知识点查缺补漏

10.1 v-once

  • 元素标签只被渲染一次,即使相关的响应式属性发生变化
const app = Vue.createApp({
  data() {
    return { count: 1 }
  },
  template: `
  <div @click="count+=1" v-once>
    {{count}}
  </div>
  `
})
const vm = app.mount('#root')

10.2 ref

1. 获取 dom 节点用的语法

const app = Vue.createApp({
  data() {
    return { count: 1 }
  },
  mounted() {
    console.log(this.$refs.count.innerHTML="hello kitty");
  },
  template: `
  <div>
    <div ref="count">{{count}}</div>
  </div>
  `
})
const vm = app.mount('#root')

2. 获取 组件引用 的语法

const app = Vue.createApp({
  data() {
    return { count: 1 }
  },
  mounted() {
    console.log(this.$refs.common.sayHello());
  },
  template: `
  <div>
    <common-item ref="common">
  </div>
  `
})
app.component('common-item', {
  methods: {
    sayHello() {
      alert('hello')
    }
  },
  template: `<div>hello world</div>`
})
const vm = app.mount('#root')

10.3 provide + inject

  • 跨组件传值 -- 一次性传递(非响应式)
const app = Vue.createApp({
  data() {
    return { count: 1 }
  },
  // provide: {
  //   count: 1
  // },
  provide() {
    return {
      count: this.count
    }
  },
  template: `
    <div>
      <child :count="count" />
    </div>
  `
})
app.component('child', {
  template: `<child-child />`
})
app.component('child-child', {
  inject: ['count'],
  template: `<div>{{ count }}</div>`
})
const vm = app.mount('#root')
posted on 2022-03-16 11:31  pleaseAnswer  阅读(29)  评论(0编辑  收藏  举报