# Vue 高级特性

# 自定义 v-model

当我们需要在自定义的组件上使用 v-model 属性时,就需要自己去实现父子组件里的 v-model 通信了。用 props 传值,子组件将需要改变的值接收,然后使用 model 添加自定义事件将其绑定在标签上。

// 父组件
<template>
	<p>
        {{name}}
    </p>
	<Son v-model="name" />
</template>
<scirpt>
import Son from './Son'
export default {
    components:{
    	Son
    },
    data(){
        return {
        	name:'asuhe'
        }
    }
}
</scirpt>
// 子组件
<template>
	<input type="text" :value="text" @input="$emit('change',$event.target.value)" >
</template>
<scirpt>
export default {
    model:{
    	prop:'name', // 对应 props的name
    	event:'change'
    }
    props:{
    	name:{
            type:String,
            default:1
        }
    }
}
</scirpt>

# $nextTick

因为 vue 是异步渲染的。在其运作过程中,data 数据改变后不会立马渲染 DOM,而是用 Document.createDocumentFragment 创建文档碎片将多次 data 数据修改后的所有 DOM 操作整合成一次再去插入 DOM 中进行渲染,这也是 vue 性能优秀的原因之一。

在 DOM 渲染之后,vue 提供了一个 $nextTick 函数,它会在 DOM 渲染之后触发,我们可以利用它来获取最新的 DOM 节点。

<template>
	<p>
        {{name}}
    </p>
</template>
<script>
	export default {
        data(){
            return {
                name:'zhangsan'
            }
        },
        mounted(){
			this.$nextTick(()=>{
                console.log('调用了$nextTick');
            })
        }
    }
</script>

# slot

当一个组件会被多次使用,且里面大部分内容不变仅有非常少部分的结构改变时,可以使用 slot 插槽,将其理解为占位符。该种通信方式适用于父组件给子组件传递数据,但它与其它通信不同的是,它可以传递结构给子组件,子组件中的 slot 标签本质上就是一个占位符。若父组件给其传递 template 则使用父组件传递过来的 template ,否则使用默认定义的 template 。插槽又分为默认插槽、具名插槽和作用域插槽。

默认插槽约定成俗只能有一个,具名插槽就是在默认插槽的基础上加上 name 属性唯一标识这个插槽,这样父组件在传递数据的时候可以根据名字精准传递到指定的插槽中。

作用域插槽中子组件的 slot 可以通过 属性传递值给父组件,然后父组件可以根据不同需求改变这个 slot 内部的显示结构,把子组件的值,传给父组件固定的区域进行操作。父组件的数据是给子组件展示的。子组件展示过程当中,数据的结构由父组件决定的。

# 默认插槽

// 子组件放置插槽
<slot></slot>
<slot name="asu"></slot>
// 父组件传递数据
<template>
    <button>点击<button>
</template>
<template slot="asu">
    <a href="http://asuhe.fun"></a>
</template>

作用域插槽

// 父组件
<template slot-scope="{todo,index}">
	<span v-if="todo.isOver" style="color:hotpink"><!--swig2--></span>  // 父组件控制子组件的样式
</template>
// 子组件
<slot :todo="todo" :index="index">
    <!--swig3-->
</slot>

作用域插槽接收子组件数据时的指令:

  • slot 属性弃用,具名插槽通过指令参数 v-slot:插槽名 的形式传入,可以简化为 #插槽名

  • slot-scope 属性弃用,作用域插槽通过 v-slot:xxx="slotProps" 的 slotProps 来获取子组件传出的属性

  • v-slot 属性只能在 <template> 上使用,但在【只有默认插槽时】可以在组件标签上使用

# 注意事项

  1. 默认插槽名为 default ,可以省略 default 直接写 v-slot
    缩写为 #时不能不写参数,写成 #default (这点所有指令都一样,v-bind、v-on)
  2. 多个插槽混用时, v-slot 不能省略 default
  3. 同样可以通过解构获取 v-slot={user} , 还可以重命名 v-slot="{user: newName}" 和定义默认值 v-slot="{user = '默认值'}"
  4. 插槽名可以是动态变化的 v-slot:[slotName]

# 动态组件

所谓动态组件就是在一个页面中,各个组件的组成是不一样的。但是在同一个路由下,里面的二级路由组合可能经常变动。有些组件我们选择展示,有些组件我们选择不展示。这种时候就可以使用动态组件。

// 动态组件 Son
<template>
	<p>asuhe</p>
</template>
// 父组件
<template>
	// 用 Component 标签表示动态组件 is为动态组件的名称
	<Component :is="NextTickName"></Component>
	/* 不能直接让 is 为具体的组件名称,必须为变量
	 错误用法 报错 <Component is="Son"></Component> */
</template>
<script>
import Son from './Son'
export default {
    components:{
        Son
    },
    data(){
        return {
            NextTickName:'Son'
        }
    }
}
</script>

# 异步组件

在某些场景下不需要加载全部的组件,我们只需要加载部分必要组件,当某些功能被使用到了我们就可以再去加载那些组件。这时我们就可以利用异步加载组件的技术,使用 import() 函数来实现这个功能,可以对我们的页面有非常大的性能提升。路由懒加载就是利用这个原理

// 异步的组件
<template>
	<p>AsyncComponent</p>
</template>
// 父组件
<template>
	<Son v-if="show"></Son>
	<button @click="load">点击加载子组件</button>
</template>
<script>
// 同步引入组件 import Son from './Son'
export default {
    components:{
       // 异步加载组件  
        Son:()=> import('./Son')
    },
    data(){
      return {
          show:false
      }  
    },
    methods:{
        load(){
            this.show = true;
        }
    }
}
</script>

# keep-alive

正常情况下当我们切换组件时,组件对象会被销毁。有时我们不希望如此,使用 keep-alive 可以将组件缓存下来。当我们在组件里来回切换的时候组件实例会被保存,不会被销毁所以也不用重复渲染,能够极大提升页面性能。

// 保活组件
<template>
	<p>AsyncComponent</p>
</template>
<template>
	<p></p>
	<keep-alive>
    	
    </keep-alive>
</template>
<script>
import Son from './Son'
export default {
    components:{
        Son
    },
    data(){
        return {
            
        }
    }
}
</script>

# mixin

html、js、css 相同时我们会封装组件。单个组件里 js 代码重复我们会封装函数。当不同的组件 js 代码重复 封装混合时,我们就可以使用 minx 混入技术,重用 js 代码。新建一个 myminxi.js 文件 在 js 文件中暴露一个对象 对象内部可以有 data methods computed... 会将 js 文件中暴露出的数据 方法等混入到组件内部。

# 使用

// 在组件内部引入 import myminxi from './myminxi.js'
// 使用 mixins:[mymixin]  例如:
import {mixin} from './mymixin'
export default {
    name: 'Daughter',
    mixins:[mixin],
    data(){
        money:1000
    }
},
-------
// myminx.js
export const mixin = {
  methods: {
    borrowMoney (count) {
      this.money -= count
    },
    gaveMoney (count) {
      this.money -= count
      // 给父组件增加 count
      this.$parent.money += count
    }
  }
}

# mixin 技术的特点

  • 可以提高代码复用,优化性能
  • 变量来源不明确,不利于阅读
  • 多 mixin 可能会造成命名冲突
  • mixin 的组件可能会出现多对多的关系,复杂度较高