Upgrade @ vue/cli4

Various issues were encountered while upgrading @vue/ CLI version 4, unfortunately not documented. It can only be recovered from fragments of memory.

npm rm -g @vue/cli
npm i -g @vue/cli
Copy the code

To quote a

NPM cache clean --force NPM I -g@vue /cli / Vue -v - This is a problem with window script permissionsCopy the code

PowerShell is run as the administrator, enter set-ExecutionPolicy remotesunet, re Y, and run vuE-v again

Throttling component in composition-API mode

Beta vue3 document

Composition – API documentation

The basic function

The first step is to implement an interval function to prevent multiple triggers. The example uses the captcha button

// components/throttle
<template>
  <div @click="start">
    <slot></slot>
  </div>
</template>
<script>
import { defineComponent, ref, onUnmounted } from 'vue'
export default defineComponent({
  name: 'throttle', props: {// Seconds: {typeSetup (props, Content) {// timeRef = ref(0) // Timer IDletStart the timer / /function start () {
      if (timeRef.value === 0) {
        timeRef.value = props.seconds
        content.emit('change', timeRef.value)
        startTime()
      } else {
        content.emit('ongoing', timeref.value)}} // Countdownfunction startTime () {
      clearTimeout(timer)
      timer = setTimeout(() => {
        if (timeRef.value <= 0) {
          clearTimeout(timer)
        } else {
          content.emit('change'OnUnmounted (() => {clearTimeout(timer)})return {
      start
    }
  }
})
</script>
Copy the code

Throttling the most basic of a countdown logic complete, the following parent can accept the countdown through on to do the corresponding business processing

// App.vue
<template>
  <div id="app">
    <throttle @ongoing="handleOngoing" @change="handleChange">
      <button>{{time ? time : 'Get captcha'}}</button>
    </throttle>
  </div>
</template>

<script>
import throttle from './components/throttle'
import { defineComponent, ref } from 'vue' 
export default defineComponent({
  name: 'App',
  components: {
    throttle
  },
  setup () {
    const timeRef = ref(0)
    function handleChange (value) {
      timeRef.value = value
    }
    functionHandleOngoing (value) {console.log(' Please continue, etc${value}Seconds `)}return {
      time: timeRef,
      handleOngoing,
      handleChange
    }
  }
})
</script>
Copy the code

Start triggers power diplomacy

Consider the downside: the fact that we are leaving the child component to handle the event alone is not flexible in many cases, such as cases where click is not triggered, or there are pre-conditions to determine whether the event is triggered or not. It would be better to pass start to the parent class. It’s like getting a captcha and knowing what your phone number is. The following changes were made to throttle:

  • Remove click and let the parent maintain the start trigger timing
  • Start accepts a CD callback, CD must return a Boolean,true means pass, proceed.
  • For greater cohesion in using components, add scope slots so that the parent component can use the component’s value directly on the template, without having to listen for change and an extra timeRef to maintain
<template> <div> <! -- slot prop --> <slot :time="timeRef"></slot>
  </div>
</template>
Copy the code
// components/throttle setup (props, content) { ... /** * start * @param {Function}cd* /function start (cd) {
      if (timeRef.value === 0) {
        const b =  cd(a)if (b) {
          timeRef.value = props.seconds
          content.emit('change', timeRef.value)
          startTime()
        }
      } else {
        content.emit('ongoing', timeRef.value) } } ... startTime ... onUnmountedreturn{start, timeRef // need to respond to the value}}Copy the code
// App.vue
<template>
  <div id="app">
    <input type="tel" placeholder="Please enter your mobile phone number." v-model="phoneRef" maxlength="11"> <! Throttle = <throttle ="throttleRef" v-slot="slotProps" @ongoing="handleOngoing">
      <button @click="handleVerify">{{slotProps.time ? slotProps.time : 'Get captcha'}}</button>
    </throttle>
  </div>
</template>

<script>
import throttle from './components/throttle'
import { defineComponent, ref } from 'vue' 
export default defineComponent({
  name: 'App',
  components: {
    throttle
  },
  setup () {
    const phoneRef = ref(' ') // Mobile phone number const throttleRef = ref(null) // Get throttl instance... HandleOngoing // Validatefunction handleVerify() {// start is transferred to other components for more flexibilityif (phoneRef.value.length === 11) {
        throttleRef.value.start(() => getCode())
      } else {
        console.warn('Please enter the correct mobile phone number'}} // Suppose this is an interface to get codefunction getCode () {
      alert('Sent successfully')
      return true
    }
    return {
      phoneRef,
      throttleRef,
      handleOngoing,
      handleVerify
    }
  }
})
</script>
Copy the code

Start asynchronous problem

Getting back to the “interface” of getCode, assuming an asynchronous, if we don’t do an await operation, then the CD returned by start will be a Promise and will return false even if we continue. In addition, in order to prevent the CD function from being triggered by clicking on the interface for many times, a loading value is added to check

// components/throttle // add async and awaitcd
let loading = false
async function start (cd) {
  if(timeRef.value === 0 && ! loading) { loading =true
    const b = await cd(a)if (b) {
      timeRef.value = props.seconds
      content.emit('change', timeRef.value)
      startTime()
    }
    loading = false
  } else {
    content.emit('ongoing', timeRef.value)
  }
}
Copy the code

Caching function

I’m going to write this, and I’m almost done. But encounter: send verification code -> countdown -> refresh -> countdown did not!! . If you don’t need back-end interaction, pure front-end, it doesn’t matter if you don’t have it. But the time of the interface is real, even if it is gone, but the interface will return a reminder that the time is not up. Being obsessive-compulsive, I decided to add a non-essential extra feature: caching, which users can turn on or off.

However, caching presents a further problem. The cached key, which is fine if only one item is cached, will be strung together when multiple items are enabled. So, we need to add another prop for caching ids.

function

  • GetCache: obtains the cache
  • SetCache: Sets the cache
  • RemoveCache: Deletes the cache

props

  • IsCache: indicates whether to enable the cache mode
  • CacheID: indicates the cacheID. The default value of 0
  • CacheObject: localStorage or sessionStorage. The default localStorage
Props: {// Seconds: {type: Number,
      default: 60
    },
    isCache: Boolean,
    cacheObject: {
      type: Object,
      default: () => localStorage
    },
    cacheID: {
      validator: () => true,
      default: 0,
    }
}
Copy the code

The advantage of using watch here is that you don’t have to insert cache logic into the original start and startTime functions

Watch (() => timeref.value,function (value) {
  if (props.isCache) {
    if (value <= 0) {
      removeCache()
    } else {
      setCache(value)
    }
  }
})
const key = 'throttle' + props.cacheID
function getCache () {
  const time = Number(props.cacheObject.getItem(key)) - Date.now()
  return time > 0 ? time / 1000 : 0
}
function setCache (seconds) {
  const time = seconds * 1000 + Date.now()
  props.cacheObject.setItem(key, time.toString())
}
function removeCache() {props. CacheObject. RemoveItem (key)} / / initialization (function () {
  if (props.isCache) {
    timeRef.value = Math.round(getCache())
    startTime()
  }
  removeCache()
})()
Copy the code

Extracting module

One of the benefits of composition-API is the lower cost of refining the process-oriented approach into functional modules. We created a use.js function in throttle/index.vue, and exported the setup time module and cache module from throttle/index.vue to use.js

// components/throttle/ues.vue
import { ref } from 'vue'
export function useTime (seconds, emit) {
  const timeRef = ref(0)
  const timerRef = ref(0)
  let loading = false/** * start * @param {Function}cd
  */
  async function start (cd) {
    if(timeRef.value === 0 && ! loading) { loading =true
      const b = await cd(a)if (b) {
        timeRef.value = seconds
        emit('change', timeRef.value)
        startTime()
      }
      loading = false
    } else {
      emit('ongoing', timeref.value)}} // Countdownfunction startTime () {
    clearTimeout(timerRef.value)
    timerRef.value = setTimeout(() => {
      if (timeRef.value <= 0) {
        clearTimeout(timerRef.value)
      } else {
        emit('change', --timeRef.value)
        startTime()
      }
    }, 1000)
  }
  return {
    timeRef,
    timerRef,
    start,
    startTime
  }
}
export function useCache (cacheID, cacheType) {
  const key = 'throttle' + cacheID
  function getCache () {
    const time = Number(window[cacheType].getItem(key)) - Date.now()
    return time > 0 ? time / 1000 : 0
  }
  function setCache (seconds) {
    const time = seconds * 1000 + Date.now()
    window[cacheType].setItem(key, time.toString())
  }
  function removeCache () {
    window[cacheType].removeItem(key)
  }
  return {
    getCache,
    setCache,
    removeCache
  }
}
Copy the code
// components/throttle/index.vue
import { useTime, useCache } from './use.js'
setup (props, { emit }) {
    const {
      timeRef,
      timerRef,
      start,
      startTime
    } = useTime(props.seconds, emit)
    const { 
      getCache,
      setCache, removeCache} = useCache(props. CacheID, props. CacheType) // Listen on watch(() => timeref.value,function (value) {
      if (props.isCache) {
        if (value <= 0) {
          removeCache()
        } else {
          setOnUnmounted (() => {clearTimeout(timerref.value)}))function init () {
      if (props.isCache) {
        timeRef.value = Math.round(getCache())
        startTime()
      }
      removeCache()
    }
    init()
    return {
      start,
      timeRef
    }
}
Copy the code

Responsive props

By refining functions, the logical structure of the entire component is clearer, and the reuse of certain parts of the content is also stronger. The problem is that you can’t throttle the parent component from 5 seconds to 10 seconds if the parent component’s seconds are responsive. SecondsRef = ref(props. Second) and watch to prop. Second.

// components/throttle/index.vue
const secondsRef = ref(props.seconds)
watch(() => props.seconds, function (value) {
  secondsRef.value = value
})
const {
  timeRef,
  timerRef,
  start,
  startTime
} = useTime(secondsRef, emit)
Copy the code
// components/throttle/ues.vue
export function useTime (secondsRef, emit) {
async function start (cd) {
    if(timeRef.value === 0 && ! loading) { loading =true
      const b = await cd(a)if(b) {timeref. value = secondsref. value // Emit ('change', timeRef.value)
        startTime()
      }
      loading = false
    } else {
      emit('ongoing', timeRef.value)
    }
  }
Copy the code

conclusion

The whole throttle is complete, from a basic countdown to a full-featured throttling, from which you learned the usage and ideas of composition-API.

Here is the warehouse address, which also has some improvements, such as changing event passing to V-Model for VUe3, of course, this is just a kick in the door, if it can help you better. Github.com/hengshanMWC…