Skip to content

一、高级特性

1. 自定义 v-model

  • 定义
    vue
    <template>
        <input type="text"
            :value="text"
            @input="$emit('change', $event.target.value)"
        >
        <!--
            1. 上面的 input 使用了 :value 而不是 v-model
            2. 上面的 change 和 model.event 要对应起来
            3. text 属性对应起来
        -->
    </template>
    
    <script>
    export default {
        model: {
            prop: 'text', // 对应 props text1
            event: 'change'
        },
        props: {
            text: String,
            default() {
                return ''
            }
        }
    }
    </script>
  • 用法
    vue
    <template>
      <div>  
         <!-- 自定义 v-model -->
         <p>{{name}}</p>
         <CustomVModel v-model="name"/> 
      </div>
    </template>
    
    <script>
      import CustomVModel from './CustomVModel';
    
      export default {
        components: {
          CustomVModel
        },
        data() {
          return {
            name: "自定义v-model",
          };
        },
      };
    </script>

2. $nextTick 【组件更新之后,获取最新的 DOM 】

  • Vue 是异步渲染
  • data 改变之后,DOM 不会立即渲染
  • $nextTick 会在 DOM 渲染之后被触发,以获取最新 DOM 节点
  • 详细参看

3. slot

  • 正常使用

    vue
    <template>
        <a :href="url">
            <slot>
                默认内容,即父组件没设置内容时,这里显示
            </slot>
        </a>
    </template>
    
    <script>
    export default {
        props: ['url'],
        data() {
            return {}
        }
    }
    </script>
    vue
      <template>
      <div>  
       <!--     slot -->
       <SlotDemo :url="website.url">
          {{website.title}}
        </SlotDemo> 
      </div>
    </template>
    
    <script>
      import SlotDemo from './SlotDemo'
    
      export default {
        components: {
          SlotDemo
        },
        data() {
          return {
            website: {
              url: "https://www.baidu.com/",
              title: "百度",
              subTitle: "面向百度编程",
            },
          };
        },
      };
    </script>
  • 作用域插槽

    • 父组件获取子组件 slot 的值

      vue
      <template>
          <a :href="url">
              <slot :slotData="website">
                  {{website.subTitle}} <!-- 默认值显示 subTitle ,即父组件不传内容时 -->
              </slot>
          </a>
      </template>
      
      <script>
      export default {
          props: ['url'],
          data() {
              return {
                  website: {
                      url: '子组件url',
                      title: '子组件的标题',
                      subTitle: '子组件副标题'
                  }
              }
          }
      }
      </script>
      vue
        <template>
        <div>  
         <!-- slot -->
         <ScopedSlotDemo :url="website.url">
            <template v-slot="slotProps">
                {{slotProps.slotData.title}}
            </template>
          </ScopedSlotDemo> 
        </div>
      </template>
      
      <script>
        import ScopedSlotDemo from './ScopedSlotDemo'
      
        export default {
          components: {
            ScopedSlotDemo
          },
          data() {
            return {
              website: {
                url: "https://www.baidu.com/",
                title: "百度",
                subTitle: "面向百度编程",
              },
            };
          },
        };
      </script>
  • 具名插槽

    vue
    <div class="container">
      <header>
        <slot name="header"></slot>
      </header>
      <main>
        <slot></slot>
      </main>
      <footer>
        <slot name="footer"></slot>
      </footer>
    </div>
    vue
    <!-- #header 是 v-slot 的缩写  -->
    <base-layout>
      <template #header>
        <h1>Here might be a page title</h1>
      </template>
    
      <p>A paragraph for the main content.</p>
      <p>And another one.</p>
    
      <template #footer>
        <p>Here's some contact info</p>
      </template>
    </base-layout>

4. 动态组件

  • 用法 :is="component-name"
  • 需要根据数据类型,动态渲染的场景。即组件类型不确定
  • 场景
    • tab 页的切换
    • 详情页显示

5. 异步组件

  • import() 函数
  • 按需加载。异步加载大组件
  • 常用于拥有 v-if 逻辑的组件
    vue
    <template>
        <div>
          <!-- 异步组件 -->
           <FormDemo v-if="showFormDemo"/>
           <button @click="showFormDemo = true">show form demo</button> 
        </div>
    </template>
      
    <script>
      
      export default {
        components: {
          FormDemo: () => import('../BaseUse/FormDemo'),
        },
        data() {
          return {
            showFormDemo: false,
          };
        },
      };
    </script>

6. keep-alive

  • 缓存组件

  • 频繁窃魂,不需要重复渲染

  • Vue 常用性能优化

    vue
    <template>
        <div>
            <button @click="changeState('A')">A</button>
            <button @click="changeState('B')">B</button>
            <button @click="changeState('C')">C</button>
    
            <keep-alive> <!-- tab 切换 -->
                <KeepAliveStageA v-if="state === 'A'"/> <!-- v-show -->
                <KeepAliveStageB v-if="state === 'B'"/>
                <KeepAliveStageC v-if="state === 'C'"/>
            </keep-alive>
        </div>
    </template>
    
    <script>
    import KeepAliveStageA from './KeepAliveStateA'
    import KeepAliveStageB from './KeepAliveStateB'
    import KeepAliveStageC from './KeepAliveStateC'
    
    export default {
        components: {
            KeepAliveStageA,
            KeepAliveStageB,
            KeepAliveStageC
        },
        data() {
            return {
                state: 'A'
            }
        },
        methods: {
            changeState(state) {
                this.state = state
            }
        }
    }
    </script>
    vue
    <template>
        <p>state A</p>
    </template>
    
    <script>
    export default {
        mounted() {
            console.log('A mounted')
        },
        destroyed() {
            console.log('A destroyed')
        }
    }
    </script>
    vue
    <template>
        <p>state B</p>
    </template>
    
    <script>
    export default {
        mounted() {
            console.log('B mounted')
        },
        destroyed() {
            console.log('B destroyed')
        }
    }
    </script>
    vue
    <template>
        <p>state C</p>
    </template>
    
    <script>
    export default {
        mounted() {
            console.log('C mounted')
        },
        destroyed() {
            console.log('C destroyed')
        }
    }
    </script>

7. mixin

  • 多个组件有相同的逻辑,抽离出来

  • mixin 存在一些问题,容易混乱

    • 多 mixin 容易造成命名冲突,直接覆盖
    • mixin 和组件可能出现多对多的关系,复杂度较高
  • 在 vue3 的 Composition API 得到了很好的解决

    js
    export default {
        data() {
            return {
                city: '北京'
            }
        },
        methods: {
            showName() {
                console.log(this.name)
            }
        },
        mounted() {
            console.log('mixin mounted', this.name)
        }
    }
    vue
    <template>
        <div>
            <p>{{ name }} {{ major }} {{ city }}</p>
            <button @click="showName">显示姓名</button>
        </div>
    </template>
    
    <script>
      import myMixin from './mixin'
    
      export default {
        // 可以添加多个,会自动合并起来
        mixins: [myMixin],
        data() {
          return {
            name: '张三',
            major: 'web 前端'
          }
        },
        methods: {},
        mounted() {
          console.log('component mounted', this.name)
        }
      }
    </script>

Released under the MIT License.