Vue3 新特性
# Vue3的改进及特点
1.性能的提升:打包大小减少 41%,初次渲染快 55%,更新快 133%,内存使用减少 54%。
2.新推出的Composition API 使组件更易维护,减少无用数据绑定页面更流畅。
4.更好TypeScript支持,可以在创建命令里直接配置,页面集成畅通无阻。
5.Teleport(瞬移组件)、Suspense(解决异步加载组件问题)和全局 API 的修改和优化。
6.Vue3兼容大部分Vue2的特性,用Vue2代码开发Vue3都可以。
# 安装
vue --version # 查看版本
注意:如果以前安装过,需要检查一下版本,因为只有最新版本(V4.5.4 以上版本)才有创建 Vue3 的选项。
npm install -g @vue/cli
使用 vue-cli 命令行创建项目
vue create vue3-1 // 根据提示自己选择配置
启动命令
yarn serve 或 npm run serve
打包命令
yarn build 或 npm run build
# 新语法 setup(),ref(),reactive()
// 注:setup是为了优化性能让程序按需引入全局统一
1.用法一
<template>
<div class="home">
<div>名字:{{ name }}</div>
<ul>
<li v-for="item in list" :key="item" @click="show(item)">{{ item }}</li>
</ul>
</div>
</template>
<script lang="ts">
import { defineComponent, ref } from "vue";
// 注:defineComponent 在TypeScript下,给予了组件正确的参数类型推断
export default defineComponent({
name: "Home",
components: {},
props:['msg'],
setup(props,context) {
// 注:setup函数是处于生命周期函数 beforeCreate 和 Created 两个钩子函数之间的函数 也就说在 setup 函数中是无法使用 data 和 methods 中的数据和方法,而methods等可以使用setup中return出去的数据。
/*
一.函数的第一个参数是 props 用于接收 props.msg
这个props是一个响应式的Proxy对象,不可以解构,解构后会失去响应,如果要用解构的方式,要用toRefs
let { msg } = toRefs(props) //但是解析成ref了要用msg.value,所以直接用props.msg更简单
二.context对象在setup()中暴露三个属性 attrs 、slots 和 emit 因为在setup函数中还没有创建Vue实例,是无法使用vm.$attrs、vm.$slots和vm.$emit的,所以这三个属性充当了这样的作用,使用方法相同。
注意:
context.attrs和vm.$attrts包含的是在实例vm.props中没有被声明识别的attribute(class和style除外)。所以setup()中参数props中暴露的变量,就不会在context.attrs中暴露。
context.slots和vm.$slots只能访问具名插槽,没有命名的插槽或者v-slot:default的是没有暴露的。
context的attrs和slots是有状态的,当组件更新时也会实时更新,所以也不要解构。但与props不同的是,它们不是响应式的,在setup()中的使用应保持只读的状态,如果要改变可以在onUpdated的周期函数中进行。
context.emit和vm.$emit可以触发实例上的监听事件。
*/
const list = ref(["深圳", "北京", "上海"]);
const name = ref("");
//注:用ref是为了转换成引用类型,让全局引用保持一致,而之前原始类型是不行的,所以要name.value的方示赋值
const show = (index: string) => {
name.value = index;
};
// 注:不return出去的数据,模板是无法使用的。
return {
list,
name,
show
};
},
});
</script>
2.用法二 reactive() 优化
<template>
<div class="home">
<div>名字:{{ data.name }}</div>
<ul>
<li v-for="item in data.list" :key="item" @click="data.show(item)">{{ item }}</li>
</ul>
</div>
</template>
<script lang="ts">
import { defineComponent, reactive } from "vue";
export default defineComponent({
name: "Home",
components: {},
setup() {
const data = reactive({
list: ["深圳", "北京", "上海"],
name: "",
show: (index: string) => {
data.name = index;
},
});
return {
data
};
},
});
</script>
2.用法三 toRefs() 优化
<template>
<div class="home">
<div>名字:{{ name }}</div>
<ul>
<li v-for="item in list" :key="item" @click="show(item)">{{ item }}</li>
</ul>
</div>
</template>
<script lang="ts">
import { defineComponent,reactive,toRefs } from "vue";
export default defineComponent({
name: "Home",
components: {},
setup() {
const data = reactive({
list: ["深圳", "北京", "上海"],
name: "",
show: (index: string) => {
data.name = index;
},
});
const refData = toRefs(data);
//不能直接解析 ...data 必须用 toRefs()
return {
...refData
};
},
});
</script>
# Vue3 生命周期函数用法, 需要引入 (注:vue2 生命周期函数不影响)
<script lang="ts">
import { defineComponent,ref,reactive,toRefs,
onBeforeMount,
onMounted,
onBeforeUpdate,
onUpdated,
onBeforeUnmount,
onUnmounted,
onActivated,
onDeactivated,
onErrorCaptured,
onRenderTracked,
onRenderTriggered
} from "vue";
export default defineComponent({
name: "Home",
components: {},
setup() {
//setup() 开始创建组件之前,在beforeCreate和created之前执行。
const data = reactive({
list: ["深圳", "北京", "上海"]
});
onBeforeMount(()=>{
//组件挂载到节点上之前执行的函数。
})
onMounted(()=>{
//组件挂载完成后执行的函数。
})
onBeforeUpdate(()=>{
//组件更新之前执行的函数
})
onUpdated(()=>{
//组件更新完成之后执行的函数。
})
onBeforeUnmount(()=>{
//组件卸载之前执行的函数。
})
onUnmounted(()=>{
//组件卸载完成后执行的函数。
})
onActivated(()=>{
//被包含在<keep-alive>中的组件,会多出两个生命周期钩子函数。被激活时执行。
})
onDeactivated(()=>{
//比如从 A 组件,切换到 B 组件,A 组件消失时执行。
})
onErrorCaptured(()=>{
//当捕获一个来自子孙组件的异常时激活钩子函数。
})
//< 调试用生命函数
onRenderTracked((event)=>{
//跟踪所有状态触发
console.log(event);
});
onRenderTriggered((event) => {
//跟踪当前状态触发
console.log(event);
//key 那边变量发生了变化
//newValue 更新后变量的值
//oldValue 更新前变量的值
//target 目前页面中的响应变量和函数
});
// 调试用生命函数 />
const refData = toRefs(data);
return {
...refData
};
},
mounted(){
console.log("vue2 生命周期");
}
});
</script>
# Vue3 watch用法
<script lang="ts">
import { defineComponent, ref, reactive, toRefs, watch } from "vue";
export default defineComponent({
name: "Home",
components: {},
setup() {
const text = ref("测试单个值");
const data = reactive({
list: ["深圳", "北京", "上海"],
name: "",
show: (index: string) => {
data.name = index;
},
});
//watch(text, 单个用法,watch([text,()=>data.name], 多个用法,注:()=>data.name 为了兼容vue2
watch([text,()=>data.name], (newValue, oldValue) => {
console.log(`new--->${newValue}`);
console.log(`old--->${oldValue}`);
});
const refData = toRefs(data);
return {
...refData,
};
},
});
</script>
# Vue3 模块化重用功能 (优化 mixins)
1.新建useTime.ts文件
import { ref } from "vue";
const time = ref("00:00:00");
const getTime = () => {
const now = new Date();
const h= now.getHours() < 10 ? "0" + now.getHours() : now.getHours();
const m = now.getMinutes() < 10 ? "0" + now.getMinutes() : now.getMinutes();
const s= now.getSeconds() < 10 ? "0" + now.getSeconds() : now.getSeconds();
time.value = h + ":" + m + ":" + s;
setTimeout(getTime, 1000);
};
export { time, getTime }
2.引入
<template>
<div class="home">
<div>时间:{{time}} <button @click="startTime">开始</button></div>
</div>
</template>
<script lang="ts">
import { defineComponent, ref } from "vue";
import { time, getTime } from './useTime';
export default defineComponent({
name: "Home",
components: {},
setup() {
const startTime = () => {
getTime();
};
return {
startTime,
time
};
},
});
</script>
# teleport 独立挂载组件(解决样式等冲突问题不挂载到app下)
1. index.html 页面新加插入点(会挂载到 #headTitie DOM下)
<div id="headTitie"></div>
<div id="app"></div>
2. 在components目录下新建 headTitle.vue
<template>
<teleport to="#headTitie">
<div class="head">
<h1>{{ title }}</h1>
</div>
</teleport>
</template>
<script lang="ts">
import { defineComponent, ref } from "vue";
export default defineComponent({
name: "headTitie",
setup() {
const title = ref("Vue3 新特性示例");
return {
title,
};
},
});
</script>
3. 在 App.vue 加
<template>
<headTitle />
<router-view />
</template>
<script lang="ts">
import headTitle from "./components/headTitle.vue";
export default {
name: "App",
components: {
headTitle,
},
};
</script>
# Suspense 异步请求组件
1. 新建Demo.vue
<template>
<div class="Demo">
<div>名字:{{ name }}</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from "vue";
export default defineComponent({
name: "Demo",
components: {},
setup() {
return new Promise((resolve, reject) => {
setTimeout(() => {
return resolve({ name: "我是 Suspense 异步请求组件" });
}, 2100);
});
},
});
</script>
2. 使用引入 home.vue
<template>
<div class="home">
<Suspense>
<template #default>
<Demo />
</template>
<template #fallback>
<p>加载中...</p>
</template>
</Suspense>
</div>
</template>
<script lang="ts">
import { defineComponent } from "vue";
import Demo from "./Demo.vue";
export default defineComponent({
name: "Home",
components: {Demo}
});
</script>