vue入门到精通(二)

news2024/11/18 12:26:22

6.组件间通信

组件有 分治 的特点,每个组件之间具有一定的独立性,但是在实际工作中使用组件的时候有互相之间传递数据的需求,此时就得考虑如何进行 组件间传值 的问题了。

image-20221215103812564

完整案例:05_component/28_parent_child_component.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>父子组件</title>
</head>
<body>
  <div id="app">
    <my-parent></my-parent>
  </div>
</body>
<template id="parent">
  <div>
    <h1>父组件</h1>
    <my-child></my-child>
  </div>
</template>
<template id="child">
  <div>
    <h3>子组件</h3>
  </div>
</template>
<script src="../lib/vue.global.js"></script>
<script>
  const Child = {
    template: '#child'
  }

  const Parent = {
    template: '#parent',
    components: {
      'my-child': Child
    }
  }

  Vue.createApp({
    components: {
      'my-parent': Parent
    }
  }).mount('#app')
</script>
</html>

6.1 Prop

学习:状态选项props 以及 实例属性 $attrs

一个组件需要显式声明它所接受的 props,这样 Vue 才能知道外部传入的哪些是 props,哪些是透传 attribute

props 需要使用 props 选项来定义:

{
  props: ['foo'],
  created() {
    // props 会暴露到 `this` 上
    console.log(this.foo)
  }
}

除了使用字符串数组来声明 prop 外,还可以使用对象的形式:

{
  props: {
    title: String,
    likes: Number
  }
}

对于以对象形式声明中的每个属性,key 是 prop 的名称,而值则是该 prop 预期类型的构造函数。比如,如果要求一个 prop 的值是 number 类型,则可使用 Number 构造函数作为其声明的值。

如果一个 prop 的名字很长,应使用 camelCase 形式,因为它们是合法的 JavaScript 标识符,可以直接在模板的表达式中使用,也可以避免在作为属性 key 名时必须加上引号。

虽然理论上你也可以在向子组件传递 props 时使用 camelCase 形式 (使用 DOM 模板时例外),但实际上为了和 HTML attribute 对齐,我们通常会将其写为 kebab-case 形式

<my-com :likeNum="100"></my-com> ===> <my-com :like-num="100"></my-com>

对于组件名我们推荐使用 PascalCase,因为这提高了模板的可读性,能帮助我们区分 Vue 组件和原生 HTML 元素。然而对于传递 props 来说,使用 camelCase 并没有太多优势,因此我们推荐更贴近 HTML 的书写风格-短横线。

所有的 props 都遵循着单向绑定原则(单项数据流),props 因父组件的更新而变化,自然地将新的状态向下流往子组件,而不会逆向传递。这避免了子组件意外修改父组件的状态的情况,不然应用的数据流将很容易变得混乱而难以理解。

另外,每次父组件更新后,所有的子组件中的 props 都会被更新到最新值,这意味着你不应该在子组件中去更改一个 prop。

6.1.1 父组件给子组件传值1

完整案例:05_component/29_parent_child_component_value1.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>父组件给子组件传值1</title>
</head>
<body>
  <div id="app">
    <my-parent></my-parent>
  </div>
</body>
<template id="parent">
  <div>
    <h1>父组件</h1>
    <!-- 
      父组件在调用子组件的地方,添加自定义的属性,如果属性的值是变量,boolean类型,number类型,
      对象,数组,null,undefined,正则,需要使用绑定属性
     -->
    <my-child :msg="msg" :flag="true" :num="1000" :obj="{a:1}" :arr="[1, 2, 3]"></my-child>
  </div>
</template>
<template id="child">
  <div>
    <h3>子组件</h3>
    <div>{{ msg }}</div>
    <div>{{ flag }}</div>
    <div>{{ num }}</div>
    <div>{{ obj }}</div>
    <div>{{ arr }}</div>
  </div>
</template>
<script src="../lib/vue.global.js"></script>
<script>
  // 在定义子组件的地方,添加一个 props 选项
  // 方式1
  //     props 书写为一个数组,数组的元素名就是之前定义好的自定义属性名,子组件的模版按照 自定义的属性名 渲染即可
  const Child = {
    template: '#child',
    props: ['msg', 'flag', 'num', 'obj', 'arr']
  }

  const Parent = {
    template: '#parent',
    components: {
      'my-child': Child
    },
    data () {
      return {
        msg: 'hello vue'
      }
    }
  }

  Vue.createApp({
    components: {
      'my-parent': Parent
    }
  }).mount('#app')
</script>
</html>

虽然上述案例已经完成了父组件给子组件传值,但是不够严谨

可能A负责父组件的编写,B负责了子组件的编写,容易造成 不知道 自定义的属性名的 数据类型

6.1.2 父组件给子组件传值2

完整案例:05_component/30_parent_child_component_value2.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>父组件给子组件传值2</title>
</head>
<body>
  <div id="app">
    <my-parent></my-parent>
  </div>
</body>
<template id="parent">
  <div>
    <h1>父组件</h1>
    <!-- 
      父组件在调用子组件的地方,添加自定义的属性,如果属性的值是变量,boolean类型,number类型,
      对象,数组,null,undefined,正则,需要使用绑定属性
     -->
    <my-child :msg="msg" :flag="true" :num="1000" :obj="{a:1}" :arr="[1, 2, 3]"></my-child>
  </div>
</template>
<template id="child">
  <div>
    <h3>子组件</h3>
    <div>{{ msg }}</div>
    <div>{{ flag }}</div>
    <div>{{ num }}</div>
    <div>{{ obj }}</div>
    <div>{{ arr }}</div>
  </div>
</template>
<script src="../lib/vue.global.js"></script>
<script>
  // 在定义子组件的地方,添加一个 props 选项
  // 方式1
  //     props 书写为一个数组,数组的元素名就是之前定义好的自定义属性名,子组件的模版按照 自定义的属性名 渲染即可
  // 方式2
  //     props 书写为一个对象,对象的key值为自定义的属性名,value值为 数据类型
  const Child = {
    template: '#child',
    // props: ['msg', 'flag', 'num', 'obj', 'arr']
    props: {
      msg: String,
      flag: Boolean,
      num: Number,
      obj: Object,
      arr: Array
    }
  }

  const Parent = {
    template: '#parent',
    components: {
      'my-child': Child
    },
    data () {
      return {
        msg: 'hello vue'
      }
    }
  }

  Vue.createApp({
    components: {
      'my-parent': Parent
    }
  }).mount('#app')
</script>
</html>

现在只能知道哪一个属性是哪一种数据类型,但是有时候我们可以不需要设置 自定义的属性(. )

<input /> <===> <input type="text" />

6.1.3 父组件给子组件传值3

完整案例: 05_component/31_parent_child_component_value3.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>父组件给子组件传值3</title>
</head>
<body>
  <div id="app">
    <my-parent></my-parent>
  </div>
</body>
<template id="parent">
  <div>
    <h1>父组件</h1>
    <!-- 
      父组件在调用子组件的地方,添加自定义的属性,如果属性的值是变量,boolean类型,number类型,
      对象,数组,null,undefined,正则,需要使用绑定属性
     -->
    <my-child :msg="msg" :flag="true" :num="1000" :obj="{a:1}" :arr="[1, 2, 3]"></my-child>
    <my-child :obj="{b:2}" ></my-child>
  </div>
</template>
<template id="child">
  <div>
    <h3>子组件</h3>
    <div>{{ msg }}</div>
    <div>{{ flag }}</div>
    <div>{{ num }}</div>
    <div>{{ obj }}</div>
    <div>{{ arr }}</div>
  </div>
</template>
<script src="../lib/vue.global.js"></script>
<script>
  // 在定义子组件的地方,添加一个 props 选项
  // 方式1
  //     props 书写为一个数组,数组的元素名就是之前定义好的自定义属性名,子组件的模版按照 自定义的属性名 渲染即可
  // 方式2
  //     props 书写为一个对象,对象的key值为自定义的属性名,value值为 数据类型
  // 方式3
  //     props 书写为一个对象
  //        对象的key值为 自定义的属性名,
  //        value值又为一个对象
  //            对象的key值为 type, 表示需要传递数据的 数据类型
  //            对象的key值为 default,表示数据的默认值, 如果数据默认值为 对象和数组,需要通过函数返回
  //            对象的key值为 required, 表示该数据是必须传递的项
  //            对象的key值为 validator, 表示需要自定义验证规则
  //        如果一个属性的值既可以是 String,也可以是number类型,通过 || 分割,或者数组书写
  const Child = {
    template: '#child',
    // props: ['msg', 'flag', 'num', 'obj', 'arr']
    // props: {
    //   msg: String,
    //   flag: Boolean,
    //   num: Number,
    //   obj: Object,
    //   arr: Array
    // }
    props: {
      msg: {
        type: String
      },
      flag: {
        type: Boolean,
        default: true
      },
      num: {
        // type: Number || String,
        // type: [Number, String],
        type: Number,
        default: 100000,
        validator: (value) => {
          return value > 20000
        }
      },
      obj: {
        required: true, 
        type: Object,
        default () {
          return { a: '1111' }
        }
      },
      arr: {
        type: Array,
        default () {
          return [4, 5, 6]
        }
      }
    }
  }

  const Parent = {
    template: '#parent',
    components: {
      'my-child': Child
    },
    data () {
      return {
        msg: 'hello vue'
      }
    }
  }

  Vue.createApp({
    components: {
      'my-parent': Parent
    }
  }).mount('#app')
</script>
</html>

6.1.4 $attrs

一个包含了组件所有透传 attributes 的对象。

透传 Attributes 是指由父组件传入,且没有被子组件声明为 props 或是组件自定义事件的 attributes 和事件处理函数。

<my-button class="btn"></my-button>

子组件模板只有button,那么my-button的class直接透传给子组件

<button class="btn"></button>

默认情况下,若是单一根节点组件,$attrs 中的所有属性都是直接自动继承自组件的根元素。而多根节点组件则不会如此,同时你也可以通过配置 inheritAttrs 选项来显式地关闭该行为。

6.2 监听事件

学习:状态选项emits、实例方法 $emit

6.2.1 子组件给父组件传值- $emit

在组件的模板表达式中,可以直接使用 $emit 方法触发自定义事件

$emit() 方法在组件实例上也同样以 this.$emit() 的形式可用

父组件可以通过 v-on (缩写为 @) 来监听事件

同样,组件的事件监听器也支持 .once 修饰符

像组件与 prop 一样,事件的名字也提供了自动的格式转换。注意这里我们触发了一个以 camelCase 形式命名的事件,但在父组件中可以使用 kebab-case 形式来监听。与 prop 大小写格式一样,在模板中我们也推荐使用 kebab-case 形式来编写监听器。

完整案例:05_component/32_child_parent_component_value1.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>子组件给父组件传值1</title>
</head>
<body>
  <div id="app">
    <my-parent></my-parent>
  </div>
</body>
<template id="parent">
  <div>
    <h1>父组件</h1>
    <!-- 
      父组件调用子组件的地方,绑定一个自定义的事件
      该事件由父组件定义,默认参数就是子组件传递给父组件的值
     -->
    <my-child v-on:my-event="getData"></my-child>
  </div>
</template>
<template id="child">
  <div>
    <h3>子组件</h3>
    <button @click="sendData">传值2000</button>
  </div>
</template>
<script src="../lib/vue.global.js"></script>
<script>
  // 在子组件的某一个事件内部,通过 this.$emit('自定义事件名', 参数)完成传递
  const Child = {
    template: '#child',
    mounted () {
      this.$emit('my-event', 1000)
    },
    methods: {
      sendData () {
        this.$emit('my-event', 2000)
      }
    }
  }

  const Parent = {
    template: '#parent',
    components: {
      'my-child': Child
    },
    methods: {
      getData (val) {
        console.log(val)
      }
    }
  }

  Vue.createApp({
    components: {
      'my-parent': Parent
    }
  }).mount('#app')
</script>
</html>

6.2.2 子组件给父组件传值-声明触发的事件

组件要触发的事件可以显式地通过 emits 选项来声明:

{
	emits: ['inFocus', 'submit']
}

这个 emits 选项还支持对象语法,它允许我们对触发事件的参数进行验证:

{
	 emits: {
        submit(payload) {
          // 通过返回值为 `true` 还是为 `false` 来判断
          // 验证是否通过
        }
      }
}

要为事件添加校验,那么事件可以被赋值为一个函数,接受的参数就是抛出事件时传入 this.$emit 的内容,返回一个布尔值来表明事件是否合法。

{
  emits: {
    // 没有校验
    click: null,

    // 校验 submit 事件
    submit: ({ email, password }) => {
      if (email && password) {
        return true
      } else {
        console.warn('Invalid submit event payload!')
        return false
      }
    }
  },
  methods: {
    submitForm(email, password) {
      this.$emit('submit', { email, password })
    }
  }
}

完整案例:05_component/33_child_parent_component_value2.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>子组件给父组件传值1</title>
</head>
<body>
  <div id="app">
    <my-parent></my-parent>
  </div>
</body>
<template id="parent">
  <div>
    <h1>父组件</h1>
    <!-- 
      父组件调用子组件的地方,绑定一个自定义的事件
      该事件由父组件定义,默认参数就是子组件传递给父组件的值
     -->
    <my-child v-on:my-event="getData"></my-child>
  </div>
</template>
<template id="child">
  <div>
    <h3>子组件</h3>
    <button @click="sendData">传值2000</button>
  </div>
</template>
<script src="../lib/vue.global.js"></script>
<script>
  // 在子组件的某一个事件内部,通过 this.$emit('自定义事件名', 参数)完成传递
  const Child = {
    template: '#child',
    // 声明触发的事件并且验证
    // emits: ['my-event'], // 声明触发的事件
    emits: { // 声明触发的事件并且验证
      'my-event': (payload) => {
        return payload > 500
      }
    },
    mounted () {
      this.$emit('my-event', 1000)
    },
    methods: {
      sendData () {
        this.$emit('my-event', 2000)
      }
    }
  }

  const Parent = {
    template: '#parent',
    components: {
      'my-child': Child
    },
    methods: {
      getData (val) {
        console.log(val)
      }
    }
  }

  Vue.createApp({
    components: {
      'my-parent': Parent
    }
  }).mount('#app')
</script>
</html>

6.2.3 自定义表单组件使用v-model

自定义事件可以用于开发支持 v-model 的自定义表单组件

<CustomInput> 组件内部需要做两件事:

  1. 将内部原生 input 元素的 value attribute 绑定到 modelValue prop
  2. 输入新的值时在 input 元素上触发 update:modelValue 事件

完整案例:05_component/34_custom_form_v-model.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>vue3-自定义组件使用 v-model</title>
</head>
<body>
  <div id="app">
    <my-input
      type="text" 
      v-model="userName"
      placeholder="用户名"
    ></my-input> {{ userName }}
    <my-input
      type="password" 
      v-model="password"
      placeholder="密码"
    ></my-input> {{ password }}
  </div>
</body>
<template id="input">
  <div>
    <input 
      :type="type"
      :placeholder="placeholder"
      :value="modelValue"
      @input="$emit('update:modelValue', $event.target.value)"
    />
  </div>
</template>
<script src="../lib/vue.global.js"></script>
<script>
  // v-model 用到自定义组件上,就相当于 
  // 父组件在调用子组件时 给子组件传了一个 叫 modelValue 的属性
  // 同时 也相当于 父组件直接给子组件绑定了一个 叫 update:modelValue 的事件
  // 在子组件模版中 将 input 的value的值赋值给 modelValue,
  // 给input绑定一个事件,事件提交 update:modelValue

  // vue2中自定义表单组件,父传给子 value 属性,事件为 input 事件
  const Input = {
    template: '#input',
    emits: ['update:modelValue'],
    props: {
      type: {
        type: String,
        default: 'text'
      },
      placeholder: {
        type: String
      },
      modelValue: {
        type: String
      }
    }
  }
  Vue.createApp({
    data () {
      return {
        userName: '',
        password: ''
      }
    },
    components: {
      MyInput: Input
    }
  }).mount('#app')
</script>
</html>

另一种在组件内实现 v-model 的方式是使用一个可写的,同时具有 getter 和 setter 的计算属性。get 方法需返回 modelValue prop,而 set 方法需触发相应的事件:

const Input = {
  template: `#input`,
  props: ['modelValue'],
  emits: ['update:modelValue'],
  computed: {
    value: {
      get() {
        return this.modelValue
      },
      set(value) {
        this.$emit('update:modelValue', value)
      }
    }
  }
}

6.2.4 多个v-model的绑定

v-model 的参数

仅限于 vue3

完整案例:05_component/35_custom_form_v-model_params.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>一个组件使用多个v-model,仅限于vue3</title>
</head>
<body>
  <div id="app">
    <my-form 
      v-model:username = "username"
      v-model:password = "password"
    ></my-form> {{ username }} -- {{ password }}
  </div>
</body>
<template id="form">
  <form>
    <div>
      <input type="text" placeholder="用户名" :value="username" @input="$emit('update:username', $event.target.value)"/>
    </div>
    <div>
      <input type="password" placeholder="密码" :value="password" @input="$emit('update:password', $event.target.value)"/>
    </div>
    <button>登录</button>
  </form>
</template>
<script src="../lib/vue.global.js"></script>
<script>
  const Form = {
    template: '#form',
    emits: ['update:username', 'update:password'],
    props: {
      username: String,
      password: String
    }
  }

  Vue.createApp({
    data () {
      return {
        username: '',
        password: ''
      }
    },
    components: {
      MyForm: Form
    }
  }).mount('#app')
</script>
</html>

6.3 透传Attribute

“透传 attribute”指的是传递给一个组件,却没有被该组件声明为 props 或 emits 的 attribute 或者 v-on 事件监听器。最常见的例子就是 classstyleid

6.3.1 attribute继承

当一个组件以单个元素为根作渲染时,透传的 attribute 会自动被添加到根元素上。举例来说,假如我们有一个 <MyButton> 组件,它的模板长这样:

<!-- <MyButton> 的模板 -->
<button>click me</button>

一个父组件使用了这个组件,并且传入了 class

<MyButton class="large" />

最后渲染出的 DOM 结果是:

<button class="large">click me</button>

这里,<MyButton> 并没有将 class 声明为一个它所接受的 prop,所以 class 被视作透传 attribute,自动透传到了 <MyButton> 的根元素上。

6.3.2 对 classstyle 的合并

如果一个子组件的根元素已经有了 classstyle attribute,它会和从父组件上继承的值合并。如果我们将之前的 <MyButton> 组件的模板改成这样:

<!-- <MyButton> 的模板 -->
<button class="btn">click me</button>

则最后渲染出的 DOM 结果会变成:

<button class="btn large">click me</button>

6.3.3 v-on 监听器继承

同样的规则也适用于 v-on 事件监听器:

<MyButton @click="onClick" />

click 监听器会被添加到 <MyButton> 的根元素,即那个原生的 <button> 元素之上。当原生的 <button> 被点击,会触发父组件的 onClick 方法。同样的,如果原生 button 元素自身也通过 v-on 绑定了一个事件监听器,则这个监听器和从父组件继承的监听器都会被触发。

6.3.4 禁用 Attributes 继承

如果你不想要一个组件自动地继承 attribute,你可以在组件选项中设置 inheritAttrs: false

最常见的需要禁用 attribute 继承的场景就是 attribute 需要应用在根节点以外的其他元素上。通过设置 inheritAttrs 选项为 false,你可以完全控制透传进来的 attribute 被如何使用。

6.3.5 多根节点的 Attributes 继承

$attrs 被显式绑定

完整案例: 05_component/36_attribute_transmission.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>透传</title>
</head>
<body>
  <div id="app">
    {{ count }}
    <my-button class="large" @click="count++"></my-button>
  </div>
</body>
<script src="../lib/vue.global.js"></script>
<template id="btn">
  <button class="btn">按钮</button>
</template>
<script>
  Vue.createApp({
    data () {
      return {
        count: 10
      }
    },
    components: {
      MyButton: { template: '#btn', inheritAttrs: false }
    }
  }).mount('#app')
</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>36_透传attribute</title>
  <style>
    .btn {
      border: 0;
      padding: 10px 20px;
    }
    .btn-success {
      background-color: rgb(29, 198, 29);
      color: #fff;
    }
    .btn-danger {
      background-color: rgb(218, 23, 39);
      color: #fff;
    }
    .btn-primary {
      background-color: rgb(75, 104, 236);
      color: #fff;
    }
  </style>
</head>
<body>
  <div id="app">
    <my-button type="a" class="btn-success" @click="print('success')" @my-event="myalert(1)"></my-button>
    <my-button type="b" class="btn-danger"  @click="print('danger')" @my-event="myalert(2)"></my-button>
    <my-button type="c" class="btn-primary"  @click="print('primary')" @my-event="myalert(3)"></my-button>
  </div>
</body>

<template id="button">
  <!-- Attributes 继承(class 与style合并, v-on事件继承) -->
  <!--  <button class="btn">按钮</button> -->
  <!-- 深层组件继承 -->
  <base-button></base-button>
</template>
<template id="base">
  <button class="btn" v-bind="$attrs" >按钮</button>
  <div >测试</div>
</template>
<script src="lib/vue.global.js"></script>
<script>
  const Base = {
    template: '#base',
    mounted () { // 在js中访问透传的 attributes
      console.log('2', this.$attrs)
    },
    // inheritAttrs: false // 不想要一个组件自动地继承 attribute,你可以在组件选项中设置
  }
  const Button = {
    template: '#button',
    components: {
      BaseButton: Base
    },
    mounted () {
      console.log('1', this.$attrs)
    },
   
  }
  

  const { createApp } = Vue

  const app = createApp({
    components: {
      MyButton: Button
    },
    methods: {
      print (msg) {
        console.log(msg)
      }
    },
    myalert (num) {
      alert(num)
    }
  })
  

  app.mount('#app')
</script>
</html>

6.4 特殊Attribute -ref

学习:实例属性refs

用于注册模板引用。

ref 用于注册元素或子组件的引用。

使用选项式 API,引用将被注册在组件的 this.$refs 对象里

放在DOM元素上,获取DOM节点,放到组件上,获取子组件的实例,可以直接使用子组件的属性和方法

完整案例:05_component/37_attribute_ref.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>父子组件</title>
</head>
<body>
  <div id="app">
    <my-parent></my-parent>
  </div>
</body>
<template id="parent">
  <div>
    <h1>父组件</h1>
    <my-child ref="child"></my-child>
    <div ref="oDiv">DOM操作</div>
  </div>
</template>
<template id="child">
  <div>
    <h3>子组件</h3>
  </div>
</template>
<script src="../lib/vue.global.js"></script>
<script>
  const Child = {
    template: '#child',
    data () {
      return {
        msg: 'child 在此'
      }
    },
    methods: {
      fn () {
        console.log('子组件的方法被调用')
      }
    }
  }

  const Parent = {
    template: '#parent',
    components: {
      'my-child': Child
    },
    mounted () {
      console.log(this.$refs)
      console.log(this.$refs.child.msg)
      this.$refs.child.fn()
      console.log(this.$refs.oDiv.innerHTML)
    }
  }

  Vue.createApp({
    components: {
      'my-parent': Parent
    }
  }).mount('#app')
</script>
</html>

6.5 $parent

当前组件可能存在的父组件实例,如果当前组件是顶层组件,则为 null

完整案例05_component/38_parent.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>父子组件</title>
</head>
<body>
  <div id="app">
    <my-parent></my-parent>
  </div>
</body>
<template id="parent">
  <div>
    <h1>父组件</h1>
    <my-child></my-child>
  </div>
</template>
<template id="child">
  <div>
    <h3>子组件</h3>
    {{ $parent.msg }}
    <button @click="$parent.fn()">点击</button>
  </div>
</template>
<script src="../lib/vue.global.js"></script>
<script>
  const Child = {
    template: '#child'
  }

  const Parent = {
    template: '#parent',
    data () {
      return {
        msg: '父组件在此'
      }
    },
    methods: {
      fn () {
        console.log('父组件的方法被调用')
      }
    },
    components: {
      'my-child': Child
    }
  }

  Vue.createApp({
    components: {
      'my-parent': Parent
    }
  }).mount('#app')
</script>
</html>

6.6$root

当前组件树的根组件实例。如果当前实例没有父组件,那么这个值就是它自己。

完整案例:05_component/39_root.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>父子组件</title>
</head>
<body>
  <div id="app">
    <my-parent></my-parent>
  </div>
</body>
<template id="parent">
  <div>
    <h1>父组件 - {{ $root.msg }}</h1>
    <my-child></my-child>
  </div>
</template>
<template id="child">
  <div>
    <h3>子组件 - {{ $root.msg }}</h3>
    <button @click="$root.fn()">按钮</button>
  </div>
</template>
<script src="../lib/vue.global.js"></script>
<script>
  const Child = {
    template: '#child'
  }

  const Parent = {
    template: '#parent',
    components: {
      'my-child': Child
    }
  }

  Vue.createApp({
    data () {
      return {
        msg: 'root'
      }
    },
    methods: {
      fn () {
        console.log('root 被调用')
      }
    },
    components: {
      'my-parent': Parent
    }
  }).mount('#app')
</script>
</html>

6.7 非父子组件传值

兄弟组件传值 - 中央事件总线传值 ---- vue2

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gVk6Gte8-1673405502848)(assets/image-20220916160921781.png)]

完整案例:05_component/40_brother_value-vue2.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>vue2兄弟组件传值</title>
</head>
<body>
  <div id="app">
    <my-content></my-content>
    <my-footer></my-footer>
  </div>

</body>
<template id="content">
  <div>当前页面是:{{ page }}</div>
</template>
<template id="footer">
  <ul>
    <li @click="sendData('首页')">首页</li>
    <li @click="sendData('分类')">分类</li>
    <li @click="sendData('购物车')">购物车</li>
    <li @click="sendData('我的')">我的</li>
  </ul>
</template>
<script src="../lib/vue.js"></script>
<script>
  // new Vue 实例作为中央事件总线 eventBus
  // 通过  eventBus.$emit('自定义事件', 数据) 传值
  // 通过 eventBus.$on('自定义事件', 回调函数)接收值,回调函数参数就是传递过来的值
  const eventBus = new Vue()
  const Content = {
    template: '#content',
    data () {
      return {
        page: '首页'
      }
    },
    mounted () {
      // 在此处接收数据
      eventBus.$on('my-event', (val) => {
        this.page = val
      })
    }

  }

  const Footer = {
    template: '#footer',
    methods: {
      sendData (val) {
        eventBus.$emit('my-event', val)
      }
    }
  }

  new Vue({
    components: {
      MyContent: Content,
      MyFooter: Footer
    }
  }).$mount('#app')
</script>
</html>

vue3中没有明确的兄弟组件传值的方案,可以使用状态提升(找到这两个组件共同的父级组件,然后通过父与子之间的传值实现)

完整案例:05_component/41_brother_value-vue3.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>vue3兄弟组件传值-状态提升</title>
</head>
<body>
  <div id="app">
    <my-content :page="page"></my-content>
    <my-footer @change-page="changePage"></my-footer>
  </div>

</body>
<template id="content">
  <div>当前页面是:{{ page }}</div>
</template>
<template id="footer">
  <ul>
    <li @click="sendData('首页')">首页</li>
    <li @click="sendData('分类')">分类</li>
    <li @click="sendData('购物车')">购物车</li>
    <li @click="sendData('我的')">我的</li>
  </ul>
</template>
<script src="../lib/vue.global.js"></script>
<script>
  const Content = {
    template: '#content',
    props: {
      page: String
    }
  }

  const Footer = {
    template: '#footer',
    methods: {
      sendData (val) {
        this.$emit('change-page', val)
      }
    }
  }

  Vue.createApp({
    data () {
      return {
        page: '首页'
      }
    },
    methods: {
      changePage (val) {
        this.page = val
      }
    },
    components: {
      MyContent: Content,
      MyFooter: Footer
    }
  }).mount('#app')
</script>
</html>

7.插槽

组件的最大特性就是 重用 ,而用好插槽能大大提高组件的可重用能力。

**插槽的作用:**父组件向子组件传递内容。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BSuWuCaV-1673405502849)(assets/34.png)]

通俗的来讲,插槽无非就是在 子组件 中挖个坑,坑里面放什么东西由 父组件 决定。

插槽类型有:

  • 单个(匿名)插槽
  • 具名插槽
  • 作用域插槽

7.1 - 插槽内容与插口

在某些场景中,我们可能想要为子组件传递一些模板片段,让子组件在它们的组件中渲染这些片段。

这里有一个 <FancyButton> 组件,可以像这样使用:

<FancyButton>
  Click me! <!-- 插槽内容 -->
</FancyButton>

<FancyButton> 的模板是这样的:

<button class="fancy-btn">
  <slot></slot> <!-- 插槽出口 -->
</button>

<slot> 元素是一个插槽出口 (slot outlet),标示了父元素提供的插槽内容 (slot content) 将在哪里被渲染。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tZfykBWj-1673405502849)(assets/slots.dbdaf1e8.png)]

最终渲染出的 DOM 是这样:

<button class="fancy-btn">Click me!</button>

完整案例:06_slot/42_slot.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>插槽</title>
</head>
<body>
  <div id="app">
    <!-- 自定义组件标签调用
    默认情况下 内部代码是无法被解析的
    可以在定义组件的模版时,通过 slot 标签显示内容
    -->
    <fancy-button>点击</fancy-button>
    <fancy-button>注册</fancy-button>
    <fancy-button>登录</fancy-button>
  </div>
</body>
<template id="btn">
  <button>
    按钮 - <slot></slot>
  </button>
</template>
<script src="../lib/vue.global.js"></script>
<script>
  const FancyButton = {
    template: '#btn'
  }
  Vue.createApp({
    components: {
      FancyButton
    }
  }).mount('#app')
</script>
</html>

7.2渲染作用域

插槽内容可以访问到父组件的数据作用域,因为插槽内容本身是在父组件模板中定义的。举例来说:

<span>{{ message }}</span>
<FancyButton>{{ message }}</FancyButton>

这里的两个 {{ message }} 插值表达式渲染的内容都是一样的。

插槽内容无法访问子组件的数据。Vue 模板中的表达式只能访问其定义时所处的作用域,这和 JavaScript 的词法作用域规则是一致的。换言之:

父组件模板中的表达式只能访问父组件的作用域;子组件模板中的表达式只能访问子组件的作用域。

完整案例:06_slot/43_slot_render_scope.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>插槽</title>
</head>
<body>
  <div id="app">
    <!-- 自定义组件标签调用
    默认情况下 内部代码是无法被解析的
    可以在定义组件的模版时,通过 slot 标签显示内容
    -->
    <span>{{ msg }}</span>
    <fancy-button>{{ msg }}</fancy-button>
  </div>
</body>
<template id="btn">
  <button>
    按钮 - <slot></slot>
  </button>
</template>
<script src="../lib/vue.global.js"></script>
<script>
  const FancyButton = {
    template: '#btn',
    data () {
      return {
        msg: '注册'
      }
    }
  }
  Vue.createApp({
    data () {
      return {
        msg: '登录'
      }
    },
    components: {
      FancyButton
    }
  }).mount('#app')
</script>
</html>

7.3默认内容

在外部没有提供任何内容的情况下,可以为插槽指定默认内容。比如有这样一个 <SubmitButton> 组件:

<button type="submit">
  <slot></slot>
</button>

如果我们想在父组件没有提供任何插槽内容时在 <button> 内渲染“Submit”,只需要将“Submit”写在 <slot> 标签之间来作为默认内容:

<button type="submit">
  <slot>
    Submit <!-- 默认内容 -->
  </slot>
</button>

现在,当我们在父组件中使用 <SubmitButton> 且没有提供任何插槽内容时:

<SubmitButton />

“Submit”将会被作为默认内容渲染:

<button type="submit">Submit</button>

但如果我们提供了插槽内容:

<SubmitButton>Save</SubmitButton>

那么被显式提供的内容会取代默认内容:

<button type="submit">Save</button>

完整案例:06_slot/44_slot_default.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>插槽</title>
</head>
<body>
  <div id="app">
    <!-- 自定义组件标签调用
    默认情况下 内部代码是无法被解析的
    可以在定义组件的模版时,通过 slot 标签显示内容
    -->
    <fancy-button>点击</fancy-button>
    <fancy-button>注册</fancy-button>
    <fancy-button></fancy-button>
  </div>
</body>
<template id="btn">
  <button>
    <slot>按钮</slot>
  </button>
</template>
<script src="../lib/vue.global.js"></script>
<script>
  const FancyButton = {
    template: '#btn'
  }
  Vue.createApp({
    components: {
      FancyButton
    }
  }).mount('#app')
</script>
</html>

7.4具名插槽(v-slot属性,#简写)

有时在一个组件中包含多个插槽出口是很有用的。举例来说,在一个 <BaseLayout> 组件中,有如下模板:

<div class="container">
  <header>
    <!-- 标题内容放这里 -->
  </header>
  <main>
    <!-- 主要内容放这里 -->
  </main>
  <footer>
    <!-- 底部内容放这里 -->
  </footer>
</div>

对于这种场景,<slot> 元素可以有一个特殊的 attribute name,用来给各个插槽分配唯一的 ID,以确定每一处要渲染的内容:

<div class="container">
  <header>
    <slot name="header"></slot>
  </header>
  <main>
    <slot></slot>
  </main>
  <footer>
    <slot name="footer"></slot>
  </footer>
</div>

这类带 name 的插槽被称为具名插槽 (named slots)。没有提供 name<slot> 出口会隐式地命名为“default”。

在父组件中使用 <BaseLayout> 时,我们需要一种方式将多个插槽内容传入到各自目标插槽的出口。此时就需要用到具名插槽了:

要为具名插槽传入内容,我们需要使用一个含 v-slot 指令的 <template> 元素,并将目标插槽的名字传给该指令:

<BaseLayout>
  <template v-slot:header>
    <!-- header 插槽的内容放这里 -->
  </template>
</BaseLayout>

v-slot 有对应的简写 #,因此 <template v-slot:header> 可以简写为 <template #header>。其意思就是“将这部分模板片段传入子组件的 header 插槽中”。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ji7v0Okf-1673405502850)(assets/named-slots.ebb7b207.png)]

完整案例:06_slot/45_slot_name.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>具名插槽</title>
</head>
<body>
  <div id="app">
    <base-layout>
      <!-- vue2可以这样写 -->
      <!-- <div slot="header">首页头部</div>
      <div>首页内容</div>
      <div slot="footer">首页底部</div> -->
      <!-- vue3 使用 v-slot指令 -->
      <template v-slot:header>
        <div>首页头部</div>
      </template>
      <div>首页内容</div>
      <template v-slot:footer>
        <div >首页底部</div> 
      </template>
    </base-layout>
    <base-layout>
      <!-- v-slot:header 可简写为 #header -->
      <template #header>
        <div>分类头部</div>
      </template>
      <!-- 默认内容要写 template 就写 v-slot:default 或者  #default -->
      <template #default>
        <div>分类内容</div>
      </template>
      <template #footer>
        <div>分类底部</div>
      </template>
    </base-layout>
  </div>
</body>
<template id="layout">
  <div class="container">
    <header class="header">
       <slot name="header">头部</slot>
    </header>
    <div class="content">
       <slot>内容</slot>
    </div>
    <footer class="footer">
       <slot name="footer">底部</slot>
    </footer>
  </div>
</template>
<script src="../lib/vue.global.js"></script>
<script>
  const BaseLayout = {
    template: '#layout'
  }

  Vue.createApp({
    components: {
      BaseLayout
    }
  }).mount('#app')
</script>
</html>

7.5动态插槽名 - 了解

动态指令参数在 v-slot 上也是有效的,即可以定义下面这样的动态插槽名:

<base-layout>
  <template v-slot:[dynamicSlotName]>
    ...
  </template>

  <!-- 缩写为 -->
  <template #[dynamicSlotName]>
    ...
  </template>
</base-layout>

注意这里的表达式和动态指令参数受相同的语法限制。

完整案例:06_slot/46_dynamic_slot_name.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>46_动态插槽名</title>
</head>
<body>
  <div id="app">
    <button @click="count++">{{ count }}</button>
    <my-com>
      <template v-slot:[type]>
        {{ type }} 父组件默认值
      </template>
    </my-com>
    
  </div>
</body>
<template id="com">
  <div >
    <slot name="dan"> 1 动态插槽名   子组件默认值 1</slot>
    <br />
    <slot name="shaung"> 2 动态插槽名   子组件默认值 2</slot>
  </div>
</template>
<script src="../lib/vue.global.js"></script>
<script>
  const { createApp } = Vue

  const Com = {
    template: '#com'
  }

  const app = createApp({
    components: {
      MyCom: Com
    },
    data () {
      return {
        count: 0
      }
    },
    computed: {
      type () {
        return this.count % 2 === 0 ? 'shaung': 'dan'
      }
    }
  })

  app.mount('#app')
</script>
</html>

7.6作用域插槽-了解

在某些场景下插槽的内容可能想要同时使用父组件域内和子组件域内的数据。要做到这一点,我们需要一种方法来让子组件在渲染时将一部分数据提供给插槽。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9cxFkquO-1673405514873)(null)]

完整案例:06_slot/47_scope_slot.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>插槽</title>
</head>
<body>
  <div id="app">
    <!-- 
      插槽的内容如果既想要访问父组件域内数据 msg,也想要访问子组件域内的数据 str num
      在定义插槽的时候给slot 添加了 str 以及 num的自定义属性
      在调用组件的时候 通过 v-slot 给组件添加 slotProps 的值
      该值将包含子组件域内的数据,以对象的形式存在
    -->
    <fancy-button v-slot="slotProps">
      {{ msg }} -- {{ slotProps.str }} -- {{ slotProps.num }}
    </fancy-button>
  </div>
</body>
<template id="btn">
  <button>
    按钮 - <slot :str="str" :num="10000"></slot>
  </button>
</template>
<script src="../lib/vue.global.js"></script>
<script>
  const FancyButton = {
    template: '#btn',
    data () {
      return {
        str: '注册'
      }
    }
  }
  Vue.createApp({
    data () {
      return {
        msg: '登录'
      }
    },
    components: {
      FancyButton
    }
  }).mount('#app')
</script>
</html>

7.7 $slots - 了解

一个表示父组件所传入插槽的对象。

通常用于手写渲染函数,但也可用于检测是否存在插槽。

每一个插槽都在 this.$slots 上暴露为一个函数,返回一个 vnode 数组,同时 key 名对应着插槽名。默认插槽暴露为 this.$slots.default

如果插槽是一个作用域插槽,传递给该插槽函数的参数可以作为插槽的 prop 提供给插槽。

在渲染函数中,可以通过 this.$slots 来访问插槽:

完整案例:06_slot/48_$slot.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>48_$slots渲染函数</title>
</head>
<body>
  <div id="app">
    <my-com>
      <template #default>1111</template>
      <template #footer>2222</template>
    </my-com>
  </div>
</body>
<template id="com">
  <div><slot>默认值</slot></div>
  <div><slot name="footer">底部默认值</slot></div>
</template>
<script src="lib/vue.global.js"></script>
<script>
  const { createApp, h } = Vue // h 代表创建一个元素 createElement

  const Com = {
    // template: '#com'
    render () {
      console.log(this.$slots)
      return [
        h('div', { class: 'content'}, this.$slots.default()), // <div class="content"><slot></slot></div>
        h('div', { class: 'footer'}, this.$slots.footer()) // <div class="footer"><slot name="footer"></slot></div>
      ]
    }
  }

  const app = createApp({
    components: {
      MyCom: Com
    }
  })

  app.mount('#app')
</script>
</html>

vue2渲染函数参照

<!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>vue2渲染函数</title>
</head>
<body>
  <div id="app">
    <my-com></my-com>
  </div>
</body>
<template id="com">
  <div>com</div>
</template>
<template id="test">
  <div>test</div>
</template>
<script src="../lib/vue.js"></script>
<script>
  const Com = {
    template: '#com'
  }
  const Test = {
    template: '#test'
  }
  // new Vue({
  //   el: '#app',
  //   components: {
  //     MyCom: Com
  //   }
  // })

  // new Vue({
  //   components: {
  //     MyCom: Com
  //   }
  // }).$mount('#app')

  new Vue({
    // render: (h) => h(Com)
    render: (createElement) => createElement('div', {}, [ createElement(Com),  createElement(Test)])
  }).$mount('#app')
</script>
</html>

8.依赖注入

通常情况下,当我们需要从父组件向子组件传递数据时,会使用 props。想象一下这样的结构:有一些多层级嵌套的组件,形成了一颗巨大的组件树,而某个深层的子组件需要一个较远的祖先组件中的部分数据。在这种情况下,如果仅使用 props 则必须将其沿着组件链逐级传递下去,这会非常麻烦:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3CbFygiU-1673405502851)(assets/prop-drilling.11201220.png)]

注意,虽然这里的 <Footer> 组件可能根本不关心这些 props,但为了使 <DeepChild> 能访问到它们,仍然需要定义并向下传递。如果组件链路非常长,可能会影响到更多这条路上的组件。这一问题被称为“prop 逐级透传”,显然是我们希望尽量避免的情况。

provideinject 可以帮助我们解决这一问题。 [1] 一个父组件相对于其所有的后代组件,会作为依赖提供者。任何后代的组件树,无论层级有多深,都可以注入由父组件提供给整条链路的依赖。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zCLa1ypq-1673405502851)(assets/provide-inject.3e0505e4.png)]

8.1 provide

要为组件后代提供数据,需要使用到 provide 选项:

{
  provide: {
    message: 'hello!'
  }
}

对于 provide 对象上的每一个属性,后代组件会用其 key 为注入名查找期望注入的值,属性的值就是要提供的数据。

如果我们需要提供依赖当前组件实例的状态 (比如那些由 data() 定义的数据属性),那么可以以函数形式使用 provide

{
  data() {
    return {
      message: 'hello!'
    }
  },
  provide() {
    // 使用函数的形式,可以访问到 `this`
    return {
      message: this.message
    }
  }
}

不会使注入保持响应性(比如祖先组件中有一个count的状态,祖先组件修改完状态,后代组件默认的值没有响应式的改变)

8.2 inject

要注入上层组件提供的数据,需使用 inject 选项来声明:

{
  inject: ['message'],
  created() {
    console.log(this.message) // injected value
  }
}

注入会在组件自身的状态之前被解析,因此你可以在 data() 中访问到注入的属性:

{
  inject: ['message'],
  data() {
    return {
      // 基于注入值的初始数据
      fullMessage: this.message
    }
  }
}

当以数组形式使用 inject,注入的属性会以同名的 key 暴露到组件实例上。在上面的例子中,提供的属性名为 "message",注入后以 this.message 的形式暴露。访问的本地属性名和注入名是相同的。

如果我们想要用一个不同的本地属性名注入该属性,我们需要在 inject 选项的属性上使用对象的形式:

{
  inject: {
    /* 本地属性名 */ localMessage: {
      from: /* 注入来源名 */ 'message'
    }
  }
}

这里,组件本地化了原注入名 "message" 所提供的的属性,并将其暴露为 this.localMessage

默认情况下,inject 假设传入的注入名会被某个祖先链上的组件提供。如果该注入名的确没有任何组件提供,则会抛出一个运行时警告。

如果在注入一个值时不要求必须有提供者,那么我们应该声明一个默认值,和 props 类似:

{
  // 当声明注入的默认值时
  // 必须使用对象形式
  inject: {
    message: {
      from: 'message', // 当与原注入名同名时,这个属性是可选的
      default: 'default value'
    },
    user: {
      // 对于非基础类型数据,如果创建开销比较大,或是需要确保每个组件实例
      // 需要独立数据的,请使用工厂函数
      default: () => ({ name: 'John' })
    }
  }
}

完整案例:07_provide/49_provide_inject.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>依赖注入</title>
</head>
<body>
  <div id="app">
    <button @click="count++">{{count}}</button>
    <my-parent></my-parent>
  </div>
</body>
<template id="parent">
  <div>
    <h1>父组件</h1>
    <my-child></my-child>
  </div>
</template>
<template id="child">
  <div>
    <h3>子组件</h3>
    <!-- {{ msg }} -- {{ count }} -->
    {{ mymsg }} -- {{ mycount }} -- {{ mytest }}
  </div>
</template>
<script src="../lib/vue.global.js"></script>
<script>
  const Child = {
    template: '#child',
    // inject: ['msg', 'count']
    inject: {
      mymsg: {
        from: 'msg'
      },
      mycount: {
        from: 'count'
      },
      mytest: {
        default: '传家宝'
      }
    }
  }

  const Parent = {
    template: '#parent',
    components: {
      'my-child': Child
    }
  }

  Vue.createApp({
    data () {
      return {
        message: 'hello',
        count: 100
      }
    },
    provide () {
      // 使用函数的形式,可以访问到 `this`
      return {
        msg: this.message,
        count: this.count
      }
    },
    components: {
      'my-parent': Parent
    }
  }).mount('#app')
</script>
</html>

发现以上案例在count值发生改变时没有更新后代数据

8.3 配合响应性 computed()

为保证注入方和供给方之间的响应性链接,我们需要使用 computed() 函数提供一个计算属性

完整案例:07_provide/50_provide_inject_computed_vue3.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>依赖注入</title>
</head>
<body>
  <div id="app">
    <button @click="count++">{{count}}</button>
    <my-parent></my-parent>
  </div>
</body>
<template id="parent">
  <div>
    <h1>父组件</h1>
    <my-child></my-child>
  </div>
</template>
<template id="child">
  <div>
    <h3>子组件</h3>
    <!-- {{ msg }} -- {{ count }} -->
    {{ mymsg }} -- {{ mycount }} -- {{ mytest }}
  </div>
</template>
<script src="../lib/vue.global.js"></script>
<script>
  const Child = {
    template: '#child',
    // inject: ['msg', 'count']
    inject: {
      mymsg: {
        from: 'msg'
      },
      mycount: {
        from: 'count'
      },
      mytest: {
        default: '传家宝'
      }
    }
  }

  const Parent = {
    template: '#parent',
    components: {
      'my-child': Child
    }
  }
  const { computed } = Vue
  Vue.createApp({
    data () {
      return {
        message: 'hello',
        count: 100
      }
    },
    provide () {
      // 使用函数的形式,可以访问到 `this`
      return {
        msg: this.message,
        count: computed(() => this.count) // 确保响应式
      }
    },
    components: {
      'my-parent': Parent
    }
  }).mount('#app')
</script>
</html>

测试得知vue2中也是如此处理数据

9.动态组件

有些场景会需要在两个组件间来回切换(Tab切换)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hETBTrCW-1673405502852)(assets/image-20220919113443278.png)]

9.1特殊 Attribute—is

用于绑定动态组件。

<!-- currentTab 改变时组件也改变 --> 
<component :is="currentTab"></component>

完整案例:08_dynamic/51_dynamic_component.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>动态组件</title>
</head>
<body>
  <div id="app">
    <ul>
      <li @click="currentTab='Home'">首页</li>
      <li @click="currentTab='Kind'">分类</li>
      <li @click="currentTab='Cart'">购物车</li>
      <li @click="currentTab='User'">我的</li>
    </ul>

    <component :is="currentTab"></component>
  </div>
</body>
<script src="../lib/vue.global.js"></script>
<script>
  const Home = {
    template: `
      <div>
        首页  
      </div>
    `
  }
  const Kind = {
    template: `
      <div>
        分类  
      </div>
    `
  }
  const Cart = {
    template: `
      <div>
        购物车  
      </div>
    `
  }
  const User = {
    template: `
      <div>
        我的  
      </div>
    `
  }

  Vue.createApp({
    data () {
      return {
        currentTab: 'Home'
      }
    },
    components: {
      Home,
      Kind,
      Cart,
      User
    }
  }).mount('#app')
</script>
</html>

如果此时给每个组件加入一个输入框,输入内容切换组件查看效果,发现切换回来数据不在

9.2 <KeepAlive>组件

缓存包裹在其中的动态切换组件

<KeepAlive> 包裹动态组件时,会缓存不活跃的组件实例,而不是销毁它们。

任何时候都只能有一个活跃组件实例作为 <KeepAlive> 的直接子节点。

完整案例:08_dynamic/52_keep-alive.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>动态组件</title>
</head>
<body>
  <div id="app">
    <ul>
      <li @click="currentTab='Home'">首页</li>
      <li @click="currentTab='Kind'">分类</li>
      <li @click="currentTab='Cart'">购物车</li>
      <li @click="currentTab='User'">我的</li>
    </ul>
    <!-- 动态组件默认切换时 执行的是组件的 销毁 和 重新创建 -->
    <!-- 可以使用 KeepAlive 保留组件的状态,避免组件的重新渲染 -->
    <keep-alive>
      <component :is="currentTab"></component>
    </keep-alive>
  </div>
</body>
<script src="../lib/vue.global.js"></script>
<script>
  const Home = {
    template: `
      <div>
        首页  <input placeholder="首页"/>
      </div>
    `,
    created () { console.log('Home created') },
    mounted () { console.log('Home mounted') },
    unmounted () { console.log('Home unmounted') }
  }
  const Kind = {
    template: `
      <div>
        分类  <input placeholder="分类"/>
      </div>
    `,
    created () { console.log('Kind created') },
    mounted () { console.log('Kind mounted') },
    unmounted () { console.log('Kind unmounted') }
  }
  const Cart = {
    template: `
      <div>
        购物车  <input placeholder="购物车"/>
      </div>
    `,
    created () { console.log('Cart created') },
    mounted () { console.log('Cart mounted') },
    unmounted () { console.log('Cart unmounted') }
  }
  const User = {
    template: `
      <div>
        我的  <input placeholder="我的"/>
      </div>
    `,
    created () { console.log('User created') },
    mounted () { console.log('User mounted') },
    unmounted () { console.log('User unmounted') }
  }

  Vue.createApp({
    data () {
      return {
        currentTab: 'Home'
      }
    },
    components: {
      Home,
      Kind,
      Cart,
      User
    }
  }).mount('#app')
</script>
</html>

当一个组件在 <KeepAlive> 中被切换时,它的 activateddeactivated 生命周期钩子将被调用,用来替代 mountedunmounted。这适用于 <KeepAlive> 的直接子节点及其所有子孙节点。

9.3activated、deactivated钩子

完整案例:08_dynamic/53_activated_deacvidated.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>动态组件</title>
</head>
<body>
  <div id="app">
    <ul>
      <li @click="currentTab='Home'">首页</li>
      <li @click="currentTab='Kind'">分类</li>
      <li @click="currentTab='Cart'">购物车</li>
      <li @click="currentTab='User'">我的</li>
    </ul>
    <!-- 动态组件默认切换时 执行的是组件的 销毁 和 重新创建 -->
    <!-- 可以使用 KeepAlive 保留组件的状态,避免组件的重新渲染 -->
    <keep-alive>
      <component :is="currentTab"></component>
    </keep-alive>
  </div>
</body>
<script src="../lib/vue.global.js"></script>
<script>
  const Home = {
    template: `
      <div>
        首页  <input placeholder="首页"/>
      </div>
    `,
    created () { console.log('Home created') },
    mounted () { console.log('Home mounted') },
    unmounted () { console.log('Home unmounted') },
    activated () { console.log('Home 显示')},
    deactivated () { console.log('Home 隐藏')}
  }
  const Kind = {
    template: `
      <div>
        分类  <input placeholder="分类"/>
      </div>
    `,
    created () { console.log('Kind created') },
    mounted () { console.log('Kind mounted') },
    unmounted () { console.log('Kind unmounted') },
    activated () { console.log('Kind 显示')},
    deactivated () { console.log('Kind 隐藏')}
  }
  const Cart = {
    template: `
      <div>
        购物车  <input placeholder="购物车"/>
      </div>
    `,
    created () { console.log('Cart created') },
    mounted () { console.log('Cart mounted') },
    unmounted () { console.log('Cart unmounted') },
    activated () { console.log('Cart 显示')},
    deactivated () { console.log('Cart 隐藏')}
  }
  const User = {
    template: `
      <div>
        我的  <input placeholder="我的"/>
      </div>
    `,
    created () { console.log('User created') },
    mounted () { console.log('User mounted') },
    unmounted () { console.log('User unmounted') },
    activated () { console.log('User 显示')},
    deactivated () { console.log('User 隐藏')}
  }

  Vue.createApp({
    data () {
      return {
        currentTab: 'Home'
      }
    },
    components: {
      Home,
      Kind,
      Cart,
      User
    }
  }).mount('#app')
</script>
</html>

要不不缓存,要缓存都缓存了,这样不好

使用 include / exclude可以设置哪些组件被缓存,使用 max可以设定最多缓存多少个

<!-- 用逗号分隔的字符串,中间不要家空格 -->
<KeepAlive include="a,b">
	<component :is="view"></component>
</KeepAlive>

<!-- 正则表达式 (使用 `v-bind`) -->
<KeepAlive :include="/a|b/">
	<component :is="view"></component>
</KeepAlive>

<!-- 数组 (使用 `v-bind`) -->
<KeepAlive :include="['a', 'b']">
	<component :is="view"></component>
</KeepAlive>

组件如果想要条件性地被 KeepAlive 缓存,就必须显式声明一个 name 选项。

完整案例:08_dynamic/54_keep_alive_include.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>动态组件</title>
</head>
<body>
    <div id="app">
       <ul>
         <li @click="currentTab='Home'">首页</li>
         <li @click="currentTab='Kind'">分类</li>
         <li @click="currentTab='Cart'">购物车</li>
         <li @click="currentTab='User'">我的</li>
       </ul>
    <!-- 动态组件默认切换时 执行的是组件的 销毁 和 重新创建 -->
       <!-- 可以使用 KeepAlive 保留组件的状态,避免组件的重新渲染 -->
       <!-- 字符串逗号分隔,千万不要加空格 -->
       <!-- <keep-alive include="home,user">
      <component :is="currentTab"></component>
       </keep-alive> -->
       <!-- 正则 -->
    <!-- <keep-alive :include="/home|user/">
         <component :is="currentTab"></component>
       </keep-alive> -->
    <!-- 数组 -->
       <keep-alive :include="['home', 'user']">
         <component :is="currentTab"></component>
       </keep-alive>
     </div>
  </body>
<script src="../lib/vue.global.js"></script>
<script>
  const Home = {
      name: 'home',
      template: `
        <div>
        首页  <input placeholder="首页"/>
        </div>
      `,
      created () { console.log('Home created') },
       mounted () { console.log('Home mounted') },
       unmounted () { console.log('Home unmounted') },
       activated () { console.log('Home 显示')},
       deactivated () { console.log('Home 隐藏')}
     }
     const Kind = {
       name: 'kind',
       template: `
         <div>
           分类  <input placeholder="分类"/>
         </div>
       `,
       created () { console.log('Kind created') },
       mounted () { console.log('Kind mounted') },
       unmounted () { console.log('Kind unmounted') },
       activated () { console.log('Kind 显示')},
       deactivated () { console.log('Kind 隐藏')}
     }
     const Cart = {
      name: 'cart',
      template: `
         <div>
           购物车  <input placeholder="购物车"/>
         </div>
       `,
       created () { console.log('Cart created') },
       mounted () { console.log('Cart mounted') },
       unmounted () { console.log('Cart unmounted') },
       activated () { console.log('Cart 显示')},
       deactivated () { console.log('Cart 隐藏')}
     }
     const User = {
       name: 'user',
       template: `
         <div>
           我的  <input placeholder="我的"/>
         </div>
       `,
       created () { console.log('User created') },
       mounted () { console.log('User mounted') },
      unmounted () { console.log('User unmounted') },
      activated () { console.log('User 显示')},
       deactivated () { console.log('User 隐藏')}
     }
   
     Vue.createApp({
       data () {
         return {
           currentTab: 'Home'
         }
       },
       components: {
         Home,
         Kind,
         Cart,
         User
       }
     }).mount('#app')
   </script>
   </html>

9.4<component>元素

一个用于渲染动态组件或元素的“元组件”

完整案例:08_dynamic/55_component_element.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>55_component元素</title>
</head>
<body>
  <div id="app">
    <input type="checkbox" v-model="flag" />
    <!-- 条件为真渲染为 a 标签,否则为 span 标签 -->
    <component :is="flag ? 'a' : 'span'">你好</component>

    <component :is="flag ? 'my-com1' : 'my-com2'"></component>
  </div>
</body>
<script src="lib/vue.global.js"></script>
<script>
  const Com1 = {
    template: `<div>com1</div>`
  }
  const Com2 = {
    template: `<div>com2</div>`
  }
  Vue.createApp({
    components: {
      MyCom1: Com1,
      MyCom2: Com2
    },
    data () {
      return {
        flag: false
      }
    }
  }).mount('#app')
</script>
</html>

也可以渲染组件

9.5 DOM 模板解析注意事项(is=“vue:xxx”)-了解

is attribute 用于原生 HTML 元素时,它将被当作 Customized built-in element,其为原生 web 平台的特性。

但是,在这种用例中,你可能需要 Vue 用其组件来替换原生元素,如 DOM 模板解析注意事项所述。你可以在 is attribute 的值中加上 vue: 前缀,这样 Vue 就会把该元素渲染为 Vue 组件(my-row-component为自定义组件):

<table>
  <tr is="vue:my-row-component"></tr>
</table>

完整案例:08_dynamic/56_DOM模板解析注意事项.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="app">
    <table>
      <tr>
        <th>序号</th>
        <th>姓名</th>
      </tr>
      <!-- <my-tr></my-tr> -->
      <tr is="vue:my-tr"></tr>
    </table>
  </div>
</body>
<template id="tr">
  <tr>
    <td>1</td>
    <td>吴大勋</td>
  </tr>
</template>
<script src="../lib/vue.global.js"></script>
<script>
  const Tr = {
    template: '#tr'
  }

  Vue.createApp({
    components: {
      MyTr: Tr
    }
  }).mount('#app')
</script>
</html>

注意不要使用绑定属性

10.异步组件

在大型项目中,我们可能需要拆分应用为更小的块,并仅在需要时再从服务器加载相关组件。Vue 提供了 defineAsyncComponent 方法来实现此功能

10.1 全局API

学习:defineAsyncComponent()

import { defineAsyncComponent } from 'vue'

const AsyncComp = defineAsyncComponent(() => {
  return new Promise((resolve, reject) => {
    // ...从服务器获取组件
    resolve(/* 获取到的组件 */)
  })
})
// ... 像使用其他一般组件一样使用 `AsyncComp`

完整案例:09_async/57_defineAsyncComponent.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>异步组件</title>
</head>
<body>
  <div id="app">
    <my-test></my-test>
    <my-com></my-com>
  </div>
</body>
<template id="com">
  <div>异步加载组件</div>
</template>
<script src="../lib/vue.global.js"></script>
<script>
  const Com = {
    template: '#com'
  }

  const MyCom = Vue.defineAsyncComponent(() => {
    return new Promise(resolve => {
      setTimeout(() => {
        resolve(Com)
      }, 3000);
    })
  })

  Vue.createApp({
    components: {
      MyTest: Com,
      MyCom
    }
  }).mount('#app')
</script>
</html>

10.2加载函数

学习:() => import()

ES 模块动态导入也会返回一个 Promise,所以多数情况下我们会将它和 defineAsyncComponent 搭配使用。类似 Vite 和 Webpack 这样的构建工具也支持此语法 (并且会将它们作为打包时的代码分割点),因此我们也可以用它来导入 Vue 单文件组件

import { defineAsyncComponent } from 'vue'

const AsyncComp = defineAsyncComponent(() =>
  import('./components/MyComponent.vue')
)

以后讲解项目时可以用到,需要在脚手架环境中使用(单文件组件中使用

10.3 <Suspense>组件

<Suspense> 是一个内置组件,用来在组件树中协调对异步依赖的处理。它让我们可以在组件树上层等待下层的多个嵌套异步依赖项解析完成,并可以在等待时渲染一个加载状态。

vue3中新增的

完整案例:09_async/58_Suspense.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>异步组件</title>
</head>
<body>
  <div id="app">
    <my-test></my-test>
    <!-- 组件未加载完毕,显示 正在加载... 加载完毕 显示组件 -->
    <Suspense>
      <my-com></my-com>
      <template #fallback>
        正在加载。。。
      </template>
    </Suspense>
  </div>
</body>
<template id="com">
  <div>异步加载组件</div>
</template>
<script src="../lib/vue.global.js"></script>
<script>
  const Com = {
    template: '#com'
  }

  const MyCom = Vue.defineAsyncComponent(() => {
    return new Promise(resolve => {
      setTimeout(() => {
        resolve(Com)
      }, 3000);
    })
  })

  Vue.createApp({
    components: {
      MyTest: Com,
      MyCom
    }
  }).mount('#app')
</script>
</html>

后期可以和 Transition,KeepAlive,路由等结合使用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/157500.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

磨金石教育摄影技能干货分享|这些可爱有趣的瞬间,是如何拍到的

1 蚂蚁扛起地球这张照片据说是摄影师花了四个小时跟踪观察蚂蚁&#xff0c;才在偶然的情况下抓拍到的。一只红色的小蚂蚁大概在一条树枝上用触角和额头顶起一颗水滴。垂直站立的样子像是在发力&#xff0c;又好像在保持平衡&#xff0c;看起来非常可爱&#xff0c;又非常的有趣…

dvwa中的文件上传漏洞

环境&#xff1a;Metasploitable2: 192.168.11.157 msfadmin/msfadmin dvwa版本&#xff1a;Version 1.0.7 (Release date: 08/09/10)kail机器&#xff1a;192.168.11.156 root/Root_123一、什么是文件上传漏洞?文件上传&#xff08;File Upload&#xff09;是大部分Web应用都…

【Java面试】泛型

文章目录说一说你对泛型的理解介绍一下泛型擦除List<? super T>和List<? extends T>有什么区别&#xff1f;说一说你对泛型的理解 Java集合有个缺点—把一个对象“丢进”集合里之后&#xff0c;集合就会“忘记”这个对象的数据类型&#xff0c;当再次取出该对象…

B站 全套java面试(200道)问题MD(题目+答案)

Java面向对象有哪些特征&#xff0c;如何应用 ​ 面向对象编程是利用类和对象编程的一种思想。万物可归类&#xff0c;类是对于世界事物的高度抽象 &#xff0c;不同的事物之间有不同的关系 &#xff0c;一个类自身与外界的封装关系&#xff0c;一个父类和子类的继承关系&…

ArcGIS基础实验操作100例--实验87计算矩形方向

本实验专栏参考自汤国安教授《地理信息系统基础实验操作100例》一书 实验平台&#xff1a;ArcGIS 10.6 实验数据&#xff1a;请访问实验1&#xff08;传送门&#xff09; 空间分析篇--实验87 计算矩形方向 目录 一、实验背景 二、实验数据 三、实验步骤 &#xff08;1&…

k8S集群切换IP

背景&#xff1a; 在实际交付场景中&#xff0c;有搬迁机房、网络未规划完成需要提前部署、客户使用DHCP分配IP、虚拟机IP更换等场景。目前统一的方案还是建议全部推到重新部署一套业务环境&#xff0c;存在环境清理不干净、重新部署成本较高、周期长、客户对产品能力质疑等问…

Mac - 通过 Script 实现更换桌面壁纸

目录 一.引言 二.OsaScript 实现自动换壁纸 1.切换壁纸脚本 2.定时获取 A.修改 Python 脚本 B.修改 Shell 脚本 C.添加 Crontab 三.Iterm2 更换背景 A.打开 Preferences B.选择 Profiles -> Window C.效果图 一.引言 前面介绍了通过 Python Crontab 实现 mac 上…

【分布式】浅谈CAP、BASE理论(1)

CAP理论 起源 CAP定理&#xff0c;又被称作布鲁尔定理。这个定理起源于加州大学柏克莱分校的计算机科学家埃里克布鲁尔在2000年的分布式计算原理研讨会上提出的一个猜想1。在2002年&#xff0c;麻省理工学院的赛斯吉尔伯特和南希林奇发表了布鲁尔猜想的证明&#xff0c;使之成…

Portraiture2023免费后期修图插件磨皮神器DR5

后期修图中有一个非常关键&#xff0c;而且不可或缺的步骤&#xff0c;那就是磨皮&#xff0c;磨皮的方法有很多种&#xff0c;但最方便快捷就能达到大片级效果的方法&#xff0c;莫过于使用磨皮插件了&#xff0c;Portraiture图像磨皮插件&#xff0c;支持PS CC- CC2023&#…

4.R语言【rehsape2包】介绍、melt( )、cast( )函数、其他使用技巧

b站课程视频链接&#xff1a; https://www.bilibili.com/video/BV19x411X7C6?p1 腾讯课堂(最新&#xff0c;但是要花钱&#xff0c;我花99&#x1f622;&#x1f622;元买了&#xff0c;感觉讲的没问题&#xff0c;就是知识点结构有点乱&#xff0c;有点废话&#xff09;&…

CAD转换PDF怎么转换?新手必备的方法

相信很多从事建筑类的相关工作的小伙伴们对CAD文件都并不陌生&#xff0c;在设计图纸和数据更改大都是使用这类文件的专业工具。这类文件的专业性较强设计出来的图纸也比较规范&#xff0c;但就有一个问题比较麻烦&#xff0c;那便是在不同设备打开需要对应的查阅软件&#xff…

【TypeScript】Ts基本概念

TypeScript基本概念 TypeScript 是什么&#xff1f; 目标&#xff1a;能够说出什么是typescript 内容&#xff1a; TS 官方文档TS 中文参考 - 不再维护 TypeScript 简称&#xff1a;TS&#xff0c;是 JavaScript 的超集&#xff0c;简单来说就是&#xff1a;JS 有的 TS 都有…

振弦采集模块参数配置工具的连接与断开

振弦采集模块参数配置工具的连接与断开 在指令区的【 COM 端口】组合框内操作完成。【端口】 下拉框&#xff1a;列出了本计算机当前已经存在的所有 COM 端口名称&#xff0c;若与模块连接的端口名称未在下拉框中列出&#xff0c;还可通过手工输入端口名的方法自由输入。 【速…

【NI Multisim 14.0原理图文件管理——新建设计文件】

目录 序言 一、原理图文件管理 二、新建设计文件 &#x1f34d;1. 空白文件 &#x1f34d; 2.系统安装模板文件 &#x1f34d;3. 自定义模板文件 序言 NI Multisim最突出的特点之一就是用户界面友好。它可以使电路设计者方便、快捷地使用虚拟元器件和仪器、仪表进行电路设…

C#,图像二值化(21)——局部阈值的韦尔纳算法(Wellner Thresholding)及源代码

1 韦尔纳算法&#xff08;Wellner Throsholding&#xff09;摘要针对计算大量缺陷时速度较慢且图像阈值不平滑的Wellner算法&#xff0c;本文提出了两种改进方案&#xff0c;第一种是一维平滑算法&#xff08;ODSA&#xff09;&#xff0c;第二种是基于第一种算法的&#xff0c…

《MYSQL实战45讲》笔记(11-20)

11&#xff1a;怎么给字符串字段加索引&#xff1f; 业务场景&#xff1a;基于字符串字段做查询。例如邮箱登录等等。 前缀索引的优势&#xff1a;相对于整个字段索引&#xff0c;前缀索引的占用空间更小。 前缀索引带来的问题&#xff1a;区分度过低的时候会额外扫描次数。 …

CATIA和CAD超全对比,来看看哪个好用

从文明伊始&#xff0c;绘图和制图就对人类发展发挥着重要作用&#xff0c;它通过图形表示使概念更容易理解&#xff0c;这导致了“工程艺术”的发展。绘图是一种使用图片和符号来呈现概念、想法或观点的方式&#xff0c;而绘图是任何现实世界对象或事物的图形表示。为了简化数…

1、Visual Studio 2017安装

目录 一、简介 二、安装步骤 三、Visual Studio 2017的使用 一、简介 Visual Studio是微软推出的一款C编译器&#xff0c;将“高级语言”翻译为“机器语言&#xff08;低级语言&#xff09;”的程序&#xff0c;VS是一个非常完整的开发工具集&#xff0c;包括了所有软件生命…

Vue2.0开发之——组件数据共享-父向子传值(38)

一 概述 组件之间的关系父向子传值子向父传值 二 组件之间的关系 2.1 组件之间的关系 在项目开发中&#xff0c;组件之间的最常见的关系分为如下两种 父子关系兄弟关系 2.2 父子组件之间的数据共享 父子组件之间的数据共享又分为: 父 -> 子共享数据子 -> 父共享数据…

070-JAVA项目实训:仿QQ即时通讯软件讲座五(讲解用户注册功能)

【上一讲】069-JAVA项目实训:仿QQ即时通讯软件讲座四(讲解系统登录功能)_CSDN专家-赖老师(软件之家)的博客-CSDN博客 本文主要内容是实现注册QQ用户功能,自动获取本机IP地址,与系统用户判断端口是否唯一,使用的主要技术如下: 1.使用数据库技术完成注册功能; 2.自动…