一. 安装
1. cdn
<script src="https://unpkg.com/vue@next"></script>
2. npm
脚手架vite
npm init vite hello-vue3 -- --template vue # 或 yarn create vite hello-vue3 --template vue
脚手架vue-cli
npm install -g @vue/cli # 或 yarn global add @vue/cli
vue create hello-vue3
# 选择 vue 3 preset
我选的这个,之前安装过vue-cli,需要先卸载再安装,要不然会报这个:
后面就是一些配置,记得选vue3项目啊!默认是vue2的,我搞了半天发现不对劲,真是一口老血。。。
*默认启动项目命令:npm run serve,不再是dev了
二. 新特性
1. 组合式API
官网上叙述了一堆,我的理解就是把处理某个逻辑的相关代码组合在一起的东西
1) setup组件选项
① 定义
组合式API的入口,一个接收props和context的函数,返回的所有内容都暴露给组件的其余部分
② 执行时间
组件创建之前(beforeCreated),所以setup里不能使用this,也不能获取data property(属性)、computed property或methods。
③ 示例
export default {
props: {
user: {
type: String,
required: true
}
},
setup(props,context){
}
}
④ props参数
它是响应式的,传入新的值时会被更新。不能使用es6解构,会消除响应性。
如需解构可用toRefs(解构全部)或toRef(解构单个,防止可选的prop没有值导致未被创建ref)
示例:
import { toRefs } from 'vue'
setup(props){
const { title } = toRefs(props)
console.log(title.value)
}
import { toRef } from 'vue'
setup(props){
const title = toRef(props,'title')
console.log(title.value)
}
⑤ context参数
一个普通js对象,可以解构。一些有用的值:
export default {
setup(props,context){
//Attribute:非响应式对象,等同于$attrs
console.log(context.attrs)
//插槽:非响应式对象,等同于$slots
console.log(context.slots)
//触发事件:方法,等同于$emit
console.log(context.emit)
//暴露公共property函数
console.log(context.expose)
}
}
⑥ 结合模板使用:如果setup返回一个对象,对象中的属性可以在模板中访问到:
<template>
<div class="setup">
<p>{{ title }}</p>
<p>{{ title1 }}</p>
</div>
</template>
<script>
import { toRef } from 'vue'
export default {
props: {
title: {
type: String,
default: "title",
},
},
setup(props) {
let title = toRef(props, 'title')
let title1 = title.value + 'aaaa'
return {
title1
}
},
};
</script>
<style>
</style>
⑦使用渲染函数&expose(搞得很像react哎
返回一个渲染函数,该函数可以直接使用同一作用于中声明的响应式状态。
不过我没搞懂返回了之后呢?怎么使用这个渲染函数?还是说怎么访问?有无大佬知道的能指点一下?
返回渲染函数之后,之前return的对象不能用了,需要使用expose,没搞懂为什么要定义一个方法再传给它。估计这个也很少用到吧。先放着。
示例:
<template>
<div class="setup">
<p>{{ title }}</p>
<p>{{ title1 }}</p>
</div>
</template>
<script>
import { toRef, h } from 'vue'
export default {
props: {
title: {
type: String,
default: "title",
},
},
setup(props) {
let title = toRef(props, 'title')
let title1 = title.value + 'aaaa'
const increment = () => title.value
expose({
increment
})
return ()=> h('div',title)
},
};
</script>
<style>
</style>
2)带ref的响应式变量
① 定义:接收参数并将其包裹在一个带有value property的对象中返回,任何可以使用该property访问或更改响应式变量的值。
示例:
import { ref } from 'vue'
const counter = ref(0) //counter: { value: 0 }
counter.value++ //counter: { value: 1 }
② 作用:为一个值/变量创建响应式引用,使其在任何地方起作用。虽然看起来有点多此一举,但是官网说是为了保持js中不同数据类型的行为统一。其实就是单独的data吧。。。
ps: reactive也能起到类似效果,但是只能接收对象类型,所以更推荐使用ref
3)setup内的生命周期钩子
① 函数名称:在vue原有的生命周期钩子函数名称前加on,如mounted => onMounted
ps:这些函数只接收一个回调函数名称,传不了参数给回调函数
示例:
setup(props){
const fn = ()=>{
console.log(1)
}
onMounted(fn) //输出1
}
4)setup内的watch
① 侦听数据源:可以是返回值的getter函数,也可以直接是ref。
② 侦听多个数据源:第一个参数改为数组形式。
ps:如果在一个函数里同时改变这两个被侦听的数据源,侦听器只会执行一次。如果想要在这种情况下每次都触发侦听器,可以使用await nextTick(),这样就可以在下一步改变之前运行。
③ 侦听响应式对象
类型为数组时需要一个由值构成的副本,对象时为一个getter函数,深度对象需要deep:true
当然如果同时监听多个对象,并且同一个函数中同时改变的值有不需要deep:true就能监听到的,比如下面的count,那其实可以不加deep:true也能监听到state.user.name,因为他们是一起改变的。
示例:
import { reactive, watch, nextTick, ref } from 'vue'
setup(){
const state = reactive({ count: 0, user: { name: 'abc' }, arr: [0,1,2] })
const count = ref(0)
watch(
[()=>state.count,count,()=>[...state.arr],()=>state],//
(count,prevCount) =>{
console.log(count,prevCount)
},
{
deep: true
}
)
const changeValues = async()=>{
count.value ++
await nextTick()
state.count ++
state.user.name = 'ccc'
state.arr.push(3)
}
changeValues()
}
5)async setup()
组合式API中组件的setup()钩子可以是异步的:
//选项式api写法:
export default {
async setup(){
const res = await fetch('xxx')
const posts = await res.json()
return { posts }
}
}
//组合式api写法:
<script setup>
const res = await fetch('...')
const posts = await res.json()
</script>
2. 依赖注入
1)provide
作用:为后代组件提供数据
export default {
provide: {
message: 'hello!'
}
}
如果提供的数据依赖当前组件,需要使用函数形式:
export default {
data (){
return {
message: 'hello',
info: 'world'
}
}
provide (){
return {
message: this.message,//此种写法无响应性
info: computed(()=>this.message),//这样写有响应性
}
}
}
2)inject
作用:后代组件获取祖先组件的数据
export default {
inject: ['message'],
created(){
console.log(this.message)
},
//可以通过data访问
data(){
myMessage: this.message
}
}
3) 使用别名和默认值:
export default{
inject: {
//获取祖先组件provide的message 命名为myMessage 如果没有获取到 则使用默认值hello
myMessage: {
from: 'message',
default: 'hello'
}
},
created(){
console.log(this.myMessage)
},
}
3. 组合式函数
1)定义:利用组合式API来封装和复用有状态逻辑的函数,命名使用use开头
2)作用:管理随时间变化的状态,比如跟踪当前鼠标在页面中的位置,触摸手势或与数据库的连接状态、异步数据请求等复杂逻辑
3)限制:在<script setup>(组合式写法)或setup()(选项式写法)钩子中同步地调用,某些场景下也可以在像onMounted这样的生命周期钩子中使用。是为了让vue能够确定当前正在被执行的是哪个组件实例,这样才能将生命周期钩子、计算属性和监听器(这俩是便于在组件被卸载时停止监听,避免内存泄漏)注册到该组件实例上。
4)tip:<script setup>是唯一在调用await之后仍可调用组合式函数的地方,编译器会在异步操作之后自动为你恢复当前的组件实例。
示例:
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
const x = ref(0)
const y = ref(0)
function update(event) {
x.value = event.pageX
y.value = event.pageY
}
onMounted(()=> window.addEventListener('mosemove',update))
onUnmounted(()=> window.removeEventListener('mousemove',update))
</script>
<template>
x: {{x}}, y: {{y}}
</template>
改造一下可以让这个函数在多个地方使用,也可以嵌套:
event.js:
import { onMounted, onUnmounted } from 'vue'
export function useEventListener(target,event,callback){
onMounted(()=>target.addEventListener(event,callback))
onUnmounted(()=>target.removeEventListener(event,callback))
}
mouse.js:
import { useEventListener } from './event'
import { ref } from 'vue'
export function useMouse(){
const x = ref(0), y = ref(0)
useEventListener(window,event,()=>{
x.value = event.pageX
y.value = event.pageY
})
return { x, y }
}
index.vue:
<script setup>
import { useMouse } from './useMouse'
const { x, y } = useMouse()
</script>
<template>
x: {{ x }}, y: {{ y }}
</template>
5)异步的带参数的组合式函数:
useFetch.js:
import { ref, isRef, unref, watchEffect } from 'vue'
export function useFetch(url){
const data = ref(null)
const error = ref(null)
async function doFetch(){
data.value = null
error.value = null
//解包可能是ref的值,是则返回value 不是则返回这个值
const urlValue = unref(url)
try {
await timeout()
//fetch是一个异步请求方式
const res = fetch(urlValue)
//.json()是fetch带的方法 把返回的json字符串转化为对象,也被包装成一个promise了
data.vaule = await res.json()
} catch (e){
error.value = e
}
}
//如果是响应式的 值变了就调
if(isRef(url)){
//有任何改动就调取,watch需要指定某个属性
watchEffect(doFetch)
}else{
doFetch()
}
return { data, error }
}
funvtion timeout(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
if(Math.random>0.7){
resolve()
}else{
//返回一个错误对象 new Error 第一个参数为message
reject(new Error('error'))
}
},300)
})
}
index.vue:
<script setup>
import { ref, computed } from 'vue'
import { useFetch } from './useFetch'
const id = ref(1)
const baseUrl = 'http://xxxx.com/'
const url = computed(()=> baseUrl + id.vaule)
const { data, error, retry } = useFetch(url)
</script>
<template>
Load post id:
<button v-for="i in 5" @click="id=i">{{ i }}</button>
<div v-if="error">
<p>error! message:{{ error.message }}</p>
<button @click="retry">retry<button>
</div>
<div v-else-if="data">
data: {{ data }}
</div>
<div v-else>
loading
</div>
</template>
6)在选项式API中使用组合式函数:
import { useMouse } from './mouse'
import { useFetch } from './fecth'
export default {
setup(){
const { x, y } = useMouse()
const { data, error, retry } = useFetch('xxx')
return { x, y, data, error, retry }
},
mounted(){
//setup返回的值可以通过this访问
console.log(this.x)
}
}
...未完待续