一、vue3初探—vue3新特性学习
二、vue3初探—在vue3和vuex4.x中使用typescript
三、vue3初探----(vue3项目)vuex4.x中使用typescript最终写法
四、在三中用到的一些TypeScript中的一些高阶类型 Omit Pick ReturnType Parameters
五、vue3初探----项目构建
六、vue3初探----vue3的一些变化
npm init @vitejs/app demo-vue3-vite
选择vue,选择vue-ts
cd demo-vue3-vite
npm install
npm run dev
向后兼容:兼容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
setup
函数是一个新的组件选项。作为在组件内使用 Composition API 的入口点。
setup会在 beforeCreate前被调用,同时在vue3中setup代替了beforeCreate和created
网上许多文章认为setup执行时间在beforeCreate
和created
之间但试验结果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,
}
},
})
推荐文章:深入理解 Vue3 Reactivity API
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()函数可以将reactive()创建出来的响应式对象,转换为普通对象,只不过这个对象上的每个属性节点,都是ref响应式数据
setup() {
let state = reactive({ count: 0 });
function add() {
state.count += 1;
}
return { ...toRefs(state) };
}
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>
创建一个ref类型数据, 并和以前的数据关联
备注
1.toRef
创建一个ref类型数据, 并和以前的数据关联
2.toRefs
批量创建ref类型数据, 并和以前数据关联
3.toRef和ref区别
ref-创建出来的数据和以前无关(复制)
toRef-创建出来的数据和以前的有关(引用)
ref-数据变化会自动更新界面
toRef-数据变化不会自动更新界面
computed()
函数用来创建计算属性,函数的返回值是一个 ref
的实例
setup() {
let num = ref(2);
function add() {
num.value += 1;
}
let double = computed(() => num.value * 2);
return { add, double, num };
}
或者,接受一个具有 get
和 set
函数的对象,用来创建可写的 ref 对象。
const count= ref(2);
const newCount = computed({
get: () => count.value,
set: val => {
count.value = val
}
})
立即执行传入的一个函数,同时响应式追踪其依赖,并在其依赖变更时重新运行该函数。
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返回一个方法调用可停止监听
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将响应式系统完成独立的一个包**@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
在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>
<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>
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>
渲染结果
在vue2中创建一个Vue组件,那么它只能有一个根节点,多了一层嵌套
在vue3中会有一个名为 Fragment 的虚拟元素,他并不会呈现
<template>
<div></div>
<div></div>
</template>
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
使用
<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()
就好了