47·灵魂前端工程师养成-Vue终极进阶属性

2022-11-08 17:12:46 浏览数 (1)

  • 指令Direvtives

  • directiveOptions的属性
  • 指令的作用

  • 混入 mixins
  • 继承extends
  • 提供 provide 和 注入 inject
    • provide 和 inject总结

-曾老湿, 江湖人称曾老大。


-多年互联网运维工作经验,曾负责过大规模集群架构自动化运维管理工作。 -擅长Web集群架构与自动化运维,曾负责国内某大型金融公司运维工作。 -devops项目经理兼DBA。 -开发过一套自动化运维平台(功能如下): 1)整合了各个公有云API,自主创建云主机。 2)ELK自动化收集日志功能。 3)Saltstack自动化运维统一配置管理工具。 4)Git、Jenkins自动化代码上线及自动化测试平台。 5)堡垒机,连接Linux、Windows平台及日志审计。 6)SQL执行及审批流程。 7)慢查询日志分析web界面。


指令Direvtives


如何自己造一个指令

之前我们学习了很多指令,v-if、v-for、v-on、v-bind...等等都是指令

那么今天,我们就来自己造一个指令出来 目标:造出v-x指令,点击即出现一个x

写法一:全局指令

代码语言:javascript复制
Vue.directive('x',directiveOptions)
// 你就可以在任意组件中使用v-x了。

写法二:局部指令

代码语言:javascript复制
new Vue({
    ...,
    directives:{
        "x":directiveOptions
    }
})

// v-x只能在该实例中使用

全局指令

main.js

代码语言:javascript复制
import Vue from "vue";
import App from "./App.vue";

Vue.config.productionTip = false;

Vue.directive('x',{
  inserted:function(el){
    el.addEventListener('click',()=>{console.log('x')})
  }
})

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

App.vue

代码语言:javascript复制
<template>
  <div id="app">
    <img v-x width="25%" src="./assets/logo.png">
    <HelloWorld msg="Hello Vue in CodeSandbox!" />
  </div>
</template>

<script>
import HelloWorld from "./components/HelloWorld";

export default {
  name: "App",
  components: {
    HelloWorld
  }
};
</script>

<style>
#app {
  font-family: "Avenir", Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>


菊部指令

只能在Helloworld组件中使用

HelloWorld.vue

代码语言:javascript复制
<template>
  <div class="hello">
    <h1 v-x>{{ msg }}</h1>
    <h3>Installed CLI Plugins</h3>
    <ul>
      <li>
        <a
          href="https://github.com/vuejs/vue-cli/tree/dev/packages/@vue/cli-plugin-babel"
          target="_blank"
          rel="noopener"
        >babel</a>
      </li>
      <li>
        <a
          href="https://github.com/vuejs/vue-cli/tree/dev/packages/@vue/cli-plugin-eslint"
          target="_blank"
          rel="noopener"
        >eslint</a>
      </li>
    </ul>
    <h3>Essential Links</h3>
    <ul>
      <li>
        <a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a>
      </li>
      <li>
        <a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a>
      </li>
      <li>
        <a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a>
      </li>
      <li>
        <a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a>
      </li>
      <li>
        <a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a>
      </li>
    </ul>
    <h3>Ecosystem</h3>
    <ul>
      <li>
        <a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a>
      </li>
      <li>
        <a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a>
      </li>
      <li>
        <a
          href="https://github.com/vuejs/vue-devtools#vue-devtools"
          target="_blank"
          rel="noopener"
        >vue-devtools</a>
      </li>
      <li>
        <a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a>
      </li>
      <li>
        <a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a>
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  name: "HelloWorld",
  directives:{
    'x':{
      inserted(el){
        el.addEventListener('click',()=>{console.log('x')})
      },
    },
  },
  props: {
    msg: String
  }
};
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
  margin: 40px 0 0;
}
ul {
  list-style-type: none;
  padding: 0;
}
li {
  display: inline-block;
  margin: 0 10px;
}
a {
  color: #42b983;
}
</style>

directiveOptions的属性


bind

代码语言:javascript复制
bind(el,info,vnode,oldVnode)

类似于created


inserted

代码语言:javascript复制
inserted(el,info,vnode,oldVnode)

类似于mounted


update

代码语言:javascript复制
update(el,info,vnode,oldVnode)

类似于updated


componentUpdated

代码语言:javascript复制
componentUpdated(el,info,vnode,oldVnode)

用的不多。


unbind

代码语言:javascript复制
unbind(el,info,vnode,oldVnode)

类似于destroyed。


举例子

自制v-on2指令,模拟v-on

先来个v-on

代码语言:javascript复制
import Vue from "vue/dist/vue.js";

Vue.config.productionTip = false;

new Vue({
  template: `
    <button v-on:click="hi">点我</button>
  `,
  methods:{
    hi(){
      console.log('hi')
    }
  }
}).$mount("#app");

v-on2

代码语言:javascript复制
import Vue from "vue/dist/vue.js";

Vue.config.productionTip = false;

new Vue({
  directives:{
    'on2':{
      inserted(el,info){
        el.addEventListener(info.arg,info.value)
      },
      unbind(el,info){
        el.removeEventListener(info.arg,info.value)
      }
    }
  },
  template: `
    <button v-on2:click="hi">点我</button>
  `,
  methods:{
    hi(){
      console.log('hi')
    }
  }
}).$mount("#app");

指令的作用

主要是拥有DOM操作 1.Vue实例/组件用于数据绑定、事件监听、DOM更新 2.Vue指令主要目的就是原生DOM操作

减少重复的DOM操作 1.如果某个DOM操作 你经常使用,那就可以封装成指令 2.如果某个DOM操作比较复杂,也可以封装为指令

混入 mixins


混入就是TMD复制

前端就喜欢把简单概念搞复杂,就是做了一个复制的操作...叫的这么高大上。

如果说directives的作用 是减少DOM操作的重复,那么mixins的作用就是减少data,methods,钩子的重复。


场景描述

假设,我们需要在每个组件上添加name和time,

在created、destroyed时,大厨提示,并报出存活时间

一共有五个组件,请问你怎么做? 一、给每个组件添加data和钩子,共五次 二、或者使用mixins减少重复


代码示例

main.js

代码语言:javascript复制
import Vue from "vue";
import App from "./App.vue"
Vue.config.productionTip = false;

new Vue({
  render: h => h(App)
}).$mount("#app");

App.vue

代码语言:javascript复制
<template>
  <div id="app">
    <Child1 />
    <button>x</button>
    <Child2 />
    <button>x</button>
    <Child3 />
    <button>x</button>
    <Child4 />
    <button>x</button>
    <Child5 />
    <button>x</button>
  </div>
</template>

<script>
  import Child1 from "./components/Child1.vue"
  import Child2 from "./components/Child2.vue"
  import Child3 from "./components/Child3.vue"
  import Child4 from "./components/Child4.vue"
  import Child5 from "./components/Child5.vue"

  export default {
    name: "App",
    components: {
      Child1,
      Child2,
      Child3,
      Child4,
      Child5,
    }
  };
</script>

<style scoped>
  #app {
    font-family: "Avenir", Helvetica, Arial, sans-serif;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    text-align: center;
    color: #2c3e50;
    margin-top: 60px;
  }
</style>

Child1.vue

代码语言:javascript复制
<template>
    <div>
        Child1
    </div>
</template>

Child2.vue

代码语言:javascript复制
<template>
    <div>
        Child2
    </div>
</template>

Child3.vue

代码语言:javascript复制
<template>
    <div>
        Child3
    </div>
</template>

Child4.vue

代码语言:javascript复制
<template>
    <div>
        Child4
    </div>
</template>

Child5.vue

代码语言:javascript复制
<template>
    <div>
        Child5
    </div>
</template>

需求:当Child1出生的时候,写一个Child1出生,点击×就关闭,并且打出Child1死亡了,以及生存时间

Child1.vue

代码语言:javascript复制
<template>
    <div>
        Child1
    </div>
</template>

<script>
    export default {
        data() {
            return {
                name: "Child1",
                time: undefined
            }
        },
        created() {
            this.time = new Date()
            console.log(`${this.name}出生了`)
        },
        beforeDestroy() {
            const now = new Date()
            console.log(`${this.name}死亡了,共生存了${now - this.time} ms`)
        }
    }
</script>

App.vue

代码语言:javascript复制
<template>
    <div id="app">
        <Child1 v-if="Child1Visible"/>
        <button @click="Child1Visible=false">x</button>
        <Child2/>
        <button>x</button>
        <Child3/>
        <button>x</button>
        <Child4/>
        <button>x</button>
        <Child5/>
        <button>x</button>
    </div>
</template>

<script>
    import Child1 from "./components/Child1.vue"
    import Child2 from "./components/Child2.vue"
    import Child3 from "./components/Child3.vue"
    import Child4 from "./components/Child4.vue"
    import Child5 from "./components/Child5.vue"

    export default {
        name: "App",
        data() {
            return {
                Child1Visible: true
            }
        },
        components: {
            Child1,
            Child2,
            Child3,
            Child4,
            Child5,
        }
    };
</script>

<style scoped>
    #app {
        font-family: "Avenir", Helvetica, Arial, sans-serif;
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
        text-align: center;
        color: #2c3e50;
        margin-top: 60px;
    }
</style>

那么,如果...Child1-Child5都需要这个需求,怎么办?重复写5遍?

SB才这么做,就是这么直白~~~

首先,我们在src目录下创建一个mixins目录

然后在mixins目录中创建一个log.js文件,将刚才的内容写入

log.js

代码语言:javascript复制
const log = {
    data() {
        return {
            name: undefined,
            time: undefined
        }
    },
    created() {
        if (!this.name){
            throw new  Error("need name")
        }
        this.time = new Date()
        console.log(`${this.name}出生了`)
    },
    beforeDestroy() {
        const now = new Date()
        console.log(`${this.name}死亡了,共生存了${now - this.time} ms`)
    }
}

export default log

其他的组件如何使用log?

Child1.vue

代码语言:javascript复制
<template>
    <div>
        {{name}}
    </div>
</template>

<script>
    import log from '../mixins/log'

    export default {
        data() {
            return {
                name: "Child1"
            }
        },
        mixins: [log]
    }
</script>

Child2.vue

代码语言:javascript复制
<template>
    <div>
        Child2
    </div>
</template>

<script>
    import log from '../mixins/log'

    export default {
        data() {
            return {
                name: "Child2"
            }
        },
        mixins: [log]
    }
</script>

继承extends


继承

此继承非彼继承,这个继承和mixins有点像,都他妈的是复制,就是写法不太一样,所以我们来是想刚才的那个需求。

需求:当Child1出生的时候,写一个Child1出生,点击×就关闭,并且打出Child1死亡了,以及生存时间,然后我们不想每个组件中,都写一个mixins:[log],那么我们及来使用继承。


代码示例

创建一个MyVue.js文件

代码语言:javascript复制
import Vue from "vue"

const MyVue = Vue.extend({
    data() {
        return {
            name: undefined,
            time: undefined
        }
    },
    created() {
        if (!this.name) {
            throw new Error("need name")
        }
        this.time = new Date()
        console.log(`${this.name}出生了`)
    },
    beforeDestroy() {
        const now = new Date()
        console.log(`${this.name}死亡了,共生存了${now - this.time} ms`)
    }
})

export default MyVue

Child1.vue

代码语言:javascript复制
<template>
    <div>
        {{name}}
    </div>
</template>

<script>
    import MyVue from "../MyVue";

    export default {
        extends: MyVue,
        data() {
            return {
                name: "Child1"
            }
        },
    }
</script>

提供 provide 和 注入 inject


一键换肤

举个例子,我们现在有一个需求,一键换肤的功能,默认是蓝色,可以切换为红色。 文字大小:默认正常,可以改成大或者小

祖先栽树(Provide) 后人乘凉(inject)


代码示例

App.vue

代码语言:javascript复制
<template>
    <div :class="`app theme-${themeName}`">
        <Child1/>
        <button>x</button>
        <Child2/>
        <button>x</button>
        <Child3/>
        <button>x</button>
        <Child4/>
        <button>x</button>
        <Child5/>
        <button>x</button>
    </div>
</template>

<script>
    import Child1 from "./components/Child1.vue"
    import Child2 from "./components/Child2.vue"
    import Child3 from "./components/Child3.vue"
    import Child4 from "./components/Child4.vue"
    import Child5 from "./components/Child5.vue"

    export default {
        name: "App",
        data() {
            return {
                themeName: "blue", // "red"
                fontSize: "normal" // "big" |"small"
            }
        },
        components: {
            Child1,
            Child2,
            Child3,
            Child4,
            Child5,
        }
    };
</script>

<style scoped>
    .app {
        font-family: "Avenir", Helvetica, Arial, sans-serif;
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
        text-align: center;
        color: #2c3e50;
        margin-top: 60px;
    }

    .app.theme-blue button {
        background: blue;
        color: white;
    }

    .app.theme-blue {
        color: darkblue;
    }

    .app.theme-red button {
        background: red;
        color: white;
    }

    .app.theme-red {
        color: darkred;
    }
</style>

换肤按钮组件

在components中创建一个ChangeThemeButton.vue文件

代码语言:javascript复制
<template>
    <div>
        <button>一键换肤</button>
    </div>
</template>

引用到Child1中

Child1.vue

代码语言:javascript复制
<template>
    <div>
        Child1
        <ChangeThemeButton/>
    </div>
</template>

<script>
    import ChangeThemeButton from "./ChangeThemeButton"

    export default {
        components: {
            ChangeThemeButton,
        }
    }
</script>

问题就出来了,就是我特么的,Child1.vue组件里面的按钮,点一下,怎么能把别人都换肤了呢?

这就触及到我的知识盲区了...

看标题... 是用provide和inject


provide提供给别人使用

App.vue

代码语言:javascript复制
<template>
    <div :class="`app theme-${themeName}`">
        <Child1/>
        <button>x</button>
        <Child2/>
        <button>x</button>
        <Child3/>
        <button>x</button>
        <Child4/>
        <button>x</button>
        <Child5/>
        <button>x</button>
    </div>
</template>

<script>
    import Child1 from "./components/Child1.vue"
    import Child2 from "./components/Child2.vue"
    import Child3 from "./components/Child3.vue"
    import Child4 from "./components/Child4.vue"
    import Child5 from "./components/Child5.vue"

    export default {
        name: "App",
        provide() {
            return {
                themeName: this.themeName,
                changeTheme: this.changeTheme,
            }
        },
        data() {
            return {
                themeName: "blue", // "red"
                fontSize: "normal" // "big" |"small"
            }
        }
        ,
        methods: {
            changeTheme() {
                if (this.themeName === 'blue') {
                    this.themeName = 'red'
                } else {
                    this.themeName = 'blue'
                }
            }
        },
        components: {
            Child1,
            Child2,
            Child3,
            Child4,
            Child5,
        }
    };
</script>

<style scoped>
    .app {
        font-family: "Avenir", Helvetica, Arial, sans-serif;
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
        text-align: center;
        color: #2c3e50;
        margin-top: 60px;
    }

    .app.theme-blue button {
        background: blue;
        color: white;
    }

    .app.theme-blue {
        color: darkblue;
    }

    .app.theme-red button {
        background: red;
        color: white;
    }

    .app.theme-red {
        color: darkred;
    }
</style>

ChangeThemeButton.vue

代码语言:javascript复制
<template>
    <div>
        <button @click="z">当前主题色:{{themeName}}换肤</button>
    </div>
</template>

<script>
    export default {
        inject: ['themeName','changeTheme'],
        methods:{
            z(){
                this.changeTheme()
            }
        }
    }
</script>


字体变化

App.vue

代码语言:javascript复制
<template>
    <div :class="`app theme-${themeName}  fontSize-${fontSizeName}`">
        <Child1/>
        <button>x</button>
        <Child2/>
        <button>x</button>
        <Child3/>
        <button>x</button>
        <Child4/>
        <button>x</button>
        <Child5/>
        <button>x</button>
    </div>
</template>

<script>
    import Child1 from "./components/Child1.vue"
    import Child2 from "./components/Child2.vue"
    import Child3 from "./components/Child3.vue"
    import Child4 from "./components/Child4.vue"
    import Child5 from "./components/Child5.vue"

    export default {
        name: "App",
        provide() {
            return {
                themeName: this.themeName,
                changeTheme: this.changeTheme,
                changeFontSize: this.changeFontSize,
            }
        },
        data() {
            return {
                themeName: "blue", // "red"
                fontSizeName: "normal" // "big" |"small"
            }
        }
        ,
        methods: {
            changeTheme() {
                if (this.themeName === 'blue') {
                    this.themeName = 'red'
                } else {
                    this.themeName = 'blue'
                }
            },
            changeFontSize(name) {
                if (['normal', 'big', 'small'].indexOf(name) >= 0) {
                    this.fontSizeName = name
                }
            }
        },
        components: {
            Child1,
            Child2,
            Child3,
            Child4,
            Child5,
        }
    };
</script>

<style>
    .app {
        font-family: "Avenir", Helvetica, Arial, sans-serif;
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
        text-align: center;
        color: #2c3e50;
        margin-top: 60px;
    }

    .app.theme-blue button {
        background: blue;
        color: white;
    }

    .app.theme-blue {
        color: darkblue;
    }

    .app.theme-red button {
        background: red;
        color: white;
    }

    .app.theme-red {
        color: darkred;
    }

    .app.fontSize-normal {
        font-size: 16px;
    }

    .app.fontSize-big {
        font-size: 20px;
    }

    .app.fontSize-small {
        font-size: 12px;
    }

    .app button {
        font-size: inherit;
    }
</style>

ChangeThemeButton.vue

代码语言:javascript复制
<template>
    <div>
        <button @click="z">换肤</button>
        <button @click="changeFontSize('big')">大字</button>
        <button @click="changeFontSize('small')">小字</button>
        <button @click="changeFontSize('normal')">正常字</button>
    </div>
</template>

<script>
    export default {
        inject: ['themeName', 'changeTheme', 'changeFontSize'],
        methods: {
            z() {
                this.changeTheme()
            },
        }
    }
</script>

Child1.vue

代码语言:javascript复制
<template>
    <div>
        Child1
        <change-theme-button/>
    </div>
</template>

<script>
    import ChangeThemeButton from "./ChangeThemeButton.vue"

    export default {
        components: {
            ChangeThemeButton,
        }
    }
</script>

provide 和 inject总结

代码语言:javascript复制
作用:大范围的data和method等共用
注意:不能只传themeName不传changeTheme,因为themeName的值是被复制给provide的

// 传递引用也可以,但是不推荐,因为容易失控

0 人点赞