# 如何写出比别人更优秀的Vue?

# watch的灵活使用

在watch中,可以直接使用函数的字面量名称;其次,声明immediate:true表示创建组件时立马执行一次。

  watch: {
    inputValue: {
      handler: 'getSearchList',
      immediate: true
    }
  }
1
2
3
4
5
6

# 自动注册全局组件

我们可以使用require.context() (opens new window) 方法来创建自己的(模块)上下文,从而实现自动动态require组件

 
 
 
 
 
 
























require.context 说明
require.context(
  directory: String,  // 搜索的目录
  includeSubdirs: Boolean /* 可选的,默认值是 true */,  // 是否搜索子目录
  filter: RegExp /* 可选的,默认值是 /^\.\/.*$/,所有文件 */,  // 匹配的目标文件格式
  mode: String  /* 可选的, 'sync' | 'eager' | 'weak' | 'lazy' | 'lazy-once',默认值是 'sync' */  // 同步还是异步
)

目录结构
|--src
|---|commponets
|-----|common

在common文件夹下新建index.js,并写入下面内容

import Vue from 'vue'
const path =require('path')
const context = require.context('./',true, /\.vue$/)
context.keys().forEach(item => {
  const filename = path.basename(item)
  const componentName = filename.replace(/\..+/,'')
  const componentConfig =  context(item)
  Vue.component(componentName, componentConfig.default || componentConfig)
})

在 main.js 写入
import './commponets/common'


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# 高阶组件

$attrs
$listeners
.sync
$props
1
2
3
4

# 动态指令参数

<div v-bind:[attr]="attributeName"></div>
//简写
<div :[attr]="attributeName"></div>

<button v-on:[eventName]="handler"></button>
//简写
<button @[eventName]="handler"></button>
1
2
3
4
5
6
7

# @hook那些事

this.$once('hook:beforeDestroy', () => clearInterval(timer);)
<v-chart
    @hook:mounted="loading = false"
    @hook:beforeUpdated="loading = true"
    @hook:updated="loading = false"
    :data="data"
/>
1
2
3
4
5
6
7

# Vue.observable( object )

这是一个 Vue2.6 中新增的 API,用来让一个对象可以响应。

import Vue from 'vue'
export const state = Vue.observable({
    count: 0,
})
export const mutations = {
    increment() {
        state.count++
    }
    decrement() {
        state.count--
    }
}
1
2
3
4
5
6
7
8
9
10
11
12

# jsx模板组件

Vue.component('child', {
            props: {
                status: {
                    type: Number,
                    required: true
                }
            },
            render(createElement) {
                const innerHTML = ['未开始', '进行中', '可领取', '已领取'][this.status]
                return createElement('button', {
                    class: {
                        active: this.status
                    },
                    attrs: {
                        id: 'btn'
                    },
                    domProps: {
                        innerHTML
                    },
                    on: {
                        click: () => console.log(this.status)
                    }
                })
            }
        })
        var app = new Vue({
            el: '#app',
            data: {
                status: 0
            }
        })
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

# 动态组件

<component v-for="(item,index) in componentList" :key="index" :is="item"></component>
1

# provide/inject组合拳

provide/inject 组合以允许一个祖先组件向其所有子孙后代注入一个依赖,可以注入属性和方法,从而实现跨级父子组件通信。在开发高阶组件和组件库的时候尤其好用

// 父组件 index.vue
data() {
    return {
        title: 'bubuzou.com',
    }
}
provide() {
    return {
        detail: {
            title: this.title,
            change: (val) => {
                console.log( val )
            }
        }
    }
}

// 孙子组件 detail.vue
inject: ['detail'],
mounted() {
    console.log(this.detail.title)  // bubuzou.com
    this.detail.title = 'hello world'  // 虽然值被改变了,但是父组件中 title 并不会重新渲染
    this.detail.change('改变后的值')  // 执行这句后将打印:改变后的值 
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# EventBus

任意组件间通信


// main.js
Vue.prototype.$eventBus = new Vue()


// 需要订阅的地方
this.$eventBus.$on('update', val => {})

// 需要发布信息的地方
this.$eventBus.$emit('update', '更新信息')

// 需要移除订阅

this.$eventBus.$off('update', {})
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 自定义指令

根据权限显示按钮

// auth.js
const AUTH_LIST = ['admin']
function checkAuth(auths) {
    return AUTH_LIST.some(item => auths.includes(item))
}
function install(Vue, options = {}) {
    Vue.directive('auth', {
        inserted(el, binding) {
            if (!checkAuth(binding.value)) {
                el.parentNode && el.parentNode.removeChild(el)
            }
        }
    })
}
export default { install }

// main.js

import Auth from './utils/auth'
Vue.use(Auth)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 使用渲染函数

<authority :auth="['admin']">
    <button>提交</button>
</authority>

// authority.js
<script>
const AUTH_LIST = ['admin', 'user', 'org']

function checkAuth(auths) {
    return AUTH_LIST.some(item => auths.includes(item))
}
export default {
    functional: true,
    props: {
        auth: {
            type: Array,
            required: true
        }
    },
    render(h, context) {
        const { props,  scopedSlots} = context
        return checkAuth(props.auth) ? scopedSlots.default() : null
    }
}


// main.js
import Authority from './components/authority'
Vue.component('authority', Authority)
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

# 自定义插件

开发 Vue 的插件应该暴露一个 install 方法。这个方法的第一个参数是 Vue 构造器,第二个参数是一个可选的选项对象。 可以通过如下4种方式来自定义插件:

MyPlugin.install = function (Vue, options) {
  // 1. 添加全局方法或 property
  Vue.myGlobalMethod = function () {
    // 逻辑...
  }
  // 2. 添加全局资源
  Vue.directive('my-directive', {
    bind (el, binding, vnode, oldVnode) {
      // 逻辑...
    }
    ...
  })
  // 3. 注入组件选项
  Vue.mixin({
    created: function () {
      // 逻辑...
    }
    ...
  })
  // 4. 添加实例方法
  Vue.prototype.$myMethod = function (methodOptions) {
    // 逻辑...
  }
}

// main.js

import MyPlugin from './plugins/plugins.js'
Vue.use(MyPlugin)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# 函数式组件实现零时变量

我们在使用插槽的时候,知道有一个叫做插槽 prop 的知识,今天我们用他和函数式组件结合在一块,实现一个零时变量的组件:

// tempvar.vue
<script>
export default {
    functional: true,
    render(h, context) {
        const { props,  scopedSlots} = context
        return scopedSlots.default && scopedSlots.default(props || {})
    }
}
</script>

<template>
  <tempvar
      :var1="`hello ${user.name}`"
      :var2="user.age ? user.age : '18'">
      <template v-slot="{var1, var2}">
        姓名: {{ var1 }}
        年龄:{{ var2 }}
      </template>
  </tempvar>
</template>
<script>
    import tempvar from '@/components/tempvar.vue'
    export default {
        data() {
            return {
                obj: {
                    name: 'bubuzou',
                    age: 12,
                },
            }
        }
        components: {
            tempvar
        }  
    }
</script>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

# 路由参数解耦

const router = [{
    path: '/home/:type/:id',
    name: 'Home',
    component: Home,
    props: (route) => ({
        type: route.params.type,
        id: route.params.id,
        sex: route.query.sex,
    })
}]

Home 组件加上 
export default {
  name: 'Home',
  props: ['type', 'id', 'sex']
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16