官网地址: https://v3.cn.vuejs.org/guide/migration/introduction.html
<div id='app'>
<div>helloWorld</div>
<div>{{msg}}</div>
</div>
<!--编译后-->
<script>
const _hoisted_1 = { id: "app" }
const _hoisted_2 = /*#__PURE__*/_createElementVNode("div", null, "helloWorld", -1 /* HOISTED */)
export function render(...) {
return (
_openBlock(),
_createBlock('div', _hoisted_1, [
_hoisted_2,
_createElementVNode('div',null,_toDisplayString(_ctx.msg),1 /* TEXT */),
])
)
}
</script>
<div @click="handleClick">点击</div>
<!--编译后-->
<script>
export function render(...) {
return (_openBlock()._createElementVNode('div',{onClick: _ctx.todo}, '点击'))
)
}
</script>
<script>
export function render(...) {
return (_openBlock()._createElementVNode('div',{
onClick: _cache[0] || (_cache[0] = (...args) => (_ctx.todo(...args)))
},'点击'))
}
</script>
Option API
:vue2创建组件时,会把数据放到data,计算属性放到computed,事件处理放到methods,监听改变放到watch;共同处理页面逻辑
Composition API
:将零散的data,methods代码重新组合,一个功能的代码放一块儿,并且可以单出拆分出函数
<template>
<div>{{msg}}</div>
</template>
<script>
export default {
setup () {
const msg = 'hello World'
return {
msg
}
}
}
</script>
注意:
<suspense>
包裹组件setup(props, context)
|| setup(props, {attrs, slots, emit})
不能解构props,否则会失去响应式 <template>
<div>{{num}}</div>
<button @click="addNum">添加</button>
</template>
<script>
import { ref } from 'vue'
export default {
setup () {
const num = ref(1)
function addNum () {
num.value = num.value + 1
}
return {
num,
addNum
}
}
}
</script>
注意:
.value
(内部通过给value属性添加getter、setter实现对数据的劫持),在模版中不需要(解析模板时会自动添加.value)<template>
<div>{{ obj.name }}</div>
<div>{{ obj.age }}</div>
<button @click="updateObj">修改</button>
</template>
<script>
import { reactive } from 'vue'
export default {
setup () {
const obj = reactive({ name: "张三", age: 25 });
const updateObj = () => {
obj.name = "王五";
obj.age = 21;
};
return {
obj,
updateObj,
};
}
}
</script>
toRef
或toRefs
将解决这个问题toRef
跟toRefs
都是复制reactive里的属性然后转成ref。因为是浅拷贝,所以修改复制出来的值时原来reactive里的数据也会跟着更新toRef
是复制reactive里的单个key转成ref,toRefs
复制reactive里的所有key转成ref<template>
<div>{{ obj.name }}</div>
<div>{{ obj.age }}</div>
<div>{{ name }}</div>
<div>{{ age }}</div>
<button @click="updateObj">修改</button>
</template>
<script>
import { reactive, toRefs, toRef } from "vue";
export default {
props: {
msg: String,
},
setup (props) {
const obj = reactive({ name: "张三", age: 25 });
// const name = toRef(obj, "name");
// const { msg } = toRefs(props)
const { name, age } = toRefs(obj);
const updateObj = () => {
obj.name = "王五";
obj.age = 21;
};
return {
obj,
name,
age,
updateObj,
};
}
}
</script>
ref
和 reactive
是把数据变为响应式,无论层级多深,始终都会对数据进行深度监听。shallowRef
和shallowReactive
可以解决这个shallowRef
只处理了.value的响应式(只监听.value的变化)。因此如果需要修改应该xxx.value={}
或者使用 shallowRef
将数据强制更新到页面shallowReactive
的数据使用shallowRef
强制更新无效<template>
<div>{{ obj.name }}</div>
<div>{{ obj.age }}</div>
<div>{{ obj.c.d }}</div>
<button @click="updateObj">修改</button>
</template>
<script>
import { shallowRef, triggerRef } from "vue";
export default {
setup() {
const obj = shallowRef({ name: "张三", age: 25, c: { d: 2 } });
const updateObj = () => {
// 这些都不会修改成功,obj对象会变,不会更新到模版
// obj.value.name = "王五";
// obj.value.age = 21;
// obj.value.c.d = 4;
// 调用此方法,上方数据会更新到页面
// triggerRef(obj);
// 如要修改,要用此方法
obj.value={ name: "王五", age: 21, c: { d: 4 } }
};
return {
obj,
updateObj,
};
},
};
</script>
shallowReactive
只处理对象最外层属性的响应式(浅响应式),默认情况下只能监听第一次的数据。如要修改则需要先改第一层的数据,然后再去更改其他层的数据<template>
<div>{{ obj.name }}</div>
<div>{{ obj.age }}</div>
<div>{{ obj.c.d }}</div>
<button @click="updateObj">修改</button>
</template>
<script>
import { shallowReactive } from "vue";
export default {
setup() {
const obj = shallowReactive({ name: "张三", age: 25, c: { d: 2 } });
const updateObj = () => {
// 这样不会监听到修改,obj会变,不会更新到模版
// obj.c.d = 4;
// 如要修改需要用下面方法,先改第一层
obj.name = "王五";
obj.age = 21;
obj.c.d = 4;
};
return {
obj,
updateObj,
};
},
};
</script>
readonly
是让一个响应式数据只读,深层只读<template>
<div>{{ obj.name }}</div>
<div>{{ obj.age }}</div>
<div>{{ obj.c.d }}</div>
<button @click="updateObj">修改</button>
</template>
<script>
import { reactive, readonly } from "vue";
export default {
setup() {
let obj = reactive({ name: "张三", age: 25, c: { d: 2 } });
obj = readonly(obj);
//这里不会修改成功(还是proxy对象,但obj不会修改成功)
const updateObj = () => {
obj.name = "王五";
obj.age = 21;
obj.c.d = 4;
console.log(obj)// 这里obj还等于之前定义的值
};
return {
obj,
updateObj,
};
},
};
</script>
shallowReadonly
是让一个响应式数据只读,浅层只读(响应式对象的最外层只读,但再深一层的属性可以被修改)<template>
<div>{{ obj.name }}</div>
<div>{{ obj.age }}</div>
<div>{{ obj.c.d }}</div>
<button @click="updateObj">修改</button>
</template>
<script>
import { shallowReadonly, reactive } from "vue";
export default {
setup() {
let obj = reactive({ name: "张三", age: 25, c: { d: 2 } });
obj = shallowReadonly(obj);
const updateObj = () => {
//这个不会修改
obj.name = "王五";
//这个不会修改
obj.age = 21;
//这个会被修改
obj.c.d = 4;
};
return {
obj,
updateObj,
};
},
};
</script>
toRaw
将一个响应式对象(由 reactive定义的响应式)转换为普通对象<template>
<div>{{ obj.name }}</div>
<div>{{ obj.age }}</div>
<div>{{ obj.c.d }}</div>
<button @click="updateObj">修改</button>
</template>
<script>
import { toRaw, reactive } from "vue";
export default {
setup() {
let obj = reactive({ name: "张三", age: 25, c: { d: 2 } });
obj = toRaw(obj);// 使用此方法,obj则变为普通对象(非proxy)
const updateObj = () => {
obj.name = "王五";
obj.age = 21;
obj.c.d = 4;
};
return {
obj,
updateObj,
};
},
};
</script>
markRaw
标记一个对象,使其不能成为一个响应式对象<template>
<div>{{ obj.name }}</div>
<div>{{ obj.age }}</div>
<div>{{ obj.c.d }}</div>
<button @click="updateObj">修改</button>
</template>
<script>
import { markRaw, reactive } from "vue";
export default {
setup() {
let data = { name: "张三", age: 25, c: { d: 2 } };
// 在此处标记后,之后使用reactive则不生效(不会成为一个proxy对象),markRaw放到reactive之后则无效
data = markRaw(data);
let obj = reactive(data);
const updateObj = () => {
obj.name = "王五";
obj.age = 21;
obj.c.d = 4;
};
return {
obj,
updateObj,
};
},
};
</script>
isRef
检查值是否为一个 ref 对象。 unref
为val = isRef(val) ? val.value : val
的语法糖const obj = ref({ name: "张三", age: 25, c: { d: 2 } });
console.log(isRef(obj))
console.log(unref(obj))
isReactive
检查值是否为一个 reactive 对象const obj = reactive({ name: "张三", age: 25, c: { d: 2 } });
console.log(isReactive(obj))
isProxy
检查对象是否是由 reactive 或 readonly 创建的 proxyconst obj = reactive({ name: "张三", age: 25, c: { d: 2 } });
console.log(isProxy(obj))
const obj = readonly({ name: "张三", age: 25, c: { d: 2 } });
console.log(isProxy(obj))
isReadonly
检查对象是否是由 readonly 创建的只读代理const obj = readonly({ name: "张三", age: 25, c: { d: 2 } });
console.log(isReadonly(obj))
track
(通知vue需要追踪后续内容的变化)trigger
(通知vue重新解析模版)<template>
<input type="text" v-model="inpValue">
<div>{{inpValue}}</div>
</template>
<script>
import {customRef} from "vue";
export default {
setup() {
// 自定义一个 myRef
function myRef(value) {
return customRef((track, trigger) => {
return {
get() {
track() // 追踪后续数据的变化
return value
},
set(newValue) {
value = newValue
trigger() // 重新解析模板
}
}
})
}
let inpValue = myRef('hello')
return {
inpValue,
}
}
}
</script>
computed
和watch
用法基本相同watchEffect
是监视所有回调中使用的数据,因为每次初始化的时候都会执行一次回掉自动获取依赖,并不用手动传需要监听的对象。注:无法获取到oldValue<template>
<div>{{ obj.name }}</div>
<div>{{ obj.age }}</div>
<input type="text" v-model="userNews"/>
<input type="text" v-model="userNews2"/>
<input type="text" v-model="userNews3"/>
<input type="text" v-model="userNews4"/>
<button @click="updateObj">修改</button>
</template>
<script>
import { ref, reactive, computed, watch, watchEffect } from "vue";
export default {
props: {
msg: String,
},
setup (props) {
const obj = reactive({ name: "张三", age: 25 });
const updateObj = () => {
obj.name = "王五";
obj.age = 21;
};
// computed只有get
const userNews = computed(() => {
return obj.name + "," + obj.age;
})
// computed同时有get和set
const userNews2 = computed({
get() {
return obj.name + "," + obj.age;
},
set(value) {
const nameAge = value.split(",");
obj.name = nameAge[0];
obj.age = nameAge[1];
},
});
// watch
const userNews3 = ref("");
watch(
obj,
(newValue, oldValue) => {
userNews3.value = newValue.name + "," + newValue.age;
},
{
immediate: true,
deep: true,
}
);
// watchEffect
const userNews4 = ref("");
watchEffect(() => {
userNews4.value = obj.name + "," + obj.age;
});
return {
obj,
userNews,
userNews2,
userNews3,
userNews4,
updateObj,
};
}
}
</script>
注意:
当使用 watch 选项侦听数组时,只有在数组被替换时才会触发回调。换句话说,在数组被改变时侦听回调将不再被触发。要想在数组被改变时触发侦听回调,必须指定 deep 选项。
onBeforeMount
-> onMounted
-> onBeforeUpdate
-> onUpdated
-> onBeforeUnmount
-> onUnmounted
-> onErrorCaptured
跟options api混用时onBeforeMount在beforeMount之前,onMounted在mounted之前。。。之后都是父beforeCreate
-> 父created
-> 父beforeMount
-> 子beforeCreate
-> 子created
-> 子beforeMount
-> 子mounted
-> 父mounted
在setup中声明周期也适用provide
、 inject
提供依赖注入 ,实现祖孙级组件通信<template>
<h1>父组件</h1>
<one />
</template>
<script>
import { provide, ref } from 'vue'
import one from './one.vue'
export default {
components: {
one
},
setup() {
const msg = ref('red')
provide('msg', msg)
return {
msg
}
}
}
</script>
<!--子组件(one)-->
<template>
<div>子组件</div>
<two />
</template>
<script>
import two from './two.vue'
export default {
components: {
two
},
}
</script>
<!--孙子组件(two)-->
<script>
import { inject } from 'vue'
export default {
setup() {
const msg = inject('msg')
return {
msg
}
}
}
</script>
this.$refs.XXX
获取,vue3中setup函数没有this,所以也有单独的获取ref的方法<template>
<input type="text" ref="inputRef" value="这是input的文本"/>
</template>
<script>
import { onMounted, ref } from "vue";
export default {
setup() {
const inputRef = ref(null);/// 本质是reactive({value:null})
onMounted(() => {
console.log(inputRef.value.value);
});
console.log(inputRef.value);// null dom还没形成
return {
inputRef,
};
},
};
</script>
import { reactive } from "vue";
export default function hookTest() {
const obj = reactive({ name: "张三", age: 25 });
return { obj };
}
<template>
<div>{{ obj.name }}</div>
<div>{{ obj.age }}</div>
</template>
<script>
import hookTest from './hookTest'
export default {
setup (props) {
const {obj} = hookTest()
return { obj };
}
}
</script>
import Vue from 'vue'
import App from './App.vue'
const app = new Vue(App)
app.$mount()
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
<div v-for="item in 5" :key="item" :ref="item">
{{ item }}
</div>
this.$refs将会是个数组
<template>
<div v-for="item in 5" :key="item" :ref="setItemRef">
{{ item }}
</div>
</template>
<script>
import { onMounted } from "vue";
export default {
setup() {
const refArray = [];
const setItemRef = (e) => {
refArray.push(e);
};
onMounted(() => {
console.log(refArray);
});
return {
setItemRef,
};
},
};
</script>
<div id="red" :id="'blue'"></div>
<!--这里div只会绑上id等于red-->
<div id="red" :id="'blue'"></div>
<!--结果为-->
<div id='red blue'></div>
value
和input
。如要修改,通过子组件的model选项中的prop值和event值来指定属性名和事件名。<child v-model="pageTitle" />
<!--等同于-->
<child :value="pageTitle" @input="pageTitle = $event" />
export default {
model: {
prop: 'title',
event: 'change'
},
props: {
title: {
type: String,
default: 'Default title'
}
},
methods:{
handleClick(val) {
this.$emit('change', val)
}
}
}
v-bind.sync
:title.sync
就要替换为v-model:title
<child :title="pageTitle" @update:title="pageTitle = $event" />
<!--等同于-->
<child :title.sync="pageTitle" />
this.$emit('update:title', newValue)
modelValue
和update:modelValue
。如要修改,直接通过v-model
后面参数v-model:title
来指定属性名,并且支持绑定多个v-model
。<child v-model="pageTitle" />
<!--等同于-->
<child :modelValue="pageTitle" @update:modelValue="pageTitle = $event" />
<child v-model:title="pageTitle" />
<!--等同于-->
<child :title="pageTitle" @update:title="pageTitle = $event" />
v-bind='$attrs'
进行将父组件不被认为props特性绑定的属性传入子组件(不包含class以及style),配合interitAttrs一起使用,如果为true则将所有attribute添加到子组件的跟标元素上。但如果为false时,因为class以及style不属于$attrs,所以仍会添加到组件的跟元素上this.$emit(方法名)
<template>
<one @open="open" />
</template>
<script>
import one from "./one.vue";
export default {
components: {
one,
},
setup() {
open = () => {
console.log(1);
};
return {
open,
};
},
};
</script>
<!--子组件-->
<template>
<div @click="open">点击</div>
</template>
<script>
export default {
emits: {
//open: null,
open: (value) => {
if (typeof value === "string") {
return true;
} else {
return false;
}
},
},
setup(props, { emit }) {
open = () => {
emit("open", 11);
};
return {
open,
};
},
};
</script>
$on
,$off
和 $once
实例方法,组件实例不再实现事件触发接口。Vue.component('typeButton',typeButton)
const typeButton = {
functional:true,//标记,无状态无实例
render(h , { props }){
const { type } = props
return <div class={ type }>{type}</div>
}
}
functional
定义 接收两个参数,props和context。context包含组件的 attrs
、slots
和 emit
propertyimport { h } from 'vue'
const DynamicHeading = (props, context) => {
return h(`h${props.level}`, context.attrs, context.slots)
}
DynamicHeading.props = ['level']
export default DynamicHeading
const asyncModal = () => import('./Modal.vue')
//带有选项
const asyncModal = {
component: () => import('./Modal.vue'),
delay: 200,
timeout: 3000,
error: ErrorComponent,
loading: LoadingComponent
}
defineAsyncComponent
中import { defineAsyncComponent } from 'vue'
const asyncModal = defineAsyncComponent(() => import('./Modal.vue'))
//带有选项
const asyncModalWithOptions = defineAsyncComponent({
loader: () => import('./Modal.vue'),
delay: 200,
timeout: 3000,
errorComponent: ErrorComponent,
loadingComponent: LoadingComponent
})
<!-- Object -->
<script>
const app = new Vue({
data: {
num: 1
}
})
</script>
<!-- Function -->
<script>
const app = new Vue({
data() {
return {
num: 2
}
}
})
</script>
<script>
import { createApp } from 'vue'
createApp({
data() {
return {
num: 1
}
}
}).mount('#app')
</script>
<script>
代码块上.变量跟函数都不需要return,即可在模板中使用<template>
<div>{{ obj.name }}</div>
<div>{{ obj.age }}</div>
</template>
<script setup>
import { ref } from "vue";
const obj = ref({ name: "张三", age: 25, c: { d: 2 } });
</script>
<teleport>
用于移动dom到指定元素disabled
属性,禁用teleport的功能,不会移动到任何位置,但他仍会出现在正常位置下(里面的所有元素依然保持正常状态)<teleport to="#some-id" />
<teleport to=".some-class" />
<teleport to="[data-teleport]" />
<teleport to="body" disabled=‘true’>
<div></div>
</teleport>
<suspense>
帮助我们创建一个平滑的用户体验<suspense>
有两个插槽default、fallback。如果default里的标签(任意深度,所有的后代组件)显示不出来,则展示fallback<template>
<suspense>
<template #default>
<todo-list />
</template>
<template #fallback>
<div>
Loading...
</div>
</template>
</suspense>
</template>
<script>
export default {
components: {
TodoList: defineAsyncComponent(() => import('./TodoList.vue'))
}
}
</script>
this.$children
访问当前实例的子组件。vue3中已移除,推荐使用$refsv-bind='$attrs'
访问父组件不被认为的props特性绑定的属性传入子组件,还可以通过在中间组件添加 v-on=‘$listeners’
使其子组件可以通过emits触发其父组件的方法$attrs
的一部分(相当于父组件的@init=‘init’。不用在中间组件添加v-on=‘$listeners’
,只需要v-bind='$attrs'
,由$attrs负责传递方法。即可在其子组件访问)const Comp = Vue.extend({
props: ['username'],
template: '<div>{{ username }}</div>'
})
new Comp({
propsData: {
username: 'Evan'
}
})
//vue3中需要这样
const app = createApp(
{
props: ['username'],
template: '<div>{{ username }}</div>'
},
{ username: 'Evan' }
)