基于vue3的原生js插件(二)

388 阅读5分钟

treemenu

  • 原理:利用vue递归组件,实现无限菜单,三个组件,难点还是在于ui的实现。
  • item

image.png

  • subitem

image.png

  • 具名插槽的语法糖
<template v-slot:title> == <template #title>
  • 不具名插槽可以v-for,具名插槽无法使用v-for
  • template可以做占位符的效果,不会被渲染,www.cnblogs.com/tu-0718/p/1…
  • v-show的位置
<template>
  <div class="wrap" @mouseenter="mouseenter" @mouseleave="mouseleave">
    <slot name='title'></slot>
    <span class="icon">&gt;</span>
    <div class="sub-item" v-show="detailShow">
      <slot></slot>
    </div>
  </div>
</template>
// mouseenter和mouseleave不应该写在icon,应该要写在wrap上面,不然的话一移动sub-item,整个div就会消失
// mouseenter和mouseleave一对,mouseover和mouseout为一对,一般用enter

selector

  • 原理:改造input框,加上一个div下拉框,达到记忆,筛选内容的ui

  • filter的使用

  // 会根据true/false来决定是否返回item
    // 箭头函数有中括号得加return
    // 不会改变原来函数
    // 得到的结果是一个数组
    var arr1 = arr.filter((item)=>item === 2)
             = arr.filter((item)=>{
               return item === 2
             })
  • map
// map必须调用return 原数组的每个item,不然返回值的数组会undefined
 // 返回一个数组,数组包含每个原数组的处理后的值,如果没有返回就为undefined占位
 // 不适合进行过滤,只适合对原数组或符合item的对象进行操作
  
 var arr1 = arr.map((item)=>{
  if(item === 2){
    return item *2
  }
  return item
})
    
var arr1 = arr.map((item)=>item*2)
  • toLowerCase/toUpperCase:注意得大写,而且只对String有用

  • includes

includes() 方法用于判断字符串是否包含指定的子字符串。
如果找到匹配的字符串则返回 true,否则返回 false。
注意: includes() 方法区分大小写。
所以得提前用转化大小写进行区分
  • 点击事件消失
写好逻辑之后发现点击item的可选项并没有触发点击事件
// 通过查询源头发现连最初的点击事件都没有触发
// 原因:div消失太快没有触发。display的消失会导致点击事件的失效
// 通过setTimeout来延缓div的消失达到点击事件的触发

oInput.addEventListener('blur',function(){
  oIcon.className = 'icon iconfont icon-arrowdown'
  // 延缓div的消失
  setTimeout(()=>{
    oMenu.style.display = 'none'
    if(this.value.length === 0){
      oPlaceholder.style.display = 'block'
    } 
  },150)
},false)
  • 记忆功能
触发blur事件,如果没有点击item还是显示最近一次的item
	// 这里的oldValue其实是props传进来的值
	// 函数的value可以是props或者setup里的变量
    <input 
      type="text" 
      class="input" 
      :value="oldValue"
      @input="itemInput"
      @focus="itemInput"
      ref="InputValue"
      @blur="rememberValue(oldValue)"
    >
    
      const rememberValue = (oldValue)=>{
      // _input.value记录的是实时的
      // instance是vue实例,里面其实也有value,但是el里面取得更加直接
      // 这个值其实是props传递过来的!!!
      const _input = instance.refs.InputValue;
      console.log(instance.refs,instance);
      console.dir(instance.refs.InputValue);
      if(_input.value.length>0){
        console.log( _input.value);
        _input.value = oldValue
      }
    }
  • 两种获取el 的方法
1. vue3的api
<div 
  class="ui-modal"
  // 设定ref属性
  ref="uiModal"
>

const instance = getCurrentInstance()
instance.refs实例下面的refs保存着el

2. 
 原理:设定一个响应式,并且通过return把div与这个值联系起来
 可以在onMounted里面进行取值操作
<div 
  class="ui-modal"
  // 设定ref属性
  ref="uiModal"
>

set(){
    // 必须名字一样,而且ref为null
    const uiModal = ref(null)
    onMounted(()=>{
        // 可以拿到值了!
        console.log(uiModal)
    })
    return {
    uiModal
    }
}

carousel

  • 实现方式:所有的图片都通过position,叠放在一个地方,通过v-if的属性来控制显示,再显示中通过transfrom=》translateX与vue3的动画来达到移动的效果。即将消失的图片x为0 - 100%,即将进来的图片为-100% - 0 。如果到一组图片的最后,直接跳到第一张(其实就是v-if显示第一张)。

  • 图片路径的动态引入

再vue的js代码中,引入图片必须用require/在html中也可以使用require
可以实现v-for的大量动态引入
<img :src="require(`./assets/${item.img_name}`)">
  • 定时器的运用与消除
vue3 中定时器得在dom消除之前被清除,即onBeforeUnmount钩子函数中使用
	// 设置定时器
	let t;
	onMounted(()=>{
      if(props.autoPlay){
        t = setInterval(()=>{
          autoplay(state.dir)
        },props.duration)
      }
    })
	// 清除定时器
    onBeforeUnmount(()=>{
      clearInterval(t)
      t = null;
    })
  • 获得slot的长度
需求:一个父组件中,内部有个slot,需要获取这个slot被替代传入的组件的个数
原理:获取实例,实例下面的slots属性是一个函数就执行这个函数,然后是一个数组,再拿到第一项的children就是被替代传入的组件的个数
let instance = getCurrentInstance()
let length = instance.slots.default()[0].children.length,
  • 对于图片的展示
需求:将这个div通过v-if展示出来,这个组件其实就是v-for出来的单个图片的组件  
    <div v-if="currentIndex === selfIndex" class="carousel-item">
      <slot></slot>
    </div>
原理:因为是v-for遍历出来的,其实有一个唯一的值可以作为selfIndex,currentIndex可以通过父组件获得
    const instance = getCurrentInstance()
    const state = reactive({
    // 因为是v-for遍历出来的,可以取得实例的vnode.key就是唯一值,相当于index
      selfIndex: instance.vnode.key,
    // 这个就是取父组件的ctx的属性
      currentIndex: instance.parent.ctx.currentIndex
    })
    
    // 父组件设置属性,刚开始是传入的初始值,后面随着值的变化达到轮播的效果
    const state = reactive({
      currentIndex:props.initial,
    })
  • props的响应式的值
产生原因:单独使用props再视图上是响应式的,但是如果把props赋值给一个值,这个值只会取得最初的props,并不会发生改变
    const state = reactive({
      // currentIndex是非响应式的,和在reactive里面创建时无关的。比较特殊
      currentIndex: instance.parent.ctx.currentIndex
    })
    // 因此需要通过watch来监听父组件的改变来改变state.currentIndex的值
    watch(()=>{
      return instance.parent.ctx.currentIndex
    },(value)=>{
      state.currentIndex = value
    })
// 类似于vue2中,也得用watch去监听props的值的改变,直接在页面上使用是非响应式的。
  • vue3的transition的使用
// 对于v-if/v-show/display就可以再外面包一层transition就可以使用过渡动画
<template>
  <transition>
    <div v-if="currentIndex === selfIndex" class="carousel-item">
      <slot></slot>
    </div>
  </transition>
</template>
// 在样式里面设置过渡效果
	// 一共有6个属性 v-enter-active,v-enter,v-enter-to,v-leave-active,v-leave,v-leave-to
	// active里面设置过渡的样式
	// 属性的顺序也有关系,一定先写.v-enter-active在写.v-enter-to
	// 通过设置位移来实现动画效果
  .v-enter-active,
  .v-leave-active{
    transition: all .2s linear;
  }
  // vue3这里有错误,本来应该是v-enter的,现在只能用v-enter-active
  .v-enter-active{
    transform: translateX(100%);
  }
  .v-enter-to{
    transform: translateX(0);
  }
  // vue3这里有错误,本来应该是v-enter的,现在只能用v-enter-active
  .v-leave-active{
    transform: translateX(0);
  }
  .v-leave-to{
    transform: translateX(-100%);
  }