【小慕读书】——Vue/Element UI/Node.js后台管理系统:Vue进阶

前言:最近在学习Vue+Element UI+Node.js小慕读书中后台管理系统开发课程,这里对学习过程作个笔记,方便自己和大家翻阅。


一、项目内容

二、技术栈

后端技术栈 Node + Express 架构

jwt: 实现登录认证

epub: epub列树解析

multer: 文件上传

xml2js: xml文件解析

adm-zip: 自定义的电子书解析功能

mysql: 数据库

免费提供的课程API: https://www.youbaobao.xyz/admin-docs/guide/

三、Vue 进阶

1、$emit 和 $on

    <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) //$on定义事件
        },
        methods: {
          handleEvents(e) {
            console.log(this.message, e)
          },
          boost() {
            this.$emit('my_events', 'my params') //$emit消费事件   
          }
        }
      })
    </script>
  • 好处:将事件的定义和事件的消费分开,实现逻辑的解耦
  1. 可以为同一个事件绑定不同的方法
    created() {
       this.$on('my_events', this.handleEvents)
       this.$on('my_events', this.handleEvents2)
    },
    
  2. 可以为不同的事件绑定同一个方法
    created() {
         this.$on(['my_events', 'my_events2'], this.handleEvents)
          console.log(_events)
    },
    //  {
    //      myevents: [f],
    //      myevents2: [f]
    //   }
    
  • this.$emit进行了try...catch的处理,抛出异常不会中断程序的执行

2. directive 用法

    <div id="root">
      <div v-loading="isLoading">{{data}}</div>
      <button @click="update">更新</button>
    </div>
    <script>
      Vue.directive('loading', {           //loading和v-loading名字一致
        update(el, binding, vnode) {
          console.log(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>

3. Vue.component 用法

    <div id="root">
      <Test :msg="message"></Test>
    </div>
    <script>
      Vue.component('Test', {                 //定义一个组件
        template: '<div>{{msg}}</div>',       
        props: {
          msg: {
            type: String,
            default: 'default message'
          }
        }
      })
      new Vue({
        el: '#root',
        data() {
          return {
            message: "Test Component"
          }
        }
      })
    </script>

4.Vue.extend 用法

     <div id="root">
      <Test :msg="message"></Test>
    </div>
    <script>
      const component = Vue.extend({    //生成Vue的构造函数VueComponent
        template: '<div>{{msg}}</div>',
        props: {
          msg: {
            type: String,
            default: 'default message'
          }
        },
        name: 'Test'
      })
      Vue.component('Test')
      new Vue({
        el: '#root',
        data() {
          return {
            message: "Test Extend Component"
          }
        }
      })
    </script>

5.Vue.extend 进阶用法

  • 给Vue实例添加自定义API
  <head>
    <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) //关键:使用$mount将LoadingComponent直接挂载到loading-wrapper上 new LoadingComponent({ props: { msg: { type: String, default: msg } } }).$mount('#loading-wrapper') return () => { document.body.removeChild(document.getElementById('loading-wrapper')) } } //构造函数挂载到Vue.prototype Vue.prototype.$loading = Loading new Vue({ el: '#root', methods: { showLoading() { //重点:实现this.$loading const hide = this.$loading('正在加载,请稍等...') setTimeout(() => { hide() }, 2000) } } }) </script>

6.Vue.use 用法

  •  把Vue,extends高阶用法——给Vue添加自定义API——做成一个插件
  <head>
    <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) { //vm: Vue的构造函数
          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()加载插件  
      Vue.use(loadingPlugin)

      new Vue({
        el: '#root',
        methods: {
          showLoading() {
            const hide = this.$loading('正在加载,请稍等...')
            setTimeout(() => {
              hide()
            }, 2000)
          }
        }
      })
    </script>

 7.组件通信 provide 和 inject

  • Vue2.2之后引入的,为了提供更好的跨组件通讯方案
    <div id="root">
      <Test></Test>
    </div>
    <script>
      function registerPlugin() {
        Vue.component('Test', {
          template: '<div>{{message}}<Test2 /></div>',

          //父组件通过provide:提供了将自身组件打包成一个属性elTest提供给子组件调用
          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属性,标识引用到的父组件的实例
          inject: ['elTest'],
          methods: {
            changeMessage() {

              //通过this.elTest直接就调用到父组件的change方法,并传入参数
              this.elTest.change(this.$options._componentTag)
            }
          }
        })
      }

      Vue.use(registerPlugin)

      new Vue({
        el: '#root'
      })
    </script>

 8.过滤器 filter

    <div id="root">
      {{ message | lower }}
    </div>
    <script>
      new Vue({
        el: '#root',

        filters: {
          //过滤器lower:将状态值全部改成小写
          lower(value) {
            return value.toLowerCase() // hello vue
          }
        },

        data() {
          return {
            message: 'Hello Vue'
          }
        }
      })
    </script> 

9.监听器 watch

    <div id="root">
      <h3>Watch 用法1:常见用法</h3>
      <input v-model="message">
      <span>{{copyMessage}}</span>
    </div>
    <div id="root2">
      <h3>Watch 用法2:绑定方法</h3>
      <input v-model="message">
      <span>{{copyMessage}}</span>
    </div>
    <div id="root3">
      <h3>Watch 用法3:deep + handler</h3>
      <input v-model="deepMessage.a.b">
      <span>{{copyMessage}}</span>
    </div>
    <div id="root4">
      <h3>Watch 用法4:immediate</h3>
      <input v-model="message">
      <span>{{copyMessage}}</span>
    </div>
    <div id="root5">
      <h3>Watch 用法5:绑定多个 handler</h3>
      <input v-model="message">
      <span>{{copyMessage}}</span>
    </div>
    <div id="root6">
      <h3>Watch 用法6:监听对象属性</h3>
      <input v-model="deepMessage.a.b">
      <span>{{copyMessage}}</span>
    </div>
      
    <script>
      new Vue({
        el: '#root',

        //常见用法1:监听器message后面直接跟一个方法  
        watch: {
          message(value) {
            this.copyMessage = value  //根据message改变copyMessage的值  
          }
        },

        data() {
          return {
            message: 'Hello Vue',
            copyMessage: ''
          }
        }
      })

      new Vue({
        el: '#root2',

        //常见用法2:监听器message后面不再直接跟一个方法,
        //           字符串绑定methods中的一个方法名
        watch: {
          message: 'handleMessage'
        },
        data() {
          return {
            message: 'Hello Vue',
            copyMessage: ''
          }
        },
        methods: {
          handleMessage(value) {
            this.copyMessage = value
          }
        }
      })

      new Vue({
        el: '#root3',

        //深度监听1: 监听deepMessage.a.b属性,deep: true
        watch: {
          deepMessage: {
            handler: 'handleDeepMessage', //handler指向一个function
            deep: true
          }
        },
        data() {
          return {
            deepMessage: {  
              a: {
                b: 'Deep Message'
              }
            },
            copyMessage: ''
          }
        },
        methods: {
          handleDeepMessage(value) {
            this.copyMessage = value.a.b
          }
        }
      })

      new Vue({
        el: '#root4',

        //获得默认值:immediate: true 页面初始化后立即执行了一次handler
        watch: {
          message: {
            handler: 'handleMessage',
            immediate: true, //相当于 created: { this.copyMessage = this.message }
          }
        },
        data() {
          return {
            message: 'Hello Vue',
            copyMessage: ''
          }
        },
        methods: {
          handleMessage(value) {
            this.copyMessage = value
          }
        }
      }),

      new Vue({
        el: '#root5',

        //绑定多个handler: 依次使用
        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>

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

    <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>

四、Vue2.6 新特性

1.Vue.observable

  • 比Vuex更方便的使用Vue的响应式属性
    <div id="root">
      {{message}}
      <button @click="change">Change</button>
    </div>
    <script>
      const state = Vue.observable({ message: 'Vue 2.6' })

      const mutation = {
        setMessage(value) {
          state.message = value
        }
      }
      new Vue({
        el: '#root',

        //注意:要在computed中计算响应式属性message的改变
        computed: {
          message() {
            return state.message
          }
        },

        methods: {
          change() {
            mutation.setMessage('Vue 3.0')
          }
        }
      })
    </script>

2.插槽 slot

  • v-slot只能用在组件或template上,用在div等标签上会报错
    <div id="root">
      <div>案例1:slot的基本用法</div>
      <Test>
        <template v-slot:header="{user}">
          <div>自定义header({{user.a}})</div>
        </template>
        <template v-slot="{user}">
          <div>自定义body({{user.b}})</div>
        </template>
      </Test>
    </div>

    <div id="root2">
      <div>案例2:Vue2.6新特性 - 动态slot</div>
      <Test>
        <template v-slot:[section]="{section}">
          <div>this is {{section}}</div>
        </template>
      </Test>
      <button @click="change">switch header and body</button>
    </div>

    <script>
      Vue.component('Test', {
        template: 
          '<div>' +
            '<slot name="header" :user="obj" :section="\'header\'">' +
              '<div>默认header</div>' +
            '</slot>' +
            '<slot :user="obj" :section="\'body\'">默认body</slot>' +
          '</div>',
        data() {
          return {
            obj: { a: 1, b: 2 }
          }
        }
      })

      new Vue({ el: '#root' })

      new Vue({ 
        el: '#root2',
        data() {
          return {
            section: 'header'
          }
        },
        methods: {
          change() {
            this.section === 'header' ?
              this.section = 'default' :
              this.section = 'header'
          }
        }
      })
    </script>

注:项目来自慕课网

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值