一、前言
今天分享一下组件的分类,主要分享一下抽象组件的用法!
二、动态组件
说明: 上一次我们定义过一个Radio单选框的组件,如果按照动态、静态分类的话,它就属于静态组件,因为这个组件标签一旦写上去之后,我的数据发生变化的时候,他一样是
属于单选框组件,他并不能成为其他组件
定义: 某一个位置渲染的组件是由于某一个数据的切换造成的
核心: 使用内置的component组件来渲染其他组件
组件标签: < component>< /component>
简单使用: 要么通过is进行绑定已经注册组件的名字,要么通过is绑定未注册组件的data或computed配置项
// 举例:每一秒中将组件根据componentName的不同完成在co-radio和co-button
// 之间进行的切换
<component :is="'componentName'"></component>
export default {
data() {
return {
componentName: 'co-radio'
}
},
methods: {
toggle() {
this.componentName = this.componentName === 'co-radio' ? 'co-button' : 'co-radio'
}
},
created() {
setInterval(()=>{
this.toggle()
}, 1000)
}
}
使用场景: 页面的切换
原理: 路径的hash值(路径后面以#开头的部分)变化,路径是不会变化的,但是hash值得改变,会执行一个hashchange的回调函数,所以可以用hash值的改变来作为动态组件的渲染数据,以此来切换页面。
<template>
// 假设现在我又两个单页面组件,一个是Home页面,一个是about页面
<a herf="#home">Home</a>
<a herf="#about">about</a>
<component :is="current"></component>
</template>
<script>
import Home from "./Home"
import about from "./about"
// 当hash值为#home的时候渲染Home组件,当hash值为#about的时候渲染about组件
routes: {
"#home": Home,
"#about": about,
}
export default {
data() {
return {
// 默认不渲染页面
current: null
}
},
beforeCreate() {
// 给window注册一个hashchange的事件
window.onhashchange = () => {
this.current = routes[window.location.hash]
}
},
}
</script>
代替:
- < a />标签的作用可以封装成< router-link />
- < component />组件可以封装成< router-view />
三、异步组件
概念: 异步组件会被切分成小的js文件放在服务器上面,如果不使用这个组件他就不会发请求去访问这个JS,如果使用这个组件它会执行这个JS文件然后执行该文件,并且将执行结果储存起来并且缓存起来供未来使用。
使用:
<template>
<a herf="#home">Home</a>
<a herf="#about">about</a>
<component :is="current"></component>
</template>
<script>
// 异步组件会按需进行加载
routes: {
// 可以使用 /* webpackChunkName: 文件名 */ 的方式给异步组件的JS片段
// 重新修改文件名
"#home": () => import(/* webpackChunkName: home */'./Home'),
"#about": () => import('./about'),
}
export default {
data() {
return {
current: null
}
},
beforeCreate() {
window.onhashchange = () => {
this.current = routes[window.location.hash]
}
},
}
</script>
四、函数式组件
概念: 给组件的template添加一个functional的属性,就可以让组件变为无状态、无实例的函数化组件。因为只是函数,所以渲染的开销相对来说,较小。
应用场景: 如果书写的vue的文件中不存在Script标签,也就是页面只起到展示的作用,但是默认情况下它会自动生成一个Script标签,也会自动的new一个实例,只不过都是空的,那么我一个纯展示的页面还要去new一个vue实例,会消耗性能,这样我们可以在template标签上面写一个functional的属性,来告诉它不要创建一个实例。
注意: 在函数式组件中,所有属性均是props属性,使用的话直接使用props.属性名就可以使用,data、slots、children、parent使用的方法也是如此
函数式组件的event:
// 思考:假设我有一个Card组件,我在父组件中给Card组件绑定了一个click的事件,
// 如果想触发这个事件的话按照之前的写法在组件的内部使用this.$emit可以触发,
// 但是函数式组件不存在实例,也就不存在this,但是可以使用listeners拿到所有
// 绑定的事件,使用具体的事件直接用 .事件名 就可以了
// 父组件:
<Card @click="foo">
export default {
methods: {
// Card组件如果使用绑定的事件向父组件传递数据的话,传递的数据就会被
// 父组件中绑定的回调函数的参数所接住,在这里就会被参数arg所接住
foo(arg) {
console.log(arg)
}
}
}
// Card组件内部:
<div @click="listeners.click"/> // 这样就可以执行了
<div @click="listeners.click(111)"/> // 也可以传递数据到Card组件外面去
五、抽象组件
作用:在组件的配置项中存在一个abstract的选项,这个选项的值是一个布尔值,如果这个值为true,这个组件本身并不会成为一个dom元素,也不会出现在组件的父组件链中,并且在抽象组件的生命周期里面,可以对包裹的子组件监听的事件进行拦截,也能对子组件进行dom操作,这样就达到了对需要的功能进行封装,但是可以不关心子组件的具体实现的效果。
注意: 抽象组件在使用的时候可以不写模板!
使用说明:
// 举例:封装一个防抖的组件,使其可以在使用之后,防抖组件内部的组件都会存在
// 防抖的效果
// 作用:比如说在登陆注册页面这里,按钮每点击一次都会发出请求,但是如果由于
// 网速等其他问题导致页面加载不出来,可能会导致页面重复的点击,这样就
// 会加重服务器的负担,为了解决这个问题,需要在这种地方进行防抖的处理
// 实现效果:
// wait为防抖函数的等待时间
// 要求:防抖组件Debounce在渲染的时候只渲染Debounce组件里面的子组件,
// 而Debounce组件不被渲染
<Debounce wait="2000">
<button>登陆<button> // 按钮会具有防抖的效果
</Debounce>
实现:
import { debounce } from "lodash" // 引入防抖函数
export default {
// 一般定义组件的时候需要给组件一个name的属性,这个name属性的主要作用
// 是在开发过程中提高组件的可维护性和可读性,同时也能够方便调试和性能
// 优化。
name: "debounce",
// 告诉vue在渲染这个组件的时候是渲染一个抽象组件
abstract: true,
// 在父组件中会绑定了一个wait的属性,那么在debounce组件内部需要使用
// props将这个属性的值接住并使用
props: {
wait: {
type: "String",
default: 350,
}
}
// 要渲染防抖组件内部包裹的子组件,但是没有模板,所以不能使用插槽slot
// 将debounce组件内部的子组件接住并渲染出来(插槽默认是默认插槽,其内
// 容可以使用this.$slots.default[0]获取),不过可以使用render函数作
// 为slot的替代品,对于render函数来说,它返回什么就会渲染什么,所以可
// 以返回this.$slots.default[0]来将传递给debounce组件内部的内容渲染
// 出来
render() {
const Vnode = this.$slots.default[0]
// 对于子节点,它可能会绑定一些事件来做一些任务,上面可以使用
// this.$slots.default[0]拿到这些子节点,同时,这些子节点所
// 绑定的事件都存放在this.$slots.default[0].data之中,而对于
// 按钮来说,一般是点击事件,那么这个所绑定的点击事件可以使用
// this.$slots.default[0].data.on.click来获取,然后防抖函数
// 可以使用Lodash中已经写好的来提高开发进度
Vnode.data.on.click = debounce(Vnode.data.on.click, this.waitd)
return Vnode
}
// 给组件添加一个install的方法,方便组件使用use去被注册
install(v) {
v.component(this.name, this)
},
}
注意: 对于js文件来说,一个js文件就是一个组件,因为它不需要模板,没有模板也就意味着它不存在样式,那么就只需要一个js的内容就可以了