Vue2.x 基本认识二:使用 Vue CLI 的 Vue 项目

注意

  • Vue CLI(手脚架)创建项目需要安装Node.js,没安装的看这里
  • 使用 Vue CLI 创建与运行项目,看这里
  • Vue CLI 创建的项目文件夹和文件说明,看这里
  • 此处使用手脚架版本为:@vue/cli 5.0.8
  • 创建的项目中有 vue.config.js 文件,在内部添加配置,防止出现要求组件名称必须是多个单词的情况。

    不配置会出现错误信息:Component name "XXX" should always be multi-word

    具体配置方式:

const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
  transpileDependencies: true,
  lintOnSave: false /*关闭语法检查*/
})

 

单文件组件

定义和引入

<template>
    <div>
        <!-- 两种组件标签编写方式 -->
        <School></School>
        <Student/>
    </div>
</template>

<script>
    // 引入其他组件(组件后面的扩展名用不用都无所谓)
    import School from "./components/School.vue";
    import Student from "./components/Student";
    // 默认暴露简写+组件定义简写
    export default {
        name: 'App',
        components: {
            School,
            Student
        }
    }
</script>

 

render 函数

render 函数返回的是虚拟 DOM,与 data 配置项平级。

render 函数被 Vue 用到的是 createElement 参数

关于不同版本的 Vue 说明

    1. vue.js 与 vue.runtime.xx.js 的区别:

      (1)vue.js 是完整版的 Vue,包含核心功能和模板解析器。

      (2)vue.runtime.xxx.js 是运行版的 Vue,值包含:核心功能,没模板解析器。

    2. 因为 vue.runtime.xxx.js 没有模板解析器,所以不能使用 template 配置项,需要使用 render 函数接收到的 createElement 函数去指定具体内容。

 

ref 属性

  作用一:该属性在传统 HTML 标签上使用,能让 Vue 能操作 DOM 。

  使用方法:

<!-- 在需要操作的 DOM 元素上加上 ref 属性 -->
<div ref="headContent"></div>

<!-- 在需要操作的地方这样使用,注意下面的 this 是指 vm 或 组件对象实例 -->
this.$refs.headContent

  作用二(重点):该属性在组件标签上使用,能拿到对应组件的对象实例

// ref 使用方式
<button @click="getDom">点击调用getDom()</button>
<div ref="headContent"></div>
<School ref="sch"></School>
// 操作被 ref 属性标记的标签,注意下面的 this 是指 vm 或 组件对象实例 
this.$refs.headContent // 会得到一个真实 DOM 元素
this.$refs.sch // 会得到 School 组件的对象实例

 

props 配置项

  功能:该配置用于接收父组件传给子组件的参数

  注意:

接收到的参数所在位置:跟 data 配置项中的属性一样,在组件对象实例上。使用方式与 data 配置项中的属性一致。

关于参数优先级:子组件接收到的参数,比子组件从 data 配置项中拿到的属性优先级高。如果接收到的参数和 data 中存在同名属性,那么接收到的参数优先使用。

参数修改问题:子组件接收到的参数不能直接修改。想要修改,可以先在 data 配置项中定义一个属性,将接收到的参数值赋给定义的属性,然后修改自定义的属性值即可。另外,如果接收到的参数是一个对象,那么修改对象里面的属性值是被允许的。也就是说 props 检测的比较浅层,跟 Vue 的深度监测不同,props 只管第一层不能修改。

使用 v-bind 绑定参数传参:相当于将一个表达式作为参数传给子组件,这种方式,可以传各种类型参数,数值,对象,数组,函数等都可以。

简单接收(父传子)

<!-- 这里是父组件的 template -->
<template>
    <div>
        <!-- 引入子组件:注意age属性的两种写法,接收到的参数是不同的类型,加了冒号相当于将一个表达式作为参数传给子组件,这种方式,可以穿各种类型参数,数值,对象,数组等都可以 -->
        <Student name="白修远" sex="男" age="18" />
        <Student name="沈宜修" sex="女" :age="16" />
    </div>
</template>

<!-- 下面都是子组件的内容 -->
<template>
    <div>
        <h1>{{ msg }}</h1>
        <h3>学生姓名:{{ name }}</h3>
        <h3>学生性别:{{ sex }}</h3>
        <h3>学生年龄:{{ age + 1 }}</h3>
    </div>
</template>

<script>
    export default {
        name: 'Student',
        data() {
            return {
                msg: "欢迎来到大雪山"
            }
        },
        props: ['name', 'sex', 'age'] // 简单声明接收
    }
</script>

  页面效果:

 

接收参数(父传子):限制参数类型

props: {
    name: String,
    sex: String,
    age: Number
}

 

接收参数(父传子):限制参数类型、设置默认值、限制必填性

props: {
    name: {
        type: String,
        require: true
    },
    sex: {
        type: String,
        default: '男'
    },
    age: Number
}

  注意:参数类型存在限制时,如果传错了会有提示,但不影响模板解析

 

接收函数(子传父) 

传参:这种传参方式与简单传参的传参和接收方式一致。就是在父组件中定义一个操作自己属性的函数,将函数作为参数通过 props 的方式传给子组件。这个函数有参数也不用管,将函数名作为要穿参数的属性值即可。

注意:传的时候属性前面加 v-bind 或它的简写形式,将函数作为表达式传给子组件,否则就是只传了个普通字符串给子组件;另外,传给子组件的函数名不能和子组件中的函数冲突。

使用:子组件通过 props 配置项接收到这个函数,就可以直接使用了,而这个函数操作的又是父组件的数据,因此可以完成传值。在下面 组件的自定义事件 章节中有案例。

 

mixins(混合或叫混入)配置项

  功能:可以把共用的配置项 data、methods 等,提取成一个混入对象,在需要使用的组件当中引入即可。

  注意:

    混入是增量的。例如组件当中存在 methods 配置项,已有的函数不会被混入对象中 methods 配置项中的函数替换,而是合并,全部提供给组件使用。

    混入时,如果存在相同的属性或者函数,则以组件当中的为主。其他生命周期钩子函数,会同时使用,先执行混入的生命周期钩子函数,在执行组件当中的生命周期钩子函数。

  定义混入对象 示例:创建一个 js 文件,内容如下

export const mixinData = {
    methods: {
        data() {
            return {
                x: 108
            }
        }
    }
}

export const mixin1Methods = {
    methods: {
        showName() {
            alert(this.name)
        }
    }
}

  局部引入以及使用混入对象 示例:

<template>
    <div id="schoolContent">
        <h3 @click="showName">学校名称:{{ name }}</h3>
        <h3>学校地址:{{ address }}</h3>
    </div>
</template>

<script>
    // 引入混入
    import {mixinData, mixin1Methods} from '../mixin'

    export default {
        name: 'School',
        data() {
            return {
                name: "大雪山修仙学院",
                address: "昆仑"
            }
        },
        // 使用混入
        mixins: [mixinData, mixin1Methods]
    }
</script>

  全局引入使用混入对象,示例:(main.js中)

// 全局引入混入
import {mixinData, mixin1Methods} from '../mixin'
// 全局使用混入(这样,Vue 实例对象和所有组件实例对象都能使用下面添加的混入)
Vue.mixin(mixinData)
Vue.mixin(mixin1Methods)

 

插件

定义插件

创建一个 js 文件,名字随便,有意义即可,下面文件名:plugins.js

export default {
    // Vue 注意,参数是创建 Vue 实例的模板,不是 Vue实例
    // x、y、z 是在引入插件时穿进来的,名称随便定义
    install(Vue, x, y, z) {
        // 定义过滤器
        
        // 定义指令

        // 定义混入

        // 给 Vue 原型上添加一个函数,vm 和 组件实例对象 都能使用
        Vue.prototype.hello = ()=> {
            console.log('你好')
        }
    }
}

引入和应用插件

  在 main.js 文件当中

// 引入插件
import plugins from './plugins'
// 引用插件
Vue.use(plugins, 1, 2, 3)

 

style 标签的属性

scoped 属性:控制样式作用域

使用:在子组件中,这个属性用在样式标签上:<style scoped>

效果:使用此属性的子组件样式只在组件 <template> 标签内生效,不会影响别的组件。

注意:如果在 父组件(类似App组件)中使用,那么父组件中的所有样式,还会影响到子组件。

lang 属性控制样式语言

使用:<style lang="less">,lang 属性的值使用 css、less 等,都是按开发需求来定。

如果要使用 less,注意添加 less 支持,Vue CLI 如果是 4 版本,安装 less 支持最好是5、6、7版本,如果手脚架是5,那么 less 支持可以安装 8、9(这里提到的版本都是大版本)

查看 Vue 项目中使用的 webpack 版本:node_modules\webpack\package.json

查看 webpack 所有版本命令:npm view webpack versions

查看 less-loader 所有版本:npm view less-loader versions

安装命令:npm i less-loader 或 npm i less-loader@[版本号],这里版本号可以不写详细版本,只写个大版本,这样默认会安装这个大版本中最新的版本。

 

组件拆分原则

根据功能点来拆分。每负责一个功能的模块,都可以拆分出来,作为一个组件。

例如:一个列表中,每个 item 都是可以增删改的,那么,这个列表可以拆成两个组件,一个用于遍历展示所有 item,一个就是 item 组件本身,二者是父子关系。

 

组件的自定义事件

作用:适用于子组件给父组件传参,父组件给子组件传函数

绑定自定义事件:两种方式

注意:下面是绑定自定义事件,如果要绑定原生的事件,例如 click 事件,在写完 @click 之后,需要加一个修饰符,也就是 @click.native,这样就会给子组件绑定一个点击事件,事件会绑定在子组件的根元素上

文件:App.vue
<
template> <div id="app"> <h1 id="welcomeTitle">{{ msg }}</h1> <!-- 通过 props 的方式,将函数作为一个参数传给子组件,由子组件触发函数 --> <School :getSchoolNameFunc="getSchoolName" /> <!-- 方式一 通过指令语法 v-on 给子组件绑定一个自定义事件 getStudentNameEvent,该事件触发,就会回调 getStudentName 函数 子组件通过 $emit 函数触发事件,至于什么时候触发,由子组件控制 子组件不再需要像 props 传参那样定义配置项接收函数,然后触发,这是绑定在子组件对象实例上的一个事件,可以直接触发 --> <!-- <Student v-on:getStudentNameEvent="getStudentName" /> --> <!-- 方式二 通过 ref 属性,使 App 组件能拿到 Sutdent 组件的对象实例,然后通过 $on 绑定一个事件到 Student 组件对象实例上 --> <Student ref="student" /> <!-- 上面两种方式绑定的事件触发方法都一样,都是通过 $emit 函数 --> </div> </template> <script> // 引入其他组件(组件后面的扩展名用不用都无所谓) import School from "./components/School.vue"; import Student from "./components/Student"; // 默认暴露简写+组件定义简写 export default { name: 'App', components: {School,Student}, data() { return { msg: "欢迎来到大雪山" } }, methods: { getSchoolName(name) { console.log("App拿到了学校名称:" + name) }, getStudentName(name) { console.log("App拿到了学生名称:" + name) } }, mounted() { this.$refs.student.$on('getStudentNameEvent', this.getStudentName) } } </script>

 

自定义事件的触发($emit)和解绑($off)

文件:Student.vue
<
template> <div id="studentContent"> <h3>学生姓名:{{ name }}</h3> <h3>学生性别:{{ sex }}</h3> <h3>学生年龄:{{ age + 1 }}</h3> <button @click="sendNameToApp">点击将学生姓名传给App组件</button> <button @click="unbind">点击解除绑定的自定义事件</button> </div> </template> <script> export default { name: 'Student', data() { return { name: "白修远", sex: "", age: 18 } }, methods: { sendNameToApp() { /** * $emit(eventName, ...args) 函数是触发指定的事件,根据事件本身需要传参 */ this.$emit('getStudentNameEvent', this.name) }, unbind() { // 解除绑定的事件:事件绑定在谁身上,谁解绑 this.$off('getStudentNameEvent') // 解绑一个自定义事件 //this.$off(['getStudentNameEvent','eventNameXxx'...]) // 解绑多个自定义事件 this.$off() // 解绑所有自定义事件 } } } </script>

    注意,调用实例销毁函数后,也会解除和销毁所有自定义事件的绑定。

 

文件:School.vue(这里主要是 props 传参)
<
template> <div id="schoolContent"> <h3>学校名称:{{ name }}</h3> <h3>学校地址:{{ address }}</h3> <button @click="sendNameToApp">点击将学校名称传给App组件</button> </div> </template> <script> export default { name: 'School', props: ["getSchoolNameFunc"], data() { return { name: "大雪山修仙学院", address: "昆仑" } }, methods: { sendNameToApp() { this.getSchoolNameFunc(this.name) } } } </script>

 

全局事件总线(重要

功能:实现任意组件之间通信

思路:让一个组件 X 能让所有组件看见。A 组件给 X 组件一个自定义事件,回调函数肯定在 A 当中(上面组件自定义事件有说)。当 C 组件想给 A 组件传参时,让 C 组件触发 X 组件上 A 定义的自定义事件,并且传参,这样 A 组件就成功收到参数了。

注意:

  • 哪个组件对象实例要传递参数,那么就在这个组件触发事件。
  • 哪个组件要收到参数,那么就在这个组件里向全局事件总线绑定事件,并且定义回调函数。
  • 在组件销毁前调用的钩子函数(beforeDestroy)中,解除对全局事件总线绑定的事件

定义方式

文件:main.js
const vm = new Vue({ render: h => h(App), beforeCreate() { Vue.prototype.$bus = this // 安装全局事件总线 } }).$mount("#root")

 

组件使用全局事件总线:绑定事件&解绑事件

文件:Student.vue
<
template> <div id="studentContent"> <h3>学生姓名:{{ name }}</h3> <h3>学生性别:{{ sex }}</h3> <h3>学生年龄:{{ age + 1 }}</h3> <button @click="destroySelf">点击自杀</button> </div> </template> <script> export default { name: 'Student', data() { return { name: "白修远", sex: "", age: 18 } }, methods: { selfIntroduction(schoolName, schoolAddress) { console.log('欢迎来到' + schoolAddress + ',在下', schoolName, this.name) }, destroySelf() { let cueWords = '[系统]你确定要自杀吗?死后将身消道陨,一切因果都将一笔勾销。' if (!confirm(cueWords)) return // 调用销毁方法,销毁当前组件对象实例 this.$destroy() } }, mounted() { // 在事件总线上面绑定一个事件:selfIntroductionEvent,回调函数是 methods 配置中的 selfIntroduction this.$bus.$on('selfIntroductionEvent', this.selfIntroduction) }, // 在销毁前,解除自己绑定在事件总线上面的事件 beforeDestroy() { this.$bus.$off('selfIntroductionEvent') } } </script>

 

组件使用全局事件总线:触发事件

文件:School.vue
<
template> <div id="schoolContent"> <h3>学校名称:{{ name }}</h3> <h3>学校地址:{{ address }}</h3> <h5>[系统]此时,一位迷途少女来到学院,见对面之人,便向对方发出询问</h5> <button @click="inquiry">你好,请问你是?</button> </div> </template> <script> export default { name: 'School', data() { return { name: "风雪修仙学院", address: "昆仑-大雪山" } }, methods: { inquiry() { /** * 触发 Student 组件绑定在全局事件总线上的事件 * 因为事件绑定在 $bus 上面,所以有下面写法, * 千万别 this.$emit,this身上并没有这个事件,使用 $emit 去触发不会报错,但也没任何提示 */ this.$bus.$emit('selfIntroductionEvent', this.name, this.address) } } } </script>

 

消息发布与订阅

功能:能够实现组件之间的任意通信

案例使用的库:pubsub-js,安装方式:npm i pubsub-js

说明:使用什么库都无所谓,消息发布与订阅是一种理念。

 

消息发布

文件:School.vue
<
script> // 引入 pubsub import pubsub from 'pubsub-js' export default { name: 'School', data() { return { name: "大雪山修仙学院", address: "昆仑" } }, methods: { // 该函数被调用时,发布消息 publishFairylandMsg() { // 发布消息 let data = { name: this.name, address: this.address } pubsub.publish('fairylandMsg', data) } } } </script>

 

消息订阅与取消订阅

文件:Student.vue
<
script> // 引入 pubsub import pubsub from 'pubsub-js' export default { name: 'Student', data() { return { name: "白修远", sex: "", age: 18 } }, methods: { /** * 写法三的回调函数:(该函数 Vue 是管理的,所以 this 是当前组件对象实例) * 第一个参数如果不需要,可以用下划线占位 */ subscribeFairylandMsg(_, data) { console.log('写法三收到消息:', data) } }, mounted() { // 订阅消息:写法一(这种写法回调函数内 this 是 undefined) // this.pubId = pubsub.subscribe('fairylandMsg', function(msgName, data){ // console.log('写法一收到', msgName, '渠道的消息:', data) // }) // 订阅消息:写法二(这种写法回调函数内 this 是当前组件对象实例,因为箭头函数 this 指向会向外层找) // this.pubId = pubsub.subscribe('fairylandMsg', (msgName, data) => { // console.log('写法二收到', msgName, '渠道的消息:', data) // }) // 订阅消息:写法三 this.pubId = pubsub.subscribe('fairylandMsg', this.subscribeFairylandMsg) }, beforeDestroy() { // 对象实例被销毁前 取消消息订阅 pubsub.unsubscribe(this.pubId) } } </script>

 

$nextTick(重要)

功能:这个函数可以让指定的代码在 Vue 将模板解析 且 DOM 更新完毕之后再执行。

场景:在一个函数中,某个 data 配置项里面的属性值发生改变时,Vue 不会立即解析模板,而是等函数执行完毕之后,才根据所有变动的结果统一的去解析模板。简而言之,不是一个属性值变了就立即解析模板的。此时如果去操作 DOM,很可能得不到想要的结果。

注意:下面的 this 是 Vue 的对象实例 或者是 组件对象实例。

this.$nextTick(function(){
    // 使某个 input 框获取焦点
    this.$refs.xxx.focus();
})

 

过度与动画

<template>
    <div>
        <button @click="isShow = !isShow">点击显示|隐藏</button>
        <!--  
            1. transition 标签如果使用了 name 属性,那么下面样式中进入和离开样式不在是“v-”,而是name属性值+“-” 
            例如:transition 有 name="transtest",那么下面就 “transtest-”。
            指定 name 属性可以用于控制多个元素使用不同的动画效果。
            2. transition 标签中,只能有一个根节点。即 id="testDiv" 的 div 元素不能有同级其他元素。
            想要实现多个元素用同样的过度效果,需要使用 transition-group 标签,每个元素都要指定不同的 key 值。
            3.集成 animate.css(官网地址:https://animate.style/) 后,transition、transition-group 标签需要用到的两个属性:
              enter-active-class=""
              leave-active-class=""
            这两个属性的值在 animate.css 官网右侧复制即可。
            使用第三方库后,不需要自己写动画过度样式了(也就是style标签中的一些类不需要使用了)
            -->
        <transition appear>
            <div class="testDiv" v-show="isShow">A</div>
        </transition>
        <transition-group>
            <div class="testDiv" v-show="!isShow" key="1">B</div>
            <div class="testDiv" v-show="isShow" key="2">C</div>
        </transition-group>
    </div>
</template>

<script>
export default {
    name:'AnimationTest',
    data() {
        return {isShow:true}
    }
}
</script>

<style scoped>
    /* 进入的起点 */
    .v-enter {}
    /* 进入过程中 */
    .v-enter-active {animation: movetest 2s;}
    /* 进入的终点 */
    .v-enter-to {}
    /* 离开的起点 */
    .v-leave {}
    /* 离开过程中 */
    .v-leave-active {animation: movetest 2s reverse;}
    /* 离开的终点 */
    .v-leave-to {}
    @keyframes movetest {
        from { transform: translateX(-100%) }
        to { transform: translateX(0) }
    }
    .testDiv {
        width: 50px;
        height: 50px;
        background-color: rgb(174, 200, 59);
    }
</style>

 

使用 axios 发送请求

在项目中安装 axios 命令:npm i axios

且不能灵活的控制请求是否走代理

代理配置方式

vue.config.js 文件当中

const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
  transpileDependencies: true,
  lintOnSave: false, /*关闭语法检查*/
  // 开启代理服务器方式一:(此方式开启代理只能配置一个地址)
  // devServer: {
  //   proxy: 'http://localhost:5000' // 需要代理的地址
  // },
  // 开启代理服务器方式二
  devServer: {
    proxy: {
      '/api': { // 请求前缀,用于配置 axios 请求地址中端口号之后的内容
        target: 'http://localhost:5000',
        pathRewrite: {'^/api':''} // 路径重写,替换请求地址中指定的字符串。这里将 /api 字符串替换为空字符串
        // ws: true, // 默认是true,用于支持 websocket
        // changeOrigin: true // 默认是true,react中默认是false,用于控制请求头中的 host 值
      },
      '/api2': { // 请求前缀,用于配置 axios 请求地址中端口号之后的内容
        target: 'http://localhost:5001',
        pathRewrite: {'^/api2':''}
      }
    }
  }
})

 

使用 vue-resource 插件发送请求

  功能:发送请求。类似 axios。

  安装:npm i vue-resource

  说明:该插件也是对 xhr 的封装,在 Vue1.x 中用的特别多。

引入插件

  main.js 文件中

// 引入 vue-resource 插件
import vueResource from 'vue-resource'
// 使用 vue-resource 插件
Vue.use(vueResource)

使用插件

  除了调用 get 函数前面写法不同,返回结果与 axios 完全一致。

this.$http.get('http://localhost:8080/api/students').then(
    response => {
        console.log('请求成功', response.data)
    },
    error => {
        console.log('请求失败', error.message)
    }
)

 

使用代理方式

<script>
    // 引入 axios
    import axios from 'axios'
    export default {
        name: 'App',
        data() {
            return {
                msg: "欢迎来到昆仑-大雪山"
            }
        },
        methods: {
            getStudentMsg() {
                /**
                 * 后端服务接口地址(目标地址):http://localhost:5000/students
                 * 方式一:这种代理方式需要修改目标地址中的端口号与本机(前端服务)端口同步。假设本服务启动端口为:8080
                 *  也就是修改为:http://localhost:8080/students
                 * 注意,方式一 devServer 配置开启的代理,只有根路径(public)下没有的资源,才走代理
                 * 否则,下面会返回根路径中的资源
                 * 方式二:这种代理方式需要修改目标地址中的端口号与本机(前端服务)端口同步,且在端口号后面加了请求前缀
                 *  目标代理地址是 http://localhost:5000/students
                 *  代理后请求地址 http://localhost:8080/api/students
                 * 注意:两种代理方式端口号以后的内容,均不能和前端服务跟路径(public 目录)中的资源同步,否则请求不到后端。
                 * 以下面的请求地址为例,如果在 public 目录下存在 api 目录,且当中存在一个叫 students 的文件,那么在发起
                 * 求情后,返回的就是前端服务中 students 文件中的内容,而不是通过代理请求到后端接口拿想要的数据。
                 */
                axios.get('http://localhost:8080/api/students').then(
                    response => {
                        console.log('请求成功', response.data)
                    },
                    error => {
                        console.log('请求失败', error.message)
                    }
                )
            }
        }
    }
</script>

  注:记录一个 github 提供的公共可用的获取用户信息的接口:https://api.github.com/search/users?q=xxx

 

插槽

默认插槽

默认插槽定义

<template>
    <div id="schoolContent">
        <h3>学校名称:{{ name }}</h3>
        <h3>学校地址:{{ address }}</h3>
        <!-- 定义一个插槽(告诉 Vue 这往这里填充内容) -->
        <slot>标签内可以写默认值,如果组件使用者不填充,就展示这里的内容</slot>
    </div>
</template>

默认插槽填充

  注意,在下面组件标签体当中编写的内容,都会填充到组件当中定义的默认插槽位置,也就是说,可以同时写多个标签填充进去,下面案例只是写一个作为示范。

<template>
    <div id="app">
        <h1 id="welcomeTitle">{{ msg }}</h1>
        <School>
            <!-- 向 School 组件定义的默认插槽填充的内容 -->
            <img src="xxx"/>
        </School>
        <Student/>
    </div>
</template>

 

具名插槽

具名插槽定义

<template>
    <div id="schoolContent">
        <h3>学校名称:{{ name }}</h3>
        <h3>学校地址:{{ address }}</h3>
        <!-- 定义具名插槽 -->
        <slot name="center">标签内可以写默认值,如果组件使用者不填充,就展示这里的内容</slot>
        <slot name="footer">标签内可以写默认值,如果组件使用者不填充,就展示这里的内容</slot>
    </div>
</template>

具名插槽填充

<template>
    <div id="app">
        <h1 id="welcomeTitle">{{ msg }}</h1>
        <School>
            <!-- 向具名插槽填充内容:填充的目标就是 name 属性值与下列标签中 slot 属性值相同的插槽 -->
            <h1 slot="center">向center插槽填充</h1>
            <h1 slot="center">向center插槽填充,多填充是追加的,不会覆盖前面填充的内容</h1>
            <!-- 
                多个元素同时向同一个插槽填充时
                可以使用 <template> 标签,该标签既可以使用上面的 slot 属性指定具体插槽
                也可以使用 Vue 2.6 提出的新写法,如下
             -->
            <template v-slot:footer>
                <h1>向footer插槽填充</h1>
                <div>向footer插槽填充</div>
            </template>
        </School>
        <Student/>
    </div>
</template>

 

作用域插槽

  作用域插槽和尚明两种插槽在定义和填充上没有区别,只是在组件定义插槽的时候,在 <slot> 标签上自定义属性,通过 v-bind 的方式绑定插槽上面的属性。此时,插槽填充者就能直接在填充时使用该属性。

作用域插槽定义

<template>
    <div id="schoolContent">
        <h3>学校名称:{{ name }}</h3>
        <h3>学校地址:{{ address }}</h3>
        <!-- 作用域插槽演示 -->
        <slot :name="name">这是默认插槽,具名插槽在搭配作用域概念是一样的,就不重复写了</slot>
    </div>
</template>

作用域插槽填充

<template> <!-- 这个是App组件的标签,不要和下面 template 标签弄混了-->
    <div id="app">
        <h1 id="welcomeTitle">{{ msg }}</h1>
        <School>
            <!-- 
                填充作用域插槽
                作用域插槽必须使用 <template> 标签
                标签内的 scope 属性指定的 params 对象可以接收到插槽传递过来的所有参数
                (scope 属性可以用新的属性代替 slot-scope)
                参数可以是某个属性,或者对象、数组,根据插槽定义时传递的参数来定
                作用域插槽也是能指定 name 属性的,与上面具名插槽使用方式一致
             -->
            <template scope="params">
                <h1>{{ params.name }}</h1>
            </template>
        </School>
        <Student/>
    </div>
</template>

 

posted @ 2023-06-23 20:11    阅读(35)  评论(0编辑  收藏  举报