🌵「硬核Vue」Vue的高级特性你真的会用吗

简介: 🌵「硬核Vue」Vue的高级特性你真的会用吗

写在前面


如今身边学习Vue的人越来越多了,其中也包括一些后端的同学,Vue自发布以来已经成功在前端领域占据一席之地,Vue相比于Angular这种笨重的框架,它的特点是小而清,另外,与React相比,上手容易。官方的定义是一套用于构建用户界面的渐进式框架


笔者理解的渐进式框架也非常简单,就是用你想用或者能用的功能特性,你不想用的部分功能可以先不用。Vue不强求你一次性接受并使用它的全部功能特性。


由此可见,Vue的确是容易上手的一个框架,然而Vue中还有一些高级功能,笔者总结出了Vue的一些高级特性,目的是帮助学习者掌握这些Vue的高级技巧,帮助读者从面试或工作中脱颖而出。


Vue 高级特性:


这里所介绍的高级特性是基于Vue2的,关于Vue3的更新会在后面的文章中作介绍


(1)自定义V-model   (2)$nextTick (3)slot

(4)动态、异步组件   (5)keep-alive (6)mixin


1. 自定义V-model


即在组件上使用v-model命令;使用场景:使用一个颜色选择器组件,或者封装一个自己公司项目的 Input ,要求输入一些项目特定的号码或者文字。此时你就不能用最简单的 Input 了,得自己封装一个 CustomInput 。


CustomVmodel组件代码:


<template>
  <div>
    <input type="text":value="text1"
    @input="$emit('change1',$event.target.value)">
    <!-- 
      1. 上面的input使用了 :value 而不是 v-model
      2. 上面的change1和model.event1要对应起来
      3. text1属性要对应起来
    -->
  </div>
</template>
<script>
export default {
  model:{
    prop:'text1',// 对应props里面的属性
    event:'change1'
  },
  props:{
    text1:String,
    default(){
      return ''
    }
  }
}
</script>
复制代码


接下来使用这个组件:


<template>
  <div>
    <p>高级特性</p>
    <p>{{name}}</p>
    <!--自定义v-model-->
    <CunstomVmodel v-model="name"/>
  </div>
</template>
<script>
import CunstomVmodel from './ComponentVmodel.vue'// 引入组件
export default {
  components:{
    CunstomVmodel// 注册组件
  },
  data(){
    return{
      name:'橙子的前端游乐园'
    }
  }
}
</script>
<style>
body{
  background-color: rgb(176, 138, 226);
}
  p:nth-child(2){
    color: lightgreen;
  }
</style>
复制代码


代码演示:


5bc128ae571d4a42b83cf3b420529c83_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.gif


奈斯!这样我们就成功在自定义的组件上使用v-model指令啦😄。


2. $nextTick


首先,我们要清楚Vue是异步渲染的,React也是。异步渲染的意思是data改变之后,DOM不会立刻渲染。如果我们想在数据改变之后立即获取“正确的DOM节点”,我们需要调用$nextTick,nextTick会在DOM渲染之后被触发,以获取最新的DOM节点。 **通过下面的Demo你将会对\nextTick有更加清楚的认识

**


$nextTick组件代码:


<template>
  <div>
    <ul ref="ul1">
      <li v-for="(item,index) in fruits" :key="index">{{item}}</li>
    </ul>
    <button @click="addItem">add</button>
  </div>
</template>
<script>
export default {
  data(){
    return{
      fruits:['橙子','柚子','菠萝']
    }
  },
  methods:{
    addItem(){
      this.fruits.push(`${Date.now()}`)
      this.fruits.push(`${Date.now()}`)
      this.fruits.push(`${Date.now()}`)
      const ulElem = this.$refs.ul1
      console.log(ulElem.childNodes.length);
    }
  }
}
</script>
复制代码


解释一下这段代码:点击add按钮,获取ul节点,并打印ul的子节点的长度(即li的个数)


b300211ef87c4a698ae1f07fc4e8bd40_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.gif


我们发现第一次添加数据之后,打印节点的长度应该是6,控制台打印的结果却是3,这正是Vue异步渲染的原因,数据改变之后,DOM不会立刻渲染。如果想在数据改变之后立刻获取到“正确的DOM”,我们就需要调用$nextTick


<template>
  <div>
    <ul ref="ul1">
      <li v-for="(item,index) in fruits" :key="index">{{item}}</li>
    </ul>
    <button @click="addItem">add</button>
  </div>
</template>
<script>
export default {
  data(){
    return{
      fruits:['橙子','柚子','菠萝']
    }
  },
  methods:{
    addItem(){
      this.fruits.push(`${Date.now()}`)
      this.fruits.push(`${Date.now()}`)
      this.fruits.push(`${Date.now()}`)
      // 1.异步渲染,$nextTick待DOM渲染完再回调
      // 2.页面渲染时会将data的修改做整合,多次data修改只会渲染一次
    this.$nextTick(()=>{
      const ulElem = this.$refs.ul1
      console.log(ulElem.childNodes.length);
    })
    }
  }
}
</script>
复制代码


代码演示:


09adedc22b134cd581bf89629f284e78_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.gif


奈斯!我们通过$nextTick实现了改变数据后,立即获取DOM节点的功能😋。


3. slot


slot的知识点偏多,主要有基本使用(基础知识点)、作用域插槽和具名插槽。


基本使用


子组件代码:


<template>
  <div>
<a :href="url">
      <slot>
        这里是默认内容
    </slot>
</a>
  </div>
</template>
<script>
export default {
  props:['url'],
  data(){
    return{}
  }
}
</script>
复制代码


子组件中我们通过props获取父组件传递过来的url,将url给a标签,然后在a标签中定义了一个插槽,这个插槽的作用就是存放父组件中写在该子组件里的内容即{{webiste.title}}(百度一下),如果内容为空则显示默认内容。


根组件代码:


<template>
  <div>
    <p>高级特性</p>
    <p>{{name}}</p>
    <SlotDemo :url="webiste.url">
      {{website.title}}
    </SlotDemo>
  </div>
</template>
<script>
import SlotDemo from './Slot.vue'
export default {
  components:{
    SlotDemo
  },
  data(){
    return{
      name:'橙子的前端游乐园',
      website:{
        url:'www.baidu.com',
        title:'百度一下'
      }
    }
  }
}
</script>
<style>
body{
  background-color: rgb(176, 138, 226);
}
  p:nth-child(2){
    color: lightgreen;
  }
</style>
复制代码


fa0a1a953cad4084868bf52feaf56768_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.png


看!这样我们就成功地往插槽中插入了数据🙃。


作用域插槽


作用域插槽存在的主要作用就是子组件把data里的值扔出来让父组件获取到。作用域插槽不是很好理解,我们直接看代码部分吧。


ScopedSlot子组件代码:


<template>
  <div>
    <a href="url">
      <slot :slotData='website'>// 这里的slotData是随意命名的,在父组件接收即可
      </slot>
    </a>
  </div>
</template>
<script>
export default {
  props:['url'],
  data(){
    return{
      website:{
        url:'https://www.jd.com/',
        title:'京东购物'
      }
    }
  }
}
</script>
复制代码


根组件代码:


<template>
  <div>
    <p>高级特性</p>
    <p>{{name}}</p>
    <ScopedSlot :url="website.url">
    <!-- 这里的template是必写的,然后slotprops是随意命名的-->
      <template v-slot="slotprops">
        {{slotprops.slotData.title}}
      </template>
    </ScopedSlot>
  </div>
</template>
<script>
import ScopedSlot from './ScopedSlot.vue'
export default {
  components:{
    ScopedSlot
  },
  data(){
    return{
      name:'橙子的前端游乐园',
      website:{
        url:'www.baidu.com',
        title:'百度一下'
      }
    }
  }
}
</script>
<style>
body{
  background-color: rgb(176, 138, 226);
}
  p:nth-child(2){
    color: lightgreen;
  }
</style>
复制代码


代码演示:

a6ba7354c4564421b8fc9e6ef80be509_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.png


看!我们在跟组件中获取到了子组件的数据并把它插入到子组件的插槽中,这就是作用域插槽存在的意义😝!


具名插槽


存在多个solt给每个slot取一个名字,大家直接看代码吧。


NamedSlot组件代码:


<template>
  <div>
  <h1><slot name='slot1'></slot></h1>
  <h2><slot></slot></h2>
  <h3><slot name='slot2'></slot></h3>
  </div>
</template>
<script>
export default {
}
</script>
复制代码


根组件代码:


<template>
  <div>
    <p>高级特性</p>
    <p>{{name}}</p>
    <NamedSlot>
      <template v-slot:slot1>
        我是h1,将插入到slot1中
      </template>
      <template v-slot:slot2>
        我是h2,将插入到slot2中
      </template>
      <p>我是p,将插入到未命名的slot中</p>
    </NamedSlot>
  </div>
</template>
<script>
import NamedSlot from './NamedSlot.vue'
export default {
  components:{
    NamedSlot
  },
  data(){
    return{}
  }
}
</script>
<style>
body{
  background-color: rgb(176, 138, 226);
}
  p:nth-child(2){
    color: lightgreen;
  }
</style>
复制代码


代码演示:


7a7fd781792344a8810c96a5b84b08c8_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.png


看,我们把数据插入到想插入的插槽中,这也就是具名插槽的作用啦😊。


4.动态、异步组件


:is = "component-name"用法;需要根据数据动态渲染的场景,即组件类型不确定


动态组件


比如说你需要开发一个新闻详情页,你只知道数据,不知道组件渲染的顺序


22c2f3b5d73f4f12a527f2f137093f6e_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.png


我们现在来实现一下这个功能:


根组件代码:


<template>
<div>
    <div v-for="(val,key) in comname" :key='key'>
    <component :is="val.type"></component>
  </div>
</div>
</template>
<script>
import MyText from './Text.vue'
import MyImage from './Image.vue'
export default {
    components:{
    MyText,
    MyImage
  },
  data(){
    return{
      comname:{
        1:{
          type:'MyText'
        },
        2:{
          type:'MyText'
        },
        3:{
          type:'MyImage'
        },
      }
    }
  }
}
</script>
复制代码


子组件代码:


<template>
  <div>
    <p>我是Text组件</p>
  </div>
</template>
<script>
export default {
  data(){
    return{}
  }
}
</script>
复制代码
<template>
  <div>
    <p>我是Image组件</p>
  </div>
</template>
<script>
export default {
}
</script>
复制代码


代码演示:


1cc63b03650f435b8a0cacd352ca5584_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.png


异步组件


异步组件存在的目的就是按需加载,比如一些比较大的组件,如Echart图标,或是代码编辑器这种,一开始就加载会非常影响性能。注册组件时会使用import()函数来引入 根组件代码:


<template>
  <div>
    <p>高级特性</p>
    <p>{{name}}</p>
    <AsyncComp v-if="isShow"/>
    <button @click="showComp">show AsyncComp</button>
  </div>
</template>
<script>
export default {
  components:{
     AsyncComp:()=>import('./AsynComponent.vue')
  },
  data(){
    return{
      name:'橙子的前端游乐园',
      website:{
        url:'www.baidu.com',
        title:'百度一下'
      },
      isShow:false
    }
  },
  methods:{
    showComp(){
      this.isShow=!this.isShow
    }
  }
}
</script>
<style>
body{
  background-color: rgb(176, 138, 226);
}
  p:nth-child(2){
    color: lightgreen;
  }
</style>
复制代码


AsynComponent代码


<template>
  <div>
     <p>输入框:</p>
    <input type="text" v-model.trim="name">
    <!-- <input type="text" v-model.lazy="name">
    <input type="text" v-model.number="age"> -->
    <p>多行文本</p>
    <textarea  cols="30" rows="10" v-model="text"></textarea>
    <p>复选框</p>
    <input id="橙子" type="checkbox" v-model="checked1">
    <label for="橙子">橙子</label>
    <input id="柚子" type="checkbox" v-model="checked2">
    <label for="柚子">柚子</label>
    <input id="菠萝" type="checkbox" v-model="checked3">
    <label for="菠萝">菠萝</label>
    <p>单选框</p>
    <input type="radio" id="male" value="male" v-model="gender">
    <label for="male">男</label>
    <input type="radio" id="female" value="female" v-model="gender">
    <label for="female">女</label>
    <p>下拉列表选择(单选)</p>
    <select name="" id="" v-model="selected">
      <option disabled value="">请选择</option>
      <option value="">A</option>
      <option value="">B</option>
      <option value="">C</option>
      <option value="">D</option>
    </select>
    <p>下拉列表选择(多选)</p>
    <select name="" id="" v-model="selectedList" multiple>
      <option disabled value="">请选择</option>
      <option value="A">A</option>
      <option value="B">B</option>
      <option value="C">C</option>
      <option value="D">D</option>
    </select>
  </div>
</template>
<script>
export default {
    data(){
    return {
      name:'橙子',
      age:18,
      text:'1314520',
      checked1:true,
      checked2:false,
      checked3:true,6
      gender:'male',
      selected:'',
      selectedList:[]
    }
  }
}
</script>
复制代码


代码演示:


1dfd91bf4d254550a87c93825807dfcd_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.gif


我们会发现只有点击按钮,要显示该组件的时候才会引入该组件并渲染,大大提升了性能,这就是异步组件的作用——按需加载👍。


5. keep-alive


缓存组件;适用于频繁切换,不需要重复渲染的场景,例如Tab栏切换。我们通过代码演示看看如何使用。


根组件代码:


<template>
  <div>
    <p>高级特性</p>
    <p>{{name}}</p>
    <KeepAlive/>
  </div>
</template>
<script>
import KeepAlive from './KeepAlive.vue'
export default {
  components:{
    KeepAlive
  },
  data(){
    return{
      name:'橙子的前端游乐园',
    }
  }
}
</script>
<style>
body{
  background-color: rgb(176, 138, 226);
}
  p:nth-child(2){
    color: lightgreen;
  }
</style>
复制代码


KeepAlive组件代码:


<template>
  <div>
    <button @click="changeState('A')">A</button>
    <button @click="changeState('B')">B</button>
    <button @click="changeState('C')">C</button>
      <KeepAliveA v-if="state==='A'"/>
      <KeepAliveB v-if="state==='B'"/>
      <KeepAliveC v-if="state==='C'"/>
  </div>
</template>
<script>
import KeepAliveA from './KeepAliveStateA.vue'
import KeepAliveB from './KeepAliveStateB.vue'
import KeepAliveC from './KeepAliveStateC.vue'
export default {
  components:{
    KeepAliveA,
    KeepAliveB,
    KeepAliveC
  },
  data(){
    return{
      state:'A'
    }
  },
  methods:{
    changeState(state){
      this.state=state
    }
  }
}
</script>
复制代码


KeepAliveA(B,C)代码:


<template>
  <div>
    This is A 组件。
  </div>
</template>
<script>
export default {
  mounted() {
    console.log('A is mounted')
  },
  destroyed(){
    console.log('A is destroyed')
  }
}
</script>
复制代码


代码演示:


721c6c075cdf4b5a8eb35a498ee32d0b_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.gif


我们发现在切换组件的时候,组件都有被销毁,这样特别消耗性能,我们需要将组件缓存起来。


82e7ae0bbe0243da9278754dd7e0ec29_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.png


我们有keepalive将这些组件包裹起来了,再来看效果


18ea700e2a6c452bab2e1748598b9a99_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.gif


看!我们通过keepalive标签成功地将组件缓存起来了,优化了性能,就很奈斯哦😀!


6. Mixin


多个组件有相同的逻辑,抽离出来;Mixin不是完美的方案会有一些方案,在Vue3.0中,提出的Composition API解决了这些问题。我们看代码部分了解一些什么是Mixin吧~ Mixin组件代码:


<template>
  <div>
    <p>我的名字是{{name}}</p>
    <p>{{age}}</p>
    <p @click="showPicture">This is my picture</p>
  </div>
</template>
<script>
  import myMixin from './Mixin'
export default {
  mixins:[myMixin],// 可以添加多个
  data(){
    return{
        name:'橙子'
    }
  },
  mounted(){
    console.log('名字 is mounted');
  }
}
</script>
复制代码


代码演示:


fac8534e8e3542a29cbb9d836e467b34_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.png


我们会发现页面多出了18,控制台多出了几条生疏的代码。这是从哪来的呢?答案就是mixn。 我们定义了Mixin.js的文件


export default {
  data() {
    return {
      age: 18,
    }
  },
  methods: {
    showPicture() {
      console.log("picture is here")
    },
  },
  mounted() {
    console.log("Mixin is mounted")
  },
}
复制代码


mixin就是把这些共同的逻辑抽离出来,然后通过mixns引入使用,避免了重复定义。


Mixn存在的一些问题:


  1. 变量来源不明确,不利于阅读
  2. 多mixin可能会造成命名冲突。
  3. mixin和组件可能组件出现多对多的关系,复杂度较高。


最后


本文总结了Vue中的高级特性,虽然你在工作中不一定能用到,但是正所谓技多不压身,掌握这些技巧也许就有发挥作用的时候呢,我们要清楚各种特性的应用场景,相信通过学习积累,读者一定能玩转Vue啦~

相关文章
|
12天前
|
缓存 JavaScript 前端开发
vue学习第四章
欢迎来到我的博客!我是瑞雨溪,一名热爱JavaScript与Vue的大一学生。本文介绍了Vue中计算属性的基本与复杂使用、setter/getter、与methods的对比及与侦听器的总结。如果你觉得有用,请关注我,将持续更新更多优质内容!🎉🎉🎉
28 1
vue学习第四章
|
12天前
|
JavaScript 前端开发
vue学习第九章(v-model)
欢迎来到我的博客,我是瑞雨溪,一名热爱JavaScript与Vue的大一学生,自学前端2年半,正向全栈进发。此篇介绍v-model在不同表单元素中的应用及修饰符的使用,希望能对你有所帮助。关注我,持续更新中!🎉🎉🎉
26 1
vue学习第九章(v-model)
|
12天前
|
JavaScript 前端开发 开发者
vue学习第十章(组件开发)
欢迎来到瑞雨溪的博客,一名热爱JavaScript与Vue的大一学生。本文深入讲解Vue组件的基本使用、全局与局部组件、父子组件通信及数据传递等内容,适合前端开发者学习参考。持续更新中,期待您的关注!🎉🎉🎉
27 1
vue学习第十章(组件开发)
|
18天前
|
JavaScript 前端开发
如何在 Vue 项目中配置 Tree Shaking?
通过以上针对 Webpack 或 Rollup 的配置方法,就可以在 Vue 项目中有效地启用 Tree Shaking,从而优化项目的打包体积,提高项目的性能和加载速度。在实际配置过程中,需要根据项目的具体情况和需求,对配置进行适当的调整和优化。
|
18天前
|
存储 缓存 JavaScript
在 Vue 中使用 computed 和 watch 时,性能问题探讨
本文探讨了在 Vue.js 中使用 computed 计算属性和 watch 监听器时可能遇到的性能问题,并提供了优化建议,帮助开发者提高应用性能。
|
18天前
|
存储 缓存 JavaScript
如何在大型 Vue 应用中有效地管理计算属性和侦听器
在大型 Vue 应用中,合理管理计算属性和侦听器是优化性能和维护性的关键。本文介绍了如何通过模块化、状态管理和避免冗余计算等方法,有效提升应用的响应性和可维护性。
|
18天前
|
存储 缓存 JavaScript
Vue 中 computed 和 watch 的差异
Vue 中的 `computed` 和 `watch` 都用于处理数据变化,但使用场景不同。`computed` 用于计算属性,依赖于其他数据自动更新;`watch` 用于监听数据变化,执行异步或复杂操作。
|
17天前
|
JavaScript 前端开发 UED
vue学习第二章
欢迎来到我的博客!我是一名自学了2年半前端的大一学生,熟悉JavaScript与Vue,目前正在向全栈方向发展。如果你从我的博客中有所收获,欢迎关注我,我将持续更新更多优质文章。你的支持是我最大的动力!🎉🎉🎉
26 3
|
19天前
|
存储 JavaScript 开发者
Vue 组件间通信的最佳实践
本文总结了 Vue.js 中组件间通信的多种方法,包括 props、事件、Vuex 状态管理等,帮助开发者选择最适合项目需求的通信方式,提高开发效率和代码可维护性。
|
17天前
|
JavaScript 前端开发 开发者
vue学习第一章
欢迎来到我的博客!我是瑞雨溪,一名热爱JavaScript和Vue的大一学生。自学前端2年半,熟悉JavaScript与Vue,正向全栈方向发展。博客内容涵盖Vue基础、列表展示及计数器案例等,希望能对你有所帮助。关注我,持续更新中!🎉🎉🎉
36 2