Vue3.0初体验(Composition API)
项目环境
- @vue-cli:4.5.4
- node: 14.8.0
安装
当前版本的vue-cli
已经支持vue3.0的preview版本,所以我们可以直接通过以下代码进行安装,在选择版本时选中vue3.0(preview)
即可。
shell
vue create [project-name]
setup
To start working with the Compsition API we first need a place where we can actually use it. In a Vue component, we call this place the
setup
.
js
export default {
// (e.g. setup (props, { attrs, slots, emit }))
setup (props, context) {
console.log(props);
console.log(context); // 在setup()中,无法访问到this.
}
}
composition API
reactive()
reactive()
用于创建一个响应式的引用值.
vue
<template>
<!-- vue3.0 内置Fragment -->
<h1>{{ title }}</h1>
<div>
{{ message }}
</div>
</template>
<script>
export default {
setup () {
const state = reactive({
title: "reactive() 用于声明一个响应式的对象",
message: "Hello Reactive.",
});
return state;
}
}
</script>
ref()
ref()
用于声明一个响应式的原始值,它返回一个对象,该对象只有一个value属性
。(e.g. const one = ref(1); // => { value: 1 }
)
我们可以通过isRef()
来判断某个值是否为一个ref
。
vue
<template>
<div class="page-container">
<h2>{{ title }}</h2>
<div class="message">{{ message }}</div>
</div>
</template>
<script lang="ts">
import {
defineComponent,
isRef,
onMounted,
ref,
} from 'vue';
export default defineComponent({
name: 'Ref',
setup () {
const title = ref('ref() 用于声明单一的原始值'),
message = ref('Hello ref.');
let nonRef= 'non-ref';
onMounted(() => {
setTimeout(() => {
message.value = title.value; // 组件挂载完3s后 将message的值变成title的值
console.log('title is ref? ', isRef(title))
console.log('nonRef is ref? ', isRef(nonRef))
}, 3000)
})
return {
title,
message
}
}
});
</script>
computed()
vue3.0提供的computed()
,它可以接受一个回调函数作为参数,也可以接受一个包含了getter、setter的对象作为参数;
vue
<template>
<div class="page-container">
<h2>Computed</h2>
<div class="message">
FirstName: {{ firstName }}
<br />
LastName: {{ lastName }}
<br />
FullName: {{ fullName }}
</div>
<button @click="changeName(fullName === 'Wu Wang' ? 'San Zhang' : 'Wu Wang')">ChangeName</button>
</div>
</template>
<script lang="ts">
import { computed, defineComponent, isRef, ref } from 'vue';
export default defineComponent({
name: 'Computed',
setup () {
let firstName = ref('San'),
lastName = ref('Zhang');
// 当参数为一个函数时,返回一个只读的ref值
const zero = computed(
() => 0
)
console.log(isRef(zero));
// 当参数为一个含有 getter 和 setter 函数的对象时,
// computed 返回一个可读写的ref值
const fullName = computed({
get () {
return firstName.value + ' ' + lastName.value;
},
set (name: string) {
let [first, last] = name.split(' ');
firstName.value = first;
lastName.value = last;
}
});
const changeName = function (name: string) {
fullName.value = name;
}
return {
firstName,
lastName,
fullName,
changeName
}
}
});
</script>
watch()
通过watch()
,我们可以很轻易地监听某个值的变化。
watch.vue
vue
<template>
<div class="page-container">
<h2>Watch</h2>
<p>
<input type="text" v-model="keyword" />
</p>
<div class="message">
{{ message }}
</div>
</div>
<watch-on-cleanup />
</template>
<script lang="ts">
import { defineComponent, ref, watch } from 'vue';
import WatchOnCleanup from '@/components/Main/WatchOnCleanup.vue';
export default defineComponent({
name: 'Watch',
components: {
WatchOnCleanup
},
setup () {
const keyword = ref(''),
message = ref('输入1秒之后将结果显示.'),
loading = ref(false);
const stop = watch(
keyword,
(keyword, prevKeyword) => {
setTimeout(() => {
if (keyword === '') {
message.value = '输入1秒之后将结果显示.'
} else {
message.value = keyword
}
}, 1000)
}
);
return {
keyword,
message
}
}
});
</script>
components/Main/WatchOnCleanup.vue
vue
<template>
<div class="page-container">
<h2>Watch onCleanup</h2>
<p>
<input type="text" v-model="keyword" />
</p>
<div class="message">
{{ message }}
</div>
</div>
</template>
<script lang="ts">
import {
defineComponent, ref, watch
} from 'vue';
export default defineComponent({
name: 'WatchOnCleanup',
setup () {
const keyword = ref(''),
message = ref('onCleanup 实现 防抖功能');
const asyncFunc = (val: string) => {
return setTimeout(() => {
message.value = val;
}, 1000);
}
const stop = watch(
keyword,
(keyword, prev, onCleanup) => {
const t = asyncFunc(keyword);
// onCleanup 如果watch监听被重复执行了,则会先清除上一次未完成的异步任务
onCleanup(() => clearTimeout(t));
}
);
// stop(); // 可以通过执行watch的返回值来结束监听动作
return {
keyword,
message
}
}
})
</script>
LifeCycle
在vue3.0中,提供了与以往版本稍有不同的生命周期钩子
beforeCreate()
->setup()
created()
->setup()
beforeMount()
->onBeforeMount()
mounted()
->onMounted()
beforeUpdate()
->onBeforeUpdate()
updated()
->onUpdated()
beforeDestroy()
->onBeforeUnmount()
destroyed()
->onUnmouted()
errorCaptured()
->onErrorCaptured()
当然,vue3.0是兼容2.x版本的,所以之前的那些生命周期钩子也是可以使用的。
vue
<template>
<div class="page-container">
<h2>{{ title }}</h2>
<ul class="list">
<li
class="item"
v-for="(item, index) of list"
:key="index"
>
<span class="old">{{ item[0] }}</span>
<span class="divider">-></span>
<span class="new">{{ item[1] }}</span>
</li>
</ul>
</div>
</template>
<script lang="ts">
import { app, el } from '@/main';
import {
defineComponent,
reactive,
ref,
onBeforeMount,
onMounted,
onBeforeUpdate,
onUpdated,
onBeforeUnmount,
onUnmounted,
onErrorCaptured,
} from 'vue';
export default defineComponent({
name: 'LifeCycle',
setup (props, context) {
const list = reactive([
['2.x', '3.0'],
['beforeCreate()', 'setup()'],
['created()', 'setup()'],
['beforeMount()', 'onBeforeMount()'],
['mounted()', 'onMounted()'],
['beforeUpdate()', 'onBeforeUpdate()'],
['updated()', 'onUpdated()'],
['beforeDestroy()', 'onBeforeUnmount()'],
['destroyed()', 'onUnmouted()'],
['errorCaptured()', 'onErrorCaptured()']
]);
let title = ref('LifeCycle');
console.log('setup called.');
onBeforeMount(() => {
console.log('onBeforeMount');
});
onMounted(() => {
console.log('onMounted');
setTimeout(() => {
title.value = title.value + ' has changed.';
}, 3000);
});
onBeforeUpdate(() => {
console.log('onBeforeUpdate');
});
onUpdated(() => {
console.log('onUpdated');
setTimeout(() => {
app.unmount(el); // 1s后卸载应用
}, 1000)
});
onBeforeUnmount(() => {
console.log('onBeforeUnmount');
})
onUnmounted(() => {
console.log('onUnmount');
});
return {
list,
title
}
},
beforeCreate () {
console.log('beforeCreate')
},
created () {
console.log('created')
},
beforeMount () {
console.log('beforeMount')
},
mounted () {
console.log('mounted')
},
beforeUpdate () {
console.log('beforeUpdate')
},
updated () {
console.log('updated');
},
beforeDestroy () {
console.log('beforeDestroy')
},
destroyed () {
console.log('destroyed')
},
errorCaptured () {
console.log('errorCaptured')
}
});
</script>
output:
plain-text
setup called.
beforeCreate
created
onBeforeMount
beforeMount
onMounted
mounted
onBeforeUpdate
beforeUpdate
onUpdated
updated
onBeforeUnmount
onUnmount
Provide() And Inject()
强大的provide()
与inject()
,意味着我们可以用来替代某些需要使用vuex
来完成工作的场景。
父组件provide()
提供的值,在其任意的子孙组件中都可以通过inject()
来获取。
父组件:
vue
<template>
<div class="page-container">
<comp-header></comp-header>
<comp-content></comp-content>
</div>
</template>
<script lang="ts">
import {
defineComponent,
provide
} from 'vue';
import CompHeader from '@/components/Main/ProvideAndInject/Header/index.vue';
import CompContent from '@/components/Main/ProvideAndInject/Content/index.vue';
export default defineComponent({
components: {
CompHeader,
CompContent
},
setup () {
provide('globalTitle', 'Provide & Inject');
provide('globalLogo', 'Logo.');
provide('globalContent', 'Vue3.0中,父组件通过provide(key, value)提供的值,在其任意的子孙组件里面都可以通过inject(key)来获取。');
}
});
</script>
components/Main/ProvideAndInject/Header/index.vue
vue
<template>
<h2>{{ title }}</h2>
<logo />
</template>
<script lang="ts">
import {
inject,
ref,
defineComponent
} from 'vue';
import Logo from './Logo.vue';
export default defineComponent({
name: 'CompHeader',
components: {
Logo
},
setup () {
const title = ref(inject('globalTitle'));
return {
title
}
}
});
</script>
components/Main/ProvideAndInject/Header/Logo.vue
vue
<template>
<div class="logo">CompHeader Sub Component<{{ logo }}></div>
</template>
<script lang="ts">
import { defineComponent, inject, ref } from 'vue';
export default defineComponent({
name: 'Logo',
setup () {
const logo = ref(inject('globalLogo'));
return {
logo
}
}
})
</script>
components/Main/ProvideAndInject/Content/index.vue
vue
<template>
<div class="content">
<p>From Views/ProvideAndInject:{{ content }}</p>
</div>
</template>
<script lang="ts">
import { defineComponent, inject, ref } from 'vue';
export default defineComponent({
name: 'CompContent',
setup () {
const content = ref(inject('globalContent'));
return {
content
}
}
})
</script>
renderFunction
vue3.0提供h()
函数来替代以往的render()
函数。
vue
<script lang="ts">
import { h, ref, defineComponent, onMounted } from 'vue';
export default defineComponent({
setup () {
const count = ref(0),
title = ref('RenderFunction');
onMounted(() => {
setInterval(() => {
count.value ++
}, 500)
})
return () => {
return h(
'div',
[
h('h2', title.value),
h('p', 'count: ' + count.value)
]
)
}
}
})
</script>