搜集资源
- Vue3 入门指南与实战案例
- Vue在线演练场
- Vue3.0中Ref与Reactive的区别是什么
- Composition API RFC
- Vue.js 中使用defineAsyncComponent 延迟加载组件
- 2022年必会Vue3.0学习 (强烈建议)
Vue3相对Vue2的变化:
- Vue3对TypeScript支持更友好,不像Vue2,所有属性都放在了this对象上,难以推倒组件的数据类型
- Vue2大量的API挂载在Vue对象的原型上,难以实现TreeShaking,Vue3可以按需导出。
- 新推出了CompositionAPI,可以把关联代码写在一起
- 更方便的支持了JSX
- Vue3 的 Template 支持多个根标签,Vue 2 不支持
- 对虚拟DOM进行了重写、对模板的编译进行了优化操作
23.在vue的template部分,访问ref变量值无需带.value
footer :style="{ paddingBottom: '0.45rem' }">
span @click="() => shareGuideRef.show()">分享span>
footer>
BaseWxShare ref="shareGuideRef"/>
22.在vue的template部分,无法使用BOM全局变量
错误写法:
div class="poster-with-card poster-canvas-content" :style="{width: `${screen.width}px`}">
正确写法:
div class="poster-with-card poster-canvas-content" :style="html2canvasStyle">
script setup lang="ts">
const html2canvasStyle = {
width: `${screen.width}px`,
};
script>
21. 在less中如何使用响应式变量语法
script>
const styleProgressWidth=ref(`${props.finishTarget/props.target*100}%`);
script>
style>
.foo{
width:v-bind(styleProgressWidth);
}
style>
20. 动态class的设置方法
script setup>
import { ref } from 'vue'
const msg = ref('Hello World!')
script>
template>
h1 :class="'red' ${msg === 'Hello World!' && 'green'}">{{ msg }}h1>
input v-model="msg">
template>
style scoped>
.red{
color:red;
}
.green{
color:green
}
style>
19. @eventName=”foo” 后面有无括号的差别
这里的eventName指的是vue内置的dom事件
- @click有括号:传指定参数
- @click无括号:默认传event参数
18.v-model可以定义多个
van-list
v-model:loading="loading"
v-model:error="error"
error-text="请求失败,点击重新加载"
@load="onLoad"
>
van-cell v-for="item in list" :key="item" :title="item" />
van-list>
17. useVModel语法糖
使用useVModel, 子组件就能修改父组件传递的属性值
父组件
div>
ChildComps
v-model:visible="visibleFeedbackModal"
/>
div>
子组件
import { useVModel } from '@vueuse/core'
const props = definePropsvisible: string;
}>();
const emit = defineEmits(['update:visible']);
// 带有类型定义的写法
const emit = defineEmitse: 'update:visible', params: any): void;
}>();
const visibleModel = useVModel(props, 'visible', emit);
const handleDialogClose = async () => {
visibleModel.value = false;
};
16. keep-alive用法示例
template>
router-view v-slot="{ Component }">
transition>
keep-alive :include="includeList">
component :is="Component" />
keep-alive>
transition>
router-view>
template>
script lang="ts"> export default {name:'AppEntry'} script>
script lang="ts" setup>
const router = useRouter();
const includeList = ref(['AuthByManagerList']);
router.beforeEach((to, from) => {
if (from.path === '/authByManagerResult' && to.path === '/authByManagerList') {
// 从客户认证结果页跳列表页,要清除缓存
includeList.value = [];
} else if (from.path === '/authByManagerList' && to.path === '/authByManagerDetail') {
// 从列表页进入详情页,要缓存列表页
includeList.value = ['AuthByManagerList'];
}
});
script>
15.computed属性完整写法
- watch的套路是:既要指明监听的属性,也要指明监听的回调函数。
- watchEffect的套路是:不需要指明监听的属性,监听中的回调函数用到了那个属性,就监听那个属性。
watchEffect跟computed有点像:
- computed注重是计算出来的值,所以必须要有返回值。
- watchEffect更注重是过程,所以不用写返回值。
const person = reactive({
fistName:"Mr",
lastName:"long"
})
// 计算属性简写
let fullName = computed(()=>{
return person.fistName + '-' + person.lastName
})
// 计算属性完整写法
let fullName = computed({
get(){
return person.fistName + '-' + person.lastName
},
set(value){
const newArr = value.split('-')
person.fistName = newArr[0]
person.lastName = newArr[1]
}
})
computed 接收参数的语法
template>
div>
div v-for="(item,index) in arr" :key="index">
{{formatName(item)}}
div>
div>
template>
const formatName = computed(()=>(item)=>`客户经理${item.name},请及时拜访你的客户${item.custName}`);
14.watch和watchEffect的区别
- watch 是需要传入侦听的数据源,而 watchEffect 是自动收集数据源作为依赖。
- watch 可以访问侦听状态变化前后的值,而 watchEffect 没有,watchEffect获取的改变后的值。
- watch 是属性改变的时候执行,当然也可以immediate,而 watchEffect 是默认会执行一次,然后属性改变也会执行。
- watch监听ref值,不用加.value,
- watch监听对象的某个属性值时,书写方式是
watch([()=>obj.propA],()=>{})
// 情况一监听ref响应式数据
watch(count,(newValue,oldValue)=>{
console.log(newValue,oldValue)
},{immediate:true}) // immediate 立即监听
// 情况二 监听多个ref响应式数据
watch([count,name],(newValue,oldValue) =>{
console.log(newValue,oldValue) // 此时value的数据是数组
})
// 情况三 监听reactvie响应式数据
// 如果watch监听是reactive定义的响应式数据,则无法获取正确的oldValue,且强制开启深度监听。
watch(person,(newValue,oldValue)=>{
console.log(newValue,oldValue) // 两个值一致都是一样的
})
// 情况四 监听reactive定义的响应式数据的某个属性(基础数据类型)
watch(()=>person.name,(newValue,oldValue) =>{
console.log(newValue,oldValue)
})
// 情况五 监听多个reactive定义的多个响应式数据的属性(基础数据类型)
watch([()=>person.name,()=>person.age],(newValue,oldValue)=>{
console.log(newValue,oldValue)
})
// 情况六 监听reactive定义的响应式数据的某个属性(复杂数据类型)
watch(() => person.class,(newValue,oldValue) =>{
// 此时的class 为 { b:{c:20 } }, 想要监听c值的变化 则需要开启deep深度监听
console.log(newValue,oldValue)
},{deep:true})
13. 判断slot命名插槽是否定义,定义才展示
slot v-if="$slots.customButton" name="customButton">slot>
Button v-else :type="buttonType" :disabled="disabled" :loading="loading">
template #icon>UploadOutlined />template> {{ btnText }}
Button>
12.useSlots用法
父组件
template>
ChildTSX>
p>I am a default slot from TSX.p>
template #msg>
p>I am a msg slot from TSX.p>
template>
ChildTSX>
template>
script setup lang="ts">
import ChildTSX from '@cp/context/Child.tsx'
script>
子组件
script lang="ts" setup>
// 注意:这是一个 .tsx 文件
import { useSlots } from 'vue'
const slots = useSlots()
const ChildTSX = ()=>{
// 渲染组件
return () => (
div>
{/* 渲染默认插槽 */}
p>{ slots.default ? slots.default() : '' }p>
{/* 渲染命名插槽 */}
p>{ slots.msg ? slots.msg() : '' }p>
div>
)
}
}
export default ChildTSX
script>
11. 父子组件通信 props/emit方式
父组件
template>
Child
ref="child"
title="用户信息"
:index="1"
:uid="userInfo.id"
:user-name="userInfo.name"
@update-age="updateAge"
/>
template>
script lang="ts" setup>
import { ref, onMounted } from 'vue'
import Child from '@cp/Child.vue'
type TMember={
id: number,
name: string
}
const child = reftypeof Child | null>()
const userInfo: TMember = {
id: 1,
name: 'Petter'
}
// 父组件调用子组件的方法
onMounted(() => child.value?.childMethod)
const updateAge = (age: number) => {
console.log(age);
}
script>
子组件
template>
p>标题:{{ title }}p>
p>索引:{{ index }}p>
p>用户id:{{ uid }}p>
p>用户名:{{ userName }}p>
template>
script lang="ts" setup>
import { withDefaults, defineProps, defineEmits, toRefs, defineExpose } from 'vue';
const props = withDefaults(definePropstitle: string;
index: number;
uid: number;
userName: string;
}>(), { userName: 'zhangsan' });
// 复杂类型赋初始值
const props = withDefaults(definePropsPropsType>(), {
value: () => {
return {
memberList: [],
deptList: [],
positionList: [],
};
},
});
const { title, index, uid, userName } = toRefs(props);
// 接受子组件传进来的方法
const emit = defineEmits(['update-age']);
setTimeout(() => {
emit('update-age', 22);
}, 2000);
const childMethod = () => {
console.log('我是子组件的方法')
}
// 子组件暴露方法给父组件
defineExpose({ childMethod })
script>
10. 使用ref操作dom
template>
p ref="msg">留意该节点,有一个ref属性p>
template>
script lang="ts" setup>
import { onMounted, ref } from "vue";
// 定义挂载节点,声明的类型详见下方附表
const msg = refHTMLElement | null>();
// 请保证视图渲染完毕后再执行节点操作 e.g. onMounted / nextTick
onMounted(() => {
// 比如获取DOM的文本
console.log(msg.value?.innerText);
});
script>
9. 给props属性赋初始值
script lang="ts" setup>
const props = withDefaults(definePropsbtnName: string;
noBtn?: boolean;
doAction: () => void;
classStyle?: string;
config:any[];
}>(),{
noBtn:true, // 设置默认值
classStyle:''
config:()=>[],
});
const mActionClass=`m-action ${props.classStyle}`;
另外一种定义props类型的方式
import type { PropType } from 'vue';
const props=defineProps({
count: {
//客户数统计
type: Number,
default: 0,
},
dayOpts: {
type: Array as PropTypename: string }[]>,
default: () => [],
}
}),
另外还有两点需要说明一下:
- Vue3组件即便用withDefaults设置了默认值, props类型定义属性要设置为可选,否则VSCode插件会有告警提示。
- vue3中的props在模板中会同步更新,在setup函数中只更新一次。当props值再次发生改变时,在setup中要用watch/watchEffect/computed才能监听到props的变化,如果props某个属性是对象,要设置深度监听,才能监听到变化。
8. toRefs和toRef的用途
借助toRefs可以在template部分,直接使用结构后的对象单个键值,写法简洁,却不失响应式。
toRef是转换reactive对象的单个值
template>
ul class="user-info">
li class="item">
span class="key">ID:span>
span class="value">{{ id }}span>
li>
li class="item">
span class="key">name:span>
span class="value">{{ name }}span>
li>
li class="item">
span class="key">age:span>
span class="value">{{ age }}span>
li>
li class="item">
span class="key">gender:span>
span class="value">{{ gender }}span>
li>
ul>
template>
script lang="ts" setup>
import { reactive, toRef, toRefs } from "vue";
interface Member {
id: number;
name: string;
age: number;
gender: string;
}
// 定义一个reactive对象
const userInfo: Member = reactive({
id: 1,
name: "Petter",
age: 18,
gender: "male",
});
// 结构reactive对象,它的字段全部是ref变量
const { id, age, gender } = toRefs(userInfo);
const name: string = toRef(userInfo, 'name');
// 2s后更新userInfo
setTimeout(() => {
userInfo.id = 2;
userInfo.name = "Tom";
userInfo.age = 20;
}, 2000);
script>
用toRefs解构ref定义的对象属性会丢失响应式
import {toRefs} from 'vue';
const foo=ref({bar:'xxx'});
const {bar}=toRefs(foo);
template>{{bar}}template>
7. ref和reactive区别
- ref可以定义任何类型的数据,但是定义数据和对象时,在script部分使用时,赋值和取值,不如reactive方便
- reactive只能用于数组,对象类型。
- ref的本质是通过reactive创建的,ref(10)=>reactive({value:10});
- reactive的本质是将每一层的数都解析成proxy对象,reactive 的响应式默认都是递归的,改变某一层的值都会递归的调用一遍,重新渲染dom。
template>
p>{{count}}p>
button @click="add">加1button>
p>{{info.name}}p>
button @click="changeName">改姓名button>
template>
script lang="ts" setup>
import { reactive, ref } from 'vue';
interface Info{
name:string;
age:number;
}
// 响应式基本类型用ref
const count = ref(0);
// 响应式引用类型用reactive
const info:Info = reactive({ age: 100,name:'zhangsan' });
const add = () => {
// 基本类型赋值时,是赋值给value属性
count.value += 1;
};
const changeName = () => {
info.name="lisi";
};
script>
6. 路由取值和跳转
5.jsx语法支持,要借助插件@vitejs/plugin-vue-jsx
script lang="tsx" setup>
const jsxNode = () => {
return div>textdiv>;
};
script>
template>
jsxNode />
template>
vite.config.ts配置
// tsx语法支持
import vueJsx from '@vitejs/plugin-vue-jsx';
export default {
plugins: [vueJsx(),],
}
4. shallowReactive, shallowRef, readonly,shallowReadonly,toRaw,markRaw区别
4.1 shallowReactive、shallowRef的之间的区别
shallowReactive:浅监视
shallowRef:不做监视
4.2 readonly和shallowReadonly
readonly:只读属性的数据,深度只读
const state2 = readonly(state)
shallowReadonly:只读的数据,浅只读的
4.3 toRaw和markRaw
toRaw将代理对象变成普通对象,数据变化,界面不会进行更新
const user = toRaw(state);
markRaw标记的对象数据,从此以后都不能在成为代理对象了
const likes = ['吃','喝'];
state.likes = markRaw(likes);
template>
h4>
button @click="triggerShallowReactiveRender">
改变第一层才会渲染
button>
h4>
p>{{ shallowReactiveState.a }}p>
h4>
button @click="triggerShallowRefRender">
失去响应式
button>
h4>
p>{{ shallowRefState.a }}p>
template>
script setup>
import { readonly,reactive, shallowReactive, shallowRef, toRaw } from "vue";
// shallowRef 与shallowReactive
// shallowRef 与shallowReactive创建的是非递归的响应对象,shallowReactive创建的数据第一层数据改变会重新渲染dom
const shallowReactiveState = shallowReactive({
a: "initShallowReactiveState",
b: {
c: "c",
},
});
//如果不改变第一层 只改变其他的数据 页面不会重新渲染,例如:
shallowReactiveState.b.c = 2;
//改变第一层的数据会导致页面重新渲染
const triggerShallowReactiveRender=()=>{
shallowReactiveState.a = "changeShallowReactiveState";
}
// shallowRef创建的对象没有响应式特性
const shallowRefState = shallowRef({
a: "initShallowRefState",
b: {
c: "c",
},
});
const triggerShallowRefRender=()=>{
//失去响应式--除非对整个对象重新赋值或者使用triggerRef(shallowRefState)触发页面更新
shallowRefState.a = "changeShallowRefState";
}
// toRaw ---只修改数据不渲染页面
var obj = { name: "test" };
var toRawState = reactive(obj);
var raw = toRaw(toRawState);
//并不会引起页面的渲染,而obj.name,toRawState.name的值均已改变
setTimeout(()=>raw.name = "zs",2000);
// 不允许修改对象的值
const readonlyState = readonly({a:1,b:2});
// 赋值会引起警告
readonlyState.a=2;
script>
3. 生命周期函数
vue2和vue3生命周期对比
beforeCreate -> 使用 setup()
created -> 使用 setup()
beforeMount -> onBeforeMount
mounted -> onMounted
beforeUpdate -> onBeforeUpdate
updated -> onUpdated
beforeDestroy -> onBeforeUnmount
destroyed -> onUnmounted
errorCaptured -> onErrorCaptured
template>
div class="home">
p>{{ count }}p>
p>{{ state.a }}p>
button @click="add">加1button>
div>
template>
script lang="ts" setup>
import {
ref,
reactive,
onBeforeMount,
onMounted,
onBeforeUpdate,
onUpdated,
onBeforeUnmount,
onUnmounted,
onErrorCaptured,
onRenderTracked,
onRenderTriggered,
onActivated,
onDeactivated,
} from "vue";
const count = ref(1);
const state = reactive({ a: 10 });
const add = () => {
count.value += 1;
state.a = state.a + 1;
};
onBeforeMount(() => {
console.log("onBeforeMount");
});
onMounted(() => {
console.log("onMounted");
});
onBeforeUpdate(() => {
console.log("onBeforeUpdate");
});
onUpdated(() => {
console.log("onUpdated");
});
onBeforeUnmount(() => {
console.log("onBeforeUnmount");
});
onUnmounted(() => {
console.log("onUnmounted");
});
onErrorCaptured((evt) => {
console.log("onErrorCaptured", evt);
});
// 只执行一次,有几个响应式api,执行几次
onRenderTracked((evt) => {
console.log("onRenderTracked", evt);
});
// 行为如同onUpdated,每次有数据更新都会执行
onRenderTriggered((evt) => {
console.log("onRenderTriggered", evt);
});
// keep-alive要用到的函数
onActivated(() => {
console.log("onActivated");
});
onDeactivated(() => {
console.log("onDeactivated");
});
script>
2.context的属性有哪些?
setup(props, context) {
const {attrs,slots,parent,root,emit,refs}=context;
return {
// ...
}
}
1. 获取更详细的报错信息
app.config.errorHandler = (err, vm, info) => {
console.log('[全局异常]', err, vm, info)
}
文章来源于互联网:纸上得来终觉浅,Vue3 新语法练起来
正文完