Vue进阶用法总结

作者: 前端辉羽 | 来源:发表于2020-07-14 10:27 被阅读0次

    本文目录:

    • 1.$emit$on
    • 2.Vue.directive
    • 3.Vue-extend进阶用法:自定义API
    • 4.Vue.use:挂载插件
    • 5.provide和inject
    • 6.过滤器filter
    • 7.监听器 watch的6种用法
    • 8.class 和 style 绑定的高级用法
    • 9.Vue.observable

    1.$emit$on

    以下代码, 点击按钮后, 控制台上会打印什么?

    <html>
      <head>
        <title>$emit 和 $on</title>
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
      </head>
      <body>
        <div id="root">
          <button @click="boost">触发事件</button>
        </div>
        <script>
          new Vue({
            el: '#root',
            data() {
              return {
                message: 'hello vue'
              }
            },
            created() {
              this.$on('my_events', this.handleEvents)
            },
            methods: {
              handleEvents(e) {
                console.log(this.message, e)
              },
              boost() {
                this.$emit('my_events', 'my params')            
              }
            }
          })
        </script>
      </body>
    </html>
    

    输出结果是hello vue my params
    原理分析:
    通过$on成功将自定义事件myEvent挂载到vue实例上,并且指定了ABC为自定义事件触发的回调函数。
    this.$on('自定义事件的名字myEvent',回调函数ABC)
    通过$emit触发自定义事件ABC,并且将'my params'作为自定义事件的实参传递出去。
    this $emit('自定义事件ABC',传递出去的实参)
    好处:
    Vue通过上面的操作,将事件定义和事件触发解耦,提升了灵活性。

    2.Vue.directive

    除了核心功能默认内置的指令 (v-model 和 v-show),Vue 也允许注册自定义指令。
    有的情况下,仍然需要对普通 DOM 元素进行底层操作,这时候就会用到自定义指令。

    <div id="root">
        <div v-loading="isLoading">{{data}}</div>
        <button @click="update">更新</button>
    </div>
    

    第一个参数loading和元素中的v-loading中的loading必须是一致的

    <script>
        Vue.directive('loading', {
            update(el, binding, vnode) {
                if (binding.value) {
                    const div = document.createElement('div')
                    div.innerText = '加载中...'
                    div.setAttribute('id', 'loading')
                    div.style.position = 'absolute'
                    div.style.left = 0
                    div.style.top = 0
                    div.style.width = '100%'
                    div.style.height = '100%'
                    div.style.display = 'flex'
                    div.style.justifyContent = 'center'
                    div.style.alignItems = 'center'
                    div.style.color = 'white'
                    div.style.background = 'rgba(0, 0, 0, .7)'
                    document.body.append(div)
                } else {
                    document.body.removeChild(document.getElementById('loading'))
                }
            }
        })
        new Vue({
            el: '#root',
            data() {
                return {
                    isLoading: false,
                    data: ''
                }
            },
            methods: {
                update() {
                    this.isLoading = true
                    setTimeout(() => {
                        this.data = '用户数据'
                        this.isLoading = false
                    }, 3000)
                }
            }
        })
    </script>
    

    当点击按钮的时候,页面呈现以下状态:

    image.png
    3秒后,loading消失,按钮上方会多出来“用户数据”四个字。
    自定义指令中传递的三个参数
    el: 指令所绑定的元素,可以用来直接操作DOM。
    binding: 一个对象,包含指令的多个属性信息。
    vnode: Vue编译生成的虚拟节点。
    除了 el 之外,其它参数都应该是只读的,切勿进行修改。
    如果需要在钩子之间(自定义指令的生命周期)共享数据,建议通过元素的 dataset 来进行。
    自定义指令的生命周期
    自定义指令有五个生命周期(也叫钩子函数),分别是 bind, inserted, update, componentUpdated, unbind
    • bind:只调用一次,指令第一次绑定到元素时调用
    • inserted:被绑定元素插入父节点时调用(父节点存在即可调用,不必存在于document中)
    • update:被绑定于元素所在的模板更新时调用,
    • componentUpdated:被绑定元素所在模板完成一次更新周期时调用。
    • unbind:只调用一次,指令与元素解绑时调用。

    3.Vue-extend进阶用法:自定义API

    <html>
      <head>
        <title>Vue.extend 用法2</title>
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
        <style>
          #loading-wrapper {
            position: fixed;
            top: 0;
            left: 0;
            display: flex;
            justify-content: center;
            align-items: center;
            width: 100%;
            height: 100%;
            background: rgba(0,0,0,.7);
            color: #fff;
          }
        </style>
      </head>
      <body>
        <div id="root">
          <button @click="showLoading">显示Loading</button>
        </div>
        <script>
          function Loading(msg) {
            const LoadingComponent = Vue.extend({
              template: '<div id="loading-wrapper">{{msg}}</div>',
              props: {
                msg: {
                  type: String,
                  default: msg
                }
              },
              name: 'LoadingComponent'
            })
            const div = document.createElement('div')
            div.setAttribute('id', 'loading-wrapper')
            document.body.append(div)
            new LoadingComponent().$mount('#loading-wrapper')
            return () => {
              document.body.removeChild(document.getElementById('loading-wrapper'))
            }
          }
          Vue.prototype.$loading = Loading
          new Vue({
            el: '#root',
            methods: {
              showLoading() {
                const hide = this.$loading('正在加载,请稍等...')
                setTimeout(() => {
                  hide()
                }, 2000)
              }
            }
          })
        </script>
      </body>
    </html>
    

    点击按钮,出现lodaing图,2秒后loading自动消失
    关键点
    1.自定义一个组件LoadingComponent,并且实现这个租金啊,并将其通过$mount挂载到vue上(mount方法是专门用来挂载扩展的) 2.定义一个loading方法,这个方法接受一个msg参数 最后把这个loading方法挂载到vue的原型上,并且起名字为loading

    自定义指令的方法:通过改变某个状态值来实现元素的变动
    自定义api的方法:完全主动触发,并且能够很方便的封装为插件,功能更强大,更为推荐

    4.Vue.use:挂载插件

    我们将loading封装为一个插件,并通过vue.use加载

    <html>
      <head>
        <title>Vue.use 用法</title>
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
        <style>
          #loading-wrapper {
            position: fixed;
            top: 0;
            left: 0;
            display: flex;
            justify-content: center;
            align-items: center;
            width: 100%;
            height: 100%;
            background: rgba(0,0,0,.7);
            color: #fff;
          }
        </style>
      </head>
      <body>
        <div id="root">
          <button @click="showLoading">显示Loading</button>
        </div>
        <script>
          const loadingPlugin = {
            install: function(vm) {
              const LoadingComponent = vm.extend({
                template: '<div id="loading-wrapper">{{msg}}</div>',
                props: {
                  msg: {
                    type: String,
                    default: 'loading...'
                  }
                }
              }, 'LoadingComponent')
              function Loading(msg) {
                const div = document.createElement('div')
                div.setAttribute('id', 'loading-wrapper')
                document.body.append(div)
                new LoadingComponent({
                  props: {
                    msg: {
                      type: String,
                      default: msg
                    }
                  } 
                }).$mount('#loading-wrapper')
                return () => {
                  document.body.removeChild(document.getElementById('loading-wrapper'))
                }
              }
              vm.prototype.$loading = Loading
            }
          }
          Vue.use(loadingPlugin)
          new Vue({
            el: '#root',
            methods: {
              showLoading() {
                const hide = this.$loading('正在加载,请稍等...')
                setTimeout(() => {
                  hide()
                }, 2000)
              }
            }
          })
        </script>
      </body>
    </html>
    

    在开发中经常用到Vue.use(VueRouter)、Vue.use(MintUI)。但是用 axios时,就不需要用 Vue.use(axios),这是因为开发者在封装 axios 时,没有写 install 这一步。
    install 是默认的方法。当外界在 use 这个组件的时候,就会调用本身的 install 方法,同时传一个 Vue 这个类的参数。

    5.provide和inject

    已经有了props和vuex,为什么还需要provide?
    props跨组件通信很麻烦,vuex学习成本高

    <html>
      <head>
        <title>组件通信 provide 和 inject</title>
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
      </head>
      <body>
        <div id="root">
          <Test></Test>
        </div>
        <script>
          function registerPlugin() {
            Vue.component('Test', {
              template: '<div>{{message}}<Test2 /></div>',
              provide() {
                return {
                  elTest: this
                }
              }, // function 的用途是为了获取运行时环境,否则 this 将指向 window
              data() {
                return {
                  message: 'message from Test'
                }
              },
              methods: {
                change(component) {
                  this.message = 'message from ' + component
                }
              }
            })
            Vue.component('Test2', {
              template: '<Test3 />'
            })
            Vue.component('Test3', {
              template: '<button @click="changeMessage">change</button>',
              inject: ['elTest'],
              methods: {
                changeMessage() {
                  this.elTest.change(this.$options._componentTag)
                }
              }
            })
          }
          Vue.use(registerPlugin)
          new Vue({
            el: '#root'
          })
        </script>
      </body>
    </html>
    

    上面的代码Test1通过provide把自身映射为elTest,经过传递,Test3页可以直接通过inject接收到elTest

    6.过滤器filter

    <html>
      <head>
        <title>过滤器 filter</title>
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
      </head>
      <body>
        <div id="root">
          {{message | lower}}
        </div>
        <script>
          new Vue({
            el: '#root',
            filters: {
              lower(value) {
                return value.toLowerCase()
              }
            },
            data() {
              return {
                message: 'Hello Vue'
              }
            }
          })
        </script>
      </body>
    </html>
    

    通过过滤器lower可以很方便的去改变数据。
    上面演示的是组件过滤器的用法,另外还可以定义全局过滤器

    Vue.filter('globalFilter',function(value){
        return value + '111'
    ))
    

    7.监听器 watch的6种用法

    6种方法的DOM元素都是一样的

    <div id="root1">
      <h3>Watch 用法5:绑定多个 handler</h3>
      <input v-model="message">
      <span>{{copyMessage}}</span>
    </div>
    

    id值root对应的是root1~root6
    root1.监听message属性,当message发生变化的话,触发逻辑代码
    root2.message对应的字符串,vue会去methods中寻找对应的方法
    root3.deepMessage后面跟的是一个对象,handler对应的是methods中的方法,deep为true代表深度监听
    root4:immediate属性代表页面加载完成后就立刻执行一次监听事件
    root5:messag后面跟的是一个数组,数组中可以依次绑定多个事件
    root6:通过'deepMessage.a.b'来深度监听某个特定属性,而不是全部

        <script>
          new Vue({
            el: '#root',
            watch: {
              message(value) {
                this.copyMessage = value
              }
            },
            data() {
              return {
                message: 'Hello Vue',
                copyMessage: ''
              }
            }
          })
          new Vue({
            el: '#root2',
            watch: {
              message: 'handleMessage'
            },
            data() {
              return {
                message: 'Hello Vue',
                copyMessage: ''
              }
            },
            methods: {
              handleMessage(value) {
                this.copyMessage = value
              }
            }
          })
          new Vue({
            el: '#root3',
            watch: {
              deepMessage: {
                handler: 'handleDeepMessage',
                deep: true
              }
            },
            data() {
              return {
                deepMessage: {
                  a: {
                    b: 'Deep Message'
                  }
                },
                copyMessage: ''
              }
            },
            methods: {
              handleDeepMessage(value) {
                this.copyMessage = value.a.b
              }
            }
          })
          new Vue({
            el: '#root4',
            watch: {
              message: {
                handler: 'handleMessage',
                immediate: true,
              }
            },
            data() {
              return {
                message: 'Hello Vue',
                copyMessage: ''
              }
            },
            methods: {
              handleMessage(value) {
                this.copyMessage = value
              }
            }
          }),
          new Vue({
            el: '#root5',
            watch: {
              message: [{
                handler: 'handleMessage',
              },
              'handleMessage2',
              function(value) {
                this.copyMessage = this.copyMessage + '...'
              }]
            },
            data() {
              return {
                message: 'Hello Vue',
                copyMessage: ''
              }
            },
            methods: {
              handleMessage(value) {
                this.copyMessage = value
              },
              handleMessage2(value) {
                this.copyMessage = this.copyMessage + '*'
              }
            }
          })
          new Vue({
            el: '#root6',
            watch: {
              'deepMessage.a.b': 'handleMessage'
            },
            data() {
              return {
                deepMessage: { a: { b: 'Hello Vue' } },
                copyMessage: ''
              }
            },
            methods: {
              handleMessage(value) {
                this.copyMessage = value
              }
            }
          })
        </script>
      </body>
    </html>
    

    8.class 和 style 绑定的高级用法

    <html>
      <head>
        <title>class 和 style 绑定的高级用法</title>
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
      </head>
      <body>
        <div id="root">
          <div :class="['active', 'normal']">数组绑定多个class</div>
          <div :class="[{active: isActive}, 'normal']">数组包含对象绑定class</div>
          <div :class="[showWarning(), 'normal']">数组包含方法绑定class</div>
          <div :style="[warning, bold]">数组绑定多个style</div>
          <div :style="[warning, mix()]">数组包含方法绑定style</div>
          <div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }">style多重值</div>
        </div>
        <script>
          new Vue({
            el: '#root',
            data() {
              return {
                isActive: true,
                warning: {
                  color: 'orange'
                },
                bold: {
                  fontWeight: 'bold'
                }
              }
            },
            methods: {
              showWarning() {
                return 'warning'
              },
              mix() {
                return {
                  ...this.bold,
                  fontSize: 20
                }
              }
            }
          })
        </script>
      </body>
    </html>
    

    9.Vue.observable

    随着组件的细化,很多时候都需要处理组件状态共享的情况, Vuex可以轻松解决这类问题,不过就像 Vuex官方文档所说的,如果应用不够大,为避免代码繁琐冗余,最好不要使用它,接下来介绍的是 vue.js 2.6 新增加的 Observable API ,通过使用这个 api 我们可以应对一些简单的跨组件数据状态共享的情况。
    observable()方法,用于设置监控属性,这样就可以监控viewModule中的属性值的变化,从而就可以动态的改变某个元素中的值,监控属性则通过返回一个函数给viewModule对象中的属性,从而来监控该属性。返回的对象可以直接用于渲染函数methods和计算属性computed内,并且会在发生改变时触发相应的更新。
    创建store.js文件

    import Vue from 'vue'
    export const state = Vue.observable({
      screenCars: {},
    })
    export const mutations = {
      updateScreenCars (payload) {
        state.screenCars = Object.assign({}, payload)
      },
    }
    

    index.vue组件中触发:

    <template>
        <div>
            <el-button @click="toSave">保存</el-button>
        </div>
    </template>
    
    <script>
    import {state, mutations} from './store.js'
    export default {
      name: 'table_form',
      computed: {
        screenCars() {
          return state.screenCars
        },
      },
      methods: {
        setTest: mutations.updateScreenCars ,
        toSave () {
          this.setTest({a:1})
        },
      },
    }
    </script>
    

    相关文章

      网友评论

        本文标题:Vue进阶用法总结

        本文链接:https://www.haomeiwen.com/subject/bijffktx.html