vue组件销毁时取消监听事件

<template>
  <div>
    <button></button>
  </div>
</template>

<script>
  export default {
     mounted(){
       this.charts = echarts.init(this.$el)
       //请求数据 赋值一系列操作。。。
       //监听窗口发生改变 resize 组件
       window.addEventListener('resize',this.$_handleResizeChart)
      //通过hook监听组件销毁钩子函数 并取消监听事件
      this.$once('hook:beforeDestroy',()=>{
        window.removeEventListener('resize',this.$_handleResizeChart)
      })
     },
     created(){
     },
     methods:{
       $_handleResizeChart(){
         
       }
     }
  }
</script>

<style lang="scss" scoped>

</style>

外部监听组件或第三方组件的生命周期函数

<template>
  <div>
    <!-- 通过@hook:updated监听组件的updated生命钩子函数 -->
    <!-- 组件的所有生命周期钩子都可以通过@hook:钩子函数名 来监听触发 -->
    <custom-select @hook:updated="$_handleSelectUpdated"></custom-select>
</template>

<script>
  import customSelect from '../components/custom-select'
  export default {
    data(){
      return{
        
      }
    },
    components:{
      customSelect
    },
    methods:{
      $_handleSelectUpdated(){
        console.log('custom-select组件的updated钩子函数被触发')
      }
    }
  }
</script>

状态管理小项目不用Vuex而是Vue.observable手写一个状态管理

store.js

/*
 * @Author: your name
 * @Date: 2020-06-26 11:58:28
 * @LastEditTime: 2020-06-26 12:04:38
 * @LastEditors: Please set LastEditors
 * @Description: In User Settings Edit
 * @FilePath: \新建文件夹\vues\src\components\store.js
 */ 
import Vue from 'vue'
// 通过Vue.observable创建一个可响应的对象
export const store = Vue.observable({
    userInfo: {},
    roleIds: []
})
// 通过mutations,修改属性
export const mutations = {
    setUserInfo(userInfo) {
        store.userInfo = userInfo
    },
    setRoleIds(roleIds) {
        store.roleIds = roleIds
    }
}

组件中使用


<template>
  <div>
   <!-- 小型的项目不需要使用vuex进行数据状态管理,可使用Vue.observable 手动打造一个Vuex -->
   {{userInfo.name}}
  </div>
</template>

<script>
  import {store,mutations} from './store'
  export default {
    data(){
      return {}
    },
    computed: {
      userInfo(){
        return store.userInfo
      }
    },
    created() {
      mutations.setUserInfo({name:'子轩'})
    },
  }
</script>

<style scoped>

</style>

Vue.extend + 单例模式去实现全局的loading

Vue.extend是一个全局的api
通过Vue.extend将组件转换为全局组件
可以开发一些全局的组件

customLoading.vue

<template>
  <transition name="custom-loading-fade">
    <!-- loadding蒙层 -->
  <div v-show="visible" class="custom-loading-mask">
    <div class="custom-loading-spinner">
      <!-- 字体图标 -->
      <i class="custom-spinner-icon"></i>
      <!-- loadding上面的文字 -->
      <p class="custom-loading-text">{{text}}</p>
    </div>
  </div>
  </transition>
</template>
<script>
  export default {
    data(){
      return{
        text:"",
        visible: false
      }
    },
  }
</script>

<style scoped>

</style>

index.js 通过 (Vue.extend 改造组件)

   import Vue from 'vue'
    import LoadingComponent from './customLoading.vue'
    // 通过Vue.extend将组件包装成一个子类
    const LoadingConstructor = Vue.extend(LoadingComponent)
    let loading = undefined
    LoadingConstructor.prototype.close = function(){
        // 如果loading有用 则去掉loading的引用
        if(loading){
            loading = undefined
        }
        // 先将组件隐藏
        this.visible = false
        //延迟300毫秒 等loading的关闭动画结束 销毁组件
        setTimeout(()=>{
            // 移除挂载的dom元素
        //this.$el  是Vue实例关联的DOM元素
            if(this.$el&&this.$el.parentNode){
                this.$el.parentNode.removeChild(this.$el)
            }
            //调用组件的$destroy方法进行组件销毁
            this.$destroy()
        },300)
    }
    const Loading = (options={})=>{
        //如果组件已渲染,则返回即可
        if(loading){
            return loading
        }
        // 要挂载的元素
        let parent = document.body
        // 组件属性
        const ots = {
            text:'',
            ...options
        }
        //通过构造函数初始化组件 相当于new Vue()
        const instance = new LoadingConstructor({
            el:document.createElement('div'),
            data:ots
        })
        // 将loading元素挂载到parent上面
        parent.appendChild(instance.$el)
        // 显示loading
        Vue.nextTick(()=>{
            instance.visible = true
        })
        // 将组件实例赋值给loading
        loading = instance
        return instance
    }
    export default Loading

在页面中使用loading

<script>
  import Loading from './index'
  export default {
    crated(){
      const loading = Loading({text:'加载中...'})
      // 三秒后关闭
      setTimeout(()=>{
        loading.close()
      },3000)
    }
  }
</script>

开发V-loading指令

loading.js

  import Vue from 'vue'
  import LoadingComponent from './customLoading.vue'
//   使用Vue.extend构建组件子类
  const LoadingConstructor = Vue.extend(LoadingComponent)
  //定义一个名为loading的指令
  Vue.directive('loading',{
    //   只调用一次 在指令第一次绑定到元素时调用,可以在这里做一些初始化的设置
    // @param {*}el 指令要绑定的元素
    //@param{*}binding 指令传入的信息 
    //包括{name:'指令名称',value:'指令要绑定的值',arg:'指令参数 v-bind:text 对应text'}
    bind(el,binding){
        const instance = new LoadingConstructor({
            el:document.createElement('div'),
            data:{}
        })
        el.appendChild(instance.$el)
        el.instance = instance
        Vue.nextTick(()=>{
            el.instance.visible = binding.value
        })
    },
    /**
     * @description: 所在组件的VNode更新时调用
     * @param {*} el
     * @param {*} binding
     */
    update(el,binding){
        // 通过对比值的变化判断Loading是否显示
        if(binding.oldValue !== binding.value){
            el.instance.visible = binding.value
        }
    },
    /**只调用一次 在指令与元素解绑时调用
     * @param {*} el
     */
    unbind(el){
        const mask = el.instance.$el
        if(mask.parentNode){
            mask.parentNode.removeChild(mask)
        }
        el.instance.$destroy()
        el.instance = undefined
    }
    
  })

在元素上面使用

<template>
  <div v-loading="visible">
  </div>
</template>

<script>
  import Loading from './index'
  export default {
     data(){
       return{
         visible: false
       }
     },
     craeted(){
       this.visible = true
       fetch().then(()=>{
         this.visible = false
       })
     }
  }
</script>

<style  scoped>

</style>

函数式组件

函数式组件与普通组件的区别

1 函数式组件需要在声明组件时指定functional
2 函数式组件没有this 由render函数的第二个参数代替
3 函数式组件没有生命周期 不能使用计算属性 watch 等等
4 函数式组件不能通过$emit 对外暴露事件  调用事件只能通过context.listeners.click的方式调用外部传入的事件
5 因为函数式组件是没有实例化的 所以通过ref去引用组件 实际上引用的是HTMLElement
6 函数式组件的props 可以不用显示声明  所以没在props声明的属性都会被自动隐式解析为prop,
普通组件的所有未声明的属性都被解析到$attrs里面,并自动挂载到组件根元素上面(可通过inheritAttrs)属性禁止

render函数式是Vue使用JSX的写法

<script>

  export default {
    // 设置functional属性指定组件为函数式组件
    functional:true,
    props:{
      avator:{
        type: String,
        default:''
      }
    },
    /**
     * @param {*} h
     * @param {*} context 函数式组件没有this props slots等都在context上面挂着
     */
    render(h,context){
      let {props} = context
      if(props.avator){
        return <img src={props.avator}></img>
      }
      return <img src="default-avator.png"></img>
    }
  }
</script>
  1. 最主要最关键的原因是函数式组件不需要实例化,无状态,没有生命周期,所以渲染性能要好于普通组件
  2. 函数式组件结构比较简单,代码结构更清晰

不用JSX的函数式组件

<template functional>
<!-- 在template上添加functional属性 -->
<!-- 生命函数式组件 -->
  <img :src="props.src?props.avatar:'default-avatar.png'" >
  <!-- // 根据函数式特点 可以省略props的声明 -->
</template>

参考于https://juejin.im/post/5eef77...


HappyCodingTop
526 声望847 粉丝

Talk is cheap, show the code!!


« 上一篇
工具函数
下一篇 »
二十年九月记