vue重学笔记六:vue3.2 使用手册
vite 构建的应用,vue3.2 版本中最新的语法糖是 <script setup>,自动执行 setup 函数。组件在编译的过程中代码运行的上下文是在 setup() 函数中,所有变量和函数都直接在这里面声明,此时已无需 renturn,其中定义的变量和函数会自动导出,可在 template 模板中直接使用。
比较 vue2.x 和 <vue3.2 与 vue3.2 的区别
vue2.x
组件 JS 模块中引入的 变量 需要在 data 中重新声明 才可在 template 模板中使用,直接使用会报错。
<vue3.2
setup 的写法是:
<script>
import { ref } from 'vue'
export default{
setup(){
const count = ref(0) // 声明基本数据类型
}
}
</script>
整个组件的结构在《vue3重新笔记二:安装or创建》一文中已描述,今天主要来说下生命周期及常用的使用方法。
生命周期
vue 3.x 中 与 vue 2.x 相比较,vue3.x 中 直接废除了 beforeCreate 和 created 函数,直接以 setup 来代替,其他的钩子函数都一样,只是在前面加上 on
vue2.x | vue3.x |
beforeCreate | 废除,setup |
created | 废除,setup |
beforeMount | onBeforeMount |
mounted | onMounted |
beforeUpdate | onBeforeUpdate |
updated | onUpdated |
beforeUnmount | onBeforeUnmount |
unmounted | onUnmounted |
errorCaptured | onErrorCaptured |
renderTracked | onRenderTracked |
renderTriggered | onRenderTriggered |
vue3.2 使用手册
常用API
该模块下的代码都在 script-setup 中。
注意事项都在代码注释中写明,此代码已经过实践。使用方法如下:
【import 引用】
// 从vue 中 按需加载,在开发过程中使用时,会自动添加 import { computed, nextTick, onMounted, reactive, ref, toRefs, watch } from "vue"; // 引用子组件与vue2.x 一样,只是不用再在 components 中声明,因 script-setup 已默认导出所有数据供 template 使用 import child from "./child.vue";
【props】的使用
defineProps 在 <script setup>中自动可用,无需导入
两个方式只能任选其一。
// 方式一 defineProps({ msg: String, }); // 方式二 const props = defineProps({ name: { type: String, default: "", }, });
【emit】 的使用
// 子组件 const emit = defineEmits(["update-name"]); //methods 在template 中调用 changeName函数 const changeName = () => { emit("update-name", "uiui"); // 执行 emit 修改:updateName 更改为 update-name 原因:v-on事件监听器在DOM模板中会自动转换为小写,所以建议使用 kebab-case模式 }; // 父组件 <script setup> // 这里用例我是在 App.vue 组件中实践的,所以 引入地址有点不同,可以忽略,使用的时候会自动引入 import { ref } from '@vue/reactivity' import HelloWorld from './components/HelloWorld.vue' const name = ref('123') const updateName = (str)=>{ name.value = str } </script> <template> <HelloWorld :name="name" @update-name="updateName" /> </template>
【data】的使用
声明响应式数据。
import { ref, reactive } from "vue"; // 声明基本类型数据 const count = ref(0); count.value = 1 // 修改 // 声明引用类型数据 const person = reactive({ name: "keri", sex: "女", }); person.name = "anni" // 修改
【mounted 】的使用
import { onMounted } from "vue"; onMounted(() => { ... // console.log("props==>", props.customStr); // 123 });
【computed】的使用
import { computed } from "vue"; // changeCount 在 template 中直接使用 const changeCount = computed(() => { return count.value + 3; });
【watch】的使用
import { reactive, watch } from "vue"; const person = reactive({ name: "keri", sex: "女", }); watch( () => person.sex, (newVal, oldVal) => { console.log("newVal==>", newVal); console.log("oldVal==>", oldVal); }, { immediate: true, deep: true, } );
【nextTick】的使用
import { reactive, nextTick} from "vue"; const person = reactive({ name: "keri", sex: "女", }); nextTick(()=>{ person.name = '小红' })
【methods】的使用
import { reactive} from "vue"; const person = reactive({ name: "keri", sex: "女", }); // methods const changePerson = (val) => { person.sex = val ? val : '男' };
【refs】的使用
vue2.x & <vue 3.2 中子组件的数据都是默认隐式暴露给父组件,但在 script-setup模式下,
所有数据只是默认 return 给 template 使用,不会暴露到组件外,
所以父组件无法直接通过挂载 refs 获取子组件的数据,此时就需要子组件暴露出数据,父组件才能拿到。
*注意:
这个位置一定要非常注意,必须要放在暴露出去数据或函数的后面,否则就会报错,
提示:Uncaught ReferenceError: Cannot access 'changePerson' before initialization
// 子组件 暴露数据 defineExpose({ ...toRefs(person), changePerson })
// 父组件 <script setup> // 这里用例我是在 App.vue 组件中实践的,所以 引入地址有点不同,可以忽略,使用的时候会自动引入 import { ref } from '@vue/reactivity' import { onMounted } from '@vue/runtime-core' import HelloWorld from './components/HelloWorld.vue' // 这个变量名 必须和 ref 命名的名字一样,要不然也会报错 const helloWD = ref('helloWD') onMounted(()=>{ helloWD.value.changePerson('sss') }) </script> <template> <HelloWorld ref="helloWD" :name="name" @update-name="updateName" /> </template>
【defineAsyncComponent】的使用
从上文中可以看出,引入组件都是直接 import 引入,但是由于vue 本身是一个单页面应用,这种引入方式会将所有的 js 打包成一个整体,如果项目很小只有几个页面,不会有什么问题,但是如果项目很大,全部采用同步载入组件,会导致一个 JS 文件很大,页面加载时长时间处于空白状态。这时候异步加载 defineAsyncComponent 的优势就体现了。
*注意:项目只有几个文件,不建议使用该功能。
defineAsyncComponent :创建一个只有在需要时才会加载的异步组件。
// 简单用法 import { defineAsyncComponent } from "vue"; const Child = defineAsyncComponent(() => import('./child.vue')) <template> <child/> <template> // 高阶用法 import { defineAsyncComponent } from "vue"; const AsyncComp = defineAsyncComponent({ // 工厂函数 loader: () => import('./Foo.vue') // 加载异步组件时要使用的组件 loadingComponent: LoadingComponent, // 加载失败时要使用的组件 errorComponent: ErrorComponent, // 在显示 loadingComponent 之前的延迟 | 默认值:200(单位 ms) delay: 200, // 如果提供了 timeout ,并且加载组件的时间超过了设定值,将显示错误组件 // 默认值:Infinity(即永不超时,单位 ms) timeout: 3000, // 定义组件是否可挂起 | 默认值:true suspensible: false, /** * * @param {*} error 错误信息对象 * @param {*} retry 一个函数,用于指示当 promise 加载器 reject 时,加载器是否应该重试 * @param {*} fail 一个函数,指示加载程序结束退出 * @param {*} attempts 允许的最大重试次数 */ onError(error, retry, fail, attempts) { if (error.message.match(/fetch/) && attempts <= 3) { // 请求发生错误时重试,最多可尝试 3 次 retry() } else { // 注意,retry/fail 就像 promise 的 resolve/reject 一样: // 必须调用其中一个才能继续错误处理。 fail() } } })
【slot】的使用
// 常规用法 // 子组件 foot.vue <template> <div>11111111</div> <slot/> <div>33333333</div> </template> // 父组件 <script setup> import Foot from './foot.vue' </script> <template> <div>......</div> <foot> 自定义模板内容,会插入子组件中 </foot> </template> // 默认值用法 // 子组件 <template> <div>11111111</div> <slot>默认模板/值</slot> <div>33333333</div> </template> // 父组件 <script setup> import Foot from './foot.vue' </script> <template> …… <foot> // 无内容,则会自动渲染默认组件/值 // 若有内容,则会替换默认组件/值 </foot> </template>
emits 定义自定义事件