前言
vue3.0 Rc
(候选发布版本)已经于7月18
上线,离正式版本发布已经不远了,鉴于此,本人就通过@vue/composition-api
这个 Vue Composition API
来事先体验下3.0的新特性,为以后能快速上手新版本做准备。
准备工作
下载与引入
- 下载体验版api
npm i @vue/composition-api
- 引入与使用
1. 在main.js中要引入全部的api
import VueCompositionApi from '@vue/composition-api'
Vue.use(VueCompositionApi)
2. 在页面中按需引入api
import { 需要使用的api } from '@vue/composition-api'
tips: main.js
和局部页面都需要引入,不能只在页面中引入。
开发与使用
reactive和toRefs
reactive
创建响应式数据,toRefs
把普通数据转换成响应式数据
<template>
<div class="home">
<span>{{name}}</span>
<span>{{num}}</span>
<button @click="btn">按钮</button>
<button @click="btn2">按钮2</button>
</div>
</template>
<script>
// reactive 创建响应式数据对象 --类似data
import { reactive, toRefs } from '@vue/composition-api'
export default {
name: 'Home',
setup () {
// state对象
const state = reactive({
name: 'hello world'
})
// modelData
const modelData = reactive({
num: 1
})
const btn = () => modelData.num++
const btn2 = () => {
state.name = '我是不双向绑定的数据,没有toRefs转换不可更改'
return state
}
return {
...state,
...toRefs(modelData), //把数据转换为响应式
btn, // 事件
btn2
}
}
}
</script>
tips:
- 在
setup
中是没有this
的- 数据、方法都写在
setup
里面。- 方法里改变值需
return
这个值- 用了
...
运算符后使用reactive
创建出来的数据都不是响应式数据了,需要使用toRefs
转换为ref()
类型的响应式数据
ref(推荐)
- 和
reactive
一样创建响应式数据,但更推荐使用。
<template>
<div class="RefCom">
<span>{{refCount}}</span>
<button @click="refCount+=1">+1</button>
</div>
</template>
<script>
import { ref, reactive } from '@vue/composition-api'
export default {
name: 'RefCom',
setup (props, { root }) {
const refCount = ref(0) //创建响应式数据
console.log(refCount.value)
const data = reactive({
refCount
})
console.log(data.refCount)
data.refCount++
console.log(data.refCount)
return {
...data,
refCount
}
}
}
</script>
- 模板上的ref--获取dom
//父组件
<template>
<div class="Father">
<h1 ref="h1Ref">父组件</h1>
<som ref="somRef"></som>
</div>
</template>
<script>
import som from './Som'
import { ref, onMounted } from '@vue/composition-api'
export default {
name: 'Father',
components: {
som
},
setup (props, { root }) {
const h1Ref = ref(null) //赋值null
const somRef = ref(null)
onMounted(() => {
console.log(h1Ref.value, 'h1的dom')
console.log(somRef.value, 'som的dom')
})
return {
h1Ref, //要和模板上ref值相同
somRef
}
}
}
</script>
//子组件
<template>
<div class="Som">
<h3>子组件</h3>
</div>
</template>
<script>
export default {
name: 'som',
setup (props, { root }) {}
}
</script>
tips:
ref
括号里的值就是refCount
的值,括号里的值可以是各种类型的值。- 在
setup
要通过xxx.value获取ref
转换的值。- 模板中无需通过xxx.value展示数据,直接{{xxx}}即可,在
return
时已经进行了转换了。ref
包裹创建出来的值是个对象,里面就一个属性value
。- 用
reactive
包裹ref
创建的值不需要通过XXX.value访问- 新的
ref
会覆盖旧的ref
的值- 通过
isRef
可以判断是否是ref
创建出来的。
computed
计算属性:可创建可读可写的计算属性。
<template>
<div class="RefCom">
<span>原值:{{refCount}}</span> |
<span>
计算属性值:{{ onlyReadComputed
}}
</span> |
<button @click="refCount+=1">+1</button>
</div>
</template>
<script>
import { ref, computed } from '@vue/composition-api'
export default {
name: 'RefCom',
setup (props, { root }) {
const refCount = ref(0)
// 只读的计算属性
const onlyReadComputed = computed(() => refCount.value + 1)
// 可读可写的计算属性
const rwComputed = computed({
get: () => refCount.value + 1,
set: value => {
refCount.value = value - 1
}
})
console.log(onlyReadComputed, '只读计算属性的值')
rwComputed.value = 11
console.log(rwComputed, '可读可写计算属性的值')
return {
refCount,
rwComputed,
onlyReadComputed
}
}
}
</script>
watch
监听数据的变化
<template>
<div class="RefCom">
<span>{{refCount}}</span>
<span>{{name}}</span>
<button @click="stopWatch">停止watch</button>
<input v-model="inputValue" />
</div>
</template>
<script>
import { ref, reactive, watch, toRefs } from '@vue/composition-api'
export default {
name: 'watch',
setup (props, { root }) {
const refCount = ref(0)
const inputValue = ref('')
const state = reactive({
name: '张总'
})
/* ---监听单个--- */
// ref
const stop = watch(
refCount,
(newValue, oldValue) => {
console.log(refCount.value)
console.log('新值:' + newValue, '旧的值:' + oldValue)
}
)
const stopWatch = () => {
stop()
}
// reactive
watch(
() => state.name,
(newValue, oldValue) => {
// console.log(refCount.value)
console.log('新值:' + newValue, '旧的值:' + oldValue)
}
)
/* ---监听多个--- */
watch(
[() => refCount, () => state.name],
([newRefCount, newName], [oldRefCount, oldName]) => {
console.log('newRefCount:' + newRefCount.value, 'newName:' + newName)
console.log('oldRefCount:' + oldRefCount.value, 'oldName:' + oldName)
}
)
setTimeout(() => {
refCount.value++
}, 1000)
setTimeout(() => {
state.name = '李总'
}, 3000)
// 异步打印
const asyncPrint = (val) => {
return setTimeout(() => {
console.log(val)
}, 1000)
}
// ref
watch(
inputValue,
(newValue, oldValue, clean) => {
const timeId = asyncPrint(newValue)
// 每当数据变化的时候清除定时器
clean(() => clearTimeout(timeId))
}
)
return {
...toRefs(state),
refCount,
stopWatch,
inputValue
}
}
}
</script>
tips:
ref
和reactive
的值的监听方法不同,reactive
需用方法返回值,() => xxx,ref
可直接使用。- 当监听多个时,不管是
ref
还是reactive
创建的值,都需要用方法返回- 在监听多个值时,用数组来解构新旧值时,新值和旧值分别在不同的数组里,和
vue2.x
不一样。watch
监听返回新值、旧值时还返回了个函数,当前函数在watch被重复执行
和stop
操作时发生,可做些清除操作。常见应用场景有防抖。- 防抖:就是对于频繁触发的事件添加一个延时同时设定一个最小触发间隔,如果触发间隔小于设定的间隔,则清除原来的定时,重新设定新的定时;如果触发间隔大于设定间隔,则保留原来的定时,并设置新的定时;防抖的结果就是频繁的触发转变为触发一次
生命周期
- beforeCreate -> setup()
- created -> setup
- beforeMount -> onBeforeMount
- mounted -> onMounted
- beforeUpdate -> onBeforeUpdate
- updated -> onUpdated
- beforeDestroy -> onBeforeUnmount
- destroyed -> onUnmounted
- errorCaptured -> onErrorCaptured
<template>
<div class="Father">
</div>
</template>
<script>
import { onMounted, onUpdated, onBeforeUnmount } from '@vue/composition-api'
export default {
name: 'Father',
setup (props, { root }) {
onMounted(() => {
console.log('onMounted')
})
onUpdated(() => {
console.log('onUpdated')
})
onBeforeUnmount(() => {
console.log('onBeforeUnmount')
})
}
}
</script>
tips:
- 去除了
beforeCreate
和created
生命周期,直接就在setup
中,setup
执行顺序 是beforeCreate
后,created
前- 其他生命周期就在原本前加上
on
,功能没有什么变化,且定义在setup
函数中- 推荐请求都放在
onMounted
中
依赖注入
- provide
//父组件
<template>
<div class="Father">
<h1>父组件</h1>
<button @click="color='red'">红色</button>
<button @click="color='blue'">蓝色</button>
<button @click="color='yellow'">黄色</button>
<som></som>
</div>
</template>
<script>
import som from './Som'
import { provide, ref } from '@vue/composition-api'
export default {
name: 'Father',
components: {
som
},
setup (props, { root }) {
const color = ref('red') //响应式的值,父组件修改可影响子孙后代
//注入值
provide('color', color)
return {
color
}
}
}
</script>
//子组件
<template>
<div class="Som">
<h3>子组件</h3>
<Grandson />
</div>
</template>
<script>
import Grandson from './Grandson'
export default {
name: 'som',
components: {
Grandson
},
setup (props, { root }) { }
}
</script>
- inject
//孙子组件
<template>
<div class="Grandson">
<h5 :style="{color:color}">孙子组件</h5>
</div>
</template>
<script>
import { inject } from '@vue/composition-api'
export default {
name: 'Grandson',
setup (props, { root }) {
//接收值
const color = inject('color')
return {
color
}
}
}
</script>
路由跳转
<template>
<div class="home">
<button @click="jump">跳转</button>
</div>
</template>
<script>
export default {
name: 'Home',
setup (props, { root }) {
const jump = () => root.$router.push('/about')
return {
jump
}
}
}
</script>
tips:root
指代的就是vue
对象,即this
,且名字是不可更改的。
props
//父
<template>
<div class="about">
<h1>This is an about page</h1>
<div>{{num}}</div>
<button @click="btn">增加</button>
<HelloWorld msg="我是props传进去的值" />
</div>
</template>
//子
<template>
<div class="hello">
<h1>{{ msg }}</h1>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
},
setup (props) {
console.log(props)
}
}
</script>
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。