VUE 入门及应用 ( 模块化 )

3.模块化

3.1.组件

可复用的页面元素可以封装成组件

3.1.1.私有组件步骤

3.1.1.1.定义格式

定义一个可以配置常用信息的文本框

let MyPass = Vue.extend({
    name: 'MyInput',
    template: `
            <div>
                <label>{{ title + ' : ' }}</label>
                <input type="text" v-model="val" :placeholder="placeholder"  @blur="checkVal" />
            </div>
        `,
    props: {
        title : {
            type: String,
            default: '姓名'
        }
    },
    computed: {
        placeholder(){
            return '请输入' + this.title
        }
    },
    data(){
        return {
            val: 'abc'
        }
    },
    methods: {
        checkVal(){
            console.log( this.val )
        }
    }
})

使用 Vue.extend() 以继承Vue的方式 定义变量 MyPass , 这样就可以使用Vue定义的属性( el 属性不能使用 )

通常也可以省略直接定义成 JSON对象

name : 组件的名称

template: 定义 组件模版, 必须只有一个根结点

props: 定义组件属性, 属性的 值 是在使用组件时赋值, 相当于 接收 父组件传来的值

data : 定义组件数据, 必须是一个函数, 不能是一个对象 (对象是单例的, 函数每次调用生成新的)

methods : 定义 组件事件

3.1.1.2.声明组件
 const vm  = new Vue({
     el: '#app',
     data: {

     },
     components: {
         MyInput
     }
 })

在 vue 对象中增加 components 属性来声明 要使用的组件

3.1.1.3.使用组件
    <div id="app">
        <my-input title="账号" ></my-input>
        <my-input title="姓名" ></my-input>
    </div>

使用时 通过 title 属性将 信息传入到 组件中,

组件可以重复使用

3.1.2.公共组件

直接通过 Vue.component() 创建的组件, 不用在vue对象中声明, 可以在多个 vue实例中使用的

这个案例 没有什么实际意义, 单纯为了说明 Vue.component() 功能

    Vue.component('my-title',{
        template: '<h1>{{msg}}</h1>',
        props: {
            msg: {
                type: String,
                default: '标题'
            }
        }
    })

页面使用

<my-title msg="测试标题"></my-title>

3.1.3.组件接值案例

3.1.3.1.案例一

vue 对象 data 属性, 增加 val 为 姓名

 data: {
            val : '姓名'
 },

页面增加 单选框 元素 与 val 双向绑定

<my-input :title="val" ></my-input>
<br>

<input type="radio" v-model="val" :value="'姓名'" /> 姓名
<input type="radio" v-model="val" :value="'学号'" /> 学号
<input type="radio" v-model="val" :value="'账号'" /> 账号

这样 选择 单选框 修改 vue中 data中的 val值, 再通过 组件的 :title="val" 向组件传值

3.1.3.2.案例二

vue 对象 data 属性, 增加 types 数组

 data: {
     types : ['姓名','学号','账号']
 },

页面使用 v-for 进行组件 赋值

 <my-input  v-for="(val,index) in types" :key="index"  :title="val" ></my-input>

3.1.4.组件回传值

子组件可以使用 $emit() 触发父组件的自定义事件。

vm.$emit( event, arg ) //触发当前实例上的事件

父组件通过自定义事件接值

3.1.4.1.修改子组件

在函数中增加 代码 , 其中 retval 对应父组件自定义事件, this.val 是传入事件函数的值

  methods: {
      checkVal(){
          console.log( this.val )
          this.$emit('retval', this.val)
      }
  }
3.1.4.2.修改组件使用代码

在组件上 增加 @retval 自定义事件, 触发 调用 showChildValue 函数

<my-input title="账号" @retval="showChildValue" ></my-input>

在 methods 中 定义函数, 可以接收传回来的参数

 methods: {
     showChildValue(val){
         console.log(val)

     }
 }

3.1.5.父子嵌套组件

注意, 使用 其它的组件 要先声明组件, 并且声明的组件要 在前面已经定义

而公共组件可以直接使用

let LoginPage = Vue.extend({
    template: `
          <div style="width: 300px;text-align: center;">
            <my-title msg="登录系统"></my-title>
            <my-input title="账号" ></my-input> <br>
            <br>
            <button @click="login" >登录</button>
          </div>
        `,
    components: {
        MyInput
    },
    methods: {
        login() {
            alert('登录成功')
        }
    }
})

3.1.6.sync修饰符

Vue.js 中的 .sync 修饰符用于简化子组件向父组件更新 props 数据的流程,尤其是在需要实现类似于双向数据绑定的情形下。

尽管 Vue.js 严格遵循单向数据流原则,即 Props向下传递,事件向上冒泡,但在某些场景下,子组件确实需要更改父组件的状态。

在 Vue 2.x 版本中,.sync 修饰符提供了一种便捷的方式来实现这种行为。

当在子组件的 prop 上使用 .sync 修饰符时,实际上是告诉 Vue 在子组件内部变更该 prop 值时,自动通过自定义事件机制将更新传递给父组件。

例如,在 Vue 2 中,如果我们有一个父组件向子组件传递 value prop,并希望子组件能同步修改这个值,我们可以这样配置:

<!-- 父组件 -->
<template>
  <child-component :value.sync="parentValue"></child-component>
</template>
<script>
export default {
  data() {
    return {
      parentValue: '初始值'
    };
  }
};
</script>
<!-- 子组件 -->
<template>
  <input type="text" v-model="localValue" />
</template>
<script>
export default {
  props: ['value'],
  data() {
    return {
      localValue: this.value
    };
  },
  watch: {
    localValue(newVal) {
      this.$emit('update:value', newVal);
    }
  }
};
</script>

在这个例子中,子组件通过 $emit('update:value', 新值) 触发自定义事件将新值传递给父组件。.sync 修饰符让父组件无需手动监听这个事件并更新自身的 parentValue,而是自动完成了这一过程。

3.1.7.props属性

在Vue 2中,组件的props主要有以下几种配置方式和类型:

3.1.7.1.配置方式:
  1. 简单声明

    Vue.component('my-component', {
      props: ['propA', 'propB'], // 使用字符串数组声明props名称
      // ...
    });
    

    这种方式下,Vue允许父组件传递任何类型的数据到子组件,并且不做任何验证。

  2. 对象形式

    Vue.component('my-component', {
      props: {
        propA: String, // 类型声明
        propB: Number,
        propC: {
          type: Boolean,
          default: false,
          required: true
        }
      },
      // ...
    });
    

    通过对象的方式可以更加细致地配置props,包括但不限于以下属性:

    • type: 指定prop接受的数据类型,如String, Number, Boolean, Array, Object, Function, Symbol, 或自定义构造函数等。
    • default: 设置prop的默认值。
    • required: 标记该prop是否必填,如果是必需的prop并且在父组件中未传递,则Vue会发出警告。
    • validator: 自定义验证函数,用于验证传入prop的值是否满足某些额外的条件。
  3. Prop验证
    除了基础类型之外,还可以使用复合类型,例如:

    propD: {
      type: Object,
      default: () => ({ value: '' }),
      validator: function(value) {
        // 自定义验证逻辑
        return typeof value === 'object' && !Array.isArray(value);
      }
    }
    
3.1.7.2.Prop类型列表:
  • String
  • Number
  • Boolean
  • Array
  • Object
  • Function
  • Symbol
  • Date
  • Vue组件实例
  • 自定义构造函数(如自定义的类)

此外,Vue还支持枚举类型(Enum),可通过数组定义允许的值:

propE: {
  type: String,
  validator: value => ['option1', 'option2'].includes(value)
}

也可以简化为:

propE: {
  type: ['option1', 'option2']
}

Vue 2并不直接支持枚举类型作为type选项,但可以通过validator实现类似效果。不过在Vue 3中引入了type可以为枚举数组的功能。

3.2.模版<template>

在 Vue.js 中,<template> 标签用于定义一个包裹多个元素的容器,它本身不会被渲染成 DOM 元素,而是作为一个逻辑分组来帮助组织模板结构。

3.2.1.包裹元素标签

通常情况下,使用 <template> 的主要原因包括:

3.2.1.1.条件渲染

当你需要基于条件来决定一组元素是否渲染时,可以将这一组元素放在 <template> 标签内,并配合 v-ifv-show 指令来控制整体渲染。

<template v-if="isVisible">
  <div>当 isVisible 为true 时 才会渲染</div>
  <p>可以是多个html 元素</p>
</template>
3.2.1.2.循环渲染

在列表渲染 v-for 中,为了更好地组织循环体内的结构,可以使用 <template> 来包裹多层或多个元素。

<ul>
  <template v-for="item in items">
    <li :key="item.id">{{ item.name }}</li>
    <template v-if="item.hasSubItems">
      <ul>
        <li v-for="subItem in item.subItems" :key="subItem.id">{{ subItem.name }}</li>
      </ul>
    </template>
  </template>
</ul>

3.2.2.简化自定义组件

组件 的 template 模版 可以独立成 html 的< template > 结点

注意这时 <template> 结点 不要写在 <div id= "app" ></div>

因为这个div 会被 vue 解析

同样要有 唯一 根结点

  <div id="app">
       <my-input title="账号" ></my-input>
   </div>


 <template id="myInput">
     <div>
         <label>{{ title + ' : ' }}</label>
         <input type="text" v-model="val" :placeholder="placeholder"  @blur="checkVal" />
     </div>
</template>

而 组件 template 属性 根据 id 关联 <template> 结点

let MyInput = {
    name: 'MyInput',
    template: "#myInput",
    props: {
        title : {
            type: String,
            default: '姓名'
        }
    },
    computed: {
        placeholder(){
            return '请输入' + this.title
        }
    },
    data(){
        return {
            val: 'abc'
        }
    },
    methods: {
        checkVal(){
            console.log( this.val )
        }
    }
}

3.3. 插槽

Vue.js 中的插槽(Slots)是一种用于组件间内容分发的强大机制,它允许开发者在父组件中定义内容,然后将这些内容插入到子组件的特定位置。

这样可以极大地提高组件的复用性和灵活性。

Vue.js 中有两种主要类型的插槽:

3.3.1.默认插槽

默认插槽是组件中没有指定名称的插槽。

在子组件中,使用 <slot> 标签作为占位符,父组件可以在这个位置插入内容。

<!-- 子组件 -->
<template id="child-card" >
  <div class="card">
    <h3>标题</h3>
    <slot>默认内容,当父组件没有插入内容时显示</slot>
  </div>
</template>

<!-- 父组件 -->
<template id="parent-card" >
  <custom-card>
    <p>这是来自父组件的自定义内容,将替换子组件的默认插槽。</p>
    <p>这些也会一起替换子组件的插槽。</p>
  </custom-card>
</template>

定义组件

// 子组件
Vue.component('custom-card', {
    template: '#child-card'
})

// 父组件
Vue.component('parent-card', {
    template: '#parent-card'
})

const vm = new Vue({
    el: '#app'
})

页面使用

<div id="app">
     <parent-card></parent-card>
</div>

3.3.2.具名插槽

具名插槽允许组件拥有多个插槽,每个插槽都有一个独特的名称,从而允许父组件插入到子组件的不同位置。

<!-- 子组件 -->
<template   id="child-card" >
  <div class="dialog">
    <header>
      <slot name="header">默认头部</slot>
    </header>
    <main>
      <slot>默认主体内容</slot>
    </main>
    <footer>
      <slot name="footer">默认底部</slot>
    </footer>
  </div>
</template>

<!-- 父组件 -->
<template id="parent-card" >
  <custom-dialog>
    <h2 slot="header">自定义头部</h2>
    <p>这是自定义主体内容</p>
    <button slot="footer">自定义底部按钮</button>
  </custom-dialog>
</template>

3.3.3.作用域插槽

作用域插槽允许子组件在插槽中传递数据给父组件插入的内容。在 Vue 2 中,使用 slot-scope 特性来接收作用域插槽的数据;

<!-- Vue 2 子组件 -->
<template id="child-card" >
    <div class="list">
        <slot v-for="item in items" :tm="item">
            <!-- 默认内容, 当父组件没有插入内容时显示 -->
            {{ item.text }}
        </slot>
    </div>
</template>

<!-- Vue 2 父组件 -->
<template id="parent-card" >
    <custom-list :items="items">
        <template slot-scope="{ tm }">
            <div>
                <strong>{{ tm.text }}</strong>
                <em>{{ tm.detail }}</em>
            </div>
        </template>
    </custom-list>
</template>

在子组件中 :

  • 子组件有一个名为 “items” 的 prop(属性),期望从父组件接收一个数组。
  • 使用 v-for 指令遍历 “items” 数组,每次迭代都会生成一个新的 slot 元素。
  • slot 元素包含了 :tm 特性,用于将当前遍历到的数组项(item)传递给父组件。

在父组件中 :

  • 父组件模板中引用了名为 “custom-list” 的组件,并向其传递一个名为 “items” 的 prop,假设它是一个包含多个对象的数组,每个对象至少包含 “text” 和 “detail” 属性。
  • 在 “custom-list” 组件内,使用 <template> 标签配合 slot-scope="{ tm }" 来接收子组件传递过来的每个数组项(item)。
  • 父组件定义了自己的内容模版,用于替换子组件中 v-for 所生成的每一个 slot。这意味着当遍历 “items” 数组时,会根据父组件提供的模版来渲染每一项内容。

两个组件的定义

// 子组件
Vue.component('custom-list', {
    template: '#child-card',
    props: {
        items: {
            type: Array,
            required: true
        }
    }
})

// 父组件
Vue.component('parent-card', {
    template: '#parent-card',
    data() {
        return {
            items: [
                { text: '王', detail: '王小二' },
                { text: '李', detail: '李小三' },
                { text: '赵', detail: '赵小四' }
            ]
        }
    }
})
3.3.3.1.实例: 格式化时间
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
    <div id="app">
        <now-date></now-date>
    </div>
    <template id="child-temp">
        <div >
            <slot  :fdate="formattedDate">
            {{ date }} : {{ formattedDate }}
            </slot>
        </div>
    </template>
    <template id="parent-temp">
        <my-date :date="date" >
            <!-- 这部分会替换  子组件的 slot部分, 并通过 slot-scope 得到 回传值  -->
            <template slot-scope="{ fdate }" >
                <div>{{ fdate }}</div>
            </template>
        </my-date>
    </template>
</body>
<script type="text/javascript">
    // 子组件 , 得到时间, 格式化后  通过  :fdate 回传
    Vue.component('my-date', {
        template: "#child-temp",
        props: {
            date: {
                type: Date,
                required: true
            }
        },
        computed: {
            formattedDate() {
                return this.date.toLocaleString()
            }
        }

    })
    
    // 父组件 , 传入当前时间
    Vue.component('now-date', {
        template: "#parent-temp",
        data() {
            return {
                date: new Date()
            }
        }
    })


    const vm = new Vue({
        el: '#app',
    })
</script>
</html>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值