vue3初探—vue3新特性学习

一、vue3初探—vue3新特性学习

二、vue3初探—在vue3和vuex4.x中使用typescript

三、vue3初探----(vue3项目)vuex4.x中使用typescript最终写法

四、在三中用到的一些TypeScript中的一些高阶类型 Omit Pick ReturnType Parameters

五、vue3初探----项目构建

六、vue3初探----vue3的一些变化

项目构建

vite

npm init @vitejs/app demo-vue3-vite
选择vue,选择vue-ts
cd demo-vue3-vite
npm install
npm run dev

vue3的新特性

向后兼容:兼容vue2的写法

性能提升

TypeScript的支持

Composition API

在这里插入图片描述

当然还有其他改变,可查看官方文档 https://v3.cn.vuejs.org/guide/migration/introduction.html#%E5%80%BC%E5%BE%97%E6%B3%A8%E6%84%8F%E7%9A%84%E6%96%B0%E7%89%B9%E6%80%A7

Composition API

setup函数

setup 函数是一个新的组件选项。作为在组件内使用 Composition API 的入口点。

setup会在 beforeCreate前被调用,同时在vue3中setup代替了beforeCreate和created

网上许多文章认为setup执行时间在beforeCreatecreated 之间但试验结果setup是在beforeCreate之前执行

<script>
export default {
  setup() {
    console.log('setup')
  },
  beforeCreate() {
    console.log('beforeCreate')
  },
  created() {
    console.log('created')
  },
}
</script>

在这里插入图片描述

vue3中的生命周期函数,可以按需导入到组件中,且只能在 setup() 函数中使用

<script>
import { onMounted, onUpdated, onUnmounted } from 'vue';
 
export default {
  setup() {
    onMounted(() => {
      console.log('mounted!');
    });
    onUpdated(() => {
      console.log('updated!');
    });
    onUnmounted(() => {
      console.log('unmounted!');
    });
    return {};
  }
};
</script>

参数

setup有连个参数 props和context

props 接收当前组件props选项的值,即获取父组件传递过来的参数

context,接收一个上下文对象,该对象中包含了一些在vue 2.x 中需要通过 this 才能访问到属性

<script lang="ts">
import {defineComponent } from 'vue';

export default defineComponent({
    props: {
        name: String
    },
    setup(props, context) {
        context.attrs;
        console.log(props.name);
        context.slots;
        context.emit;
    }
});
</script>

返回值

函数返回的内容可作为模板渲染

<template>
    <p>{{name}}</p>
</template>  
<script lang="ts">
import { defineComponent, ref } from 'vue';
export default defineComponent({
    setup() {
        let name = ref('guoang');
        return {
            name
        };
    }
});
</script>

getCurrentInstance

支持访问内部组件实例

在这里插入图片描述

 const internalInstance = getCurrentInstance() 

getCurrentInstance在await之后调用无法获取到内部组件实例

因为vue在执行时不会等待setup执行完毕,而在setup后面上下文会被设为null所以在await之后获取到是null

export default defineComponent({
	async setup() {
		const result = ref('')
		result.value = await resultValue()
        const internalInstance = getCurrentInstance() 
        console.log('await之后getCurrentInstance()获取不到',internalInstance) //internalInstance = null
		return {
			result,
		}
	},
})

在这里插入图片描述

响应式api

响应式api

推荐文章:深入理解 Vue3 Reactivity API

reactive

reactive()函数接收一个普通对象,返回该普通对象的响应式代理对象

reactive()接收一个泛型来定义他的类型

<template>
    <p>{{user.name}}</p>
    <p>{{user.age}}</p>
</template>

<script  lang="ts">
interface User {
    name: string;
    age: number;
}
import { reactive, defineComponent } from 'vue';
export default defineComponent({
    setup() {
        let user = reactive<User>({
            name: 'guaong',
            age: 0
        });
        return {
            user
        };
    }
});
</script>

如果reactive生成的数据如果被展开就失去了响应性

 setup() {
        let state = reactive({ count: 0 });
        function add() {
            state.count += 1;
        }
        return { ...state };
 }
toRefs

toRefs()函数可以将reactive()创建出来的响应式对象,转换为普通对象,只不过这个对象上的每个属性节点,都是ref响应式数据

 setup() {
        let state = reactive({ count: 0 });
        function add() {
            state.count += 1;
        }
        return { ...toRefs(state) };
 }

ref

reactive()负责复杂的数据结构,ref()则可以把基本的数据结构包装成响应式

ref()函数接收一个参数值,返回一个响应式的数据对象。该对象只包含一个指向内部值的 .value 属性

在模板中访问时,无需通过.value属性,它会自动展开

ref()接收一个泛型来定义他的类型

<template>
    <!-- 无需加value -->
    <h2>{{num}}</h2>
    <button @click="add">累加</button>
</template>
<script lang="ts">
import { ref,defineComponent } from 'vue';

export default defineComponent( {
    setup() {
        let num = ref(2);
        function add() {
            num.value += 1; //需要加value
        }
        return { add, num };
    }
});
</script>
toRef

创建一个ref类型数据, 并和以前的数据关联

备注

  1.toRef
  创建一个ref类型数据, 并和以前的数据关联
  2.toRefs
  批量创建ref类型数据, 并和以前数据关联
  3.toRef和ref区别
  ref-创建出来的数据和以前无关(复制)
  toRef-创建出来的数据和以前的有关(引用)
  ref-数据变化会自动更新界面
  toRef-数据变化不会自动更新界面

computed

computed() 函数用来创建计算属性,函数的返回值是一个 ref 的实例

    setup() {
        let num = ref(2);
        function add() {
            num.value += 1;
        }
        let double = computed(() => num.value * 2);
        return { add, double, num };
    }

或者,接受一个具有 getset 函数的对象,用来创建可写的 ref 对象。

const count= ref(2);
const newCount = computed({
  get: () => count.value,
  set: val => {
    count.value = val
  }
})
watchEffect

立即执行传入的一个函数,同时响应式追踪其依赖,并在其依赖变更时重新运行该函数。

const count = ref(0)

watchEffect(() => console.log(count.value))

触发时机

const count = ref(0)    
watchEffect(() => {
      const text = count.value ;
      console.log(text);
    }, {
      flush: 'post' // 'pre'组件更新前运行,'post'组件更新后运行,默认为'pre'
    }
watch

参数watch第一个参数传入的 要监听的对象,第二个参数是回调函数,监听的对象改变就会执行回调函数,第三个参数深度监听或立即执行的配置

返回值 watch返回一个方法调用可停止监听

const unwatch = watch(
            () => state.count,
            (newVal, oldVal) => {
                console.log(oldVal);
                console.log(newVal);
            },
            { deep: true, imediate: true }
        );
        const stop = () => {
            unwatch();
        };

在这里插入图片描述

也可以监听多个对象

watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => {
  /* ... */
})
vue3的响应式系统

vue3将响应式系统完成独立的一个包**@vue/reactivity**,也就是说ref,reactive,effect… 这些方法可以用在任何项目中(浏览器,node),这些方法不再依赖于vue,只需要import { reactive, effect } from “@vue/reactivity”;就可以使用

封装一个操作localStorage的方法

import { reactive, effect } from "@vue/reactivity";

/**
 * @Author: guoang
 * @description: 操作localStorage
 * @param key 需要获取localStorage的key
 * @param defaultValue 默认值,获取不到localStorage对应key的数据则返回默认值
 * @return 一个响应式对象
 */
export default function newStorage<T extends {}>(key: string, defaultValue = {}): Partial<T>  {
    let data: Partial<T>= reactive({});
    Object.assign(data, window.localStorage.getItem(key) && JSON.parse(<string>(localStorage.getItem(key))) || defaultValue);
    //在data改变时会触发effect中的方法重新设置localStorage
    effect(() => {
        window.localStorage.setItem(key, JSON.stringify(data))
    });
    return data;
}

使用

<template>
    <label>姓名:</label>
    <span>{{userInfo.name}}</span>
    <label>年龄:</label>
    <span>{{userInfo.age}}</span>
    <button @click="changeUserInfo">changeUserInfo</button>
</template>
<script lang="ts">
import { reactive, computed, ref, onMounted } from 'vue';
import newStorage from './newStorage';
interface UserInfo{
    name: string
    age: number|string
}
export default {
    setup() {
        let userInfo = newStorage<UserInfo>('userInfo', {
            name: '名称',
            age: 100
        });
        const changeUserInfo = () => {
            userInfo.name = 'guoang';
            userInfo.age = '120';
        };
        return { userInfo, changeUserInfo };
    }
};
</script>

效果

在这里插入图片描述

点击changeUserInfo更新视图同时更新localStorage

在这里插入图片描述

模板 Refs

在vue2.X中使用vm.$refs

在vue3中

<template>
    <div ref="divRef"></div>
</template>
<script lang="ts">
import { defineComponent, onMounted, ref } from 'vue';
export default defineComponent( {
    setup() {
        // 创建一个值为null的ref的对象,并返回
        const divRef = ref(null);
        console.log(divRef)
        return{
            divRef 
        }
    }
});
</script>

setup语法糖

<template>
    <Foo />
    <div v-click-outside />
</template>
<script setup>
import { defineProps, defineEmit, useContext } from 'vue'; //导入组件直接使用
import Foo from './Foo.vue';
import vClickOutside from 'v-click-outside'; //导入指令直接使用    *导入指令前面必须加v
// 书写组合式 api 就像在正常的 setup 中一般,但是不需要进行手动地进行 return
//const count = ref(0) 也可以这样
ref: count = 1;
const inc = () => {
    // 直接操作变量 不用.value
    count++;
};
// 使用 props
const props = defineProps({
    foo: String
});
//使用 emit
const emit = defineEmits(['update', 'delete']);
// 获取 slots 和 attrs
const { slots, attrs } = useContext();
</script>

vue新组件Teleport Fragment Suspense

Teleport

Teleport 提供了一种简单的方法,使我们可以控制要在DOM中哪个父对象下呈现HTML。

index.html

<body>
		<div id="app"></div>
		<div id="web"></div>
		<script type="module" src="/src/main.ts"></script>
	</body>

vue中

<template>
    <div>
        我在id=app
    </div>
    <!-- to 属性就是目标位置 -->
    <teleport to="#web">
        <div>我在id=web</div>
    </teleport>
</template>

渲染结果

在这里插入图片描述

Fragment

在vue2中创建一个Vue组件,那么它只能有一个根节点,多了一层嵌套

<template>
    <div>
        <div></div>
        <div></div>
    </div>
</template>

在vue3中会有一个名为 Fragment 的虚拟元素,他并不会呈现

<template>
    <div></div>
    <div></div>
</template>
Suspense

Suspense异步组件

setup 中返回一个 Promise 对象

defineComponent 包裹要导出的实例对象

<template>
    <h1>{{ result }}</h1>
</template>

<script lang="ts">
import { defineComponent, ref } from 'vue';
export default defineComponent({
    async setup() {
        const result = ref('');
        result.value = await resultValue();
        return {
            result
        };
    }
});
const resultValue = (): Promise<string> => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('HelloWorld');
        }, 3000);
    });
};
</script>

app.vue

使用 <Suspense> 包裹所有异步组件相关代码

<Suspense> 下 <template #default> 插槽包裹异步组件

<Suspense> 下 <template #fallback> 插槽包裹渲染异步组件之前的内容

<template>
    <Suspense>
        <template #default>
            <HelloWorld></HelloWorld>
        </template>
        <template #fallback>
            <h1>Loading...</h1>
        </template>
    </Suspense>
</template>

<script lang="ts" setup>
import HelloWorld from './components/HelloWorld.vue';
</script>

在这里插入图片描述
三秒后
在这里插入图片描述

注意点

解构props属性,如果直接在 setup 中引用,必须要加 toRefs

    const { users } = toRefs(props)

setup语法糖中导入指令前面必须加v

import vClickOutside from 'v-click-outside'; //导入指令直接使用    *导入指令前面必须加v(vClickOutside)
注意点

解构props属性,如果直接在 setup 中引用,必须要加 toRefs

const { users } = toRefs(props)

setup语法糖中导入指令前面必须加v

import vClickOutside from 'v-click-outside'; //导入指令直接使用    *导入指令前面必须加v

watchEffect() 与 effect() 的区别

watchEffect() 会维护与组件实例以及组件状态(是否被卸载等)的关系,如果一个组件被卸载,那么 watchEffect() 也将被 stop,但 effect() 则不会,普通开发中不推荐直接用 effect() 啦,使用 watchEffect() 就好了

Logo

前往低代码交流专区

更多推荐