🌵「硬核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啦~

相关文章
|
2月前
|
JavaScript
Vue中如何实现兄弟组件之间的通信
在Vue中,兄弟组件可通过父组件中转、事件总线、Vuex/Pinia或provide/inject实现通信。小型项目推荐父组件中转或事件总线,大型项目建议使用Pinia等状态管理工具,确保数据流清晰可控,避免内存泄漏。
301 2
|
1月前
|
缓存 JavaScript
vue中的keep-alive问题(2)
vue中的keep-alive问题(2)
280 137
|
5月前
|
人工智能 JavaScript 算法
Vue 中 key 属性的深入解析:改变 key 导致组件销毁与重建
Vue 中 key 属性的深入解析:改变 key 导致组件销毁与重建
777 0
|
5月前
|
JavaScript UED
用组件懒加载优化Vue应用性能
用组件懒加载优化Vue应用性能
|
4月前
|
人工智能 JSON JavaScript
VTJ.PRO 首发 MasterGo 设计智能识别引擎,秒级生成 Vue 代码
VTJ.PRO发布「AI MasterGo设计稿识别引擎」,成为全球首个支持解析MasterGo原生JSON文件并自动生成Vue组件的AI工具。通过双引擎架构,实现设计到代码全流程自动化,效率提升300%,助力企业降本增效,引领“设计即生产”新时代。
403 1
|
4月前
|
JavaScript 安全
在 Vue 中,如何在回调函数中正确使用 this?
在 Vue 中,如何在回调函数中正确使用 this?
259 0
|
5月前
|
JavaScript 前端开发 开发者
Vue 自定义进度条组件封装及使用方法详解
这是一篇关于自定义进度条组件的使用指南和开发文档。文章详细介绍了如何在Vue项目中引入、注册并使用该组件,包括基础与高级示例。组件支持分段配置(如颜色、文本)、动画效果及超出进度提示等功能。同时提供了完整的代码实现,支持全局注册,并提出了优化建议,如主题支持、响应式设计等,帮助开发者更灵活地集成和定制进度条组件。资源链接已提供,适合前端开发者参考学习。
465 17
|
5月前
|
JavaScript 前端开发 UED
Vue 表情包输入组件实现代码及详细开发流程解析
这是一篇关于 Vue 表情包输入组件的使用方法与封装指南的文章。通过安装依赖、全局注册和局部使用,可以快速集成表情包功能到 Vue 项目中。文章还详细介绍了组件的封装实现、高级配置(如自定义表情列表、主题定制、动画效果和懒加载)以及完整集成示例。开发者可根据需求扩展功能,例如 GIF 搜索或自定义表情上传,提升用户体验。资源链接提供进一步学习材料。
273 1
|
5月前
|
存储 JavaScript 前端开发
如何高效实现 vue 文件批量下载及相关操作技巧
在Vue项目中,实现文件批量下载是常见需求。例如文档管理系统或图片库应用中,用户可能需要一次性下载多个文件。本文介绍了三种技术方案:1) 使用`file-saver`和`jszip`插件在前端打包文件为ZIP并下载;2) 借助后端接口完成文件压缩与传输;3) 使用`StreamSaver`解决大文件下载问题。同时,通过在线教育平台的实例详细说明了前后端的具体实现步骤,帮助开发者根据项目需求选择合适方案。
507 0
|
5月前
|
JavaScript 前端开发 UED
Vue 项目中如何自定义实用的进度条组件
本文介绍了如何使用Vue.js创建一个灵活多样的自定义进度条组件。该组件可接受进度段数据数组作为输入,动态渲染进度段,支持动画效果和内容展示。当进度超出总长时,超出部分将以红色填充。文章详细描述了组件的设计目标、实现步骤(包括props定义、宽度计算、模板渲染、动画处理及超出部分的显示),并提供了使用示例。通过此组件,开发者可根据项目需求灵活展示进度情况,优化用户体验。资源地址:[https://pan.quark.cn/s/35324205c62b](https://pan.quark.cn/s/35324205c62b)。
248 0