5--vue组件 props传参 soft插槽

一. 组件

a. 定义
  1. 组件就是扩展自定义 的html标签
  2. 组件是一个功能的集合,可以重复的使用
  3. 组件能使一个复杂的系统分割为一些简单的组件
  4. 组件使前端分工协作更加方便
  5. 组件在小项目中会显得复杂,项目越大,优势就越明显
b. 组件名(大小写)

定义组件名的方式有两种:

  1. 使用 kebab-case
    <my-component-name>
  2. 使用 PascalCase
    <MyComponentName>
c. 使用

全局组件:它们在注册之后可以用在任何新创建的 Vue 根实例 (new Vue) 的模板中。

Vue.component(组件名,{
      template:`<div>组件的模板里面只能够有一个根组件</div>`
})
    <组件名></组件名>   //使用组件

局部组件:

1. 定义组件
let counter = {
     template:`<div>只能有一个根节点{{num}}</div>`,
     data:function(){ return {num:1}}
     methods:{}
}
2. 注册组件
new Vue({
    components:{counter,...}
})
3. 使用组件
    <counter></counter>
d. 组件扩展:模块系统
在模块系统中局部注册

如果你还在阅读,说明你使用了诸如 Babel 和 webpack 的模块系统。在这些情况下,我们推荐创建一个 components 目录,并将每个组件放置在其各自的文件中。

然后你需要在局部注册之前导入每个你想使用的组件。例如,在一个假设的 ComponentB.jsComponentB.vue 文件中:

import ComponentA from './ComponentA'
import ComponentC from './ComponentC'

export default {
  components: {
    ComponentA,
    ComponentC
  },
  // ...
}

现在 ComponentAComponentC 都可以在 ComponentB 的模板中使用了。

基础组件的自动化全局注册

可能你的许多组件只是包裹了一个输入框或按钮之类的元素,是相对通用的。我们有时候会把它们称为基础组件,它们会在各个组件中被频繁的用到。

所以会导致很多组件里都会有一个包含基础组件的长列表:

import BaseButton from './BaseButton.vue'
import BaseIcon from './BaseIcon.vue'
import BaseInput from './BaseInput.vue'

export default {
  components: {
    BaseButton,
    BaseIcon,
    BaseInput
  }
}

而只是用于模板中的一小部分:

<BaseInput
  v-model="searchText"
  @keydown.enter="search"
/>
<BaseButton @click="search">
  <BaseIcon name="search"/>
</BaseButton>

幸好如果你使用了 webpack (或在内部使用了 webpack 的 Vue CLI 3+),那么就可以使用 require.context 只全局注册这些非常通用的基础组件。这里有一份可以让你在应用入口文件 (比如 src/main.js) 中全局导入基础组件的示例代码:

import Vue from 'vue'
import upperFirst from 'lodash/upperFirst'
import camelCase from 'lodash/camelCase'

const requireComponent = require.context(
  // 其组件目录的相对路径
  './components',
  // 是否查询其子目录
  false,
  // 匹配基础组件文件名的正则表达式
  /Base[A-Z]\w+\.(vue|js)$/
)

requireComponent.keys().forEach(fileName => {
  // 获取组件配置
  const componentConfig = requireComponent(fileName)

  // 获取组件的 PascalCase 命名
  const componentName = upperFirst(
    camelCase(
      // 获取和目录深度无关的文件名
      fileName
        .split('/')
        .pop()
        .replace(/\.\w+$/, '')
    )
  )

  // 全局注册组件
  Vue.component(
    componentName,
    // 如果这个组件选项是通过 `export default` 导出的,
    // 那么就会优先使用 `.default`,
    // 否则回退到使用模块的根。
    componentConfig.default || componentConfig
  )
})

2. 案例-全局组件

a. 全局组件-textvue组件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>组件</title>    
    <script src="./js/vue.js"></script>   
     <style>
         .modal{
             position: absolute;
             left: 0;
             top:0;
             right: 0;
             bottom:0;
             background-color: rgba(0,0,0,.7);
         }
         .content{
            width: 400px;
            height: 300px;
            position: absolute;
            left:50%;
            top:50%;
            transform: translate(-50%,-50%);
            background-color: #fff;
            border-radius: 4px;
            overflow: hidden;
         }
         .title{
             line-height: 38px; text-align: center;
             height: 38px;
             background-color:dodgerblue;
             color: #fff;    
         }
     </style>
</head>
<body>
<div id="app">
     <!-- 第三步使用组件 -->
     <button @click="flag=1">武汉</button>   <button @click="flag=2">中国</button>  

     <model  v-if="flag==1" @close="flag=null" maskclose="true">
         <p slot="title">武汉加油</p>
         <p slot="content">全中国人民都在支持你,4月花开我,来碗热干面</p>
     </model>
     <model   v-if="flag==2" @close="flag=null">
        中国加油
    </model>
     <counter></counter>
     <counter></counter>
     <counter></counter>
     <!-- <child :pnum="pnum" @numchange="pnum=$event"></child>
     <child ></child>
     <child :pnum="8"></child> -->
     <!-- <child :pnum="'abc'"></child> -->
     <!-- <h1>{{pnum}}</h1> -->
     <div v-for="(item,index) in list ">
         {{item.name}} <child :pnum="item.num"   @numchange="item.num=$event"></child>
     </div>
     <h1>{{ totalNum}}</h1>
    
</div>
<script>
    var model = {
        props:{
            maskclose:{default:false}
        },
        template:`  <div class="modal" @click="maskclose?$emit('close',1):''">         
                        <div class="content"> 
                            <div class="title">
                                <slot name="title"></slot>
                            </div>
                                <slot name="content"></slot>
                            <br><button @click="$emit('close',1)">x</button>    
                            <br> <slot></slot>             
                        </div>      
                    </div>`
    }
    var counter = {
        template:`<div>
               <button @click="reduce">-</button>
               <input v-model.number="num"/>
               <button @click="add">+</button>
            </div>`,
        data:function(){return {
            num:1,       // num初始值
            step:1,      // 每次步进值
            max:999,     // 最大限定
            min:1        // 最小限定
        }},
        methods:{
            add(){
                this.num+=this.step;    // 单击加 step 1
                if(this.num>this.max){this.num=this.max}   // 如果超过最大值 等于最大值
            },
            reduce(){
                this.num-=this.step;
                if(this.num<this.min){this.num=this.min}
            }
        }
    }
    // 第一步定义组件
    var child = {
        template:`<span><button @click="add()">{{num}}</button></span>`,    
        props:
            {
                pnum:{
                    default:1,                              
                },        
             },
        data(){return {num:this.pnum}},
        methods:{
            add(){
                this.num++;
                this.$emit("numchange",this.num)
            }
        }
    }
    new Vue({
        el:"#app",
        created() {
            this.$on("numchange",function(e){console.log(e)})
        },
        data:{   
             list:[{name:"pt",num:2},{name:"xj",num:4}],
             pnum:5,    
             flag:null,  
        },
        computed:{
            totalNum(){
                return this.list.reduce((a,b)=>a.num+b.num)
            }
        },
    // 第二步注册组件
        components:{
            counter,
            child,
            model,
        }     
    })
</script>
</body>
</html>

b. 全局组件2

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>组件</title>    
    <script src="./js/vue.js"></script>   
</head>
<body>
<div id="app">
     <!-- 第三步使用组件 -->
     <counter></counter>
     <counter></counter>
     <counter></counter>
</div>
<script>
    var counter = {
        template:`<div>
               <button @click="reduce">-</button>
               <input v-model.number="num"/>
               <button @click="add">+</button>
            </div>`,
        data:function(){return {
            num:1,
            // num初始值
            step:1,
            // 每次步进值
            max:999,
            // 最大限定
            min:1
            // 最小限定
        }},
        methods:{
            add(){
                this.num+=this.step;
                // 单击加 step 1
                if(this.num>this.max){this.num=this.max}
                // 如果超过最大值 等于最大值
            },
            reduce(){
                this.num-=this.step;
                if(this.num<this.min){this.num=this.min}
            }
        }
    }
    // 第一步定义组件
    new Vue({
        el:"#app",
        data:{             
        },
    // 第二步注册组件
        components:{
            counter
        }   
    })
// 1. 组件就是扩展自定义 的html标签
// 2. 组件是一个功能的集合,可以重复的使用
// 3. 组件能使一个复杂的系统分割为一些简单的组件
// 4. 组件使前端分工协作更加方便
// 5. 组件在小项目中会显得复杂,项目越大,优势就越明显
// 6. 全局组件
</script>
</body>
</html>

3. 组件数据传递-父传子

props 组件的数据传递

  1. 父亲传递给子元素
    a. props属性传参的方法
    b. 父亲传递给子元素的参数是单向数据流(只读)
    c. 喜欢修改父亲传递过来参数 data(){return {num:this.pnum}}
<steper :pnum="p1" @numchange="item.num=$event">
const steper = {
   template:  `<div @click=“$emit('numchange',num)”></ div>`,
   props:['pnum'],
   data(){return {num:this.pnum}}
}
  1. 子元素传递给父元素
    a. 事件方式向父亲元素传递
    b. 父亲通过 v-on事件名=“” 接收数据

3.子元素接收参数的形式props

props:['pnum','min',...]
prpos:{
   pnum:{
   type:[Number,String,Boolean,Array,Object],
   default:5
// 设置默认值
    }
}

案例1-组件间数据的传递

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>组件数据的传递-子组件给父亲组件发送事件</title>    
    <script src="./js/vue.js"></script>   
</head>
<body>
<div id="app">
    <steper :pnum="p1"></steper>
    <steper :pnum="p2"></steper>
    <h2>{{p1}}</h2>
    <h2>{{p2}}</h2>
</div>
<script>  
    // 1.定义组件
const steper = {
    template:`<div style="display:inline-block">                
                    <input type="text" v-model="num"/> 
                    <button @click="num++">+</button>         
               </div>`,
    // 在模板里面可以使用pnum
    data(){return {num:this.pnum}},
    props:["pnum"]
    // 通过props来接受父亲传递过来的参数 pnum
    // props 传递过来的数据只读,不能进行双向绑定v-model 和修改
}   
new Vue({
    el:"#app",
    data:{  
      p1:15,
      p2:8  
    },      
    // 2.注册组件
    components:{steper}
})
</script>
</body>
</html>

案例2-组件间数据的传递(综合)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>组件数据的传递-子组件给父亲组件发送事件</title>    
    <script src="./js/vue.js"></script>   
</head>
<body>
<div id="app">
   <h1>购物车</h1>
   <div v-for="(item,index) in list" :key="index">
       <span>{{item.name}}</span>
       <span>{{item.price}}</span>
       <steper :pnum="item.num" @numchange="item.num=$event" ></steper>
   </div>
   <h2>总数量:{{allNum}}</h2>
   <h2>总价格:{{allPrice}}</h2>
</div>
<script>
    
    // 1.定义组件
const steper = {
    template:`<div style="display:inline-block">
                <button @click="num--;$emit('numchange',num)">-</button>
                <input type="text" v-model="num"/>
                <button @click="add()">+</button>
        </div>`,
    // 在模板里面可以使用pnum
    data(){return {num:this.pnum}},
    props:{
        pnum:{
            default:1,
            // type:Number String,Boolean Array
        }
    },
    methods:{
        add(){
            this.num++;
            this.$emit("numchange",this.num)
        }
    }
    // 通过props来接受父亲传递过来的参数 pnum
    // props 传递过来的数据只读,不能进行双向绑定v-model 和修改
}   
new Vue({
    el:"#app",
    data:{  
        list:[
            {name:"红宝书",num:3,price:24},
            {name:"蓝宝书",num:1,price:34},
            {name:"绿宝书",num:6,price:18},
            {name:"彩宝书",num:5,price:22},

        ]         
    },
    computed:{
       allNum(){
           let n=0;
           this.list.forEach(item=>{
               n+=item.num;
           })
           return n;
       },
       allPrice(){
           let n = 0;
           this.list.forEach(item=>{
               n+=item.num*item.price;
           })
           return n;
       }
           
    },
    // 2.注册组件
    components:{steper}

})

</script>
</body>
</html>

4. 组件的插槽

1.基础
   <slot> 在组件内部 嵌套的内容都会被插入到这个标签里面
2.命名插槽
   a. 调用组件

<modal>
    <div slot="title">标题</title>
    <div slot="content">内容</title>
</modal>

   b. 组件内部

<div class="title">
     <slot name="title"></slot>
</div>
<div class="content">
     <slot name="content"></slot>
</div>

案例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>组件的插槽</title>    
    <script src="./js/vue.js"></script>
    <style>
        .block{ border: 1px solid #f0f0f0;}
        .title{ color:#fff;background-color: dodgerblue;}
    </style>   
</head>
<body>
<div id="app"> 
        <block>
             <p slot="title">中国加油</p>
              <div slot="content"> 中国是中华人民共和国</div>
        </block>

        <block>
            <p slot="title">武汉加油</p>
            <div slot="content"> 全中国人民都等着吃热干面呢。</div>
            <button>确定</button>
       </block>     
</div>
<script>
const block = {
    template:`
    <div class="block"> 
        <div class="title">
            <slot name="title"></slot>
        </div>
        <div class="content">
            <slot name="content"></slot>
        </div>
        <slot></slot>
    </div>`
}
new Vue({
    el:"#app",
    data:{          
    }, 
    components:{block} 
})
// 1. vue组件像html标签一样
</script>
</body>
</html>
组件插槽.png

5. 弹出框

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>组件的插槽</title>    
    <script src="./js/vue.js"></script>
    <style>
        *{margin: 0; padding: 0;}
        body{padding: 50px;}
        .modal{
            position: absolute;
            left: 0; top:0; bottom:0; right:0;
            background-color: rgba(0,0,0,.6);}        
        .modal .content{
            width: 400px; height: 300px; 
            position: absolute; left:50%; top:50%; 
            transform: translate(-50%,-50%);
            background-color: #fff; }       
        .modal .title{
            line-height: 48px; color:#fff;
            background-color:rgb(9, 97, 238);
            position: relative; text-align: center;   }     
        .modal .close{
            position: absolute;
            line-height: 48px;
            width: 48px; right:0; top:0;
            text-align: center; cursor: pointer; }       
        .modal .body{
            padding:15px;
        }
    </style>   
</head>
<body>
<div id="app"> 
  <button @click="num=1">武汉</button> <button @click="num=2">中国</button> <br>
  <modal v-if="num==1" @close="num=null">
      <span slot="title">武汉加油</span>
      <div  slot="content">待到四月花开,热腾腾热干面,走起来</div>
  </modal>
  <modal v-if="num==2"  @close="num=null" >
    <span slot="title">中国加油</span>
    <div slot="content">中国是一个5千年历史大国,什么困难都遇到过,都能迈过去!</div>
</modal>
     
</div>
<script>
const modal = {
    template:`
    <div class="modal" >
        <div class="content">
            <div class="title"><slot name="title"></slot> <span class="close" @click="$emit('close',null)">×</span></div>
            <div class="body"><slot name="content"></slot></div>
        </div>
    </div>
    `,  
}
new Vue({
    el:"#app",
    data:{  
        num:null,        
    }, 
    components:{modal}
})
</script>
</body>
</html>

6.综合案例(书单购物车)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>表格修改</title>
    <style>
        *{margin: 0; padding: 0;}
        body{padding: 50px;}
        .modal{
            position: absolute;
            left: 0; top:0; bottom:0; right:0;
            background-color: rgba(0,0,0,.6);}        
        .modal .content{
            width: 400px; height: 300px; 
            position: absolute; left:50%; top:50%; 
            transform: translate(-50%,-50%);
            background-color: #fff; }       
        .modal .title{
            line-height: 48px; color:#fff;
            background-color:rgb(9, 97, 238);
            position: relative; text-align: center;   }     
        .modal .close{
            position: absolute;
            line-height: 48px;
            width: 48px; right:0; top:0;
            text-align: center; cursor: pointer; }       
        .modal .body{
            padding:15px;
        }
        .table{ border-collapse: collapse; width: 800px;}
        .table td{ border:1px solid #f0f0f0; line-height: 38px; text-align: center;}
        .table .th{ background-color: dodgerblue; color:#fff}
    </style>
    <script src="./js/vue.js"></script>
</head>
<body>
    <div id="app">
        <input type="text" placeholder="标题" v-model="newItem.title"> 
        <input type="text" placeholder="作者" v-model="newItem.author"> 
        <input type="date" placeholder="日期" v-model="newItem.date"> 
        <button @click="addItem">确定</button> 
        <!-- 单击确定执行 addItem 添加新的条目到list里面 -->
        <table class="table">
            <tr class="th"><td>序号</td>
            <td>标题</td>
            <td>作者</td>
            <td>日期</td>
            <td>操作</td></tr>
            <tr v-for="(item,index) in list" :key="index">
                <td>{{index+1}}</td>
                <td>{{item.title}}</td>
                <td>{{item.author}}</td>
                <td>{{item.date}}</td>
                <td><span @click="delItem(item)">删除</span> | <span @click="toEdit(item,index)">编辑</span></td>  
                <!-- 单击删除 执行删除命令      |    单击编辑设置flag为true 也就显示了编辑弹框 -->
            </tr>
        </table>
        <!-- 编辑弹窗是显示有flag值来控制 -->
        <div class="modal" v-if="flag">
            <div class="content">
                <div class="title">
                    编辑
                    <div class="close" @click="flag=false">×</div>
                    <!-- 单击x设置flag为false 关闭了编辑弹框 -->
                </div>
                <div class="body">
                    <input type="text" placeholder="标题" v-model="tempItem.title"> <br>
                    <input type="text" placeholder="作者" v-model="tempItem.author">  <br>
                    <input type="date" placeholder="日期" v-model="tempItem.date"> <br>
                    <button @click="sureEdit">确定</button><button @click="flag=false">取消</button>
                </div>
            </div>
        </div>

    </div>
    <script>
        new Vue({
            el:"#app",
            data:{
                cIndex:null,//代表当前正在编辑的行数
                flag:false,//控制是否显示编辑弹框
                newItem:{title:'',author:'',date:''},// 绑定新添加的item条目
                tempItem:{title:'',author:'',date:''},// 绑定正则编辑的item条目
                list:[                                // 列表数据
                {title:"水知道答案",author:"mumu",date:"2019-02-09"},
                {title:"前端的密码",author:"zql",date:"2019-03-15"}
               ]
            },
            methods:{
                // 确定编辑
                sureEdit(){
                    this.list[this.cIndex] = {...this.tempItem};
                    // console.log(this.list[this.cIndex]);
                    // 把当前正在编辑的行 赋值为 tempItem解构后的值
                    this.flag = false;
                    // 关闭编辑器
                },
                // 准备编辑 -打开编辑弹框
                toEdit(item,index){
                    this.tempItem = {...item};
                    // 设置tempItem的值为 传过来的Item值
                    this.cIndex = index;//设置当前编辑的行数为传递过来的index
                    this.flag = true;//显示编辑框
                },
                // 添加item
                addItem(){
                    // 把新的条目展开 创建一个新的对象插入到list最前面
                    this.list.unshift({...this.newItem});
                    // 清空newItem
                    this.newItem={title:'',author:'',date:''};
                },
                // 删除item
                delItem(item){
                    let ind = this.list.indexOf(item);
                    this.list.splice(ind,1);
                }
            }
        })      
    </script>
</body>
</html>
表格书单.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,635评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,628评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,971评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,986评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,006评论 6 394
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,784评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,475评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,364评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,860评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,008评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,152评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,829评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,490评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,035评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,156评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,428评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,127评论 2 356

推荐阅读更多精彩内容

  • 什么是组件? 组件 (Component) 是 Vue.js 最强大的功能之一。组件可以扩展 HTML 元素,封装...
    youins阅读 9,481评论 0 13
  • 探索Vue高阶组件 高阶组件(HOC)是 React 生态系统的常用词汇,React 中代码复用的主要方式就是使用...
    君惜丶阅读 979评论 0 2
  • 探索Vue高阶组件高阶组件(HOC)是 React 生态系统的常用词汇,React 中代码复用的主要方式就是使用高...
    videring阅读 10,626评论 5 30
  • 一、了解Vue.js 1.1.1 Vue.js是什么? 简单小巧、渐进式、功能强大的技术栈 1.1.2 为什么学习...
    蔡华鹏阅读 3,329评论 0 3
  • 目录 课时01🍡安装渲染——声明式文本插值元素属性条件指令循环指令事件监听双向绑定组件应用定义使用渲染 课时02🍡...
    一点金光阅读 218评论 0 0