Vue从零开始之Vue组件

文章目录

  • Vue 组件
    • 组件基础
      • 组件模板抽离的写法
      • 组件中定义data/方法
    • 父子组件通信传值
      • 父传子:props属性
        • props大小写问题
      • 子传父: 自定义事件
        • 数据双向绑定时 子传父
    • 父子组件访问
      • 父访问子 c h i l d r e n 和 children和 childrenrefs
      • 子访问父 p a r e n t 和 parent和 parentroot
    • Vue 插槽
      • slot与slot-scope

Vue 组件

组件基础

组件化就是将一个页面拆成一个个可复用功能块(组件),方便扩展和复用.

注意:在实际开发中,我们并不会用以下方式开发组件,而是采用 vue-cli 创建 .vue 模板文件的方式开发,以下方法只是为了让大家理解什么是组件。

组件使用的三个步骤:

  • 创建组件构造器: 调用Vue.extend()方法,Vue2之后可以将extend中的对象在注册组件时传入
  • 注册组件
    • 使用 Vue.component() 方法注册全局组件,所有Vue实例都可使用
    • 在Vue实例中使用components注册局部组件,只有注册的Vue实例才能使用,注册的Vue实例的父实例不能使用该局部组件
  • 使用组件
    • 注意局部组件的作用域
    • 子组件中不能直接访问父组件的数据

组件是可复用的 Vue 实例,一般称new出来的Vue实例为根组件.

  • 组件名定义时如果使用的是驼峰命名法,在使用名字时要改为短横线分隔命名法
  • 每个组件必须只有一个根元素
<div id="vue">
    <ul>
        
        
        <my-component-li v-for="item in items" v-bind:aaa="item">my-component-li>
    ul>
div>
<script src="https://cdn.jsdelivr.net/npm/vue">script>
<script type="text/javascript">
    const cpnc1 = Vue.extend({
      
        template: `
            

啊啊啊啊

`
}) // 组件中注册组件 const cpnc2 = Vue.extend({ template: `

哈哈哈哈哈

`
, //组件构造器中注册组件, components:{ cpn1: cpnc1 } }) //注册组件 cpn1为组件名 Vue.component('cpn1',cpnc1) // 使用语法糖注册组件,{}就是传入extend的对象 Vue.component('my-component-li', { //使用 props 属性向子组件传递数据,通过v-bind绑定属性 props: ['aaa'], //组件的模板 template: '
  • Hello { {aaa}}
  • '
    }); var vm = new Vue({ el: '#vue', data: { items: ["张三", "李四", "王五"] } }); const app= new Vue({ el: '#app', //注册局部组件 components: { // cpn为使用组件的标签名,cpnc为组件构造器 cpn: cpnc2 // 使用语法糖可以省略 extend cpu2: { template: `

    哈哈哈哈哈

    `
    , components:{ cpn1: cpnc1 } } } })
    script>

    组件模板抽离的写法

    • script标签
    //type必须为 text/x-template
    <script type="text/x-template" id="cpn">
        <li>Hello</li>
    script>
    
    <script src="https://cdn.jsdelivr.net/npm/vue">script>
    <script type="text/javascript">
        Vue.component('myli', {
          
            template: '#cpn'
        });
    script>
    
    • template标签
    <template id="cpn">
        <li>Helloli>
    template>
    
    <script src="https://cdn.jsdelivr.net/npm/vue">script>
    <script type="text/javascript">
        Vue.component('myli', {
          
            template: '#cpn'
        });
    script>
    

    组件中定义data/方法

    • 组件中的data只能使用函数定义,不能直接使用对象定义
    • 由于data定义为了函数,所以同一个组件创建的多个标签之间的data数据互不干扰
      • 不同标签的data都是通过函数在堆中重新创建的
      • 若在Vue实例外定义变量,data函数中直接返回则不同标签间共享该变量
    <script type="text/javascript">
        Vue.component('myli', {
          
            template: '

    { {title}}

    '
    , data(){ return { title: 'abc' } }, //组件中定义方法与Vue实例中一样 methods: { } }); // 强制组件创建的不同标签间data数据共享 const obj={ count: 0 } Vue.component('myliii', { template: '

    { {count}}

    '
    , data(){ return obj } });
    script>

    父子组件通信传值

    父传子:props属性

    • 子组件中定义props的属性
    • 在组件模板中绑定属性
    • 在标签中对props定义的属性进行传值

    避免在子组件中直接改变props属性的值,而是在子组件中使用data或计算属性接收props属性的值后再进行双向绑定或修改

    <div id="vue">
        <ul>
            
            
            <my-component-li v-for="item in items" v-bind:aaa="item">my-component-li>
        ul>
    div>
    <script src="https://cdn.jsdelivr.net/npm/vue">script>
    <script type="text/javascript">
        Vue.component('my-component-li', {
          
            //props可使用数组或对象
            
            //使用数组只能指定变量名
            //props: ['aaa'],
            
            //使用对象时可指定类型,默认值,是否必传
                //类型是对象或数组时,默认值必须是函数
                // 还可验证自定义类型
            props:{
          
                // 只指定类型
                aaa: String,
                // 指定类型和默认值,必传值
                bbb: {
          
                    type: String,
                    default: 'aaaaaa',
                    required: true
                }
                cage: Number,
                cpp: {
          
                    type: Array,
                    default(){
          
                        return []
                    }
                },    
                propE: {
          
                  type: Object,
                  // 对象或数组默认值必须从一个工厂函数获取
                  default: function () {
          
                    return {
           message: 'hello' }
                  }
                },
                // 自定义验证函数
                propF: {
          
                  validator: function (value) {
          
                    // 这个值必须匹配下列字符串中的一个
                    return ['success', 'warning', 'danger'].indexOf(value) !== -1
                  }
                }
            }
            template: '
  • Hello { {aaa}}
  • '
    , data(){ return { //将 props中的aaa赋值给data daaa: this.aaa } } }); var vm = new Vue({ el: '#vue', data: { items: ["张三", "李四", "王五"] } });
    script>

    props大小写问题

    HTML 中的 attribute 名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符。所以使用驼峰命名法的 props 名需要使用其等价的短横线分隔命名

    Vue.component('blog-post', {
      // 在 JavaScript 中是 驼峰 的
      props: ['postTitle'],
      template: '<h3>{
        { postTitle }}h3>'
    })
    
    
    <blog-post post-title="hello!">blog-post>
    

    子传父: 自定义事件

    • 子组件模板中监听某个事件,发生时调用子组件中的方法
    • 子组件方法中通过this.$emit('自定义事件名', 参数)设置自定义事件
    • 父组件模板监听 自定义事件,发生时调用父组件中的方法
    • 父组件方法中可操作父组件的数据,最终达到子组件向父组件传值的过程
    <div id="vue">
        
        
        <myarr @itemclick="cpnclick">myarr>
    div>
    
    <template id="cpn">
        <div>
            <button v-for="item in items" @click="btnclick(item)">
                {
        {item}}
            button>
        div>
    template>
    <script src="https://cdn.jsdelivr.net/npm/vue">script>
    <script type="text/javascript">
        Vue.component('myarr', {
          
            template: "#cpn",
            data(){
          
                return{
          
                   items: ["张三", "李四", "王五"]
                }
            },
            methods: {
          
                btnclick(item){
          
                    // 子组件获取到点击的按钮
                    console.log(item);
                    // 子组件向父组件发送指定事件
                    this.$emit('itemclick', item)
                }
            }
        });
        var vm = new Vue({
          
            el: '#vue',
            methods: {
          
                cpnclick(item): {
          
                    // 父组件监听到事件
                    console.log('cpnclick',item);
                }
            }
        });
    script>
    

    数据双向绑定时 子传父

    • v-model拆成v-bind:valueinput事件,在不影响数据双向绑定的前提下,在input事件调用的方法中添加自定义事件完成子组件向父组件传值.
      注意:可能产生类型错误,在发生错误的地方自己转换即可.
    • 使用v-model+watch实现,v-model完成数据的双向绑定,watch监听数据发生改变时执行自定义事件.
    props:{
        name: ''
    }
    data(){
        num: 0
    }
    // watch 监听props/data中数据的改变
        // watch的属性名必须和props/data中的相同
        // watch可得到新旧值,也可值得到新值
    watch:{
        name(newValue,oldValue){
            
        },
        num(newValue){
            
        }
    }
    

    父子组件访问

    有时候我们需要父子组件之间可以直接访问,而不是仅仅传值通信

    父访问子 c h i l d r e n 和 children和 childrenrefs

    • $children: 获取全部的子组件
    • $refs: 获取指定的子组件(常用)
    this.$children //返回子组件的数组
    this.$children[0].方法名/data属性名 // 执行/获取第一个子组件的方法/属性值
    
    
    
    this.$refs.aaa //获取指定的子组件
    this.$refs.aaa.name //获取aaa子组件的name属性值
    

    子访问父 p a r e n t 和 parent和 parentroot

    • $parent: 获取父组件
    • $root: 获取根组件即new出来的Vue实例
    this.$parent //获取父组件
    this.$root //获取根组件
    

    Vue 插槽

    slot与slot-scope

    在 Vue.js 中我们使用 元素作为承载分发内容的出口,作者称其为 插槽,可以应用在组合组件的场景中,相当于占位符,提高组件的扩展性.

    • 单插槽
    
    Vue.component('chacao', {
        template: '

    默认值

    ' });

    传入的值

    传入的值1

    传入的值2

    传入的值3

    • 多插槽,指定name,指定具名插槽替换

    slot的name属性值 和 替换元素的slot属性值相等时,替换指定插槽

    Vue.component('chacao', {
        template: '

    ' });

    指定插槽替换

    插槽的作用域:

    定义一个名为 todo 的待办事项组件, 该组件中放置了两个插槽,分别为 todo-title 和 todo-items

    Vue.component('todo', {
        template: '
    ' });

    定义一个名为 todo-title 的待办标题组件

    Vue.component('todo-title', {
        props: ['title'],
        template: '
    { {title}}
    ' });

    定义一个名为 todo-items 的待办内容组件

    Vue.component('todo-items', {
        props: ['item', 'index'],
        template: '
  • { {index + 1}}. { {item}}
  • ' });

    初始化

    var vm = new Vue({ el: '#vue', data: { todoItems: ['《刀剑神域3》', '《关于我转生成为史莱姆这件事》', '《实力至上主义教室》'] } });

    父模板中的变量会使用父组件的属性,上面的div属于根组件的模板,会从根组件寻找变量而不是从子组件中寻找

    slot-scope:作用域插槽
    :让替换插槽的内容能够访问子组件中的数据

    常用于父组件对子组件数据渲染方式不满意的情况下,获取到子组件的数据自行展示.

    Vue.component('todo', { template: '
    ', data(){ return{ todoItems: ['《刀剑神域3》', '《关于我转生成为史莱姆这件事》', '《实力至上主义教室》'] } } }); var vm = new Vue({ el: '#vue' });

    你可能感兴趣的:(Vue,vue,javascript,js,html,前端)