葡萄美酒夜光杯,欲饮琵琶产品催。客户现场君莫笑,古来埋坑几人回?

最近一直在开发后台管理系统,日复一日的重复着表单表格表格表单,标准的CV仔,感觉好无聊,如何能在这种无聊的开发过程中去提升自己,小编今天又整理了一波新的Vue实战技巧,这些技巧,不用则已,一用惊人。同时你也可以点击下面的链接阅读近期小编的文章。

阅读小编近期的热门Vue相关文章,感谢各位掘友和群友支持,每周一,不见不散

实战技巧,Vue原来还可以这样写 获赞 1600+

绝对干货~!学会这些Vue小技巧,可以早点下班和女神约会了 获赞 950+

看到赚到!重读vue2.0风格指南,我整理了这些关键规则 获赞 120+

了解选项合并策略,自定义生命周期钩子函数

当你使用Vuemixins的时候,是否有发现,如果混入的methods里面的方法与组件的方法同名,则会被组件方法覆盖,但是生命周期函数如果重名,混入的与组件自身的都会被执行,且执行顺序是先混入和自身,这是怎么做到的呢?

1. 了解Vue合并策略

Vue中,不同的选项有不同的合并策略,比如 data,props,methods是同名属性覆盖合并,其他直接合并,而生命周期钩子函数则是将同名的函数放到一个数组中,在调用的时候依次调用,具体可参考小编前面的一篇文章绝对干货~!学会这些Vue小技巧,可以早点下班和女神约会了

Vue中,提供了一个api, Vue.config.optionMergeStrategies,可以通过这个api去自定义选项的合并策略。

在代码中打印

console.log(Vue.config.optionMergeStrategies)

控制台打印内容

通过上图可以看到Vue所有选项的合并策略函数,我们可以通过覆盖上面的方法,来自定义合并策略函数,不过一般用不到。

2. 通过合并策略自定义生命周期函数

背景

最近客户给领导反馈,我们的系统用一段时间,浏览器就变得有点卡,不知道为什么。问题出来了,本来想甩锅到后端,但是浏览器问题,没法甩锅啊,那就排查吧。

后来发现页面有许多定时器,ajax轮询还有动画,打开一个浏览器页签没法问题,打开多了,浏览器就变得卡了,这时候我就想如果能在用户切换页签时候将这些都停掉,不久解决了。百度里面上下检索,找到了一个事件visibilitychange,可以用来判断浏览器页签是否显示。

有方法了,就写呗

export default {  created() {    window.addEventListener('visibilitychange', this.$_hanldeVisiblityChange)    // 此处用了hookEvent,可以参考小编前一篇文章    this.$on('hook:beforeDestroy', () => {      window.removeEventListener(        'visibilitychange',        this.$_hanldeVisiblityChange      )    })  },  methods: {    $_hanldeVisiblityChange() {      if (document.visibilityState === 'hidden') {        // 停掉那一堆东西      }      if (document.visibilityState === 'visible') {        // 开启那一堆东西      }    }  }}

通过上面的代码,可以看到在每一个需要监听处理的文件都要写一堆事件监听,判断页面是否显示的代码,一处两处还可以,文件多了就头疼了,这时候小编突发奇想,定义一个页面显示隐藏的生命周期钩子,把这些判断都封装起来,哪里需要点哪里,so easy(点读机记得广告费)。

自定义生命周期钩子函数

定义生命周期函数 pageHiddenpageVisible

import Vue from 'vue'

// 通知所有组件页面状态发生了变化const notifyVisibilityChange = (lifeCycleName, vm) => {  // 生命周期函数会存在$options中,通过$options[lifeCycleName]获取生命周期  const lifeCycles = vm.$options[lifeCycleName]  // 因为使用了created的合并策略,所以是一个数组  if (lifeCycles && lifeCycles.length) {    // 遍历 lifeCycleName对应的生命周期函数列表,依次执行    lifeCycles.forEach(lifecycle => {      lifecycle.call(vm)    })  }  // 遍历所有的子组件,然后依次递归执行  if (vm.$children && vm.$children.length) {    vm.$children.forEach(child => {      notifyVisibilityChange(lifeCycleName, child)    })  }}

// 添加生命周期函数export function init() {  const optionMergeStrategies = Vue.config.optionMergeStrategies  // 定义了两个生命周期函数 pageVisible, pageHidden  // 为什么要赋值为 optionMergeStrategies.created呢  // 这个相当于指定 pageVisible, pageHidden 的合并策略与 created的相同(其他生命周期函数都一样)  optionMergeStrategies.pageVisible = optionMergeStrategies.beforeCreate  optionMergeStrategies.pageHidden = optionMergeStrategies.created}

// 将事件变化绑定到根节点上面// rootVm vue根节点实例export function bind(rootVm) {  window.addEventListener('visibilitychange', () => {    // 判断调用哪个生命周期函数    let lifeCycleName = undefined    if (document.visibilityState === 'hidden') {      lifeCycleName = 'pageHidden'    } else if (document.visibilityState === 'visible') {      lifeCycleName = 'pageVisible'    }    if (lifeCycleName) {      // 通过所有组件生命周期发生变化了      notifyVisibilityChange(lifeCycleName, rootVm)    }  })}

应用

  1. main.js主入口文件引入
import { init, bind } from './utils/custom-life-cycle'

// 初始化生命周期函数, 必须在Vue实例化之前确定合并策略init()

const vm = new Vue({  router,  render: h => h(App)}).$mount('#app')

// 将rootVm 绑定到生命周期函数监听里面bind(vm)

  1. 在需要的地方监听生命周期函数
export default {  pageVisible() {    console.log('页面显示出来了')  },  pageHidden() {    console.log('页面隐藏了')  }}

provideinject,不止父子传值,祖宗传值也可以

Vue相关的面试经常会被面试官问道,Vue父子之间传值的方式有哪些,通常我们会回答,props传值,$emit事件传值,vuex传值,还有eventbus传值等等,今天再加一种provideinject传值,离offer又近了一步。(对了,下一节还有一种)

使用过React的同学都知道,在React中有一个上下文Context,组件可以通过Context向任意后代传值,而Vueprovideinject的作用于Context的作用基本一样

先举一个例子

使用过elemment-ui的同学一定对下面的代码感到熟悉

<template>  <el-form :model="formData" size="small">    <el-form-item label="姓名" prop="name">      <el-input v-model="formData.name" />    el-form-item>    <el-form-item label="年龄" prop="age">      <el-input-number v-model="formData.age" />    el-form-item>    <el-button>提交el-button>  el-form>template><script>export default {  data() {return {formData: {name: '',age: 0      }    }  }}script>

看了上面的代码,貌似没啥特殊的,天天写啊。在el-form上面我们指定了一个属性size="small",然后有没有发现表单里面的所有表单元素以及按钮的 size都变成了small,这个是怎么做到的?接下来我们自己手写一个表单模拟一下

自己手写一个表单

我们现在模仿element-ui的表单,自己自定义一个,文件目录如下


自定义表单custom-form.vue

<template>  <form class="custom-form">    <slot>slot>  form>template><script>export default {props: {// 控制表单元素的大小    size: {type: String,default: 'default',// size 只能是下面的四个值      validator(value) {return ['default', 'large', 'small', 'mini'].includes(value)      }    },// 控制表单元素的禁用状态    disabled: {type: Boolean,default: false    }  },// 通过provide将当前表单实例传递到所有后代组件中  provide() {return {customForm: this    }  }}script>

在上面代码中,我们通过provide将当前组件的实例传递到后代组件中,provide是一个函数,函数返回的是一个对象

自定义表单项custom-form-item.vue

没有什么特殊的,只是加了一个label,element-ui更复杂一些

<template>  <div class="custom-form-item">    <label class="custom-form-item__label">{{ label }}label>    <div class="custom-form-item__content">      <slot>slot>    div>  div>template><script>export default {props: {label: {type: String,default: ''    }  }}script>

自定义输入框 custom-input.vue

<template>  <divclass="custom-input":class="[      `custom-input--${getSize}`,      getDisabled && `custom-input--disabled`    ]"  >    <input class="custom-input__input" :value="value" @input="$_handleChange" />  div>template><script>/* eslint-disable vue/require-default-prop */export default {props: {// 这里用了自定义v-model    value: {type: String,default: ''    },size: {type: String    },disabled: {type: Boolean    }  },// 通过inject 将form组件注入的实例添加进来  inject: ['customForm'],computed: {// 通过计算组件获取组件的size, 如果当前组件传入,则使用当前组件的,否则是否form组件的    getSize() {return this.size || this.customForm.size    },// 组件是否禁用    getDisabled() {const { disabled } = thisif (disabled !== undefined) {return disabled      }return this.customForm.disabled    }  },methods: {// 自定义v-model    $_handleChange(e) {this.$emit('input', e.target.value)    }  }}script>

form中,我们通过provide返回了一个对象,在input中,我们可以通过inject获取form中返回对象中的项,如上代码inject:['customForm']所示,然后就可以在组件内通过this.customForm调用form实例上面的属性与方法了

**在上面代码中我们使用了自定义v-model,关于自定义v-model可以阅读小编前面的文章绝对干货~!学会这些Vue小技巧,可以早点下班和女神约会了 **

在项目中使用

<template>  <custom-form size="small">    <custom-form-item label="姓名">      <custom-input v-model="formData.name" />    custom-form-item>  custom-form>template><script>import CustomForm from '../components/custom-form'import CustomFormItem from '../components/custom-form-item'import CustomInput from '../components/custom-input'export default {components: {    CustomForm,    CustomFormItem,    CustomInput  },  data() {return {formData: {name: '',age: 0      }    }  }}script>

执行上面代码,运行结果为:

<form class="custom-form">  <div class="custom-form-item">    <label class="custom-form-item__label">姓名label>    <div class="custom-form-item__content">

      <div class="custom-input custom-input--small">        <input class="custom-input__input">      div>    div>  div>form>

通过上面的代码可以看到,input组件已经设置组件样式为custom-input--small

inject格式说明

除了上面代码中所使用的inject:['customForm']写法之外,inject还可以是一个对象。且可以指定默认值

修改上例,如果custom-input外部没有custom-form,则不会注入customForm,此时为customForm指定默认值

{  inject: {    customForm: {      // 对于非原始值,和props一样,需要提供一个工厂方法      default: () => ({        size: 'default'      })    }  }}

如果我们希望inject进来的属性的名字不叫customForm,而是叫parentForm,如下代码

inject: {    // 注入的属性名称    parentForm: {      // 通过 from 指定从哪个属性注入      from: 'customForm',      default: () => ({        size: 'default'      })    }  },  computed: {    // 通过计算组件获取组件的size, 如果当前组件传入,则使用当前组件的,否则是否form组件的    getSize() {      return this.size || this.parentForm.size    }  }

使用限制

  1. provideinject的绑定不是可响应式的。但是,如果你传入的是一个可监听的对象,如上面的customForm: this,那么其对象的属性还是可响应的。

  2. Vue官网建议provideinject 主要在开发高阶插件/组件库时使用。不推荐用于普通应用程序代码中。因为provideinject在代码中是不可追溯的(ctrl + f可以搜),建议可以使用Vuex代替。但是,也不是说不能用,在局部功能有时候用了作用还是比较大的。

dispatchbroadcast,这是一种有历史的组件通信方式

dispatchbroadcast是一种有历史的组件通信方式,为什么是有历史的,因为他们是Vue1.0提供的一种方式,在Vue2.0中废弃了。但是废弃了不代表我们不能自己手动实现,像许多UI库内部都有实现。本文以element-ui实现为基础进行介绍。同时看完本节,你会对组件的$parent,$children,$options有所了解。

方法介绍

$dispatch: $dispatch会向上触发一个事件,同时传递要触发的祖先组件的名称与参数,当事件向上传递到对应的组件上时会触发组件上的事件侦听器,同时传播会停止。

$broadcast: $broadcast会向所有的后代组件传播一个事件,同时传递要触发的后代组件的名称与参数,当事件传递到对应的后代组件时,会触发组件上的事件侦听器,同时传播会停止(因为向下传递是树形的,所以只会停止其中一个叶子分支的传递)。

$dispatch实现与应用

1. 代码实现

 // 向上传播事件 // @param {*} eventName 事件名称 // @param {*} componentName 接收事件的组件名称 // @param {...any} params 传递的参数,可以有多个

function dispatch(eventName, componentName, ...params) {  // 如果没有$parent, 则取$root  let parent = this.$parent || this.$root  while (parent) {    // 组件的name存储在组件的$options.componentName 上面    const name = parent.$options.name    // 如果接收事件的组件是当前组件    if (name === componentName) {      // 通过当前组件上面的$emit触发事件,同事传递事件名称与参数      parent.$emit.apply(parent, [eventName, ...params])      break    } else {      // 否则继续向上判断      parent = parent.$parent    }  }}

// 导出一个对象,然后在需要用到的地方通过混入添加export default {  methods: {    $dispatch: dispatch  }}

2. 代码应用

  • 在子组件中通过$dispatch向上触发事件

    import emitter from '../mixins/emitter'export default {  name: 'Chart',  // 通过混入将$dispatch加入进来  mixins: [emitter],   mounted() {     // 在组件渲染完之后,将组件通过$dispatch将自己注册到Board组件上    this.$dispatch('register', 'Board', this)  }}
  • Board组件上通过$on监听要注册的事件

    export default {  name: 'Board',  created() {    this.$on('register',(component) => {      // 处理注册逻辑    })  }}

$broadcast实现与应用

1. 代码实现

  //向下传播事件  // @param {*} eventName 事件名称  // @param {*} componentName 要触发组件的名称  // @param  {...any} params 传递的参数

function broadcast(eventName, componentName, ...params) {  this.$children.forEach(child => {    const name = child.$options.name    if (name === componentName) {      child.$emit.apply(child, [eventName, ...params])    } else {      broadcast.apply(child, [eventName, componentName, ...params])    }  })}

// 导出一个对象,然后在需要用到的地方通过混入添加export default {  methods: {    $broadcast: broadcast  }}

2. 代码应用

在父组件中通过$broadcast向下触发事件

import emitter from '../mixins/emitter'export default {  name: 'Board',  // 通过混入将$dispatch加入进来  mixins: [emitter],  methods:{      //在需要的时候,刷新组件      $_refreshChildren(params) {       this.$broadcast('refresh', 'Chart', params)   }  }}

在后代组件中通过$on监听刷新事件

export default {  name: 'Chart',  created() {    this.$on('refresh',(params) => {      // 刷新事件    })  }}

总结

通过上面的例子,同学们应该都能对$dispatch$broadcast有所了解,但是为什么Vue2.0要放弃这两个方法呢?官方给出的解释是:”因为基于组件树结构的事件流方式实在是让人难以理解,并且在组件结构扩展的过程中会变得越来越脆弱。这种事件方式确实不太好,我们也不希望在以后让开发者们太痛苦。并且 $dispatch$broadcast 也没有解决兄弟组件间的通信问题。“

确实如官网所说,这种事件流的方式确实不容易让人理解,而且后期维护成本比较高。但是在小编看来,不管黑猫白猫,能抓老鼠的都是好猫,在许多特定的业务场景中,因为业务的复杂性,很有可能使用到这样的通信方式。但是使用归使用,但是不能滥用,小编一直就在项目中有使用。

插槽,我要钻到你的怀里

插槽,相信每一位Vue都有使用过,但是如何更好的去理解插槽,如何去自定义插槽,今天小编为你带来更形象的说明。

默认插槽

大学毕业刚上班,穷鬼一个,想着每个月租房还要掏房租,所以小编决定买一个一居室,东拼西凑借了一堆债,终于凑够了首付,买了一个小小的毛坯房。我们可以把这个一居室的毛坯房想想成一个组件,这个房子的户型,面积,楼层都是固定的,但是室内如何装修,摆什么家具,这个却是由你来决定的,房间内部就可以理解为插槽,允许用户去自定义内容。

1. 开发商终于将一居室开发完交房了

<template>

  <div class="one-bedroom">

    <slot>slot>  div>template>

2. 小编要开始装修了

<template>

  <one-bedroom>

    <span>先放一个小床,反正没有女朋友span>    <span>再放一个电脑桌,在家还要加班写bugspan>  one-bedroom>template><script>import OneBedroom from '../components/one-bedroom'export default {components: {    OneBedroom  }}script>

具名插槽

过了几年,小编有了女朋友,准备结婚了,一居室房间肯定不行啊,丈母娘嫌小不同意,没办法,只能又凑钱买大房子,买了一个两居室(穷逼一个),因为是两居室,所以有了主卧和次卧之分,装修是否也不能把主卧和次卧装修的一模一样,所以就需要进行区分。将房子想想成组件,那么组件就有两个插槽,并且需要起名字进行区分。

1. 开发商终于开发完交房了

<template>  <div class="two-bedroom">

    <div class="master-bedroom">

      <slot>slot>    div>

    <div class="secondary-bedroom">

      <slot name="secondard">slot>    div>  div>template>

2. 小编要卖血攒钱装修了

<template>  <two-bedroom>

    <div>      <span>放一个大床,要结婚了,嘿嘿嘿span>      <span>放一个衣柜,老婆的衣服太多了span>      <span>算了,还是放一个电脑桌吧,还要写bugspan>    div>

    <template v-slot:secondard>      <div>        <span>父母要住,放一个硬一点的床,软床对腰不好span>        <span>放一个衣柜span>      div>    template>  two-bedroom>template><script>import TwoBedroom from '../components/slot/two-bedroom'export default {components: {    TwoBedroom  }}script>

作用域插槽

装修的时候,装修师傅问我洗衣机是要放到卫生间还是阳台,一般情况下开发商会预留放洗衣机的位置。而这个位置可以理解为插槽传的参数,这个就是作用域插槽。

1. 看一下卫生间插槽传了什么参数

<template>  <div class="two-bedroom">

    <div class="toilet">

      <slot name="toilet" v-bind="{ washer: true }">slot>    div>  div>template>

2. 把洗衣机放到卫生间

<template>  <two-bedroom>

    <template v-slot:toilet="scope">

      <span v-if="scope.washer">这里放洗衣机span>    template>  two-bedroom>template>

插槽默认值

小编的同事不想等期房,所以就买了二手房,二手房前业主都装修好了,可以直接入住。当然也可以重新装修,下面是同事买的二手房。

1. 这是装修好的二手房

<template>  <div class="second-hand-house">    <div class="master-bedroom">

      <slot>        <span>这里有一张水床,玩的够嗨span>        <span>还有一个衣柜,有点旧了span>      slot>    div>

    <div class="secondary-bedroom">

      <slot name="secondard">        <span>这里有一张婴儿床span>      slot>    div>  div>template>

2. 同事决定先把主卧装修了,以后结婚用

<second-hand-house>

    <div>      <span>放一个大床,要结婚了,嘿嘿嘿span>      <span>放一个衣柜,老婆的衣服太多了span>      <span>算了,还是放一个电脑桌吧,还要写bugspan>    div>  second-hand-house>

结语

不要吹灭你的灵感和你的想象力; 不要成为你的模型的奴隶。——文森特・梵高

如果喜欢小编,可以关注下面的公众号,了解更多干货。同时,小手一赞,艳遇不断。

vue 监听浏览器页面关闭_前方高能,这是最新的一波Vue实战技巧,不用则已,一用惊人...相关推荐

  1. vue监听浏览器刷新和关闭事件,并在页面关闭/刷新前发送请求

    vue监听浏览器刷新和关闭事件,并在页面关闭/刷新前发送请求 1.需求背景: 2.需求分析: 3.实现方式: 4.实现方式解析: 1)浏览器页面事件基础 2)在mounted监听beforeunloa ...

  2. vue监听浏览器tab切换

    vue监听浏览器tab切换 描述 监听tab切换浏览器页面,在离开当前页面和再次进来发生事件监听,类似与小程序的show函数 visibilitychange 详解 解决 created() {doc ...

  3. Vue监听浏览器前进后退物理返回键(浏览器自带的)

    Vue监听浏览器前进后退物理返回键 在某些需求下要监听用户物理按键. popstate事件只会在页面不跳转的情况下,路由发生变化触发. created() { // 创建vm实例后执行// 浏览器控制 ...

  4. [轉]Flash/Flex监听浏览器的关闭事件

    FROM : http://blog.ityao.com/archives/581 如果想用Flash/Flex监听浏览器的关闭事件, 可以通过JavaScript的window.onbeforeun ...

  5. vue 监听map数组变化_解决vue无法侦听数组及对象属性的变化问题

    一.数组 1.可以监听到的情况 如push.splice.=赋值(array=[1,2,3]) 2.无法监听到的情况 使用下标修改某个元素(这种比较常见) array[index] = 1 objec ...

  6. 监听浏览器刷新/关闭页面

    首先,使用onunload或onbeforeunload可以监听浏览器关闭事件,但是无法区分关闭与刷新.以下js代码可以部分监听关闭浏览器的事件! 可以在标签中添加方法: <!DOCTYPE h ...

  7. 巨坑!监听浏览器窗口关闭onbeforeunload不起作用(脱坑了啊!!)

    ----------------重要通知-------------- Hello,本人的博客文章已更新至个人网站(www.jonexu.cn) 文章中有问题可以到网站联系博主,后续新的文章也将更新在个 ...

  8. 【不得不看的几种js监听浏览器返回,关闭,刷新】

    监听浏览器切换页面 //切换页面document.addEventListener('visibilitychange', function () { //浏览器切换事件if (document.vi ...

  9. HTML父页面监听子页面关闭并回调方法

    1.方式一:父页面监听 父页面中: //此处省略新窗口的设置参数var win = window.open("url", "name", "featu ...

最新文章

  1. windows10如何下载和安装latex
  2. [CODEVS] 2189 数字三角形W
  3. 解决递归中的重复计算问题
  4. python3 csv 读入数组_如何将CSV数据读入NumPy中的记录数组?
  5. java中length,length(),size()区别
  6. Haskell分解质因数
  7. 从客户端登陆服务器的配置文件,从客户端登陆服务器的配置
  8. 三星魔术师软件安装:检测到漏洞 此程序将被关闭
  9. gc cr block lost
  10. html关键技术包括,物联网的关键技术包括哪些
  11. 一文带你了解什么是CDN?
  12. 银行双活数据中心建设项目实践
  13. BUU刷题记录-[0CTF 2016]piapiapia
  14. vscode vuejs项目import报错找不到模块“@/assets/image/BackGroun.png”或其相应的类型声明。
  15. 揭秘开心农场开发团队:初期仅15万元创业基金
  16. OGRE 火炬之光(torchlight) 骨骼动画
  17. 暑期強檔 飯店業者與樂團結盟系列優惠
  18. java刷新透视表数据源,Java 创建、刷新Excel透视表/设置透视表行折叠、展开
  19. 博弈论Nim取子问题,困扰千年的问题一行代码解决
  20. 借助智慧数字经营怎么进行营销推广?

热门文章

  1. 真的,没人能逃开“真香定律”
  2. ​“国产”AI框架争相开源,“领头羊”百度飞桨将扔重磅炸弹?
  3. 台前与幕后的 5G 战争
  4. 5G 套餐最快本月开售;谷歌被指骗取人脸数据;Calibre 4.0 发布 | 极客头条
  5. 超 60 万 GPS 定位服务被曝漏洞,用户信息或将暴露!
  6. 这款颜值高的到底是剃须刀还是充电宝,这么黑科技?
  7. 数据有为 智在决策 | 观远数据2019智能决策峰会圆满落幕
  8. 助力开发者的魔法式“绝招儿”,你了解几个?
  9. 任正非回应“华为营收下降300亿美元”;今日头条澄清「通讯录不属于用户隐私」;Kotlin 1.3.40发布 | 极客头条...
  10. 谁说大龄程序员不能学 Java?