Coding With Fun
Home Docker Django Node.js Articles Python pip guide FAQ Policy

An article takes you to the new API in Vue3


May 31, 2021 Article blog


Table of contents


Not long ago, Vue 3.0 was officially released, and this article takes you through Vue 3's new API.

1. Initialize the project

// ① npm i -g @vue/cli
// ② vue create my-project
// ③ npm install @vue/composition-api -S


// ④ main,js
import Vue from 'vue'
import VueCompositionApi from '@vue/composition-api'
Vue.use(VueCompositionApi)

2. Setup method

Setup is the new method of manipulating component properties in vue3.x, which is a unified API that exposes all properties and methods within the component.

2.1 Timing of execution

Setup's execution time is before: after beforeCreate is created

setup(props, ctx) {
    console.log('setup')
  },
  beforeCreate() {
    console.log('beforeCreate')
  },
  created() {
    console.log('created')
  },

2.2 Accept props data





// 通过 setup 函数的第一个形参,接收 props 数据:
setup(props) {
  console.log(props)
},
// 在 props 中定义当前组件允许外界传递过来的参数名称:
props: {
    p1: String
}
/*
{}
p1: "传值给 com-setup"
get p1: ƒ reactiveGetter()
set p1: ƒ reactiveSetter(newVal)
__proto__: Object
*/

2.3 context

The second parameter of the setup function is a context object that contains some useful properties that need to be accessed through this in vue 2.x, and in vue 3.x, they are accessed as follows:

setup(props, ctx) {
    console.log(ctx)
    console.log(this) // undefined
  },
/*
attrs: Object
emit: ƒ ()
listeners: Object
parent: VueComponent
refs: Object
root: Vue
...
*/

Note: This cannot be accessed in the setup() function

3. reactive

The reactive function receives a normal function that returns a responsive data object.

The reactive function is equivalent to the Vue.observable() function in vue 2.x, and the reactive() function is provided in vue 3.x to create responsive data objects, as follows:



  <div>

    
    <p>当前的 count 值为:{{count}}</p>
    <button @click="count += 1">+1</button>
  </div>






import {reactive} from '@vue/composition-api'
export default {
  setup(props, ctx) {
    // 创建响应式数据对象,得到的 state 类似于 vue 2.x 中 data() 返回的响应式对象
    const state = reactive({ count: 0 })
    state.count += 1
    console.log(state)
     // setup 函数中将响应式数据对象 return 出去,供 template 使用
    return state
  }
}

4. ref

The ref() function is used to create a responsive data object based on a given value, and the return value of the ref() function call is an object that contains only one .value property:



  <div>
    <h3>02.ref.vue 文件</h3>
    <p>refCount:{{refCount}}</p>
    <button @click="refCount += 1">+1</button>
  </div>






import { ref } from '@vue/composition-api'
export default {
  setup() {
    // / 创建响应式数据对象 count,初始值为 0
    const refCount = ref(0)
    // 如果要访问 ref() 创建出来的响应式数据对象的值,必须通过 .value 属性才可以,只有在setup内部才需要 .value 属性
    console.log(refCount.value) // 输出 0
    // 让 refCount 的值 +1
        refCount.value++
    // 再次打印 refCount 的值
        console.log(refCount.value) // 输出 1
    return {
      refCount
    }
  }
}

4.1 Access the responsive data created by ref in the reactive object

When a responsive data object created by ref() is mounted on reactive(), the responsive data object is automatically expanded to the original value and can be accessed directly without the need for .value, for example:

setup() {
  const refCount = ref(0)
  const state = reactive({refCount})
  console.log(state.refCount) // 输出 0
  state.refCount++ // 此处不需要通过 .value 就能直接访问原始值
  console.log(refCount) // 输出 1
  return {
    refCount
  }
}

Note: The new ref overrides the old ref, and the sample code is as follows:

setup() {
  // 创建 ref 并挂载到 reactive 中
  const c1 = ref(0);
  const state = reactive({ c1 });


  // 再次创建 ref,命名为 c2
  const c2 = ref(9);
  // 将 旧 ref c1 替换为 新 ref c2
  state.c1 = c2;
  state.c1++;


  console.log(state.c1); // 输出 10
  console.log(c2.value); // 输出 10
  console.log(c1.value); // 输出 0
}

5. isRef

isRef() to determine whether a value is an object created by ref(; scenario: When you need to expand a value that might be created for ref(), for example:

import { ref, reactive, isRef } from "@vue/composition-api";
export default {
  setup() {
    const unwrapped = isRef(foo) ? foo.value : foo
  }
};

6. toRefs

The toRefs() function converts a responsive object created by reactive() into a normal object, except that each property node on the object is responsive data of the ref() type.



  <div>
    <h3>03.toRefs.vue文件</h3>
    <p>{{ count }} - {{ name }}</p>
    <button @click="count += 1">+1</button>
    <button @click="add">+1</button>
  </div>






import { reactive, toRefs } from "@vue/composition-api";
export default {
  setup() {
    // 响应式数据
    const state = reactive({ count: 0, name: "zs" });
    // 方法
    const add = () => {
      state.count += 1;
    };
    return {
      // 非响应式数据
      // ...state,
      // 响应式数据
      ...toRefs(state),
      add
    };
  }
};

7. Computed calculation properties

7.1 Read-only calculated properties



  <div>
    <h3>04.computed.vue文件</h3>
    <p>refCount: {{refCount}}</p>
    <p>计算属性的值computedCount : {{computedCount}}</p>
    <button @click="refCount++">refCount + 1</button>

        
    <button @click="computedCount++">计算属性的值computedCount + 1</button>
  </div>






import { computed, ref } from '@vue/composition-api'
export default {
  setup() {
    const refCount = ref(1)
    // 只读
    let computedCount = computed(() => refCount.value + 1)
    console.log(computedCount)
    return {
      refCount,
      computedCount
    }
  }
};

7.2 Readable writeable calculated properties



  <div>
    <h3>04.computed.vue文件</h3>
    <p>refCount: {{refCount}}</p>
    <p>计算属性的值computedCount : {{computedCount}}</p>
    <button @click="refCount++">refCount + 1</button>
  </div>






import { computed, ref } from '@vue/composition-api'
export default {
  setup() {
    const refCount = ref(1)
    // 可读可写
    let computedCount = computed({
      // 取值函数
      get: () => refCount.value + 1,
      // 赋值函数
      set: val => {
        refCount.value = refCount.value -5
      }
    })
    console.log(computedCount.value)
    // 为计算属性赋值的操作,会触发 set 函数
    computedCount.value = 10
    console.log(computedCount.value)
    // 触发 set 函数后,count 的值会被更新
    console.log(refCount.value)
    return {
      refCount,
      computedCount
    }
  }
};

8. watch

The watch() function is used to monitor changes in certain data items, triggering certain actions that need to be imported on demand before use:

import { watch } from '@vue/composition-api'

8.1 Basic usage



  <div>
    <h3>05.watch.vue文件</h3>
    <p>refCount: {{refCount}}</p>
  </div>






import { watch, ref } from '@vue/composition-api'
export default {
  setup() {
    const refCount = ref(100)
    // 定义 watch,只要 count 值变化,就会触发 watch 回调
    // 组件在第一次创建的时候执行一次 watch
    watch(() => console.log(refCount.value), { lazy: false})
    setInterval(() => {
      refCount.value += 2
    }, 5000)
    return {
      refCount
    }
  }
};

8.2 Monitor the data source

Monitor data sources of the reactive type:



  <div>
    <h3>05.watch.vue文件</h3>
    <p>count: {{count}}</p> // 不是响应式数据
  </div>






import { watch, ref, reactive } from '@vue/composition-api'
export default {
  setup() {
    const state = reactive({count: 100})
    watch(
      // 监听count
      () => state.count,
      // 如果变换 执行以下函数
      (newVal, oldVala) => {
        console.log(newVal, oldVala)
      },
      { lazy: true }
    )
    setInterval(() => {
      state.count += 2
    }, 5000)
    return state
  }
};

Monitor ref-type data sources:

export default {
  setup() {
    // 定义数据源
    let count = ref(0);
    // 指定要监视的数据源
    watch(count, (count, prevCount) => {
      console.log(count, prevCount)
    })
    setInterval(() => {
      count.value += 2
    }, 2000)
    console.log(count.value)
    return {
      count
    }
  }
};

8.3 Listen to multiple data sources

Monitor data sources of the reactive type:

export default {
  setup() {
    const state = reactive({count: 100, name: 'houfei'})
    watch(
      // 监听count name
      [() => state.count, () => state.name],
      // 如果变换 执行以下函数
      ([newCount, newName], [oldCount, oldName]) => {
        console.log(newCount, oldCount)
        console.log(newName, oldName)
      },
      { lazy: true} // 在 watch 被创建的时候,不执行回调函数中的代码
    )
    setTimeout(() => {
      state.count += 2
      state.name = 'qweqweewq'
    }, 3000)
    return state
  }
};

Monitor ref-type data sources:

export default {
  setup() {
    // 定义数据源
    const count = ref(10)
    const name = ref('zs')
    // 指定要监视的数据源
    watch(
      [count, name],
      ([newCount, newName], [oldCount, oldName]) => {
        console.log(newCount, oldCount)
        console.log(newName, oldName)
      },
      { lazy: true}
    )
    setInterval(() => {
      count.value += 2
    }, 2000)
    console.log(count.value)
    return {
      count
    }
  }
};

8.4 Clear monitoring

Watch monitoring created within the setup() function automatically stops when the current component is destroyed. If you want to explicitly stop a monitoring, you can call the return value of the watch() function, as follows:



// 创建监视,并得到 停止函数
const stop = watch(() => {
  /* ... */
})


// 调用停止函数,清除对应的监视
stop()


  <div>

    
    <p>count: {{ count }}</p>
    <button @click="stopWatch">停止监听</button>
  </div>






import { watch, ref, reactive } from "@vue/composition-api";
export default {
  setup() {
    // 定义数据源
    const count = ref(10)
    const name = ref('zs')
    // 指定要监视的数据源
    const stop = watch(
      [count, name],
      ([newCount, newName], [oldCount, oldName]) => {
        console.log(newCount, oldCount)
        console.log(newName, oldName)
      },
      { lazy: true}
    )
    setInterval(() => {
      count.value += 2
      name.value = 'houyue'
    }, 2000)
    // 停止监视
    const stopWatch = () => {
      console.log("停止监视,但是数据还在变化")
      stop()
    }
    console.log(count.value)
    return {
      stop,
      count,
      stopWatch
    }
  }
};



8.5 Clear invalid asynchronous tasks in watch

Sometimes, when the values monitored by the watch change, or after the watch itself is stopped, we expect to be able to clear those invalid asynchronous tasks, at which point a cleanup registrator function is provided in the watch callback function to perform the cleanup work. This clear function is called as follows:

The watch was repeated

Watch is forced to stop

The code examples in Template are as follows:



  <div>

    
    <input type="text" v-model="keywords" />
    <p>keywords:--- {{ keywords }}</p>
  </div>

The code examples in Script are as follows:



import { watch, ref, reactive } from "@vue/composition-api";


export default {
  setup() {
    // 定义响应式数据 keywords
    const keywords = ref("");


    // 异步任务:打印用户输入的关键词
    const asyncPrint = val => {
      // 延时 1 秒后打印
      return setTimeout(() => {
        console.log(val);
      }, 1000);
    };


    // 定义 watch 监听
    watch(
      keywords,
      (keywords, prevKeywords, onCleanup) => {
        // 执行异步任务,并得到关闭异步任务的 timerId
        const timerId = asyncPrint(keywords);
        // 如果 watch 监听被重复执行了,则会先清除上次未完成的异步任务
        onCleanup(() => clearTimeout(timerId));
      },
      // watch 刚被创建的时候不执行
      { lazy: true }
    );


    // 把 template 中需要的数据 return 出去
    return {
      keywords
    };
  }
};

9. Provide and inject component transfer values

Provide() and inject() enable data transfer between nested components. T hese two functions can only be used in setup() functions. The parent component uses the provide() function to pass data down;

9.1 Share normal data

App.vue Root Component:



  <div id="app">
    <h1>父组件</h1>
    <button @click="color = 'blue'">蓝色</button>
    <button @click="color = 'red'">红色</button>
    <button @click="color = 'yellow'">黄色</button>

    

    
  </div>






import { ref, provide } from '@vue/composition-api'
import Son from './components/06.son.vue'


export default {
  name: 'app',
  components: {
    'son': Son
  },
  setup() {
    const color = ref('green')
    provide('themecolor', color)
    return {
     color
    }
  }
}

06.son.vue son component:



  <div>
    <h3 :style="{color: color}">son 组件</h3>

    
  </div>






import { inject } from '@vue/composition-api'
import Grandson from './07.grandson.vue'
export default {
    components: {
    'grandson': Grandson
  },
  setup() {
    const color = inject('themecolor')
    return {
     color
    }
  }
}

07.grandson.vue son component:



  <div>
    <h5 :style="{color: color}">grandson 组件</h5>
  </div>






import { inject } from '@vue/composition-api'
export default {
  setup() {
    const color = inject('themecolor')
    return {
      color
    }
  }
}

9.2 Share ref responsive data

App.vue Root Component:



  <div id="app">
    <h1>父组件</h1>

    
  </div>






import { provide } from '@vue/composition-api'
import Son from './components/06.son.vue'


export default {
  name: 'app',
  components: {
    'son': Son
  },
  setup() {
    provide('themecolor', 'red')
  }
}

06.son.vue son component:



  <div>
    <h3 :style="{color: color}">son 组件</h3>

    
  </div>






import { inject } from '@vue/composition-api'
import Grandson from './07.grandson.vue'
export default {
    components: {
    'grandson': Grandson
  },
  setup() {
    const color = inject('themecolor')
    return {
      color
    }
  }
}

07.grandson.vue son component:

template>
  <div>
    <h5 :style="{color: color}">grandson 组件</h5>
  </div>






import { inject } from '@vue/composition-api'
export default {
  setup() {
    const color = inject('themecolor')
    return {
      color
    }
  }
}

10. The reference to the node is template ref

10.1 Dom reference



  <div>
    <h3 ref="h3Ref">TemplateRefOne</h3>
  </div>






import { ref, onMounted } from '@vue/composition-api'


export default {
  setup() {
    // 创建一个 DOM 引用
    const h3Ref = ref(null)


    // 在 DOM 首次加载完毕之后,才能获取到元素的引用
    onMounted(() => {
      // 为 dom 元素设置字体颜色
      // h3Ref.value 是原生DOM对象
      h3Ref.value.style.color = 'red'
    })


    // 把创建的引用 return 出去
    return {
      h3Ref
    }
  }
}

10.2 References to components

App parent component:



  <div id="app">
    <h1>父组件</h1>
    <button @click="showComRef">展示子组件的值</button>

    
  </div>








import Son from './components/06.son.vue'


export default {
  name: 'app',
  components: {
    'son': Son
  },
  setup() {
    const comRef = ref(null) 
    const showComRef = () => {
      console.log(comRef)
      console.log('str1的值是' + comRef.value.str1)
      comRef.value.setStr1()
    }
    return {
      comRef,
      showComRef
    }
  }
}

06.son.vue subcompetity:



  <div>
    <h3 :style="{color: color}">son 组件</h3>
    <p>{{str1}}</p>
  </div>






import { ref } from '@vue/composition-api'
export default {
  setup() {
    const str1 = ref('这是一个子组件!!')
    const setStr1 = () => {
      str1.value = '被赋值了'
    }
    return {
      str1,
      setStr1
    }
  }
}

11 nextTick



  <div>
    <h3>09.nextTick 组件</h3>
    <p>学习 $nextTick</p>
    <button v-if="isShowInput === false" @click="showInput">展示文本框</button>
    <input type="text" v-else ref="ipt">
  </div>






export default {
  data() {
    return {
      isShowInput: false
    }
  },
  methods: {
    showInput() {
      this.isShowInput = !this.isShowInput
      // console.log(this.$refs)
      this.$nextTick(() => {
        this.$refs.ipt.focus()
      })
    }
  }
}

That's what W3Cschool编程狮 has to say about the new API in Vue3, and I hope it will help you.