前端进阶必会的六个Vue高级特性

373 阅读5分钟

vue高级特性

1、自定义 v-model

<template>
    <div>
        <p>vue 高级特性</p>
        <hr>

        <!-- 自定义 v-model -->
        <p>{{name}}</p>
        <CustomVModel v-model="name"/>
        //也可以这样写,value对应$emit第二个参数
        <!-- <CustomVModel :text="name" @change1="value=>name=value"/> -->
    </div>
</template>

<script>
import CustomVModel from './CustomVModel'


export default {
    components: {
        CustomVModel
    },
    data() {
        return {
            name: '自定义model',
        }
    }
}
</script>

<template>
    <!-- 例如:vue 颜色选择 -->
    <input type="text"
        :value="text"
        @input="$emit('change1', $event.target.value)"
    >
    <!--
        1. 上面的 input 使用了 :value 而不是 v-model
        2. 上面的 change1 和 model.event1 要对应起来
        3. text1属性对应起来
    -->
</template>

<script>
export default {
    model: {
        prop: 'text', // 对应 props text
        event: 'change1' //对应$emit第一个参数
    },
    props: {
        text: String,
        default() {
            return ''
        }
    }
}
</script>

提示

一个组件上的v-mode1默认会利用名为value的prop和名为input的事件,但是像单选框、复选框等类型的输入控件可能会将value attribute用于不同的目的。model选项可以用来避免这样的冲突

拓展

vue2.3.0+ 新增 .sync修饰符一样可以达到同样的效果

子组件:this.$emit('update:visible', visible), 使用update:my-prop-name 的模式触发事件

上面列子可以改为:

父组件:

<CustomVModel :text="name" @update:text="val=>name=val"></components>	
//简写
<CustomVModel :text.sync="name"/>

子组件:

  <input type="text" :value="text" @input="syncChang" />
      
  methods: {
      syncChang(event) {
        this.$emit("update:text", event.target.value);
      }
    }

官方案例

2、$nextTick

说明

Vue是异步渲染,data改变之,DOM不会立刻渲染,$nextTick 会在DOM渲染之后被触发,以获取最新的DMO节点

<template>
  <div id="app">
    <ul ref="ul1">
        <li v-for="(item, index) in list" :key="index">
            {{item}}
        </li>
    </ul>
    <button @click="addItem">添加一项</button>
  </div>
</template>

<script>
export default {
  name: 'app',
  data() {
      return {
        list: ['a', 'b', 'c']
      }
  },
  methods: {
    addItem() {
        this.list.push(`${Date.now()}`)
        this.list.push(`${Date.now()}`)
        this.list.push(`${Date.now()}`)
        
 		const ulElem = this.$refs.ul1
        console.log( ulElem.childNodes.length )
    }
  }
}
</script>

如上展示,会获取不到正确的数组长度length,这个时候就的用$nextTick

    addItem() {
        this.list.push(`${Date.now()}`)
        this.list.push(`${Date.now()}`)
        this.list.push(`${Date.now()}`)
        this.$nextTick(() => {
          // 获取 DOM 元素
          const ulElem = this.$refs.ul1
          console.log( ulElem.childNodes.length )
        })
    }

说明

  • 异步渲染,$nextTick 待 DOM 渲染完再回调
  • 页面渲染时会将 data 的修改做整合,多次 data 修改只会渲染一次(也就是只会调用一个$nextTick)

3、solt插槽

作用

让父组件可以往子组件中插入一段内容(不一定是字符串,可以是其它的组件,只要是符合Vue标准的组件或者标签都可以)

3.1 默认插槽

<template>
    <div>
        <SlotDemo :url="website.url">
            {{website.title}}
        </SlotDemo>
    </div>
</template>

<script>
import SlotDemo from './CustomVModel'


export default {
    components: {
        SlotDemo
    },
    data() {
        return {
            website: {
                url: 'http://imooc.com/',
                title: 'imooc',
                subTitle: '程序员的梦工厂'
            },
        }
    }
}
</script>



<template>
    <a :href="url">
        <slot>
            默认内容,即父组件没设置内容时,这里显示
        </slot>
    </a>
</template>

<script>
export default {
    props: ['url'],
    data() {
        return {}
    }
}
</script>

3.2 作用域插槽

作用

让父组件可以访问到子组件的数据(算是一种组件通讯的方式)

<template>
    <div>
        <ScopedSlotDemo :url="website.url">
           <template v-slot="slotProps">
                {{slotProps.slotData.title}}
            </template>
        </ScopedSlotDemo>
    </div>
</template>

<script>
import ScopedSlotDemo from './CustomVModel'


export default {
    components: {
        ScopedSlotDemo
    },
    data() {
        return {
            website: {
                url: 'http://imooc.com/',
                title: 'imooc',
                subTitle: '程序员的梦工厂'
            },
        }
    }
}
</script>


<template>
    <a :href="url">
        <slot :slotData="website">
            {{website.subTitle}} <!-- 默认值显示 subTitle ,即父组件不传内容时 -->
        </slot>
    </a>
</template>

<script>
export default {
    props: ['url'],
    data() {
        return {
            website: {
                url: 'http://wangEditor.com/',
                title: 'wangEditor',
                subTitle: '轻量级富文本编辑器'
            }
        }
    }
}
</script>

讲解

子组件内部的slot标签上绑定动态数据 soltData='website' (website是 data中的数据)

在父组件调用子组件ScopedSlotDemo中 插入template ,v-slot='slotProps' (slotProps可以自定义名字)

最后就可以通过slotProps.slotData获取到子组件中的website

3.3 具名插槽

4、动态、异步组件

4.1 动态组件

说明

需要根据数据,动态渲染场景(组件类型不确定),使用

<template>
    <div>
        <div v-for="(val, index) in newsData": key="val.id">
          <component :is="val. compnent"/>
        </div>
    </div>
</template>

<script>
import Text from './Text'
import Image from './Image'
import Video from './Video'
export default {
    components: {
        Text,
        Image,
        Video
    },
    data() {
        return {
              newsData:[
                {
                    id:1,
                    compnent:'Text'
                },
                {
                    id:1,
                    compnent:'Image'
                },
                {
                    id:1,
                    compnent:'Video'
                }
            ]
        }
    }
}
</script>

注意

即使一个组件也不能写成 (会报错)

4.2 异步组件

>作用

只有用到的时候才加载

import Text from './Text'
import Image from './Image'
import Video from './Video'
export default {
    components: {
        Text,
        Image,
        Video
    },
//这些都是同步加载组件,意思就是进入这个页面不管有没有用到都会加载进来

如上图所示,还没有点击按钮显示Form组件 ,就能在文件中搜索到Form组件中的内容

接下来让我们看看异步组件

 components: {
  	FormDemo: () => import('../BaseUse/FormDemo'),     
  },
    //异步引入

如上图 ,一开始加载页面是搜不到Form组件内容的,只有当我没点击了按钮才加载组件文件进来

5、keep-alive

说明

频繁切换,不需要重复渲染的时候就需要使用keep-alive 缓存组件,下次使用直接拿缓存,页面不需要重新加载组件,这是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'"/> 
            <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>

A、b、c只是三个简单的组件

<template>
    <p>state A</p>
</template>

<script>
export default {
    mounted() {
        // eslint-disable-next-line
        console.log('A mounted')
    },
    destroyed() {
        // eslint-disable-next-line
        console.log('A destroyed')
    }
}
</script>

6、mixin

作用

多个组件有相同逻辑,就会抽离出来,使用mixn混入

例如:

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

mixin.js

export default {
    data() {
        return {
            city: '北京'
        }
    },
    methods: {
        showName() {
            console.log(this.name)
        }
    },
    mounted() {
        console.log('mixin mounted', this.name)
    }
}

说明

  • 上面代码运,会执行mixin.js中的钩子函数,然后在执行index.vue`中的钩子函数
  • mixin.js文件中能获取index.vue中的name属性
  • index.vue文件点击能触发mixin.js中的showName事件

注意

  • 同个文件可以混入多个mixin,有多个混入的时候,按混入顺序依次执行mixin中的钩子函数,最后在执行文件的钩子函数
  • 多个混入还会造成属性覆盖

以上内容纯属个人理解,若有不对,请回复纠正

关注【前端知识小册】,第一时间获取前端优质文章!