目录
1:创建自己的第一个vue
1.1:项目结构
1.2:创建自己的第一个vue3项目
1.2.1:main.ts项目入口
1.2.2:App.vue项目根组件
1.2.3:启动项目
2:添加子组件
2.1:子组件person
2.2:子组件需要注册到根组件app.vue中,分为导入、注册、使用
3:子组件分析setup
3.1:setup函数
3.2:setup函数语法糖
3.2.1:自动注册app.vue
4:ref响应式数据(用于axios的ajax请求)
4.1:默认不是响应式,修改不会改变
4.2:ref函数实现响应式数据(原始类型)
4.3:reactive函数实现响应式数据(只支持对象类型)
5:vue指令
5.1:v-bind绑定属性
5.2:v-model双向数据绑定
5.3:v-for循环解析数据
6:计算属性computed(只读不能修改)有缓存机制
7:watch监视用来数据修改后执行回调函数
8:标签的ref属性
8.1:普通标签的ref属性,用来屏蔽标签的id相同情况下的错误
8.2:组件标签的ref属性,用来子组件向父组件传递数据
9:props 父组件向子组件传递数据,子组件不能修改
10:vue3的生命周期
11:组合式API的Hooks(逻辑复用)
11.1:使用hooks之前
11.2:使用hook之后
1:创建自己的第一个vue
1.1:项目结构
首先我们知道了vue的项目结构
1.2:创建自己的第一个vue3项目
首先删除src下的所有文件,自己创建main.ts和App.vue
1.2.1:main.ts项目入口
// 第一步:创建main.ts文件,作为项目的入口文件 //1:引入Vue的createApp函数和根组件App.vue,作用是创建Vue应用实例和挂载根组件 import { createApp } from 'vue' //2:引入根组件App.vue,作为应用的根组件 import App from './App.vue' //3:创建Vue应用实例 const app11 = createApp(App) //4:挂载应用实例到index.html中的#app元素上 app11.mount('#app')1.2.2:App.vue项目根组件
<!-- 根组件 每一个vue组件都是从这里开始的,文件扩展名是.vue --> <!-- 一个合法的Vue组件有三部分组成 :模板template、脚本script、样式style--> <template> <div id="app"> <h1>Hello Vue3!</h1> </div> </template> <!-- 基于ts --> <script lang="ts"> // 默认导出 在main.ts中会导入这个组件 export default { // 影响到VUE浏览器插件的显示 name: 'App', setup() { // 组件的逻辑代码 } } </script> <!-- scoped 局部样式 --> <style scoped> /* #.id .class 设置样式*/ #app { font-family: Avenir, Helvetica, Arial, sans-serif; background-color: #f5f5f5; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 60px; } </style>1.2.3:启动项目
2:添加子组件
2.1:子组件person
<!-- person子组件 --> <template> <div class="person"> <h1>我是person组件</h1> <h2>姓名:{{ name }}</h2> <h2 class="phone"></h2> <button @click="showPhone()">显示联系电话@click</button> <button v-on:click="showPhone()">显示联系电话v-on:click</button> </div> </template> <script lang="ts"> export default { // vue的插件组件显示名字 name: 'Person', //vue3中使用setup函数来定义组件的逻辑代码 ,组件的逻辑代码。初始化执行一次 //setup只会执行一次 setup() { //准备数据 let name: string = '张三'; let phone: number = 1234567890; function showPhone() { alert(phone) }; //返回数据给模板使用 return { name: name,//暴露参数 showPhone: showPhone //暴露方法 }; } } </script> <style lang="css" scoped> .person { border: 1px solid #ccc; padding: 20px; margin: 20px; } </style>2.2:子组件需要注册到根组件app.vue中,分为导入、注册、使用
<!-- 根组件 每一个vue组件都是从这里开始的,文件扩展名是.vue --> <!-- 一个合法的Vue组件有三部分组成 :模板template、脚本script、样式style--> <template> <div id="app"> <h1>Hello Vue3!</h1> <!-- 3:使用子组件 --> <person></person> </div> </template> <!-- 基于ts --> <script lang="ts"> // 1:导入子组件 import Person from './componnets/person.vue' // 默认导出 在main.ts中会导入这个组件 export default { //2:注册子组件 components:{Person} } </script> <!-- scoped 局部样式 --> <style scoped> /* #.id .class 设置样式*/ #app { font-family: Avenir, Helvetica, Arial, sans-serif; background-color: #f5f5f5; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 60px; } </style>3:子组件分析setup
3.1:setup函数
<!-- person子组件 --> <template> <div class="person"> <h1>我是person组件</h1> <h2>姓名:{{ name }}</h2> <h2 class="phone"></h2> <button @click="showPhone()">显示联系电话@click</button> <button v-on:click="showPhone()">显示联系电话v-on:click</button> </div> </template> <script lang="ts"> export default { // vue的插件组件显示名字 name: 'Person', //vue3中使用setup函数来定义组件的逻辑代码。初始化执行一次 setup() { //准备数据 let name: string = '张三'; let phone: number = 1234567890; //定义方法 function showPhone() { alert(phone) }; //returu 返回数据给模板使用,这里是对象 return { name: name,//暴露参数 showPhone: showPhone //暴露方法 }; // return function(){ // return "渲染函数,会覆盖掉person组件中template中的所有模板内容" // }; } } </script> <style lang="css" scoped> .person { border: 1px solid #ccc; padding: 20px; margin: 20px; } </style>3.2:setup函数语法糖
3.2.1:自动注册app.vue
<!-- 根组件 每一个vue组件都是从这里开始的,文件扩展名是.vue --> <!-- 一个合法的Vue组件有三部分组成 :模板template、脚本script、样式style--> <template> <div id="app"> <h1>Hello Vue3!</h1> <!-- 3:使用子组件 --> <person></person> <cat></cat> </div> </template> <!-- 基于ts setup语法糖 不需要注册了 只需导入和使用组件--> <script lang="ts" setup> // 1:导入子组件 import Person from './componnets/person.vue' import Cat from './componnets/cat.vue'; // // 默认导出 在main.ts中会导入这个组件 // export default { // //2:注册子组件 // components:{Person,Cat} // } </script>4:ref响应式数据(用于axios的ajax请求)
4.1:默认不是响应式,修改不会改变
<template> <div> <h1>狗狗</h1> <h1>名字:{{ name }}</h1> <h1>年龄:{{ age }}</h1> <button v-on:click="changename">修改名字不会改变,需要响应式</button> <button v-on:click="changeage">修改age不会改变,需要响应式</button> </div> </template> <script lang="ts" setup> let name = "小黑" let age = 0; function changename() { name = "小黑1"; } function changeage() { age++; } </script> <style></style>4.2:ref函数实现响应式数据(原始类型)
原理:
//reference implementation(引用实现) refimpl
//ref → RefImpl 类实例,靠类的 get value / set value劫持;有dep、_rawValue、_value、__v_isRef。
//如果set value 时,会触发 dep.notify(),通知所有依赖它的响应式数据更新
//如果get value 时,会触发 dep.depend(),将当前响应式数据添加到 dep 中
<script setup> import { reactive, ref } from 'vue' //ref创建响应式数据,需要通过value属性来访问和修改 //适合的数据类型:字符串、数字、布尔值、对象、数组、函数等 //为什么基础类型必须用 ref // JS 的基本类型(number/string/boolean)不是对象,不能劫持 get、set。 // 所以 Vue 把简单值包在一个对象的 .value 属性上,就可以劫持读写,实现响应式。为什么基础类型必须用 ref const name = ref('张三'); const age = ref(18); const flag = ref(true); //这里的name和flag都是响应式数据,但是name和flag的value属性是字符串和布尔值 //reference implementation(引用实现) refimpl //ref → RefImpl 类实例,靠类的 get value / set value劫持;有dep、_rawValue、_value、__v_isRef。 //如果set value 时,会触发 dep.notify(),通知所有依赖它的响应式数据更新 //如果get value 时,会触发 dep.depend(),将当前响应式数据添加到 dep 中 //reactive → 返回 Proxy 对象,没有 RefImpl 这套属性。 // RefImpl { // dep: Dep, // __v_isRef: true, // __v_isShallow: false, // _rawValue: '张三', // _value: '张三' // } console.log(name); console.log(flag); // 定义一个函数,用于改变姓名 function changeName() { //响应式数据的改变,会导致模板中的数据更新,实现响应式效果 //使用ref创建的响应式数据,需要通过value属性来访问和修改 name.value = '李四' + Math.random().toString().substring(2, 10) } // 定义一个函数,用于改变年龄 function changeAge() { age.value++ } //reactive创建响应式数据,不需要通过value属性来访问和修改 //reactive创建的响应式数据,是对象,不能直接在模板中使用,需要解构赋值到变量中 //Proxy(Object) {name: '张三', age: 18, flag: true} //reactive 就是给对象 / 数组套上一层 Proxy 代理壳子。 //Proxy:JS 原生 API,可以拦截对象的读取、修改、删除等所有操作 let person = reactive({ name: '张三', age: 18, flag: true, cat: { name: '小白', color: '黄色', } }) console.log(person); // reactive定义一个基础类型的响应式数据,无效,不能在模板中使用 let address=reactive('中国') console.log(address); // 定义一个函数,用于改变是否显示 function changeFlag() { person.flag = !person.flag } // 定义一个函数,用于改变姓名2 不需要通过value属性来访问和修改 function changeName1() { person.name = "李四" + Math.random().toString().substring(2, 10) } // 定义一个函数,用于改变年龄2 不需要通过value属性来访问和修改 function changeAge1() { person.age++ } // 定义一个函数,用于改变地址 reactive不能创建基础类型的响应式数据,只能创建对象类型的响应式数据 function changeAddress() { address = "美国" } // 定义一个函数,用于改变猫的姓名 function changeCatName() { person.cat.name = '小白白' } // 定义一个函数,用于改变猫的颜色 function changeCatColor() { console.log(person.cat.color); person.cat.color = 'red' } // 定义一个函数,用于改变个人的信息 function changePerson() { //直接修改person,会导致模板中的数据更新,丢失响应式效果 //person 变量 → 👉 Proxy 代理对象(包装原始对象) //但是这里的person person 变量 → 👉 普通对象(不再是 Proxy!) // person={ // name: '张三9', // age: 189, // flag: true, // cat: { // name: '小白9', // color: '黄色9', // } // } //笨方法 一个一个修改 // person.cat.name = '小白2' // person.cat.color = '黄色2' //也可以这样做 Object.assign(person, { name: '张三9', age: 189, flag: true, cat: { name: '小白9', color: '黄色9', } }) } </script> <template> <div> <h1>Person 组件</h1> <p>姓名: {{ name }}</p> <button @click="changeName">改变姓名1</button> <hr> <!--在模板中使用响应式数据,不需要通过value属性来访问 --> <p>年龄: {{ age }}</p> <button @click="changeAge">改变年龄1</button> </div> <hr> <div> <h1>reactive 组件</h1> <p>姓名: {{ person.name }}</p> <p>年龄: {{ person.age }}</p> <p>是否显示: {{ person.flag }}</p> <!-- reactive定义一个基础类型的响应式数据,无效,不能在模板中使用 --> <p>地址: {{ address }}</p> <!-- reactive定义一个对象类型的响应式数据,有效,可以在模板中使用 --> <p>猫的姓名: {{ person.cat.name }}</p> <p>猫的颜色: {{ person.cat.color }}</p> <button @click="changeFlag">改变是否显示</button> <button @click="changeName1">改变姓名2</button> <button @click="changeAge1">改变年龄2</button> <button @click="changeAddress">改变地址</button> <button @click="changeCatName">改变猫的姓名</button> <button @click="changeCatColor">改变猫的颜色</button> <button @click="changePerson">改变个人的信息</button> </div> </template> <style scoped> h1 { color: red; } </style>4.3:reactive函数实现响应式数据(只支持对象类型)
原理:
//reactive 就是给对象 / 数组套上一层 Proxy 代理壳子。
//Proxy:JS 原生 API,可以拦截对象的读取、修改、删除等所有操作
//Proxy(Object) {name: '张三', age: 18, flag: true}
底层是代理对象
<script setup> import { reactive, ref } from 'vue' //ref创建响应式数据,需要通过value属性来访问和修改 //适合的数据类型:字符串、数字、布尔值、对象、数组、函数等 //为什么基础类型必须用 ref // JS 的基本类型(number/string/boolean)不是对象,不能劫持 get、set。 // 所以 Vue 把简单值包在一个对象的 .value 属性上,就可以劫持读写,实现响应式。为什么基础类型必须用 ref const name = ref('张三'); const age = ref(18); const flag = ref(true); //这里的name和flag都是响应式数据,但是name和flag的value属性是字符串和布尔值 //reference implementation(引用实现) refimpl //ref → RefImpl 类实例,靠类的 get value / set value劫持;有dep、_rawValue、_value、__v_isRef。 //如果set value 时,会触发 dep.notify(),通知所有依赖它的响应式数据更新 //如果get value 时,会触发 dep.depend(),将当前响应式数据添加到 dep 中 //reactive → 返回 Proxy 对象,没有 RefImpl 这套属性。 // RefImpl { // dep: Dep, // __v_isRef: true, // __v_isShallow: false, // _rawValue: '张三', // _value: '张三' // } console.log(name); console.log(flag); // 定义一个函数,用于改变姓名 function changeName() { //响应式数据的改变,会导致模板中的数据更新,实现响应式效果 //使用ref创建的响应式数据,需要通过value属性来访问和修改 name.value = '李四' + Math.random().toString().substring(2, 10) } // 定义一个函数,用于改变年龄 function changeAge() { age.value++ } //reactive创建响应式数据,不需要通过value属性来访问和修改 //reactive创建的响应式数据,是对象,不能直接在模板中使用,需要解构赋值到变量中 //Proxy(Object) {name: '张三', age: 18, flag: true} //reactive 就是给对象 / 数组套上一层 Proxy 代理壳子。 //Proxy:JS 原生 API,可以拦截对象的读取、修改、删除等所有操作 let person = reactive({ name: '张三', age: 18, flag: true, cat: { name: '小白', color: '黄色', } }) console.log(person); // reactive定义一个基础类型的响应式数据,无效,不能在模板中使用 let address=reactive('中国') console.log(address); // 定义一个函数,用于改变是否显示 function changeFlag() { person.flag = !person.flag } // 定义一个函数,用于改变姓名2 不需要通过value属性来访问和修改 function changeName1() { person.name = "李四" + Math.random().toString().substring(2, 10) } // 定义一个函数,用于改变年龄2 不需要通过value属性来访问和修改 function changeAge1() { person.age++ } // 定义一个函数,用于改变地址 function changeAddress() { address = "美国" } </script> <template> <div> <h1>Person 组件</h1> <p>姓名: {{ name }}</p> <button @click="changeName">改变姓名1</button> <hr> <!--在模板中使用响应式数据,不需要通过value属性来访问 --> <p>年龄: {{ age }}</p> <button @click="changeAge">改变年龄1</button> </div> <hr> <div> <h1>reactive 组件</h1> <p>姓名: {{ person.name }}</p> <p>年龄: {{ person.age }}</p> <p>是否显示: {{ person.flag }}</p> <!-- reactive定义一个基础类型的响应式数据,无效,不能在模板中使用 --> <p>地址: {{ address }}</p> <!-- reactive定义一个对象类型的响应式数据,有效,可以在模板中使用 --> <p>猫的姓名: {{ person.cat.name }}</p> <p>猫的颜色: {{ person.cat.color }}</p> <button @click="changeFlag">改变是否显示</button> <button @click="changeName1">改变姓名2</button> <button @click="changeAge1">改变年龄2</button> <button @click="changeAddress">改变地址</button> </div> </template> <style scoped> h1 { color: red; } </style>5:vue指令写在template里边
5.1:v-bind绑定属性简写:
<template> <div> <h1>vue指令1: 1:所有的指令需要写在template中 2:v-开头 v-bind指令: 1:v-bind:将setup函数暴露的数据,绑定到标签的属性 </h1> <h2>v-bing指令</h2> <img v-bind:src="imgObj.src" :alt="imgObj.alt"> <button @click="changeImg">改变图片1</button> <button @click="changeImg" v-bind:disabled="haha">改变图片2</button> </div> <h1 v-bind:title="name">v-bind指令组件动态的绑定一个或多个 attribute,也可以是组件的 prop。</h1> <p v-bind:title="name" v-bind:style="{'color': 'red'}">姓名: {{ name }}</p> <p v-bind:title="age.toString()" v-bind:style="{'color': 'green'}">年龄: {{ age }}</p> <img :src="img2" alt="图片" width="200" height="200"> <div :style="{'background-color': 'red'}"> 这是div1 </div> <!-- 绑定对象形式的 attribute --> <div v-bind="{'id': someProp, 'other-attr': otherProp}"></div> 这是div2 </template> <script lang="ts" setup> import { reactive } from 'vue'; let haha=false; let imgObj = reactive({ src: "https://picsum.photos/id/1036/800/500", alt: "没有图片" }) function changeImg() { imgObj.src = "https://picsum.photos/id/1048/800/500" } import { ref } from 'vue' // 通过 import 引入图片,Vite 会处理路径并返回正确的 URL import img2 from '../img/2.png' let name = ref('张三') let age = ref(18) let someProp = ref('haha') let otherProp = ref('aa') </script> <style></style>5.2:v-model双向数据绑定
修改脚本数据,页面数据更新,修改页面数据,脚本数据也会更新
<script setup lang="ts"> import { ref } from 'vue' const username = ref('') const password = ref('') function login() { // 直接拿变量,就是输入框里面的内容 console.log(username.value, password.value) } </script> <template> <input v-model="username" type="text" placeholder="用户名" /> {{ username }} <br> <input v-model="password" type="password" placeholder="密码" /> {{ password }} </br> <br> <button @click="login">登录</button> </template> <style></style>5.3:v-for循环解析数据
<div> <h2>v-for</h2> <ul> <li v-for="(person,index) in listPerson" :key="person.id"> ID:{{person.id}},名字: {{person.name}}, 年龄:{{person.age}} ,index {{index}} </li> </ul> <ul> <li v-for="(value,name,index) in persons" :key="name"> 属性值:{{value}},属性名字: {{name}}, 属性索引:{{index}} </li> </ul> </div> let listPerson = [ { id:1,name: '哈哈1', age: 21 }, { id:2,name: '哈哈2', age: 22 }, { id:3,name: '哈哈3', age: 23 }, ] let persons={id:1,name: '哈哈1', age: 21}6:计算属性computed(只读不能修改)有缓存机制
作用:基于已有响应式数据,计算出新的数据;带缓存,依赖不变就不会重复执行。
computed方法的值只要不发生变化,只会执行一次,无论几次调用,
核心特点(面试必背)
有缓存:依赖的数据没有发生变化,多次访问计算属性,函数只执行一次,直接拿缓存结果。
依赖响应式数据,依赖变,自动重新计算。
默认只读,不能直接赋值修改;可以配置 get + set 做成可写计算属性。
不要写异步请求、定时器在 computed 里面(computed 是同步计算)。
<script setup lang="ts" name="Person"> import { ref, computed } from 'vue' //没有使用计算属性,姓名是 firstName + lastName 的结果 let firstName = ref('') let lastName = ref('') // 计算属性,姓名是 firstName + lastName 的结果 // 计算属性的值是根据依赖的响应式数据变化而变化的 let fullName1 = (() => { console.log('fullName1 被调用了'); return firstName.value + lastName.value }) let fullName2 = computed(() => { console.log('fullName2 被调用了'); return firstName.value +"-"+ lastName.value }) </script> <template> <h1>没有使用计算属性</h1> <span>姓: <input v-model="firstName" type="text" placeholder="请输入姓"></input></span> <span>名: <input v-model="lastName" type="text" placeholder="请输入名"></input></span> <span>姓名: {{ firstName + lastName }}</span> <hr> <h1>不使用计算属性1.1</h1> <span>姓名: {{ fullName1() }}</span> <h1>不使用计算属性1.2</h1> <span>姓名: {{ fullName1() }}</span> <h1>使用计算属性2.1</h1> <span>姓名: {{ fullName2 }}</span> <h1>使用计算属性2.2</h1> <span>姓名: {{ fullName2 }}</span> </template> <style scoped> h1 { color: red; } </style> //结果 fullName1 被调用了 fullName1 被调用了 fullName2 被调用了7:watch监视用来数据修改后执行回调函数
作用:监听响应式数据变化,当数据发生改变时,执行一段业务逻辑(副作用)
computed:根据依赖算出新数据,要有 return,同步计算,有缓存
watch:监听数据变动,做后续动作,可以写异步、接口、存本地存储、弹窗,不需要 return
场景 1:搜索框,输入关键词,调后端搜索接口(高频)
<script setup lang="ts" name="Person"> //WATCH 监听响应式数据的变化 import { ref, watch } from 'vue' let name = ref('') // 监听 firstName 变化 就会执行回调函数 watch(() => name.value, (newVal, oldVal) => { // 模拟axios请求后台搜索接口 console.log('调用搜索接口,参数', newVal, oldVal) // api.get('/search',{params:{keyword:newVal}}) }) </script> <template> <h1>watch 监听响应式数据的变化</h1> <span>百度搜索: <input v-model="name" type="text" placeholder="请输入搜索内容"></input></span> <span>搜索内容: {{ name }}</span> <hr> <span>搜索结果</span> </template> <style scoped> h1 { color: red; } </style>8:标签的ref属性
8.1:普通标签的ref属性,用来屏蔽标签的id相同情况下的错误
父组件标签 <template> <div class="fu"> <!-- ref的名字一样。也不影响 --> <h1 ref="h1">父组件1</h1> <button @click="getfu">父元素点击</button> <zi></zi> </div> </template> <script lang="ts" setup> import { ref } from 'vue'; import zi from './zi.vue'; let h1 = ref(); function getfu() { console.log("父", h1.value) //<h1>父组件1</h1> } </script> <style> .fu { background-color: blue; } </style> 子组件标签 <template> <div class="zi"> <h1>子组件1</h1> <!-- ref的名字一样。也不影响 --> <button ref="h1">子组件点击</button> <button @click="getzibut">子组件点击获取</button> </div> </template> <script lang="ts" setup> import { ref } from 'vue'; //一下代码在组件创建过程中执行 let h1=ref();//zibut必须和ref="h1"的名字一样 function getzibut(){ console.log(h1.value) //<button>子组件点击</button> } </script> <style> .zi { background-color: aqua; } </style>8.2:组件标签的ref属性,用来子组件向父组件传递数据
父组件 <template> <div class="fu"> <!-- ref的名字一样。也不影响 --> <h1 ref="h1">父组件1</h1> <button @click="getfu">父元素点击</button> <!-- 用ref="muzi"标记子组件 创建整个子组件实例 --> <zi ref="muzi"></zi> <button @click="getfushili">父元素点击获取子组件实例</button> </div> </template> <script lang="ts" setup> import { ref } from 'vue'; import zi from './zi.vue'; let h1 = ref(); //这行代码核心 获取到子组件实例,需要子组件暴露 let muzi = ref(); function getfu() { console.log("父", h1.value) //<h1>父组件1</h1> } function getfushili() { //子组件实例 Proxy(Object) {__v_skip: true}[[Handler]]: // Object[[Target]]: Proxy(Object)[[IsRevoked]]: false //获取到子组件,然后操作子组件的变量 方法 console.log("子组件实例", muzi.value) console.log("普通数据1", muzi.value.a) console.log("响应数据1", muzi.value.b) muzi.value.b.age=100; //修改数据,子组件数据也会改变 muzi.value.myfunction(); } </script> <style> .fu { background-color: blue; } </style> 子组件 <template> <div class="zi"> <h1>子组件1</h1> <!-- ref的名字一样。也不影响 --> <button ref="h1">子组件点击</button> <button @click="getzibut">子组件点击获取</button> </div> </template> <script lang="ts" setup> import { reactive, ref } from 'vue'; //一下代码在组件创建过程中执行 let h1 = ref();//zibut必须和ref="h1"的名字一样 function getzibut() { console.log(h1.value) //<button>子组件点击</button> } //子组件普通数据和响应式数据 let a = "子组件普通数据"; let b = reactive({ name: "haha", age: 20 }); function myfunction() { console.log("子组件函数执行了"); } //暴露子组件的数据和函数,以便父组件使用 defineExpose({ a:a, b:b, myfunction:myfunction }) </script> <style> .zi { background-color: aqua; } </style>9:props 父组件向子组件传递数据,子组件不能修改
父组件 <template> <div class="fu"> <h1>父组件1</h1> <!-- 父组件通过props传递数据 --> <zi name="jack" :age="age" :list1="lists"></zi> </div> </template> <script lang="ts" setup> import { ref } from 'vue'; import zi from './zi.vue'; let age = ref(20) let lists = ref([ { name: "aa", age: 1 }, { name: "bb", age: 2 } ]) </script> <style> .fu { background-color: blue; } </style> 子组件 <template> <div class="zi"> <h1>子组件1</h1> <h1>名字:{{ name }}</h1> <h1>年龄:{{ age }}</h1> <ul> <li v-for="item in list1" :key="item.id"> id: {{ item.id }} name: {{ item.name }} age: {{ item.age }} </li> </ul> </div> </template> <script lang="ts" setup> // 全局函数,数组接受父组件数据 defineProps(["name", "age", "list1"]) // console.log("name", name); // console.log("age", age); </script> <style> .zi { background-color: aqua; } </style>10:vue3的生命周期
1:创建 2:挂载 3:更新 4:卸载
<template v-if="flag"> <div> <h1>组件的生命周期</h1> <h2>计数器:{{num}}</h2> <button @click="changeNum">点击</button> </div> </template> <script lang="ts" setup> import { onBeforeMount, onBeforeUnmount, onBeforeUpdate, onMounted, onUnmounted, onUpdated,ref } from 'vue'; let num=ref(0); let flag=ref(true); function changeNum(){ num.value++; } // 生命周期:创建执行一次 console.log("生命周期:创建") //生命周期:挂载 onBeforeMount(()=>{ console.log("挂载前") }) onMounted(()=>{ console.log("挂载后") }) //生命周期:修改 onBeforeUpdate(()=>{ console.log("修改前") }) onUpdated(()=>{ console.log("修改后") }) //生命周期:修改 onBeforeUnmount(()=>{ console.log("销毁前") }) onUnmounted(()=>{ console.log("销毁后") }) </script>11:组合式API的Hooks(逻辑复用)
11.1:使用hooks之前
<template> <div> <h1>不使用hooks</h1> <h1>数字{{ num }}</h1> <button @click="add">计数器+1</button> <div> <img v-for="item in images" :key="item" :src="item" alt="加载失败"> <button @click="getImage">添加图片</button> </div> </div> </template> <script lang="ts" setup> import axios from 'axios'; import { ref } from 'vue'; let num = ref(0); function add() { num.value++; } //'https://api.btstu.cn/sjbz/api.php?format=json' let images = ref(["https://picsum.photos/800/600"]); async function getImage() { // let response=await axios.get("https://api.btstu.cn/sjbz/api.php?format=json"); // let josn=response.data; // console.log(josn) const res = await axios.get('https://httpbin.org/image/png', { responseType: 'blob' }) const url = URL.createObjectURL(res.data) console.log('图片blob地址:', url) images.value.push(url); } </script> <style></style>11.2:使用hook之后
新建userCount.ts和userImg.ts,做到代码复用
import { ref } from "vue"; //默认导出 export default function () { //数据 let num = ref(0); //方法 function add() { num.value++; } //返回值 return { num, add }; } import { ref } from "vue"; //默认导出 export default function () { //数据 let num = ref(0); //方法 function add() { num.value++; } //返回值 return { num, add }; } <template> <div> <h1>不使用hooks</h1> <h1>数字{{ num }}</h1> <button @click="add">计数器+1</button> <div> <img v-for="item in images" :key="item" :src="item" alt="加载失败"> <button @click="getImage">添加图片</button> </div> </div> </template> <script lang="ts" setup> //导入钩子 import useCount from '../hooks/useCount'; import userImg from '../hooks/userImg'; //调用钩子变量和方法 const { num, add } = useCount(); const { images, getImage } = userImg() </script> <style></style>