Vue3全家桶 - Vue3 - 【6】组件(注册组件 + 组件通信 + 透传属性和事件 + 插槽 + 单文件CSS + 依赖注入)

news2024/11/18 5:29:33

组件

一、 注册组件

1.1 ❌ 全局注册

  • 目标文件:main.js
  • 语法
    import { createApp } from 'vue'
    import App from './App.vue'
    const app = createApp(App)
    
    // 全局注册
    app.component('组件名字', 需要注册组件)
    
    app.mount('#app')
    
  • 缺陷:
    • 全局注册,但并没有被使用的组件无法生产打包时被自动移除(也叫tree-shaking)。如果,你全局注册了一个组件,即使它并没有被实际使用,它仍然会出现在打包后的JS文件中;
    • 全局注册在大型项目中使用项目依赖关系变得不那么明确。在父组件中使用子组件时,不太容易定位子组件的实现。和使用过多的全局变量一样,这可能会影响应用长期的可维护性;

1.2 ✅ 局部注册

  • 哪里注册哪里使用;
  • 选项式的局部注册和Vue2的局部注册一样,这里就不过多介绍了;
  • <script setup>的单文件中:
    • 不用注册,导入之后直接使用;
    • 示例展示:
      <script setup>
          import ComponentA from './ComponentA.vue'
      </script>
      
      <template>
          <ComponentA />
      </template>
      
  • 如果没有<script setup>
    • 需要使用 components 选项来显示注册;
    • 对于每个 components 对象里的属性,它们的 key 名就是注册的组件名,而值就是相应组件的实现;
    • 示例展示:
      import ComponentA from './ComponentA.js'
      
      export default {
          components: {
              // 使用了ES6对象简写语法
              ComponentA
          },
          setup() {
              // ...
          }
      }
      

二、 组件通信

2.1 父传子 - props

  • 需要在子组件中声明 props 来接收传递的数据的属性,有两种声明 props 的方式:
    • ❌ 字符串数组形式;
    • 对象形式

2.1.1 声明与使用

  • 组合式API:
    • 采用 defineProps() 宏来声明接收传递的数据;
    • JS 中可使用 defineProps() 返回的对象来访问声明的自定义属性;
    • template 中,可直接访问 defineProps() 中声明的自定义属性;
  • 选项式API:
    • 可以提供 props 选项来声明接收传递的数据;
    • JS 中可使用 this.$props 来访问声明的自定义的属性;
    • template 中,可直接使用 props 中声明的自定义属性;

2.1.2 ❌ 字符串数组形式

  • 组合式API:
    <script setup>
        // 使用 defineProps 宏来声明
        // 声明接收父组件传递的属性值:自定义属性
        defineProps(['属性名1', '属性名2', ……]) 
    </script>
    
  • 选项式API:
    <script>
        export default {
            // 使用 props 选项
            props: ['属性名1', '属性名2', ……]
        }
    </script>
    

2.1.3 ✅ 对象形式

  • 对象形式声明的 props ,可以对传递的值进行校验,如果传递的值不满足类型要求,会在浏览器控制台抛出警告来提醒使用者;
  • 对象形式声明的 propskeyprop的名称,值则为约束条件;
  • 对象中的属性
  • type:类型,如Number、String、Boolean、Array、Object、Date、Function、Symbol等等
  • default:默认值,对象数组 应当使用 工厂函数返回(就是用箭头函数返回默认值);
  • required:是否必填,布尔值reqired 和 default 二选一);
  • validator:自定义校验,函数类型;

组合式API

  • 示例展示:
    • 父组件:
      <script setup>
        import Son from '@/components/18-组件通信/父传子.vue'
        import { ref, reactive, onMounted } from 'vue'
      
        let name = ref('才浅')
      
        let age = ref(22)
      
        let gender = ref('男')
      
        const obj = reactive({
          title: '目前主要掌握的技术栈',
          class: 'Vue2、Vue3、JS',
          post: '初级前端开发工程师'
        })
      
        let flag = ref(true)
      </script>
      
      <template>
        <h4>组件通信 - 父传子</h4>
        <!-- 1.有flag,不传obj,有默认值 -->
        <Son :name="name" :age="age" :gender="gender" flag></Son>
      
        <!-- 2.无flag,传递obj,无默认值 -->
        <Son :name="name" :age="age" :gender="gender" :obj="obj"></Son>
      
        <!-- 3.无flag,不传obj,有默认值 -->
        <Son :name="name" :age="age" :gender="gender"></Son>
      
        <!-- 4.都不传递 -->
        <Son></Son>
      </template>
      
    • 子组件:
      <script setup>
          import { ref, reactive, onMounted } from 'vue'
      
          // EXPLAIN defineProps() - 声明接收父组件传递的属性值:自定义属性
      
          // EXPLAIN 字符串数组形式
          // let propsData = defineProps(['name', 'age', 'gender', 'obj', 'flag'])
      
          // EXPLAIN 对象形式
          const propsData = defineProps({
              // EXPLAIN 一个类型直接写就可以
              name: String,
              // EXPLAIN 两个类型及以上,就需要用到数组包裹起来
              age: [Number, String], // age 既可以是数组也可以是字符串
              gender: String,
              flag: Boolean,
              obj: {
                  typr: Object, // 指明类型
                  // EXPLAIN required 和 default 二者出现一个(既然是必传,要什么默认值,反之,同理)
                  // required: true,  // 是否必传
                  default: () => ({
                      title: '喜欢的动漫',
                      class: '完美世界、不良人、一念永恒、吞噬星空、……',
                      post: '废柴'
                  })
              }
          })
      
          // EXPLAIN JS中,需要通过 defineProps() 返回的对象来访问声明的自定义属性 
          const print = () => {
              Object.keys(propsData).forEach(item => {
                  console.log(`propsData.${item} = ${propsData[item]}`)
              })
          }
      
          const addTechnology = () => {
      
          }
      
          // EXPLAIN 子组件不能直接修改父组件传递的数据,会在控制台抛出一个警告
          const changeNameProp = () => {
              propsData.name = '废柴'
          }
      </script>
      
      <template>
          <!-- template 中使用 父组件传递的数据,直接使用自定义属性名即可 -->
          <div>
              <p>姓名:{{ name }}</p>
              <p>年龄:{{ age }}</p>
              <p>性别:{{ gender }}</p>
          </div>
          <hr>
      
          <div>
              <h4>{{ obj.title }}</h4>
              <p>{{ obj.class }}</p>
              <p>{{ obj.post }}</p>
          </div>
          <hr>
      
          <button @click="print">打印 propsData 中的值</button>
          <hr>
      
          // 鼠标右键点击,修改值
          <button @click="addTechnology" @mousedown.right="changeNameProp">增加掌握技术栈 - 子传父</button>
      </template>
      
    • 效果展示:
      • flag,不传obj,有默认值:
        • 直接写flag,子组件就显示 true;
          image.png
      • 无flag,传递obj,无默认值:
        • 即使有默认值,显示的数据还是传递的数据;
        • 不写flag,子组件显示 false;
          image.png
      • 无flag,不传obj,有默认值:
        image.png
      • 都不传递,并且obj没设默认值:
        • 除了flag,其他的都是undefined;
          image.png
        • 在控制台,抛出一个关于 obj必传 的警告;
          image.png
      • 子组件中的prop是只读的,不能修改值:
        image.png

选项式API

  • 父组件:
    <script>
    import ButtonVue from './components/Button.vue';
    export default {
        components: { ButtonVue },
        data: () => ({
            isError: false, // 主题
            isFlat: false, // 阴影
            btnText: '普通按钮'// 按钮文本
        })
    }
    </script>
    
    <template>
        主题:<input type="checkbox" v-model="isError">
        阴影:<input type="checkbox" v-model="isFlat">
        按钮文本:<input type="text" v-model="btnText">
        <hr>
        <!-- 父向子传值,可采用属性的方式赋值 -->
        <ButtonVue :title="btnText" :error="isError" :flat="isFlat" />
    </template>
    
  • 子组件:
    <script>
    export default {
        // 自定义属性选项
        props: {
            title: {
                type: String,
                required: true
            },
            error: Boolean,
            flat: Boolean,
            tips: {
                type: String,
                default: '我是一个普通的按钮'
            }
        },
        methods: {
            showPropsData() {
                // 在选项式 API JS 中,可以通过 this.$props 来访问 props 中的内容
                console.log(this.$props)
                console.log(this.$props.title)
                console.log(this.$props.error)
                console.log(this.$props.flat)
            },
            changeErrorProps() {
                // 不能直接修改 props 的数据,因为是只读的
                this.$props.error = !this.$props.error
            }
        }
    }
    </script>
    
    <template>
        <!-- 在视图模板上,可直接使用 props 中的属性 -->
        <button :title="tips" :class="{ error, flat }" @click="showPropsData" @mousedown.right="changeErrorProps">
            {{ title }}
        </button>
    </template>
    
    <style>
    button {
        border: none;
        padding: 12px 25px;
    }
    
    .error {
        background-color: rgb(197, 75, 75);
        color: white;
    }
    
    .flat {
        box-shadow: 0 0 10px grey;
    }
    </style>
    
  • 🔺 注意
    • 所有prop默认都是可选的(可传可不传),除非声明了required: true(必传);
    • Boolean 外的为传递的可选 prop 将会有一个默认值 undefined
    • Boolean 类型的为传递 prop 将被转换为 false
    • 🔺 子组件中 props 中的数据是 只读 的,只能通过 父组件 进行 更改
    • props 的检验失败后,Vue会抛出一个控制台警告(在开发模式下);
    • prop 的校验是在组件实例被创建之前:
      • 在选项式 API 中,实例的属性(比如datacomputed等) 将在defaultvalidator函数中不可用;
      • 在组合式 API 中,defineProps宏中的参数不可以访问 <script setup> 中定义的其他变量,因为在编译时整个表达式都会被移到外部的函数中;

特别提醒:

  • 关于Boolean类型转换:
  • 为了更贴近原生boolean attributes的行为,声明为Boolean类型的props有特别的类型转换规则:
    • 如声明时:
      • defineProps({ error: Boolean })
    • 传递数据时:
      • <MyComponent error /> :相当于 <MyComponent :error="true" />
      • <MyComponent /> :相当于 <MyComponent :error="false" />

2.2 子传父 - 事件

  • 有时候,父组件在使用子组件时,子组件如何给父组件传值呢?
    1. 子组件声明自定义事件;
    2. 子组件触发自定义事件并进行传值;
    3. 父组件使用子组件时监听对应的自定义事件,并执行父组件中的函数(获取子组件传递值);

  • 1️⃣ 子组件 - 声明组件事件
    • 选项式API 中,子组件 可通过 emits 选项来 声明自定义事件
    • 组合式API 中,子组件 可通过 defineEmits() 宏来 声明自定义事件
    • 声明自定义事件的方式:
      • 字符串数组式声明自定义事件
      • 对象式声明自定义事件
    • 语法
      • 组合式API
        • 字符串数组:
          <script setup>
              // 为了方便,此处的变量名,在后面语法演示就用emit代替
              const 变量名 = defineEmits(['事件1', '事件2', '事件3', ……])
          </script>
          
        • 对象形式:
          <script setup>
              // 为了方便,此处的变量名,在后面语法演示就用emit代替
              const 变量名 = defineEmits({
                  事件1: null,
                  事件2: (val) => {
                      // return true 表示传递的数据通过校验
                      // return false 表示传递的参数没有通过校验,控制台会有警告语句
                      // 也可以自己写一个警告语句
                      // console.warn('xxx')
                      // 不管通没通过校验,val都会传递给父组件
                  }
              })
          </script>
          
      • 选项式API
        • 字符串数组:
          export default {
              emits: ['事件1', '事件2', '事件3', ……]
          }
          
        • 对象形式:
          export default {
              emits: {
                  事件1: null,
                  事件2: (val) => {
                      // return true 表示传递的数据通过校验
                      // return false 表示传递的参数没有通过校验,控制台会有警告语句
                      // 也可以自己写一个警告语句
                      // console.warn('xxx')
                      // 不管通没通过校验,val都会传递给父组件
                  }
              }
          
    • 采用对象形式的好处:
      • 采用对象式声明自定义事件,还可以进行校验传递的参数是否符合预期要求;
      • 对象式声明自定义事件中,属性名自定义事件名属性值 则是 是否验证传递的参数
        • 属性值为 null 则不需要验证;
        • 属性值为 函数 ,参数为传递的数据,函数返回 true 则验证通过,返回 false 则验证失败,验证失败可以用警告语句提示开发者;
      • 🔺 注意
        • 无论是 true 还是 false 都会继续执行下去,父组件都会获取到传递的值

  • 2️⃣ 子组件 - 触发组件事件
    • 组合式API:
      • 可调用 defineEmits() 宏返回的 emit(event, ...args) 函数来触发当前组件自定义的事件;
      • event:触发事件名,字符串类型;
      • ...args:传递参数,可没有,可多个;
    • 选项式API:
      • 可通过组件的当前实例 this.$emit(event, ...args) 来触发当前组件自定义的事件;
    • 语法
      • 选项式API
        // 触发自定义的某个事件
        this.$emit('在emits选项中定义的事件名', 向父组件传递的值)
        // 在 template 中写时,this 可以省略
        
      • 组合式API
        const emit = defineEmits({
            自定义事件1: null,
            自定义事件2: (val) => {
                // 对向父组件传递的数据进行校验
            }
        })
        
        const 子组件事件名 = () => {
            emit('自定义事件', 向父组件传递的数据)
        }
        

  • 3️⃣ 父祖件 - 监听子组件自定义事件
    • 使用 v-on:event="callback" 或者 @event="callback" 来监听子组件是否触发了该事件;
      • event:事件名字(camelCase 形式命名的事件,在父组件中可以使用camel-case形式来监听);
      • callback:回调函数,如果子组件触发该事件,那么在父组件中执行对应的回调函数,回调函数声明参数可自动接收到触发事件传来的值;
    • 语法
      • 组合式API
        <script setup>
            const 父组件触发事件的事件名 = (val) => {
                // val 就是子组件向父组件传递的数据
                // 父组件就可以进行相关操作了
            }
        </script>
        <template>
            <子组件标签 @子组件中自定义事件名="父组件中要触发的事件" />
        </template>
        
      • 选项式API
        <script>
            export default {
                methods: {
                    父组件触发事件的事件名(val) {
                        // val 就是子组件向父组件传递的数据
                        // 父组件就可以进行相关操作了
                    }
                }
            }
        </script>
        <template>
            <子组件标签 @子组件中自定义事件名="父组件中要触发的事件" />
        </template>
        

  • 基于上一小节的代码的示例展示:
    • 父组件:
      <script setup>
          import Son from '@/components/18-组件通信/父传子.vue'
          import {ref, reactive } from 'vue'
      
          let name = ref('才浅')
      
          let age = ref(22)
      
          let gender = ref('男')
      
          const obj = reactive({
              title: '目前主要掌握的技术栈',
              species: 'Vue2、Vue3、JS',
              post: '初级前端开发工程师'
          })
      
          const addSpecies = (val) => {
              obj.species += val
          }
      
          let flag = ref(true)
      </script>
      
      <template>
          <h4>组件通信 - 父传子</h4>
          <!-- 1.有flag -->
          <!-- <Son :name="name" :age="age" :gender="gender" flag></Son> -->
      
          <!-- 2.无flag,传递obj,无默认值 -->
          <!-- <Son :name="name" :age="age" :gender="gender" :obj="obj"></Son>-->
      
          <!-- 3.无flag,不传obj,有默认值 -->
          <!-- <Son :name="name" :age="age" :gender="gender"></Son> -->
      
          <!-- 4.都不传递 -->
          <!-- <Son></Son> -->
      
          <!-- 5.子组件向父组件传值 -->
          <Son :name="name" :age="age" :gender="gender" :obj="obj" @change-obj="addSpecies"></Son>
      </template>
      
    • 子组件:
      <script setup>
          import { ref, reactive } from 'vue'
      
          // EXPLAIN defineProps() - 声明接收父组件传递的属性值:自定义属性
      
          // EXPLAIN 字符串数组形式
          // let propsData = defineProps(['name', 'age', 'gender', 'obj', 'flag'])
      
          // EXPLAIN 对象形式
          const propsData = defineProps({
              // EXPLAIN 一个类型直接写就可以
              name: String,
              // EXPLAIN 两个类型及以上,就需要用到数组包裹起来
              age: [Number, String], // age 既可以是数组也可以是字符串
              gender: String,
              flag: Boolean,
              obj: {
                  type: Object, // 指明类型
                  // EXPLAIN required 和 default 二者只能出现一个(既然是必传,要什么默认值;反之同理)
                  required: true,  // 是否必传
                  // default: () => ({
                  //   title: '喜欢的动漫',
                  //   class: '完美世界、不良人、一念永恒、吞噬星空、……',
                  //   post: '废柴'
                  // })
              }
          })
      
          // TODO 自定义事件
          // EXPLAIN 字符串数组形式
          // const emit = defineEmits(['changeObj'])
      
          // EXPLAIN 对象形式
          const emit = defineEmits({
              // 不需要对参数进行验证,属性值写 null 即可
              // changObj: null,
              // 对传递的参数进行验证,函数返回true表示验证通过,反之则不通过,不管通不通过,该值都会传给父组件
              changeObj: (val) => val.constructor === String
          })
      
          // EXPLAIN JS中,需要通过 defineProps() 返回的对象来访问声明的自定义属性
          const print = () => {
              Object.keys(propsData).forEach(item => {
                  console.log(`propsData.${item} = ${propsData[item]}`)
              })
          }
      
          // TODO 向父组件传递数据
          const addTechnology = () => {
              emit('changeObj', 'TypeScript, React')
          }
      
          // EXPLAIN 子组件不能直接修改父组件传递的数据,会在控制台抛出一个警告
          const changeNameProp = () => {
              propsData.species = '废柴'
          }
      </script>
      
      <template>
          <!-- template 中使用 父组件传递的数据,直接使用自定义属性名即可 -->
          <div>
              <p>姓名:{{ name }}</p>
              <p>年龄:{{ age }}</p>
              <p>性别:{{ gender }}</p>
          </div>
          <hr>
      
          <div>
              <h4>{{ obj.title }}</h4>
              <p>{{ obj.species }}</p>
              <p>{{ obj.post }}</p>
          </div>
          <hr>
      
          <button @click="print">打印 propsData 中的值</button>
          <hr>
      
          <button @click="addTechnology" @mousedown.right="changeNameProp">增加掌握技术栈 - 子传父</button>
      </template>
      

  • 子传父案例:
    • 选项式API
      • 父组件:
        <script>
            import StudentVue from './components/Student.vue';
        
            export default {
                components: { StudentVue },
                data: () => ({
                    student: {
                        name: 'Jack',
                        age: 18,
                        sex: '男'
                    }
                }),
                methods: {
                // 获取子组件传递值
                    getNewAge(newAge) {
                        console.log('年龄的新值:' + newAge)
                        this.student.age = newAge
                    },
                    getNewAgeAndName(newAge, newName) {
                        console.log('年龄的新值:' + newAge)
                        console.log('名字的新值:' + newName)
                        this.student.age = newAge
                        this.student.name = newName
                    },
                    getNewStudent(stu){
                        console.log('学生新值:');
                        console.log(stu);
                        this.student.age = stu.age
                        this.student.name = stu.name
                        this.student.sex = stu.sex
                    }
                }
            }
        </script>
        
        <template>
            {{ student }}
            <hr>
            <StudentVue 
                @change-student="getNewStudent"
                @change-age-and-name="getNewAgeAndName" 
                @change-age="getNewAge" />
        </template>
        
      • 子组件:
        <script>
            export default {
                // 自定义事件选项 - 字符串数组(不能对传递的参数进行校验)
                // emits: ['changeAge', 'changeAgeAndName', 'changeStudent']
                
                // 自定义事件选项 - 对象形式
                emits: {
                    changeAge: null,  // 无需验证
                    changeAgeAndName: null,  // 无需验证
                    changeStudent: stu => {
                        if (stu.age <= 0) {
                            console.warn('年龄不得小于等于0')
                            // false:验证不通过,会有警告语句,父组件依旧可以获取该值
                            return false
                        }
                        // true:验证通过
                        return true
                    }
                },
                methods: {
                    emitEventAge() {
                        // 选项式通过 this.$emit 触发自定义事件,并传值
                        this.$emit('changeAge', 30)
                    }
                }
            }
        </script>
        
        <template>
            <button @click="emitEventAge">更改年龄</button>
            <br>
            <br>
            <button @click="$emit('changeAgeAndName', 10, 'Annie')">
                更改年龄和名字
            </button>
            <br>
            <br>
            <button @click="$emit('changeStudent', { age: 40, name: 'Drew', sex: '男' })">
                更改学生(验证通过)
            </button>
            <br>
            <br>
            <button @click="$emit('changeStudent', { age: -10, name: 'Tom', sex: '男' })">
                更改学生(验证失败)
            </button>
        </template>
        
    • 组合式API
      • 父组件:
        <script setup>
            import { reactive } from 'vue';
            import StudentVue from './components/Student.vue';
        
            let student = reactive({
                name: 'Jack',
                age: 18,
                sex: '男'
            })
        
            // 获取子组件传递值
            function getNewAge(newAge) {
                console.log('年龄的新值:' + newAge)
                student.age = newAge
            }
            function getNewAgeAndName(newAge, newName) {
                console.log('年龄的新值:' + newAge)
                console.log('名字的新值:' + newName)
                student.age = newAge
                student.name = newName
            }
            function getNewStudent(stu){
                console.log('学生新值:');
                console.log(stu);
                student.age = stu.age
                student.name = stu.name
                student.sex = stu.sex
            }
        </script>
        
        <template>
            {{ student }}
            <hr>
            <StudentVue 
                @change-student="getNewStudent"
                @change-age-and-name="getNewAgeAndName" 
                @change-age="getNewAge" />
        </template>
        
      • 子组件:
        <script setup>
            // 自定义事件 - 字符串数组形式
            const emit = defineEmits(['changeAge', 'changeAgeAndName', 'changeStudent'])
            
            // 自定义事件 - 对象形式
            let emit = defineEmits({
                changeAge: null, // 无需验证
                changeAgeAndName: null, // 无需验证
                changeStudent: stu => {
                    if (stu.age <= 0) {
                        console.warn('年龄不得小于等于0')
                        // false:验证不通过,会有警告语句,父组件依旧可以获取该值
                        return false
                    }
                    // true:验证通过
                    return true
                }
            })
        
            function emitEventAge() {
                // 选项式通过 this.$emit 触发自定义事件,并传值
                emit('changeAge', 30)
            }
        </script>
        
        <template>
            <button @click="emitEventAge">更改年龄</button>
            <br>
            <br>
            <button @click="emit('changeAgeAndName', 10, 'Annie')">
                更改年龄和名字
            </button>
            <br>
            <br>
            <button @click="emit('changeStudent', { age: 40, name: 'Drew', sex: '男' })">
                更改学生(验证通过)
            </button>
            <br>
            <br>
            <button @click="emit('changeStudent', { age: -10, name: 'Tom', sex: '男' })">
                更改学生(验证失败)
            </button>
        </template>
        

2.3 组件通信 - 购物车综合案例

三、 透传属性和事件

  • Vue3官网-透传;

3.1 如何透传属性和事件

  • 父组件在使用子组件的时候,如何“透传属性和事件”给子组件呢?
    1. 透传属性和事件并没有在子组件中用 propsemits 声明;
    2. 透传属性和事件最常见的如 click、class、id、style
    3. 子组件 只有 一个根元素 时,透传属性事件自动添加该根元素 上;如果根元素已有 classstyle 属性,它会自动合并
  • 示例展示:
    • 父组件:
      <script setup>
          import { ref, reactive } from 'vue'
          import Son1 from '@/components/20-透传属性和事件/子1.vue'
          const test = () => console.log('透传属性和事件')
      </script>
      
      <template>
          <!--
              透传的属性 (class、style)并没有在子组件中的 props 中声明
              透传的事件 (click) 并没有在子组件中的 emits 中声明
          -->
          <Son1
              class="son1"
              style="background-color: rgb(0, 255, 243)"
              @click="test"
          ></Son1>
      </template>
      
    • 子组件:
      <script setup>
      </script>
      
      <template>
          <div 
              class="son-content"
              :style="{
                  border: '4px solid purple',
                  boxShadow: '0 0px 8px 2px grey'
          }">
              <h3>子组件</h3>
          </div>
      </template>
      
    • 效果展示:
      • 可以看到父组件子组件标签上绑定的属性和事件会出现在子组件中;
        image.png

3.2 如何禁止透传属性和事件

  • 当只有 一个根元素 的时候,透传属性和事件自动添加该根元素上
  • 禁止透传属性和事件:
    • 添加给子组件
    • 组合式API:
      • <script setup> 中,需要一个额外的<script>默认导出一个对象,来写inheritAttrs: false 选项来禁止;
    • 选项式API:
      • 可以在组件选项中设置 inheritAttrs: false来阻止;
  • 示例展示:
    • 基于上一小结的代码;
    • 子组件(组合式API):
      // TODO 禁止透传属性和事件
      <script>
          export default {
              // 阻止自动透传给唯一的根组件
              inheritAttrs: false
          }
      </script>
      <script setup>
          import { ref, reactive, onMounted } from 'vue'
          onMounted(() => {});
      </script>
      
      <template>
          <div 
              class="son-content"
              :style="{
                  border: '4px solid purple',
                  boxShadow: '0 0px 8px 2px grey'
          }">
              <h3>子组件</h3>
          </div>
      </template>
      
    • 子组件(选项式API):
      // TODO 禁止透传属性和事件
      <script>
          export default {
              // 阻止自动透传给唯一的根组件
              inheritAttrs: false
          }
      </script>
      
      <template>
          <div 
              class="son-content"
              :style="{
                  border: '4px solid purple',
                  boxShadow: '0 0px 8px 2px grey'
          }">
              <h3>子组件</h3>
          </div>
      </template>
      
  • 效果展示:
    image.png

3.3 多根元素的透传属性和事件

  • 多根节点的组件没有自动“透传属性和事件”,由于 vue 不确定要将“透传属性和事件”绑定到哪里【也就是说,透传属性和事件默认会绑定到唯一的根节点上】,所以我们需要 v-bind="$attrs" 来显示绑定,否则将会抛出一个运行警告;
    image.png
  • 注意
    • 绑定对象:子组件中接受透传属性和事件 的元素;
    • 绑定元素数量不受限制;
    • 绑定该属性之后,阻止透传就不会生效;
  • 代码展示:
    • 还是基于 7.3.1 中的代码;
    • 父组件代码没变;
    • 子组件:
      <script setup>
          import { ref, reactive, onMounted } from 'vue'
          onMounted(() => {});
      </script>
      
      <template>
          <button class="chip" :="$attrs"> 普通纸片1 </button>
          <button class="chip" :="$attrs"> 普通纸片2 </button>
          <button class="chip"> 普通纸片3 </button>
          <div class="son-content">
              <h3>子组件1</h3>
          </div>
      </template>
      
      <style scoped>
          .son-content {
              margin-top: 10px;
              border: 4px solid purple;
              box-shadow: 0 0px 8px 2px grey;
          }
      </style>
      
  • 效果展示:
    image.png

3.4 访问 透传属性和事件

  • 组合式API
    • <script setup> 中引入 useAttra() 来访问一个组件的 “透传属性和事件”;
  • 选项式API
    • 通过 this.$attrs 来访问 “透传的属性和事件”
  • 🔺 注意
    • 虽然这里的 attrs 对象总是反映为最新的 “透传属性和事件”,但它并 不是响应式 的(考虑到性能因素),你不能通过侦听器去监听它的变化;
    • 如果 需要响应式,可以使用 prop 或者也可以使用 onUpdated() 使得在每次更新时结合最新的 attrs 执行副作用;
  • 示例展示:
    • 还是基于 7.3.1 的代码,父组件代码没有变;
    • 子组件:
      • 组合式API:
        <script setup>
            import { ref, reactive, onMounted, useAttrs } from 'vue'
            // TODO 使用 变量 接收 透传的属性和事件
            const attrs = useAttrs()
        
            const attrsTest = () => {
                console.log(attrs)
                console.log(attrs.value)
                // attrs 不是响应式的数据,可以进行解构
                const { class: className, style, onClick } = attrs
                console.log(className, style, onClick)
            }
        
            // TODO 执行透传的事件
            const transferEvent = attrs.onClick
        </script>
        
        <template>
            <button class="chip" :="$attrs" @mousedown.right="attrsTest"> 普通纸片1 </button>
            <button class="chip" :="$attrs"> 普通纸片2 </button>
            <button class="chip" @click="attrs.onClick"> 执行透传事件 - 1 </button>
            <button class="chip" @click="transferEvent"> 执行透传事件 - 2 </button>
            <div class="son-content">
                <h3>子组件1</h3>
            </div>
        </template>
        
        <style scoped>
            .son-content {
                margin-top: 10px;
                border: 4px solid purple;
                box-shadow: 0 0px 8px 2px grey;
            }
        </style>
        
        image.png
        • 如果没有使用 <script setup>attrs 会作为 setup() 上下文对象的一个属性暴露:
        export default {
            setup(props, ctx) {
                // 透传 attribute 被暴露为 ctx.attrs
                console.log(ctx.attrs)
            }
        }
        

四、 插槽

  • Vue3官网-插槽;
  • 插槽这块和Vue2没有什么区别,更详细的笔记或例子请移步Vue2 - 插槽;

4.1 什么是插槽

  • 在封装组件时,可以使用 <slot> 元素把不确定的、希望由用户指定的部分定义为插槽;可以理解为给预留的内容提供占位符;
  • 插槽也可以提供默认内容,如果组件的使用者没有为插槽提供任何内容,则插槽内的默认内容会生效;
  • 如果在封装组件时没有预留任何<slot>插槽,用户提供传递一些模板片段内容会失效;
  • image.png

4.2 具名插槽

  • 如果在封装组件时需要预留多个插槽节点,则需要为每个 <slot> 插槽指定具体的 name 名称,这种带有具体名称的插槽叫做 具名插槽
  • 没有指定 name 名称的插槽,会有隐含的名称叫做 default(匿名插槽);
  • <template>元素上使用 v-slot:slotName#slotName 向指定的具名插槽提供内容;

4.3 作用域插槽

  • 如何在向插槽提供内容的同时获得子组件域内的数据:
    • 在声明插槽时使用属性值的方式来传递子组件的数据,这种带有数据的插槽称为作用域插槽
    • template 元素上使用 v-slot:slotNmae="slotProps"#slotNmae="slotPros" 的方式来访问插槽传递的数据;
    • 如果没有使用 template 元素,而是直接在使用子组件中直接给默认插槽提供内容,我们可以在使用该子组件时用 v-slot="slotProps" 来接收该插槽传递的数据对象;
  • 注意
    • slot 插槽上的 name 是一个 Vue 特别保留的属性,不会在作用域插槽中访问到;

五、 单文件组件CSS功能

  • 默认情况下,写在.vue组件中的样式会全局生效,很容易造成多个组件之间的样式冲突问题;
  • 导致组件之间样式冲突的根本原因:
    • 单页面应用过程中,所有组件的 DOM 结构,都是基于唯一的 index.html 页面进行呈现的;

5.1 组件作用域CSS

  • <style>标签带有scoped属性后:
    • 它的CSS只会影响当前组件的元素,父组件的样式将不会渗透到子组件中;
    • 该组件的所有元素编译后会自带一个特定的属性;
      • data-v- + 8位随机哈希值
        image.png
    • <style scoped> 内的选择器,在编译后会自动添加特定的属性选择器;
    • 子组件的根节点会同时被父组件的作用域样式和子组件的作用域样式影响,主要是为了让父组件可以从布局的角度出发,调整其子组件根元素的样式;
      image.png
      <!-- 让下方选择器的样式只作用在该组件中,或者子组件的根元素上 -->
      <!-- 该组件的所有元素及子组件中的根元素会加上固定的属性 -->
      <!-- 该CSS选择器都会自动添加固定的属选择器 -->
      <style scoped>
       /* 选择器 */
      </style>
      

5.2 深度选择器

  • 处于 scoped 样式中的选择器如果想要做更【深度】的选择,既,能影响到子组件,可以使用 :deep() 这个 伪类
  • :deep() 解释(我自己这么理解的):
    .box:deep(h3) {}
    /* 影响 .box 下的 所有h3标签 */
    /* 如果这个h3是直接写在template下面(没有父级容器),则不会受影响 */
    
    :deep(.h3) {}
    /* 影响当前组件所有类名为 .h3 的元素和子组件中的所有类名为 .h3 元素 */
    

5.3 CSS中的v-bind()

  • 单文件组件的 <style> 标签支持 v-bind() CSS 函数将 CSS 的值链接到动态的组件状态(变量);
  • 这个语法同样也适用于 <script setup> ,且支持JavaScript表达式(需要用引号包裹起来);
  • 实际的值会被编译成 哈希化CSS自定义属性,因此CSS本身仍是静态的;
  • 自定义属性 会通过 内联样式 的方式应用到 组件的某个元素上,并且在 源值变更 的时候 响应式地更新
  • 示例展示:
    <script setup>
        import { ref, reactive, onMounted } from 'vue'
        const buttonTheme =reactive({
            backColor: '#6fbbff',
            textColor: '#fff'
        })
    </script>
    
    <template>
        <button>普通按钮</button>
    </template>
    
    <style scoped>
        button {
            background-color: v-bind('buttonTheme.backColor');
            color: v-bind('buttonTheme.textColor');
        }
    </style>
    
  • 效果展示:
    image.png

六、 依赖注入

  • Vue3官网-依赖注入;
  • 业务场景:
    • 有一个深层的子组件,需要一个较远的祖先组件的部分数据;
  • 解决方案:
    • 使用props沿着组件链逐级传递下去;
      image.png
    • 可以在祖先组件中使用provide提供数据,后代组件使用inject注入数据;
      image.png

6.1 provide(提供数据)

  • 在应用层方面:
    • 可通过 app.provide() 为后代提供数据;
    • 语法
      app.provide('属性名', '传递的数据')
      
    • 示例展示:
      • 目标文件:main.js
        // NOTE 从 vue 中导入 createApp 函数
        import { createApp } from 'vue'
        
        import './style.css'
        
        // NOTE 导入根组件 App.vue
        import App from './App.vue'
        
        // NOTE 通过 createApp 函数创建应用实例
        let app = createApp(App)
        
        // NOTE 应用层main注册,为所有的组件提供一个数据
        app.provide('userName', '禁止摆烂_才浅')
        
        // NOTE mount 函数,将应用实例渲染在容器元素里面
        app.mount('#app')
        
      • App.vue组件:
        <script setup>
            import Vue1 from '@/components/22-依赖注入/1.vue'
            import { ref, reactive, onMounted } from 'vue'
        </script>
        
        <template>
            <div class="area" style="background-color: purple;">
                <h3>App 组件</h3>
                <Vue1></Vue1>
            </div>
        </template>
        
        <style scoped>
            .area {
                padding: 10px;
                color: #fff;
            }
        </style>
        
      • 1.vue组件(App.vue的子组件):
        <script setup>
            import Vue2 from './2.vue'
            import { ref, reactive, onMounted } from 'vue'
        </script>
        
        <template>
            <div class="area-1" style="background-color: yellow;">
                <h3>App的子组件</h3>
                <Vue2></Vue2>
            </div>
        </template>
        
        <style scoped>
            .area-1 {
                padding: 10px;
                color: #000;
            }
        </style>
        
      • 2.vue组件(App.vue的孙子组件):
        <script setup>
            import { ref, reactive, onMounted, inject } from 'vue'
            let userName = inject('userName')
        </script>
        
        <template>
            <div class="area-2" style="background-color: #00ffe3;">
                <h3>App的孙子组件</h3>
                <h5>应用层【main.js】提供给的数据: <span>{{ userName }}</span></h5>
            </div>
        </template>
        
        <style scoped>
            .area-2 {
                padding: 10px;
                color: #000;
            }
            
            span {
                padding: 4px;
                border: 3px solid red;
            }
        </style>
        
    • 效果展示:
      image.png
  • 在组件中如何提供:
    • 组合式API
      • <script setup>中,可通过provide()函数来为后代组件提供数据;
      • 语法
        import { provide } from 'vue'
        
        provide('注入数据时的名字', 要提供的数据)
        
      • 示例展示:
        • 目标文件:App.vue
          • App.vue
            <script setup>
                import Vue1 from '@/components/22-依赖注入-组合式API/1.vue'
                import { ref, computed, provide } from 'vue'
            
                // TODO 定义响应式数据
                let title = '博客'
                let subtitle = ref('每天进步一点点')
            
                // TODO 定义方法
                const changeSubtitle =  (val) => {
                    subtitle.value = val
                }
            
                // TODO 使用 provide() 提供数据
                provide('title', title)
                provide('subtitle', subtitle)
                provide('changeSubtitle', changeSubtitle);
            </script>
            
            <template>
                <div class="area" style="background-color: purple;">
                    <h3>App 组件</h3>
                    标题:<input type="text" v-model="title"><br>
                    副标题:<input type="text" v-model="subtitle">
                    <Vue1></Vue1>
                </div>
            </template>
            
            <style scoped>
                .area {
                    padding: 10px;
                    color: #fff;
                }
            </style>
            
      • 运行展示:见 6.2 组合式API
    • 选项式API:【推荐使用函数的方式】
      • 可以通过provide选项为后代提供数据;
      • 语法
        export default {
            provide: {
                return {
                    // 注入非响应式数据
                    注入数据时的名字: 要提供的数据(this.变量名),
                    
                    // 注入响应式数据
                    注入数据时的名字: computed(() => this.变量名),
                    
                    // 注入函数
                    注入数据时的名字: 当前组件的函数名(this.函数名)
                }
            }
        }
        
      • 如果想访问到组件的实例thisprovide必须采用函数的方式【不能用箭头函数】,为保证注入方和供给方之间的响应性链接,必须借助组合式API中的computed()函数提供计算属性,还可以提供修改响应式数据的函数(响应式数据的修改,尽量放在同一组件中,为了好维护);
      • 🔺 注意
        • provide选项通中通过computed()提供的响应式的数据,需要设置app.config.unwrapInjectedREf = true以保证注入会自动解包这个计算属性。这将会在Vue3.3后成为一个默认行为,而我们暂时在此告知此项配置以避免后续升级对代码的破坏性。在3.3后就不要这样做了;
      • 示例展示:
        • 目标文件:App.vue + 2.vue
          • App.vue:
            <!-- 选项式API -->
            <script>
                import Vue1 from '@/components/22-依赖注入/1.vue'
                import { computed } from 'vue'
                export default {
                    components: { Vue1 },
                    data: () => ({
                        title: '博客',
                        subtitle: '每天进步一点点'
                    }),
                    methods: {
                        changeSubtitle (val) {
                          this.subtitle = val
                        }
                    },
                    // NOTE 选项式API中,使用 provide 选项来给后代组件提供数据
                    // NOTE 但是这种方式无法访问组件的实例 this
                    /* provide: {
                    title: '博客',
                    title: this.title
                    } */
            
                    // NOTE 想要访问组件的实例 this,provide必须是函数的形式,并且不能是箭头函数
                    provide() {
                        return {
                        // NOTE 提供的数据 不是响应式的数据 - 注入方 和 提供方 没有响应式的连接
                        title: this.title,
                        // NOTE 提供的数据是 响应式的数据
                        subtitle: computed(() => this.subtitle),
                        // NOTE 提供 修改响应式数据 的函数
                        changeSubtitle: this.changeSubtitle
                        }
                    }
                }
            </script>
            
            <template>
                <div class="area" style="background-color: purple;">
                    <h3>App 组件</h3>
                    标题:<input type="text" v-model="title"><br>
                    副标题:<input type="text" v-model="subtitle">
                    <Vue1></Vue1>
                </div>
            </template>
            
            <style scoped>
                .area {
                    padding: 10px;
                    color: #fff;
                }
            </style>
            
          • 2.vue:
            <!-- 选项式API -->
            <script>
                export default {
                    inject: ['userName', 'title', 'subtitle', 'changeSubtitle']
                }
            </script>
            
            <template>
                <div class="area-2" style="background-color: #00ffe3;">
                    <h3>App的孙子组件</h3>
                    <h5>应用层【main.js】提供的数据: <span>{{ userName }}</span></h5>
                    <h5>App.vue 提供的 非响应式 的数据: <span>{{ title }}</span></h5>
                    <!-- <h5>App.vue 提供的 响应式 的数据: <span>{{ subtitle.value }}</span></h5> -->
                    <h5>App.vue 提供的 响应式 的数据: <span>{{ subtitle }}</span></h5>
                    <button @click="changeSubtitle('禁止摸鱼')">修改App组件中subtitle的值</button>
                </div>
            </template>
            
            <style scoped>
                .area-2 {
                    padding: 10px;
                    color: #000;
                }
            
                span {
                    padding: 4px;
                    border: 3px solid red;
                }
            </style>
            
        • 效果展示:
          image.png

6.2 inject(注入)

组合式API

  • 访问
    • 可通过inject()的返回值来注入祖先组件提供的数据;
  • 语法
    import{ inject } from 'vue'
    // 有祖先组件提供的数据 / 没有祖先组件提供数据【没有祖先组件提供数据的时候会抛出一个警告】
    const 变量名 = inject('注入的数据名')
    // 没有祖先组件提供数据,在当前组件可以设置默认值
    const 变量名 = inject('注入的数据名', 默认值)
    // 默认值可以是任何类型的数据
    
    请将 语法 和 实例 结合阅读
    
  • 如果提供数据的值是一个 ref,注入进来的会是该 ref对象和提供方保持响应式链接
  • 如果注入的数据并没有在祖先组件中提供,则会抛出一个警告,可在 provide()第二个参数 设置默认值 来解决;
  • 它们可以在 JS视图模板直接访问
  • 示例展示:
    • 2.vue
      <script setup>
          import { inject, onMounted } from 'vue';
      
          // TODO 使用 inject() 函数的返回值来注入祖先组件提供的数据
          const _userName = inject('userName')  // 应用层提供的数据
          const title = inject('title')  // 普通数据 - 非响应式数据
          const _subtitle = inject('subtitle')  // ref响应式数据 - _subtitle 也是 ref 对象
          const changeSubtitle = inject('changeSubtitle')  // 函数
          const likes = inject('likes')  // 祖先组件并没有提供数据,则会抛出一个警告
          const anime = inject('anime', '完美世界')  // 祖先组件没有提供数据,当前组件可以在 inject() 的第二个参数设置默认值
      
          onMounted(() => {
              console.log('应用层提供的数据:', _userName)
              console.log('祖先组件提供的非响应式数据:', title)
              console.log('祖先组件提供的响应式数据:', _subtitle)
              console.log('祖先组件提供的函数:', changeSubtitle)
              console.log('注入的数据,祖先组件并没有提供,就会抛出一个警告:', likes)
              console.log('祖先组件没有提供 anime 这个数据,当前组件设置在注入的时候默认值:', anime)
          });
      </script>
      
      <template>
          <div class="area-2" style="background-color: #00ffe3;">
              <h3>App的孙子组件</h3>
              <h5>应用层【main.js】提供的数据: <span>{{ _userName }}</span></h5>
              <h5>App.vue 提供的 非响应式 的数据: <span>{{ title }}</span></h5>
              <!-- <h5>App.vue 提供的 响应式 的数据: <span>{{ subtitle.value }}</span></h5> -->
              <h5>App.vue 提供的 响应式 的数据: <span>{{ _subtitle }}</span></h5>
              <button @click="changeSubtitle('禁止摸鱼')">修改App组件中subtitle的值</button>
              <h5>注入的数据,祖先组件并没有提供,就会抛出一个警告: <span>{{ `${likes}` }}</span></h5>
              <h5>祖先组件没有提供 anime 这个数据,当前组件设置再注入的时候默认值: <span>{{ anime }}</span></h5>
          </div>
      </template>
      
      <style scoped>
          .area-2 {
              padding: 10px;
              color: #000;
          }
      
          span {
              padding: 4px;
              border: 3px solid red;
          }
      
          h5 {
              padding-left: 20px;
              text-align: left;
          }
      </style>
      
  • 运行展示:
    image.png

选项式API

  • 访问
    • 可通过 inject选项 来声明需要注入祖先组件提供的数据,它们可以在 JS中直接通过this来访问,在 视图模板 中也可以 直接访问
  • 语法
    export default {
         // 注入方法一:字符串数组
        inject: ['注入的数据名1', '注入的数据名2', '注入的函数名',……]
    
        // 注入方法二:对象
        inject: {
            新的变量名: { from: '注入的数据名' },
    
            // 当 新的变量名 == 注入的数据名 时,form 选项可以省略
            注入的数据名(新的变量名): {},
    
            // 祖先组件没有提供数据,会抛出一个警告
            // 解决方法:在当前组件,可以使用 default 选项设置默认值
            新的变量名: { from: '注入的数据名', default: 默认值 }
        }
    }
    请将 语法 和 实例 结合阅读
    
  • inject采用对象的形式来注入祖先组件提供的数据有哪些好处?
    • 可用本地属性名注入祖先组件提供的数据【如相同时(本地接收名注入名和接收的数据名),form选项可省略】;
    • 如果注入的数据并没有在祖先组件中提供,则会抛出警告,可采用 default选项设置默认值来解决;
  • 示例展示:(2.vue
    <!-- 选项式API -->
    <script>
        export default {
            // TODO 注入数据选项 - 祖先组件提供的数据
            // inject: ['userName', 'title', 'subtitle', 'changeSubtitle'],
    
            // TODO 采用对象形式注入
            inject: {
                _userName: {
                    from: 'userName'
                },
                title: {},
                _subtitle: {
                    from: "subtitle"
                },
                changeSubtitle: {},
                // EXPLAIN 如果注入的数据,祖先组件并没有提供数据,就会抛出一个警告
                likes: {
                    from: 'xiHa'
                },
                // EXPLAIN 解决:当前组件可以设置默认值
                anime: {
                    // 没有组件提供这个 anime 数据
                    from: 'anime',
                    default: '完美世界'
                }
            },
            mounted() {
                console.log('应用层提供的数据', this._userName)
                console.log('祖先组件提供的数据', this.title, this._subtitle)
                console.log('祖先组件提供的函数', this.changeSubtitle)
                console.log('注入的数据,祖先组件并没有提供,就会抛出一个警告', this.likes)
                console.log('祖先组件没有提供 anime 这个数据,当前组件设置再注入的时候默认值', this.anime)
            },
        }
    </script>
    
    <template>
        <div class="area-2" style="background-color: #00ffe3;">
            <h3>App的孙子组件</h3>
            <h5>应用层【main.js】提供的数据: <span>{{ _userName }}</span></h5>
            <h5>App.vue 提供的 非响应式 的数据: <span>{{ title }}</span></h5>
            <!-- <h5>App.vue 提供的 响应式 的数据: <span>{{ subtitle.value }}</span></h5> -->
            <h5>App.vue 提供的 响应式 的数据: <span>{{ _subtitle }}</span></h5>
            <button @click="changeSubtitle('禁止摸鱼')">修改App组件中subtitle的值</button>
            <h5>注入的数据,祖先组件并没有提供,就会抛出一个警告: <span>{{ `${likes}` }}</span></h5>
            <h5>祖先组件没有提供 anime 这个数据,当前组件设置再注入的时候默认值: <span>{{ anime }}</span></h5>
        </div>
    </template>
    
    <style scoped>
        .area-2 {
            padding: 10px;
            color: #000;
        }
    
        span {
            padding: 4px;
            border: 3px solid red;
        }
    
        h5 {
            padding-left: 20px;
            text-align: left;
        }
    </style>
    
  • 运行展示:
    image.png

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

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

相关文章

怎样在CSDN赚点零花钱

请教一下各位大佬&#xff0c;看到你们在CSDN很多都几万粉丝以上&#xff0c;能不能分享一下有什么涨粉的经验&#xff0c;还有怎样转化为额外收益……感谢各位提供宝贵的经验&#xff0c;谢谢……

多目灰度cam手势追踪系统——MegaTrack

一、前言 本文是对Facebook Oculus发布的一篇VR方向&#xff08;手势追踪&#xff09;论文的解读。Oculus是一家做VR的公司&#xff0c;2014年被FaceBook收购了&#xff0c;本次参考的论文就是FaceBook Oculus团队的最新论文。论文2020年7月发表于SIGGRAPH。 因为最终是要给大…

Go语言简介

一.Go语言简介 1.1 优点 自带gc静态编译&#xff0c;编译好后&#xff0c;扔服务器直接运行简单思想&#xff0c;没有继承&#xff0c;多态和类等丰富的库和详细开发文档语法层支持并发&#xff0c;和拥有同步并发的channel类型&#xff0c;使并发开发变得非常方便简洁语法&am…

手机和电脑同步的好用记事本软件有哪些

我常常需要随手记录各种信息&#xff0c;以便随时查阅和使用。比如&#xff0c;在下班路上&#xff0c;我会用手机记录明天要处理的工作事项、购物清单&#xff0c;或是某个突然迸发的创意想法&#xff1b;而在办公室&#xff0c;我则需要在电脑上整理会议纪要、项目计划&#…

[AutoSar]BSW_Com012 CAN TP 模块介绍

目录 关键词平台说明一、知识储备二、缩写对照表三、CAN TP 所在架构位置四、CAN TP 的主要作用五、CAN TP 在 autosar 架构中的基本概念5.1、CAN TP 的处理模式5.2 数据一致性5.3 静态配置 六、功能规范6.1 Services provided to upper layer6.1.1 Initialization and shutdow…

基于cnn的卷机神经网络的项目毕业课题实践应用(毕业选题-深度学习-卷及神经网络)

这些项目可以作为毕业课题选择&#xff0c;共计超过20个&#xff1a; 往期热门项目回顾&#xff1a; 计算机视觉项目大集合 改进的yolo目标检测-测距测速 路径规划算法 图像去雨去雾目标检测测距项目 交通标志识别项目 yolo系列-重磅yolov9界面-最新的yolo 姿态识别…

Mybatis操作sql报错ibatis.binding.BindingException: Parameter ‘empId‘ not found.

你们好&#xff0c;我是金金金。 场景 在使用Mybatis操作sql语句过程当中&#xff0c;更新操作&#xff0c;报错信息如下&#xff1a;Caused by: org.apache.ibatis.binding.BindingException: Parameter ‘empId’ not found. Available parameters are [arg1, arg0, param1, …

PyTorch搭建AlexNet训练集

本次项目是使用AlexNet实现5种花类的识别。 训练集搭建与LeNet大致代码差不多&#xff0c;但是也有许多新的内容和知识点。 1.导包&#xff0c;不必多说。 import torch import torch.nn as nn from torchvision import transforms, datasets, utils import matplotlib as p…

电子价签前景璀璨,汉朔科技革新零售行业的数字化新篇章

新型商超模式数字化“秘密武器”——电子价签 传统纸质价签&#xff0c;只要商品价格、日期等信息发生变化&#xff0c;就必须重新打印进行手动替换。电子价签的应用使传统的人工申请、调价、打印、营业员去货架前端更换等变价流程均可省略&#xff0c;所有门店的价格由后台统…

Linux命令深入学习——列出帮助手册,开机关机

linux中有多种方法查看一个不熟悉命令的详细信息&#xff0c;如 ls --help&#xff0c;help ls&#xff0c;man ls&#xff0c;info ls 在linux系统中可以使用命令进行开关机以及相关基础操作 同时在进行写入操作时&#xff0c;可以使用快捷键进行操作

图论(二)之最短路问题

最短路 Dijkstra求最短路 文章目录 最短路Dijkstra求最短路栗题思想题目代码代码如下bellman-ford算法分析只能用bellman-ford来解决的题型题目完整代码 spfa求最短路spfa 算法思路明确一下松弛的概念。spfa算法文字说明&#xff1a;spfa 图解&#xff1a; 题目完整代码总结ti…

【LeetCode每日一题】2129. 将标题首字母大写

文章目录 [2129. 将标题首字母大写](https://leetcode.cn/problems/capitalize-the-title/)思路&#xff1a;代码&#xff1a; 2129. 将标题首字母大写 思路&#xff1a; 1.先根据空格&#xff0c;将每个单词切割&#xff0c;依次遍历 2.用StringBuilder来对结构进行拼接 3.…

element plust的表格 el-table数据不按列展示

ElementPlus的表格demo代码放到原生的html <template><el-table :data"tableData" style"width: 100%"><el-table-column prop"date" label"Date" width"180" /><el-table-column prop"name"…

使用Python查询和下载Sentinel卫星数据

欢迎学习本教程,了解如何使用 Python 访问和下载 Sentinel 卫星数据。在深入探讨技术方面之前,让我们先了解一下哨兵卫星是什么以及它们为何如此重要。 哨兵家族。资料来源:欧空局。 Sentinel 卫星是欧洲航天局 (ESA) 开发的一组地球观测任务,是哥白尼计划的一部分,该计划…

Spark性能优化指南——高级篇

调优概述 有的时候&#xff0c;我们可能会遇到大数据计算中一个最棘手的问题——数据倾斜&#xff0c;此时Spark作业的性能会比期望差很多。数据倾斜调优&#xff0c;就是使用各种技术方案解决不同类型的数据倾斜问题&#xff0c;以保证Spark作业的性能。 数据倾斜发生时的现…

一文了解Cornerstone3D中窗宽窗位的3种设置场景及原理

&#x1f506; 引言 在使用Cornerstone3D渲染影像时&#xff0c;有一个常用功能“设置窗宽窗位&#xff08;windowWidth&windowLevel&#xff09;”&#xff0c;通过精确调整窗宽窗位&#xff0c;医生能够更清晰地区分各种组织&#xff0c;如区别软组织、骨骼、脑组织等。…

SSM整合项目(校验)

文章目录 1.前端校验1.需求分析2.HomeView.vue的数据池中添加校验规则3.HomeView.vue 绑定校验规则![image-20240311213428771](https://img-blog.csdnimg.cn/img_convert/7770bfa16814a0efd4eb818c9869a5bd.png)4.验证是否生效5.如果验证不通过&#xff0c;阻止用户提交表单1.…

机器学习之分类回归模型(决策数、随机森林)

回归分析 回归分析属于监督学习方法的一种&#xff0c;主要用于预测连续型目标变量&#xff0c;可以预测、计算趋势以及确定变量之间的关系等。 Regession Evaluation Metrics 以下是一些最流行的回归评估指标: 平均绝对误差(MAE):目标变量的预测值与实际值之间的平均绝对差…

webpack5零基础入门-4使用webpack处理less文件

1.安装less npm install less -D 2.创建less文件 .box{width: 100px;height: 100px;background: red; } 3.引入less文件并打包 执行npx webpack 报错无法识别less文件 4.安装less-loader并配置 npm install less-loader9 -D 这里指定一下版本不然会因为node版本过低报错 …

Java 启动参数 -- 和 -D写法的区别

当我们配置启动1个java 项目通常需要带一些参数 例如 -Denv uat , --spring.profiles.activedev 这些 那么用-D 和 – 的写法区别是什么&#xff1f; 双横线写法 其中这种写法基本上是spring 和 spring 框架独有 最常用的无非是就是上面提到的 --spring.profiles.activede…