setup
setup函数,是在beforeCreate之前执行(创建实例前$el)。由于在执行setup 时尚未创建组件实例,因此在 setup 选项中没有 this。
setup 参数
使用setup时,它接受两个参数:
- props: 组件传入的属性
- context:包含3个属性
props是响应式的, 当传入新的props 时,会及时被更新。由于是响应式的, 所以不可以使用ES6解构,解构会消除它的响应式。这段代码会让props不再支持响应式:
import { defineComponent } from 'vue'
export default defineComponent ({
props: { name: '冷冬' },
setup(props, context) {
let name = props.name
console.log(name);
return { name }
},
})
context提供了最常用的三个属性:attrs、slot 和emit,分别对应Vue2中的 a t t r 属 性 、 s l o t 插 槽 和 attr属性、slot插槽 和 attr属性、slot插槽和emit发射事件。
context.attrs:传入组件中但是未被props接收的对象。
context.emit:用于触发当前组件实例上的传值事件。
context.slots:用来访问被插槽分发的内容(一般用于使用渲染函数来书写一个组件时)
数据定义
- ref:创建响应式的数据变量。传入的为基本数据类型,例如字符串、数字、boolean 等,返回值是一个响应式的对象,这个对象上只包含一个 value 属性
- isRef:判断生成的响应式数据是否是通过 ref 方法生成的(是true、否false)
import { ref } from 'vue'
setup() {
//定义
const msg = ref('你好')
//修改
const changeMsg =()=>{
msg.value ='msg被改了'
}
console.log("isRef:",isRef(msg)); // isRef:true
//导出
return {
msg,
changeMsg
}
}
- reactive:创建响应式的数据对象。传入的为引用类型,例如数组、对象等,但不能代理基本类型值,返回一个响应式的数据对象。
- isReactive: 判断生成的响应式数据是否是通过 isReactive 方法生成的,还能判断readonly代理的对象是否是由reactive创建(是true、否false)
- toRefs:将一个 reactive 对象转化为属性全部为 ref 对象的普通对象
- JSON.parse(JSON.stringify(Array)):阻止数据响应式的发生。
<div>{{ msg }}</div>
import { reactive } from 'vue'
setup() {
//定义
const state = reactive({
msg: '你好'
})
//修改
const changeMsg = () => {
state.msg = 'msg被改了'
}
console.log("state:",isReactive(state)); // state:true
//导出
return {
...toRefs(state),
changeMsg
}
}
- readonly:创建一个只读代理,对变量创建只读。
- isReadonly:检查代理是否由readonly创建(是true,否false)。
import { readonly } from 'vue';
export default {
setup() {
const user = { name: 'ul', age: 20 };
const copy = readonly(user);
const users = reactive({ name: 'li', age: 30 });
const copys = readonly(users);
function getClick(){
user.age++; // 21
copy.age++; // warning! target is readonly.
users.age++; // 31
copys.age++; // warning! target is readonly.
}
return { copy,copys,getClick };
}
}
- isProxy:对象是否由reactive创建或者是readonly创建的代理(是true,否false)。
// ref创建
const refVal = ref(123);
isProxy(refVal) // false
// reactive创建
const reactiveVal = reactive({ age: 20 });
isProxy(reactiveVal) // true
- toRaw:返回reactive或者是readonly代理的原始对象。
import { toRaw, reactive, readonly, isProxy } from 'vue';
const user = { name:'立冬' };
const reactiveUser = reactive(user);
const readonlyUser = readonly(reactiveUser);
console.log(toRaw(reactiveUser) === user) // true
console.log(toRaw(readonlyUser) === user) // true
console.log(isProxy(toRaw(reactiveUser))) // false
console.log(isProxy(toRaw(readonlyUser))) // false
- markRaw: 标记一个对象,被标记后,该对象永远不会被转换为代理。
import { markRaw, reactive, isReactive } from 'vue';
const user = markRaw({});
const reactiveUser = reactive(user);
console.log(isReactive(reactiveUser)) // false
const reactiveUser1 = reactive({ user });
console.log(isProxy(toRaw(reactiveUser1))) // true
console.log(isProxy(toRaw(reactiveUser1.user))) // false
// 在reactive创建代理时,被markRaw标记的嵌套对象不会被reactive创建代理
- shallowReactive:创建一个反应式代理,但是只是浅度创建。
import { isReactive, shallowReactive } from 'vue';
const data = shallowReactive({
count: 10,
content: {
age: 20
}
})
data.count++ // 11 逻辑层数据已发生变化,视图重新渲染
isReactive(data.content) // false 因为data.content属于深度嵌套,未被代理
data.content.age++ // 21 逻辑层数据已发生变化,但是视图层不会被更新渲染
- shallowReadonly:创建一个只读代理,但是只是浅度创建。
import { isReadonly, shallowReadonly } from 'vue';
const data = shallowReadonly({
count: 10,
content: {
age: 20
}
})
data.count++ // warning! target is readonly.
isReadonly(data.content) // false 因为data.content属于深度嵌套,未被代理
data.content.age++ // 21 深度嵌套未被代理,所以操作成功。
自定义指令:directive
beforeMount() {}, // 挂载前
mounted() {}, // 挂载后
beforeUpdate() {}, // 新增,更新前
updated() {}, // 更新后
beforeUnmount() {}, // 新增,卸载前
unmounted() {} // 卸载后
创建自定义指令
//定义文档的标题: <div v-title>编辑框</div>
const title = {
mounted(el:any) {
document.title = el.innerText;
el.remove();
},
};
main.js 初始化自定义指令
import * as directives from "./common/directives" // 批量定义加载全局自定义指令
Object.keys(directives).forEach((key:any) => {
app.directive(key, directives[key]);
});
依赖注入
- provide:父组件创建
<son-com ref="sonRef" :customForm=custom/>
setup(){
// 使用ref给子组件传值
const sonRef = ref(null);
function popUpShow(){
let item = {};
sonRef.value.mySes = '懂经济';
}
// 给子组件传值
const custom = reactive({
name: '紫色'
});
// 使用依赖注入(provide )给子组件传值
const person = reactive({name: 'bob', age:32});
provide('person', person);
return {
sonRef, custom, popUpShow
}
}
- inject:子组件获取
<div>{{ customForm.name }}</div>
export default {
props: {
customForm: Object
},
setup(props,context) {
// 获取ref给子组件传的值
let mySes = ref(null);
// 可以直接在html中使用customForm.name,在setup中使用要props.customForm
console.log("父组件传的参数:",props.customForm);
// 在所有子组件中都能获取provide中的数据
const person = inject('person');
return {
mySes,
loadDate,
person
}
}
}
监听属性
watch(count, (val, old) => { // 监听字符串
console.log("count改变了");
});
watch(()=>search.status, (val, old) => { // 监听对象
console.log("count改变了");
});
// watch监听器:变化前,变化后的值都能获取。一次监听多个数据(对象,字符串)
watch([()=>search.status, count], ([name, count], [preName, preCount]) => {
console.log("search.status的数据变化:",name,preName,',count的数据变化:',count,preCount);
});
// watchEffect监听器:只能获取变化后的值,初始化时会执行一次回调函数来自动获取依赖(监控所有的响应式数据)
watchEffect(() => {
console.log(search.status);
onInvalidate(() => {}); // 在watchEffect(重新运行/停止的时候)执行
});
路由
路由的useRoute,useRouter 相当于vue2的 this. r o u t e ( ) , t h i s . route(),this. route(),this.router()。
import { useRoute, useRouter } from 'vue-router'
export default {
setup () {
const route = useRoute()
const router = useRouter()
router.push({ name: 'home' })
return {}
},
}
- useRoute:获取当前的路由信息
$route.path 字符串,对应当前路由的路径,总是解析为绝对路径,如"/foo/bar"。
$route.params 一个 key/value 对象,包含了动态片段和全匹配片段,如果没有路由参数,就是一个空对象。
$route.query 一个 key/value 对象,表示 URL 查询参数。如果没有查询参数,则是个空对象。
$route.hash 当前路由的hash值 (不带#) ,如果没有 hash 值,则为空字符串。
$route.fullPath 完成解析后的 URL,包含查询参数和hash的完整路径。
$route.matched 数组,包含当前匹配的路径中所包含的所有片段所对应的配置参数对象。
$route.name 当前路径名字
$route.meta 路由元信息
- useRouter:全局路由的实例,是router构造方法的实例。
$router.push('/home/index') // /home/index
$router.push({path:'/home/index'}) // /home/index
$router.push({name:'user', params:{userId:123}}) // 跳转到路由的名称
$router.push({path:'/register/index',query:{plan:'123'}}) // /register/index?plan=123
<router-link :to="{path:`/home/edit/${ props.item.id }`}" tag="a" target="_blank" ></router-link>
注意:push方法其实和<router-link></router-link>是等同的。
push方法的跳转会向 history 栈添加一个新的记录,当我们点击浏览器的返回按钮时可以看到之前的页面。
$router.go(1) // 前进
$router.go(-1) // 后退
$router.replace('/search'); //
{ path: '/:pathMatch(.*)*' } // 没有匹配的路由
自定义 Hooks
vue3 的 hook 函数 相当于 vue2 的 mixin, 不同在与 hooks 是函数。
import { getCurrentInstance,ref } from 'vue'
import { useRouter } from 'vue-router';
const right = () => {
let index = ref('123');
return {
index
}
}
export { right }
getCurrentInstance
获取当前组件的实例、上下文。
import { getCurrentInstance } from 'vue';
// 获取当前组件实例
const instance = getCurrentInstance();
// 获取当前组件的上下文,下面两种方式都能获取到组件的上下文。
const { ctx } = getCurrentInstance(); // 方式一,这种方式只能在开发环境下使用,生产环境下的ctx将访问不到
const { proxy } = getCurrentInstance(); // 方式二,此方法在开发环境以及生产环境下都能放到组件上下文对象(推荐)
// ctx 中包含了组件中由ref和reactive创建的响应式数据对象,以及以下对象及方法;
proxy.$attrs
proxy.$data
proxy.$el
proxy.$emit
proxy.$forceUpdate
proxy.$nextTick // 开启异步任务
proxy.$options
proxy.$parent
proxy.$props
proxy.$refs
proxy.$root // proxy.$root.$route proxy.$root.$router
proxy.$slots
proxy.$watch
修饰符
.stop // 阻止冒泡行为,不让当前元素的事件继续往外触发
.prevent // 阻止事件本身行为,如阻止超链接的点击跳转,form表单的点击提交
.capture // 改变js默认的事件机制,默认是冒泡,capture功能是将冒泡改为倾听模式
.self // 只有是自己触发的自己才会执行,如果接受到内部的冒泡事件传递信号触发,会忽略掉这个信号
.once // 点击事件将只会触发一次
.passive // 滚动事件的默认行为 (即滚动行为) 将会立即触发,而不会等待 onScroll 完成。这个 .passive 修饰符尤其能够提升移动端的性能。
.keyup // 按键修饰符
.exact // 修饰符允许你控制由精确的系统修饰符组合触发的事件。
.passive 和 .prevent 不能一起使用(.prevent 将会被忽略)
.self 和 .stop 区别:
self只响应当前元素自身触发的事件,不会响应经过冒泡触发的事件,并不会阻止冒泡继续向外部触发。
stop是从自身开始不向外部发射冒泡信号
全局命令
ref
<p v-for="(item,index) in renderData" :key="index" ref="nodes"></p>
console.log(this.$refs.nodes) // vue3 返回所有循环的p元素节点
console.log(this.$refs.nodes) // vue2 返回循环后最后一个P元素节点
v-if和v-for优先级
当v-if和v-for同时作用于一个元素上时:
Vue2.x中v-for的优先级会高于v-if;
Vue3.x中v-if的优先级会高于v-for;
使用禁忌:Vue3.x中不能将v-for和v-if放在同一个元素上。只能使用v-for嵌套v-if使用
使用建议:官方建议使用计算属性来处理,即提高性能,又能兼容Vue3.x。
mount(节点挂载)
在 main.js 挂载
import { createApp } from 'vue';
const app = createApp({});
app.mount('#my-app');
unmount(取消挂载)
在main.js 中取消挂载
import { createApp } from 'vue';
const app = createApp({})
// 挂载
app.mount('#my-app');
// 取消挂载
setTimeout(() => app.unmount('#my-app'), 2000);
use(插件)
与Vue 2.x类似,如果参数为对象,则需要提供一个install的方法;如果参数本身就是一个方法,那么这个方法将默认为安装插件的方法;在进行方法的调用时,Vue将作为第一个参数传入方法中。
注意:同一个插件多次调用use时,该插件也只能被安装一次。
import { createApp } from 'vue';
const app = createApp({})
import router from './router/index';
app.use(router);
app.mount('#my-app');
config:Vue应用配置
config:包含Vue应用程序全局配置的对象,在挂载应用之前配置相应的属性。
devtools(类型: Boolean,默认: true):配置是否允许开启vue-devtools检查,一般在开发环境中是true,生产环境中为false。
errorHandler(类型:Function,参数err:错误内容,vm:对应的实例,info:Vue特定的错误信息,如某个生命周期中出现的错误):为组件在实例化或渲染过程中捕获到的错误进行处理的方法。
warnHandler(类型:Function, 参数msg:警告内容,vm:对应的实例,trace:组件的层次追踪):为Vue在运行时捕获到的警告进行处理的方法。
globalProperties(类型:any):用于添加到应用程序中任何组件都能使用的全局属性,当与组件内部的属性冲突时,将优先使用组件内部的属性值。可代替Vue2中的Vue.prototype。
isCustomElement(类型:(tag: string) => boolean):用于来识别Vue之外的自定义元素(如,三方web组件api),如果组件或元素符合这个条件,则组件不会被实例化,Vue也不会对组件或元素发出警告信息。
optionMergeStrategies(类型:[key: string] : Function):为Vue自定义选项定义合并策略。
performance(类型:boolean,默认:false):设置此项为true时,将在浏览器的devtool性能/时间线面板中启用组件的初始化,编译,渲染及修补程序跟踪。仅在开发模式和支持performance.mark api的浏览器中工作
生命钩子
let { onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted, one rrorCaptured, onRenderTracked, onRenderTriggered} = Vue;
setup() {
// vue3.x生命周期写在setup中
onBeforeMount(() => { }); // 挂载开始之前被调用
onMounted(() => { }); // 实例被挂载后调用,不会保证所有的子组件也被挂载完
onBeforeUpdate(() => { }); // 更新渲染DOM前
onUpdated(() => { }); // 更新渲染DOM完成,不会保证所有的子组件也都一起被重绘
onBeforeUnmount(() => { }); // 销毁组件实例之前,此时实例仍然是完全正常的
onUnmounted(() => { }); // 销毁组件实例完成,组件实例的所有指令被解除绑定、事件侦听器被移除、子组件实例被卸载。
one rrorCaptured(() => { }); // 当捕获一个来自子孙组件的错误时被调用
onRenderTriggered((event) => { }); // 状态触发(它会跟踪你变化值的信息,并且新值和旧值都会给你明确的展示出来)
onRenderTracked((event) => { }); // 状态跟踪(所有我们用return返回去的值,它都会跟踪)
},
如果把 onRenderTracked 比喻成散弹枪,每个值都进行跟踪,那 onRenderTriggered 就是狙击枪,只精确跟踪发生变化的值,进行针对性调试。
event对象属性的详细介绍:
- key 那边变量发生了变化
- newValue 更新后变量的值
- oldValue 更新前变量的值
- target 目前页面中的响应变量和函数