• Web前端-Vue2+Vue3基础入门到实战项目-Day5(自定义指令, 插槽, 案例商品列表, 路由入门)


    自定义指令

    基本使用

    • 自定义指令: 自己定义的指令, 可以封装一些dom操作, 扩展额外功能
    • 全局注册
      // 1. 全局注册指令
      Vue.directive('focus', {
        // inserted 会在 指令所在的元素, 被插入到页面中时触发
        inserted (el) {
            // el 就是指令所绑定的元素
            // console.log(el)
            el.focus()
        }
      })
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
    • 局部注册
      <script>
      export default {
      // mounted(){
      //   this.$refs.inp.focus()
      // }
      
      // 2. 局部注册指令
      directives: {
          // 指令名: 指令的配置项
          focus: {
          inserted (el) {
              el.focus()
          }
          }
      }
      }
      script>
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17

    指令的值

    • v-指令名=“指令值”, 通过等号可以绑定指令的值
    • 通过binding.value可以拿到指令的值
    • 通过update钩子, 可以监听指令值的变化, 进行dom更新操作
    <template>
      <div>
        <h1 v-color="color1">指令的值1测试h1>
        <h1 v-color="color2">指令的值2测试h1>
      div>
    template>
    
    <script>
    export default {
      data () {
        return {
          color1: 'red',
          color2: 'green'
        }
      },
      directives: {
        color: {
          // 1. inserted 提供的是元素被添加到页面中时的逻辑
          inserted (el, binding) {
            // binding.value 就是指令的值
            el.style.color = binding.value
          },
          // 2. update 指令的值修改的时候触发, 提供值变化后, dom更新的逻辑
          update(el, binding){
            // console.log(binding.value)
            el.style.color = binding.value
          }
        }
      }
    }
    script>
    
    <style>
    
    style>
    
    • 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

    封装v-loading指令

    • 核心思路
      1. 准备类名loading, 通过伪元素提供遮罩层
      2. 添加或移除类名, 实现loading蒙层的添加移除
      3. 利用指令语法, 封装v-loading通用指令
        inserted钩子中, binding.value判断指令的值, 设置默认状态
        update钩子中, binding.value判断指令的值, 更新类名状态
    <template>
      <div class="main">
        <div class="box" v-loading="isLoading">
          <ul>
            <li v-for="item in list" :key="item.id" class="news">
              <div class="left">
                <div class="title">{{ item.title }}div>
                <div class="info">
                  <span>{{ item.source }}span>
                  <span>{{ item.time }}span>
                div>
              div>
    
              <div class="right">
                <img :src="item.img" alt="">
              div>
            li>
          ul>
        div>
      div>
    template>
    
    <script>
    // 安装axios =>  yarn add axios
    import axios from 'axios'
    
    // 接口地址:http://hmajax.itheima.net/api/news
    // 请求方式:get
    export default {
      data () {
        return {
          list: [],
          isLoading: true
        }
      },
      directives: {
        loading(el, binding){
          binding.value ? el.classList.add('loading') : el.classList.remove('loading')
        },
        update(el, binding){
          binding.value ? el.classList.add('loading') : el.classList.remove('loading')
        }
      },
      async created () {
        // 1. 发送请求获取数据
        const res = await axios.get('http://hmajax.itheima.net/api/news')
        
        setTimeout(() => {
          // 2. 更新到 list 中
          this.list = res.data.data
          this.isLoading = false
        }, 2000)
      }
    }
    script>
    
    <style>
    /* 伪类 - 蒙层效果 */
    .loading:before {
      content: '';
      position: absolute;
      left: 0;
      top: 0;
      width: 100%;
      height: 100%;
      background: #fff url('./loading.gif') no-repeat center;
    }
    
    /* .box2 {
      width: 400px;
      height: 400px;
      border: 2px solid #000;
      position: relative;
    } */
    
    .box {
      width: 800px;
      min-height: 500px;
      border: 3px solid orange;
      border-radius: 5px;
      position: relative;
    }
    .news {
      display: flex;
      height: 120px;
      width: 600px;
      margin: 0 auto;
      padding: 20px 0;
      cursor: pointer;
    }
    .news .left {
      flex: 1;
      display: flex;
      flex-direction: column;
      justify-content: space-between;
      padding-right: 10px;
    }
    .news .left .title {
      font-size: 20px;
    }
    .news .left .info {
      color: #999999;
    }
    .news .left .info span {
      margin-right: 20px;
    }
    .news .right {
      width: 160px;
      height: 120px;
    }
    .news .right img {
      width: 100%;
      height: 100%;
      object-fit: cover;
    }
    style>
    
    • 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
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116

    插槽

    默认插槽

    • 使用步骤
      1. 先在组件内用slot占位
      2. 使用组件时, 传入具体标签内容插入
    • 插槽中的内容会作为默认值
    • MyDialog.vue
      <template>
        <div class="dialog">
          <div class="dialog-header">
            <h3>友情提示h3>
            <span class="close">✖️span>
          div>
      
          <div class="dialog-content">
            
            <slot>slot>
          div>
          <div class="dialog-footer">
            <button>取消button>
            <button>确认button>
          div>
        div>
      template>
      
      <script>
      export default {
        data () {
          return {
      
          }
        }
      }
      script>
      
      <style scoped>
      * {
        margin: 0;
        padding: 0;
      }
      .dialog {
        width: 470px;
        height: 230px;
        padding: 0 25px;
        background-color: #ffffff;
        margin: 40px auto;
        border-radius: 5px;
      }
      .dialog-header {
        height: 70px;
        line-height: 70px;
        font-size: 20px;
        border-bottom: 1px solid #ccc;
        position: relative;
      }
      .dialog-header .close {
        position: absolute;
        right: 0px;
        top: 0px;
        cursor: pointer;
      }
      .dialog-content {
        height: 80px;
        font-size: 18px;
        padding: 15px 0;
      }
      .dialog-footer {
        display: flex;
        justify-content: flex-end;
      }
      .dialog-footer button {
        width: 65px;
        height: 35px;
        background-color: #ffffff;
        border: 1px solid #e1e3e9;
        cursor: pointer;
        outline: none;
        margin-left: 10px;
        border-radius: 3px;
      }
      .dialog-footer button:last-child {
        background-color: #007acc;
        color: #fff;
      }
      style>
      
      • 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
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53
      • 54
      • 55
      • 56
      • 57
      • 58
      • 59
      • 60
      • 61
      • 62
      • 63
      • 64
      • 65
      • 66
      • 67
      • 68
      • 69
      • 70
      • 71
      • 72
      • 73
      • 74
      • 75
      • 76
      • 77
      • 78
    • App.vue
      <template>
        <div>
          
          <MyDialog>你确认要删除吗MyDialog>
      
          <MyDialog>你确认要退出吗MyDialog>
        div>
      template>
      
      <script>
      import MyDialog from "./components/MyDialog.vue"
      export default {
        data() {
          return {}
        },
        components: {
          MyDialog,
        },
      }
      script>
      
      <style>
      body {
        background-color: #b3b3b3;
      }
      style>
      
      • 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

    具名插槽

    • slot占位, 给name属性起名字来区分
    • template配合v-slot:插槽名分发内容
    • v-slot:插槽名 可以简化为 #插槽名

    App.vue

    <template>
      <div>
        <MyDialog>
          
          <template v-slot:head>
            <div>我是大标题div>
          template>
          <template v-slot:content>
            <div>我是内容div>
          template>
          <template #footer>
            <button>确认button>
            <button>取消button>
          template>
        MyDialog>
      div>
    template>
    
    <script>
    import MyDialog from './components/MyDialog.vue'
    export default {
      data () {
        return {
    
        }
      },
      components: {
        MyDialog
      }
    }
    script>
    
    <style>
    body {
      background-color: #b3b3b3;
    }
    style>
    
    • 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

    MyDialog.vue

    <template>
      <div class="dialog">
        <div class="dialog-header">
          
          <slot name="head">slot>
        div>
    
        <div class="dialog-content">
          <slot name="content">slot>
        div>
        <div class="dialog-footer">
          <slot name="footer">slot>
        div>
      div>
    template>
    
    <script>
    export default {
      data() {
        return {}
      },
    }
    script>
    
    <style scoped>
    * {
      margin: 0;
      padding: 0;
    }
    .dialog {
      width: 470px;
      height: 230px;
      padding: 0 25px;
      background-color: #ffffff;
      margin: 40px auto;
      border-radius: 5px;
    }
    .dialog-header {
      height: 70px;
      line-height: 70px;
      font-size: 20px;
      border-bottom: 1px solid #ccc;
      position: relative;
    }
    .dialog-header .close {
      position: absolute;
      right: 0px;
      top: 0px;
      cursor: pointer;
    }
    .dialog-content {
      height: 80px;
      font-size: 18px;
      padding: 15px 0;
    }
    .dialog-footer {
      display: flex;
      justify-content: flex-end;
    }
    .dialog-footer button {
      width: 65px;
      height: 35px;
      background-color: #ffffff;
      border: 1px solid #e1e3e9;
      cursor: pointer;
      outline: none;
      margin-left: 10px;
      border-radius: 3px;
    }
    .dialog-footer button:last-child {
      background-color: #007acc;
      color: #fff;
    }
    style>
    
    • 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
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74

    作用域插槽

    • 作用: 可以给插槽上绑定数据, 供将来使用组件时使用
    • 使用步骤:
      1. 给slot标签, 以添加属性的方式传值
      2. 所有属性都会被收集到一个对象中
      3. template中, 通过#插槽名="obj"接收
    • App.vue
      <template>
        <div>
          <MyTable :data="list">
            
            <template #default="obj">
              <button @click="del(obj.row.id)">
                删除
              button>
            template>
          MyTable>
          <MyTable :data="list2">
            <template #default="{ row }">
              <button @click="show(row)">
                查看
              button>
            template>
          MyTable>
        div>
      template>
      
      <script>
      import MyTable from './components/MyTable.vue'
      export default {
        data () {
          return {
            list: [
              { id: 1, name: '张小花', age: 18 },
              { id: 2, name: '孙大明', age: 19 },
              { id: 3, name: '刘德忠', age: 17 },
            ],
            list2: [
              { id: 1, name: '赵小云', age: 18 },
              { id: 2, name: '刘蓓蓓', age: 19 },
              { id: 3, name: '姜肖泰', age: 17 },
            ]
          }
        },
        components: {
          MyTable
        },
        methods: {
          del(id){
            this.list = this.list.filter(item => item.id !== id)
          },
          show(row){
            // console.log(row)
            alert(`姓名: ${row.name}\n年纪: ${row.age}`)
          }
        }
      }
      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
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
    • MyTable.vue
      <template>
        <table class="my-table">
          <thead>
            <tr>
              <th>序号th>
              <th>姓名th>
              <th>年纪th>
              <th>操作th>
            tr>
          thead>
          <tbody>
            <tr v-for="(item, index) in data" :key="item.id">
              <td> {{index+1}} td>
              <td> {{item.name}} td>
              <td> {{item.age}} td>
              <td>
                
                <slot :row="item" msg="test">slot>
                
                
              td>
            tr>
          tbody>
        table>
      template>
      
      <script>
      export default {
        props: {
          data: Array,
        },
      }
      script>
      
      <style scoped>
      .my-table {
        width: 450px;
        text-align: center;
        border: 1px solid #ccc;
        font-size: 24px;
        margin: 30px auto;
      }
      .my-table thead {
        background-color: #1f74ff;
        color: #fff;
      }
      .my-table thead th {
        font-weight: normal;
      }
      .my-table thead tr {
        line-height: 40px;
      }
      .my-table th,
      .my-table td {
        border-bottom: 1px solid #ccc;
        border-right: 1px solid #ccc;
      }
      .my-table td:last-child {
        border-right: none;
      }
      .my-table tr:last-child td {
        border-bottom: none;
      }
      .my-table button {
        width: 65px;
        height: 35px;
        font-size: 18px;
        border: 1px solid #ccc;
        outline: none;
        border-radius: 3px;
        cursor: pointer;
        background-color: #ffffff;
        margin-left: 5px;
      }
      style>
      
      • 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
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53
      • 54
      • 55
      • 56
      • 57
      • 58
      • 59
      • 60
      • 61
      • 62
      • 63
      • 64
      • 65
      • 66
      • 67
      • 68
      • 69
      • 70
      • 71
      • 72
      • 73
      • 74
      • 75
      • 76
      • 77
      • 78
      • 79
      • 80

    案例 - 商品列表

    my-tag 标签组件的封装
    1. 创建组件 - 初始化
    2. 实现功能
      2.1 双击显示, 并且自动聚焦
        v-if v-else @dblclick
        自动聚焦
        1. $nextTick => $refs 获取到dom, 进行focus获取焦点
        2. 封装v-focus指令
      2.2 失去焦点, 隐藏输入框
        @blur 操作 isEdit
      2.3 回显标签内容
        回显的标签信息是父组件传递过来的
        v-model实现功能(简化代码) v-model => :value 和 @input
        组件内部通过props接收, :value设置给输入框
      2.4 内容修改, 回车=> 修改标签信息
        @keyup.enter, 触发事件 $emit('input', e.target.value)
    ------------------------
    my-table 表格组件的封装
    1. 数据不能写死, 动态传递表格渲染的数据 props
    2. 结构不能写死 - 多处结构自定义 [具名插槽]
      2.1 表头支持自定义
      2.2 主体支持自定义
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • App.vue
      <template>
        <div class="table-case">
          <MyTable :data="goods">
            <template #head>
              <th>编号th>
              <th>名称th>
              <th>图片th>
              <th width="100px">标签th>
            template>
            <template #body="{item, index}">
              <td> {{index+1}} td>
              <td> {{item.name}} td>
              <td>
                <img :src="item.picture" />
              td>
              <td>
                
                <MyTag v-model="item.tag">MyTag>
              td>
            template>
          MyTable>
        div>
      template>
      
      <script>
      import MyTag from './components/MyTag.vue'
      import MyTable from './components/MyTable.vue'
      export default {
        name: 'TableCase',
        components: {
          MyTag,
          MyTable
        },
        methods: {
      
        },
        data() {
          return {
            goods: [
              {
                id: 101,
                picture:
                  'https://yanxuan-item.nosdn.127.net/f8c37ffa41ab1eb84bff499e1f6acfc7.jpg',
                name: '梨皮朱泥三绝清代小品壶经典款紫砂壶',
                tag: '茶具',
              },
              {
                id: 102,
                picture:
                  'https://yanxuan-item.nosdn.127.net/221317c85274a188174352474b859d7b.jpg',
                name: '全防水HABU旋钮牛皮户外徒步鞋山宁泰抗菌',
                tag: '男鞋',
              },
              {
                id: 103,
                picture:
                  'https://yanxuan-item.nosdn.127.net/cd4b840751ef4f7505c85004f0bebcb5.png',
                name: '毛茸茸小熊出没,儿童羊羔绒背心73-90cm',
                tag: '儿童服饰',
              },
              {
                id: 104,
                picture:
                  'https://yanxuan-item.nosdn.127.net/56eb25a38d7a630e76a608a9360eec6b.jpg',
                name: '基础百搭,儿童套头针织毛衣1-9岁',
                tag: '儿童服饰',
              },
            ],
          }
        },
      }
      script>
      
      <style lang="less" scoped>
      .table-case {
        width: 1000px;
        margin: 50px auto;
        img {
          width: 100px;
          height: 100px;
          object-fit: contain;
          vertical-align: middle;
        }
      }
      style>
      
      • 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
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53
      • 54
      • 55
      • 56
      • 57
      • 58
      • 59
      • 60
      • 61
      • 62
      • 63
      • 64
      • 65
      • 66
      • 67
      • 68
      • 69
      • 70
      • 71
      • 72
      • 73
      • 74
      • 75
      • 76
      • 77
      • 78
      • 79
      • 80
      • 81
      • 82
      • 83
      • 84
      • 85
    • MyTable.vue
      <template>
        <table class="my-table">
          <thead>
            <tr>
              <slot name="head">slot>
            tr>
          thead>
          <tbody>
            <tr v-for="(item ,index) in data" :key="item.id">
              <slot name="body" :item="item" :index="index">slot>
            tr>
          tbody>
        table>
      template>
      
      <script>
      export default {
        props: {
          data: {
            type:Array,
            required: true
          },
        },
        components: {
        }
      }
      script>
      
      <style  lang="less" scoped>
      .my-table {
        width: 100%;
        border-spacing: 0;
        img {
          width: 100px;
          height: 100px;
          object-fit: contain;
          vertical-align: middle;
        }
        th {
          background: #f5f5f5;
          border-bottom: 2px solid #069;
        }
        td {
          border-bottom: 1px dashed #ccc;
        }
        td,
        th {
          text-align: center;
          padding: 10px;
          transition: all 0.5s;
          &.red {
            color: red;
          }
        }
        .none {
          height: 100px;
          line-height: 100px;
          color: #999;
        }
      }
      style>
      
      • 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
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53
      • 54
      • 55
      • 56
      • 57
      • 58
      • 59
      • 60
      • 61
    • MyTag.vue
      <template>
        <div class="my-tag">
          <input 
            v-if="isEdit"
            v-focus
            @blur="isEdit = false"
            ref="inp"
            class="input" 
            type="text" 
            :value="value"
            @keyup.enter="handleEnter"
            placeholder="输入标签">
          <div 
            v-else
            @dblclick="handleClick"
            class="text">
            {{value}}
          div>
        div>
      template>
      
      <script>
      export default {
        data () {
          return {
            isEdit: false
          }
        },
        props: {
          value: String
        },
        methods: {
          handleClick(){
            // 双击后, 切换到显示状态
            this.isEdit = true
            // // 由于Vue是异步dom更新, 所以要等dom更新完, 再获取焦点
            // this.$nextTick(() => {
            //   // 获取焦点
            //   this.$refs.inp.focus()
            // })
          },
          handleEnter(e){
            if(e.target.value.trim() === ''){
              return alert('标签内容不能为空')
            }
            // 子传父, 将回车时, [输入框的内容] 提交给父组件更新
            // 由于父组件是v-model, 触发事件, 需要触发input事件
            this.$emit('input', e.target.value)
            this.isEdit = false
          }
        }
      }
      script>
      
      <style lang="less" scoped>
      .my-tag {
        cursor: pointer;
        .input {
          appearance: none;
          outline: none;
          border: 1px solid #ccc;
          width: 100px;
          height: 40px;
          box-sizing: border-box;
          padding: 10px;
          color: #666;
          &::placeholder {
            color: #666;
          }
        }
      }
      style>
      
      • 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
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53
      • 54
      • 55
      • 56
      • 57
      • 58
      • 59
      • 60
      • 61
      • 62
      • 63
      • 64
      • 65
      • 66
      • 67
      • 68
      • 69
      • 70
      • 71
      • 72

    路由入门

    单页面应用程序

    • 单页面应用程序
      • 所有功能在一个html页面上实现
      • 优点: 按需更新性能高, 开发效率高, 用户体验好
      • 缺点: 学习成本, 首屏加载慢, 不利于SEO
      • 应用场景: 系统类/内部/文档类/移动端 网站

    路由基本使用

    路由的使用步骤 5 + 2
    5个基础步骤
    1. 下载 v3.6.5
    2. 引入
    3. 安装注册 Vue.use(Vue插件)
    4. 创建路由对象
    5. 注入到new Vue中,建立关联
    
    2个核心步骤
    1. 建组件(views目录),配规则
    2. 准备导航链接,配置路由出口(匹配的组件展示的位置) 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • App.vue
      <template>
        <div>
          <div class="footer_wrap">
            <a href="#/find">发现音乐a>
            <a href="#/my">我的音乐a>
            <a href="#/friend">朋友a>
          div>
          <div class="top">
            
            <router-view>router-view>
          div>
        div>
      template>
      
      <script>
      export default {};
      script>
      
      <style>
      body {
        margin: 0;
        padding: 0;
      }
      .footer_wrap {
        position: relative;
        left: 0;
        top: 0;
        display: flex;
        width: 100%;
        text-align: center;
        background-color: #333;
        color: #ccc;
      }
      .footer_wrap a {
        flex: 1;
        text-decoration: none;
        padding: 20px 0;
        line-height: 20px;
        background-color: #333;
        color: #ccc;
        border: 1px solid black;
      }
      .footer_wrap a:hover {
        background-color: #555;
      }
      style>
      
      • 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
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
    • main.js
      import Vue from 'vue'
      import App from './App.vue'
      
      import Find from './views/Find'
      import My from './views/My'
      import Friend from './views/Friend'
      import VueRouter from 'vue-router'
      Vue.use(VueRouter) // VueRouter插件初始化
      
      const router = new VueRouter({
        // routes 路由规则们
        // route  一条路由规则 { path: 路径, component: 组件 }
        routes: [
          { path: '/find', component: Find },
          { path: '/my', component: My },
          { path: '/friend', component: Friend },
        ]
      })
      
      Vue.config.productionTip = false
      
      new Vue({
        render: h => h(App),
        router
      }).$mount('#app')
      
      
      • 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

    来源

    黑马程序员. Vue2+Vue3基础入门到实战项目

  • 相关阅读:
    虚幻引擎:C++网络属性同步
    基于SSM的大学校医管理系统
    低代码:让软件开发不再遥不可及
    图论 2023.11.20
    边缘计算的优势
    dvaJs在react 项目中的简单使用
    泊车功能专题介绍 ———— AVP系统技术要求之人机交互&云平台
    房台怎么管理?/
    1 | Nessus使用
    Vue学习笔记之Vuex 6.25
  • 原文地址:https://blog.csdn.net/Y_cen/article/details/133987638