因为vue3不再有Vue构造函数,所以一些全局的自定义属性或者方法也没法通过Vue.prototype.xx挂在到vm实例上,这时候需要借助app.config.globalProperties来实现这一功能
//如果项目采用了typescript,那么就需要扩展一下`@vue/runtime-core`模块,否则在使用的时候会报找不到$http属性
//在main.ts加上如下代码
declare module '@vue/runtime-core' {
interface ComponentCustomProperties {
$http: any;
}
}
定义全局方法
import { createApp } from 'vue'
const app = createApp(App)
app.config.globalProperties.$http = () => {
//do something
}
使用全局方法
import { getCurrentInstance } from 'vue'
// getCurrentInstance必须在.vue文件中才获取得到
const { proxy } = getCurrentInstance() as any
console.log('proxy', proxy.$http )
## vue2.X
process.env.VUE_APP_BASE_API
## vue3.2
const href = import.meta.env.VITE_APP_CONSOLE_URL + path
// 定义颜色变量
let stateStyle = reactive({
color: 'red',
fontSize: '20px'
})
function changeColor() {
stateStyle.color = 'blue'
stateStyle.fontSize = '30px'
}
computed是多对一的关系,而watch则是一对多的关系;vue3也提供了两个函数来侦听数据源的变化:watch和watchEffect。
const a = ref(0)
watch(a,(newval,oldval)=>{
console.log(newval,oldval)
})
const b = ref(0)
watch([a,b],(newval,oldval)=>{
console.log(newval,oldval)
})
const data = reactive({
a:1,
b:2,
c:3
})
watch(data,(newVal,oldVal)=>{
console.log(newVal,oldVal)
})
watch([()=>data.a,()=>data.b],(newVal,oldVal)=>{
console.log(newVal,oldVal)
})
watch(
() => state.count,
(count, prevCount) => {
// 1 0
console.log(count, prevCount);
}
);
侦听深度嵌套的对象属性变化时,需要设置deep:true
注意⚠️
侦听响应式对象始终返回该对象的引用,所以打印结果发现都是改变后的值。
解决:对侦探的对象进行深拷贝
import _ from "lodash";
const deepObj = reactive({
a: {
b: {
c: "hello",
},
},
});
watch(
() => _.cloneDeep(deepObj),
(val, old) => {
// new hello hello
console.log(val.a.b.c, old.a.b.c);
},
{ deep: true }
);
deepObj.a.b.c = "new hello";
一般侦听都会在组件销毁时自动停止,但是有时候我们想在组件销毁前手动的方式进行停止,可以调用watch返回的stop函数进行停止:
const count = ref(0);
const stop = watch(count, (count, prevCount) => {
// 不执行
console.log(count, prevCount);
});
setTimeout(()=>{
count.value = 2;
}, 1000);
// 停止watch
stop();
import { reactive, ref, watch, watchEffect } from "vue";
const count = ref(0);
const state = reactive({
year: 2021,
});
watchEffect(() => {
console.log(count.value);
console.log(state.year);
});
setInterval(() => {
count.value++;
state.year++;
}, 1000);
// 分析: watchEffect会在页面加载时自动执行一次,追踪响应式依赖;在加载后定时器每隔1s执行时,watchEffect都会监听到数据的变化自动执行,每次执行都是获取到变化后的值。
const num = ref(0);
const double = computed(() => num.value * 2);
num.value++;
// 2
console.log(double.value);
// Warning: computed value is readonly
double.value = 4
可以使用get和set函数创建一个可读写的ref对象
const num = ref(0);
const double = computed({
get: () => num.value * 2,
set: (val) => (num.value = val / 2),
});
num.value++;
// 2
console.log(double.value);
double.value = 8
// 4
console.log(num.value);
案例
num:{{ num }}
double-num:{{ doubleNum }}
reactive主要负责复杂数据结构,而ref主要处理基本数据结构,但ref本身也是能处理对象和数组的:
let book = reactive({
name: 'Learn Vue',
year: 2020,
title: 'Chapter one'
})
// 1.会消除它的响应式
let {
name,
} = book
name = 'new Learn'
// Learn Vue
console.log(book.name);
// 2.保存响应式
let {
name,
} = toRefs(book)
// 注意这里解构出来的name是ref对象
// 需要通过value来取值赋值
name.value = 'new Learn'
// new Learn
console.log(book.name);
//3.通过readonly来创建一个只读的对象
const copy = readonly(book);
//Set operation on key "name" failed: target is readonly.
copy.name = "new copy";
我是一个模态框
//解析: Teleport中的modal div就被传送到了body的底部;虽然在不同的地方进行渲染,但是Teleport中的元素和组件还是可以和父组件进行数据通信。Teleport接收两个参数to和disabled:
to - string:必须是有效的查询选择器或 HTMLElement,可以id或者class选择器等。
disabled - boolean:如果是true表示禁用teleport的功能,其插槽内容将不会移动到任何位置,默认false不禁用
作用:suspense在等待异步组件时额外渲染一些内容,使用户拥有更好的体验
定义:是Vue3推出的一个内置组件,它允许我们的程序在等待异步组件时渲染一些后备的内容,可以让我们创建一个平滑的用户体验;Vue中加载异步组件其实在Vue2.x中已经有了,我们用的vue-router中加载的路由组件其实也是一个异步组件
语法关键:提供了两个slot插槽,一个default默认,一个fallback加载中的状态:
// 1.定义
// 全局定义异步组件
//src/main.js
import { defineAsyncComponent } from "vue";
const AsyncButton = defineAsyncComponent(() =>
import("./components/AsyncButton.vue")
);
app.component("AsyncButton", AsyncButton);
// 组件内定义异步组件
// src/views/Home.vue
import { defineAsyncComponent } from "vue";
export default {
components: {
AsyncButton: defineAsyncComponent(() =>
import("../components/AsyncButton")
),
},
};
// 2.使用-定义异步加载的子组件
export default {
components: {
AsyncButton: defineAsyncComponent({
delay: 100,
timeout: 3000,
loader: () => import("../components/AsyncButton"),
errorComponent: ErrorComponent,
onError(error, retry, fail, attempts) {
if (attempts <= 3) {
retry();
} else {
fail();
}
},
}),
},
};
// 3.使用异步加载的子组建
组件加载中...
所谓的Fragment,就是片段;在vue2.x中,要求每个模板必须有一个根节点,在Vue3中我们可以直接不需要根节点
.sync修饰符 => v-model:propName
const Mixin = {
data() {
return {
user: {
name: 'Jack',
id: 1,
address: {
prov: 2,
city: 3,
},
}
}
}
}
const Component = {
mixins: [Mixin],
data() {
return {
user: {
id: 2,
address: {
prov: 4,
},
}
}
}
}
// vue2结果:
{
id: 2,
name: 'Jack',
address: {
prov: 4,
city: 3
}
}
// vue3结果:
user: {
id: 2,
address: {
prov: 4,
},
}
在Vue3中,key值应该被放置在template标签上
在vue3中,如果一个元素同时定义了v-bind="object"和一个相同的单独的属性,那么声明绑定的顺序决定了最后的结果(后者覆盖前者)
在vue2.x中,如果一个元素同时定义了v-bind="object"和一个相同的单独的属性,那么这个单独的属性会覆盖object中的绑定
// vue2.x
// vue3.x
vue2.x中,在v-for上使用ref属性,通过this.$refs会得到一个数组:
vue3不再自动创建数组,而是将ref的处理方式变为了函数,该函数默认传入该节点:
在vue3中,v-if比v-for有更高的优先级。因此下面的代码,在vue2.x中能正常运行,但是在vue3中v-if生效时并没有item变量,因此会报错:
{{ item }}