ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

vue基础(第二章 指令 )

vue基础(第二章 指令 ) 目录1:创建自己的第一个vue1.1:项目结构1.2:创建自己的第一个vue3项目1.2.1main.ts项目入口1.2.2App.vue项目根组件1.2.3启动项目2:添加子组件2.1子组件person2.2子组件需要注册到根组件app.vue中分为导入、注册、使用3子组件分析setup3.1setup函数3.2setup函数语法糖3.2.1自动注册app.vue4ref响应式数据用于axios的ajax请求4.1默认不是响应式修改不会改变4.2ref函数实现响应式数据原始类型4.3reactive函数实现响应式数据只支持对象类型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:创建自己的第一个vue1.1:项目结构首先我们知道了vue的项目结构1.2:创建自己的第一个vue3项目首先删除src下的所有文件自己创建main.ts和App.vue1.2.1main.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.2App.vue项目根组件!-- 根组件 每一个vue组件都是从这里开始的文件扩展名是.vue -- !-- 一个合法的Vue组件有三部分组成 模板template、脚本script、样式style-- template div idapp h1Hello Vue3!/h1 /div /template !-- 基于ts -- script langts // 默认导出 在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; } /style1.2.3启动项目2:添加子组件2.1子组件person!-- person子组件 -- template div classperson h1我是person组件/h1 h2姓名{{ name }}/h2 h2 classphone/h2 button clickshowPhone()显示联系电话click/button button v-on:clickshowPhone()显示联系电话v-on:click/button /div /template script langts 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 langcss scoped .person { border: 1px solid #ccc; padding: 20px; margin: 20px; } /style2.2子组件需要注册到根组件app.vue中分为导入、注册、使用!-- 根组件 每一个vue组件都是从这里开始的文件扩展名是.vue -- !-- 一个合法的Vue组件有三部分组成 模板template、脚本script、样式style-- template div idapp h1Hello Vue3!/h1 !-- 3:使用子组件 -- person/person /div /template !-- 基于ts -- script langts // 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; } /style3子组件分析setup3.1setup函数!-- person子组件 -- template div classperson h1我是person组件/h1 h2姓名{{ name }}/h2 h2 classphone/h2 button clickshowPhone()显示联系电话click/button button v-on:clickshowPhone()显示联系电话v-on:click/button /div /template script langts 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 langcss scoped .person { border: 1px solid #ccc; padding: 20px; margin: 20px; } /style3.2setup函数语法糖3.2.1自动注册app.vue!-- 根组件 每一个vue组件都是从这里开始的文件扩展名是.vue -- !-- 一个合法的Vue组件有三部分组成 模板template、脚本script、样式style-- template div idapp h1Hello Vue3!/h1 !-- 3:使用子组件 -- person/person cat/cat /div /template !-- 基于ts setup语法糖 不需要注册了 只需导入和使用组件-- script langts setup // 1:导入子组件 import Person from ./componnets/person.vue import Cat from ./componnets/cat.vue; // // 默认导出 在main.ts中会导入这个组件 // export default { // //2:注册子组件 // components:{Person,Cat} // } /script4ref响应式数据用于axios的ajax请求4.1默认不是响应式修改不会改变template div h1狗狗/h1 h1名字{{ name }}/h1 h1年龄{{ age }}/h1 button v-on:clickchangename修改名字不会改变需要响应式/button button v-on:clickchangeage修改age不会改变需要响应式/button /div /template script langts setup let name 小黑 let age 0; function changename() { name 小黑1; } function changeage() { age; } /script style/style4.2ref函数实现响应式数据原始类型原理//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 代理壳子。 //ProxyJS 原生 API可以拦截对象的读取、修改、删除等所有操作 let person reactive({ name: 张三, age: 18, flag: true, cat: { name: 小白, color: 黄色, } }) console.log(person); // reactive定义一个基础类型的响应式数据无效不能在模板中使用 let addressreactive(中国) 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 h1Person 组件/h1 p姓名: {{ name }}/p button clickchangeName改变姓名1/button hr !--在模板中使用响应式数据不需要通过value属性来访问 -- p年龄: {{ age }}/p button clickchangeAge改变年龄1/button /div hr div h1reactive 组件/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 clickchangeFlag改变是否显示/button button clickchangeName1改变姓名2/button button clickchangeAge1改变年龄2/button button clickchangeAddress改变地址/button button clickchangeCatName改变猫的姓名/button button clickchangeCatColor改变猫的颜色/button button clickchangePerson改变个人的信息/button /div /template style scoped h1 { color: red; } /style4.3reactive函数实现响应式数据只支持对象类型原理//reactive 就是给对象 / 数组套上一层 Proxy 代理壳子。//ProxyJS 原生 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 代理壳子。 //ProxyJS 原生 API可以拦截对象的读取、修改、删除等所有操作 let person reactive({ name: 张三, age: 18, flag: true, cat: { name: 小白, color: 黄色, } }) console.log(person); // reactive定义一个基础类型的响应式数据无效不能在模板中使用 let addressreactive(中国) 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 h1Person 组件/h1 p姓名: {{ name }}/p button clickchangeName改变姓名1/button hr !--在模板中使用响应式数据不需要通过value属性来访问 -- p年龄: {{ age }}/p button clickchangeAge改变年龄1/button /div hr div h1reactive 组件/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 clickchangeFlag改变是否显示/button button clickchangeName1改变姓名2/button button clickchangeAge1改变年龄2/button button clickchangeAddress改变地址/button /div /template style scoped h1 { color: red; } /style5:vue指令写在template里边5.1:v-bind绑定属性简写template div h1vue指令1 1:所有的指令需要写在template中 2:v-开头 v-bind指令: 1:v-bind:将setup函数暴露的数据绑定到标签的属性 /h1 h2v-bing指令/h2 img v-bind:srcimgObj.src :altimgObj.alt button clickchangeImg改变图片1/button button clickchangeImg v-bind:disabledhaha改变图片2/button /div h1 v-bind:titlenamev-bind指令组件动态的绑定一个或多个 attribute也可以是组件的 prop。/h1 p v-bind:titlename v-bind:style{color: red}姓名: {{ name }}/p p v-bind:titleage.toString() v-bind:style{color: green}年龄: {{ age }}/p img :srcimg2 alt图片 width200 height200 div :style{background-color: red} 这是div1 /div !-- 绑定对象形式的 attribute -- div v-bind{id: someProp, other-attr: otherProp}/div 这是div2 /template script langts setup import { reactive } from vue; let hahafalse; 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/style5.2:v-model双向数据绑定修改脚本数据页面数据更新修改页面数据脚本数据也会更新script setup langts import { ref } from vue const username ref() const password ref() function login() { // 直接拿变量就是输入框里面的内容 console.log(username.value, password.value) } /script template input v-modelusername typetext placeholder用户名 / {{ username }} br input v-modelpassword typepassword placeholder密码 / {{ password }} /br br button clicklogin登录/button /template style/style5.3:v-for循环解析数据div h2v-for/h2 ul li v-for(person,index) in listPerson :keyperson.id ID{{person.id}},名字 {{person.name}}, 年龄{{person.age}} ,index {{index}} /li /ul ul li v-for(value,name,index) in persons :keyname 属性值{{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 langts namePerson 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-modelfirstName typetext placeholder请输入姓/input/span span名: input v-modellastName typetext 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 langts namePerson //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 h1watch 监听响应式数据的变化/h1 span百度搜索: input v-modelname typetext placeholder请输入搜索内容/input/span span搜索内容: {{ name }}/span hr span搜索结果/span /template style scoped h1 { color: red; } /style8:标签的ref属性8.1:普通标签的ref属性用来屏蔽标签的id相同情况下的错误父组件标签 template div classfu !-- ref的名字一样。也不影响 -- h1 refh1父组件1/h1 button clickgetfu父元素点击/button zi/zi /div /template script langts 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 classzi h1子组件1/h1 !-- ref的名字一样。也不影响 -- button refh1子组件点击/button button clickgetzibut子组件点击获取/button /div /template script langts setup import { ref } from vue; //一下代码在组件创建过程中执行 let h1ref();//zibut必须和refh1的名字一样 function getzibut(){ console.log(h1.value) //button子组件点击/button } /script style .zi { background-color: aqua; } /style8.2:组件标签的ref属性用来子组件向父组件传递数据父组件 template div classfu !-- ref的名字一样。也不影响 -- h1 refh1父组件1/h1 button clickgetfu父元素点击/button !-- 用refmuzi标记子组件 创建整个子组件实例 -- zi refmuzi/zi button clickgetfushili父元素点击获取子组件实例/button /div /template script langts 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.age100; //修改数据子组件数据也会改变 muzi.value.myfunction(); } /script style .fu { background-color: blue; } /style 子组件 template div classzi h1子组件1/h1 !-- ref的名字一样。也不影响 -- button refh1子组件点击/button button clickgetzibut子组件点击获取/button /div /template script langts setup import { reactive, ref } from vue; //一下代码在组件创建过程中执行 let h1 ref();//zibut必须和refh1的名字一样 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; } /style9:props 父组件向子组件传递数据子组件不能修改父组件 template div classfu h1父组件1/h1 !-- 父组件通过props传递数据 -- zi namejack :ageage :list1lists/zi /div /template script langts 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 classzi h1子组件1/h1 h1名字{{ name }}/h1 h1年龄{{ age }}/h1 ul li v-foritem in list1 :keyitem.id id: {{ item.id }} name: {{ item.name }} age: {{ item.age }} /li /ul /div /template script langts setup // 全局函数数组接受父组件数据 defineProps([name, age, list1]) // console.log(name, name); // console.log(age, age); /script style .zi { background-color: aqua; } /style10:vue3的生命周期1:创建 2:挂载 3:更新 4:卸载template v-ifflag div h1组件的生命周期/h1 h2计数器{{num}}/h2 button clickchangeNum点击/button /div /template script langts setup import { onBeforeMount, onBeforeUnmount, onBeforeUpdate, onMounted, onUnmounted, onUpdated,ref } from vue; let numref(0); let flagref(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(销毁后) }) /script11:组合式API的Hooks逻辑复用11.1:使用hooks之前template div h1不使用hooks/h1 h1数字{{ num }}/h1 button clickadd计数器1/button div img v-foritem in images :keyitem :srcitem alt加载失败 button clickgetImage添加图片/button /div /div /template script langts setup import axios from axios; import { ref } from vue; let num ref(0); function add() { num.value; } //https://api.btstu.cn/sjbz/api.php?formatjson let images ref([https://picsum.photos/800/600]); async function getImage() { // let responseawait axios.get(https://api.btstu.cn/sjbz/api.php?formatjson); // let josnresponse.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/style11.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 clickadd计数器1/button div img v-foritem in images :keyitem :srcitem alt加载失败 button clickgetImage添加图片/button /div /div /template script langts setup //导入钩子 import useCount from ../hooks/useCount; import userImg from ../hooks/userImg; //调用钩子变量和方法 const { num, add } useCount(); const { images, getImage } userImg() /script style/style
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进