提交学习笔记专用
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

666 lines
18 KiB

  1. ### setup 概述
  2. `setup`是`Vue3`中一个新的配置项,值是一个函数,组件中所用到的:数据、方法、计算属性、监视......等等,均配置在`setup`中。
  3. 特点如下:
  4. - `setup`函数返回的对象中的内容,可直接在模板中使用。
  5. - `setup`中访问`this`是`undefined`。
  6. - `setup`函数会在`beforeCreate`之前调用,它是“领先”所有钩子执行的。
  7. ```vue
  8. <template>
  9. <div class="person">
  10. <h2>姓名:{{name}}</h2>
  11. <h2>年龄:{{age}}</h2>
  12. <button @click="changeName">修改名字</button>
  13. <button @click="changeAge">年龄+1</button>
  14. <button @click="showTel">点我查看联系方式</button>
  15. </div>
  16. </template>
  17. <script lang="ts">
  18. export default {
  19. name:'Person',
  20. setup(){
  21. // 数据,原来写在data中(注意:此时的name、age、tel数据都不是响应式数据)
  22. let name = '张三'
  23. let age = 18
  24. let tel = '13888888888'
  25. // 方法,原来写在methods中
  26. function changeName(){
  27. name = 'zhang-san' //注意:此时这么修改name页面是不变化的
  28. console.log(name)
  29. }
  30. function changeAge(){
  31. age += 1 //注意:此时这么修改age页面是不变化的
  32. console.log(age)
  33. }
  34. function showTel(){
  35. alert(tel)
  36. }
  37. // 返回一个对象,对象中的内容,模板中可以直接使用
  38. return {name,age,tel,changeName,changeAge,showTel}
  39. }
  40. }
  41. </script>
  42. ```
  43. ### setup 的返回值
  44. - 若返回一个**对象**:则对象中的:属性、方法等,在模板中均可以直接使用**(重点关注)。**
  45. - 若返回一个**函数**:则可以自定义渲染内容,代码如下:
  46. ```jsx
  47. setup(){
  48. return ()=> '你好啊!'
  49. }
  50. ```
  51. ### setup 与 Options API 的关系
  52. - `Vue2` 的配置(`data`、`methos`......)中**可以访问到** `setup`中的属性、方法。
  53. - 但在`setup`中**不能访问到**`Vue2`的配置(`data`、`methos`......)。
  54. - 如果与`Vue2`冲突,则`setup`优先。
  55. ### setup 语法糖
  56. `setup`函数有一个语法糖,这个语法糖,可以让我们把`setup`独立出去,代码如下:
  57. ```vue
  58. <template>
  59. <div class="person">
  60. <h2>姓名:{{name}}</h2>
  61. <h2>年龄:{{age}}</h2>
  62. <button @click="changName">修改名字</button>
  63. <button @click="changAge">年龄+1</button>
  64. <button @click="showTel">点我查看联系方式</button>
  65. </div>
  66. </template>
  67. <script lang="ts">
  68. export default {
  69. name:'Person',
  70. }
  71. </script>
  72. <!-- 下面的写法是setup语法糖 -->
  73. <script setup lang="ts">
  74. console.log(this) //undefined
  75. // 数据(注意:此时的name、age、tel都不是响应式数据)
  76. let name = '张三'
  77. let age = 18
  78. let tel = '13888888888'
  79. // 方法
  80. function changName(){
  81. name = '李四'//注意:此时这么修改name页面是不变化的
  82. }
  83. function changAge(){
  84. console.log(age)
  85. age += 1 //注意:此时这么修改age页面是不变化的
  86. }
  87. function showTel(){
  88. alert(tel)
  89. }
  90. </script>
  91. ```
  92. 扩展:上述代码,还需要编写一个不写`setup`的`script`标签,去指定组件名字,比较麻烦,我们可以借助`vite`中的插件简化
  93. 1. 第一步:`npm i vite-plugin-vue-setup-extend -D`
  94. 2. 第二步:`vite.config.ts`
  95. ```jsx
  96. import { defineConfig } from 'vite'
  97. import VueSetupExtend from 'vite-plugin-vue-setup-extend'
  98. export default defineConfig({
  99. plugins: [ VueSetupExtend() ]
  100. })
  101. ```
  102. 3. 第三步:`<script setup lang="ts" name="Person">`
  103. ## 【ref 创建:基本类型的响应式数据】
  104. - **作用:**定义响应式变量。
  105. - **语法:**`let xxx = ref(初始值)`。
  106. - **返回值:**一个`RefImpl`的实例对象,简称`ref对象`或`ref`,`ref`对象的`value`**属性是响应式的**。
  107. - **注意点:**
  108. - `JS`中操作数据需要:`xxx.value`,但模板中不需要`.value`,直接使用即可。
  109. - 对于`let name = ref('张三')`来说,`name`不是响应式的,`name.value`是响应式的。
  110. ```vue
  111. <template>
  112. <div class="person">
  113. <h2>姓名:{{name}}</h2>
  114. <h2>年龄:{{age}}</h2>
  115. <button @click="changeName">修改名字</button>
  116. <button @click="changeAge">年龄+1</button>
  117. <button @click="showTel">点我查看联系方式</button>
  118. </div>
  119. </template>
  120. <script setup lang="ts" name="Person">
  121. import {ref} from 'vue'
  122. // name和age是一个RefImpl的实例对象,简称ref对象,它们的value属性是响应式的。
  123. let name = ref('张三')
  124. let age = ref(18)
  125. // tel就是一个普通的字符串,不是响应式的
  126. let tel = '13888888888'
  127. function changeName(){
  128. // JS中操作ref对象时候需要.value
  129. name.value = '李四'
  130. console.log(name.value)
  131. // 注意:name不是响应式的,name.value是响应式的,所以如下代码并不会引起页面的更新。
  132. // name = ref('zhang-san')
  133. }
  134. function changeAge(){
  135. // JS中操作ref对象时候需要.value
  136. age.value += 1
  137. console.log(age.value)
  138. }
  139. function showTel(){
  140. alert(tel)
  141. }
  142. </script>
  143. ```
  144. ## 【reactive 创建:对象类型的响应式数据】
  145. - **作用:**定义一个**响应式对象**(基本类型不要用它,要用`ref`,否则报错)
  146. - **语法:**`let 响应式对象= reactive(源对象)`。
  147. - **返回值:**一个`Proxy`的实例对象,简称:响应式对象。
  148. - **注意点:**`reactive`定义的响应式数据是“深层次”的。
  149. ```vue
  150. <template>
  151. <div class="person">
  152. <h2>汽车信息:一台{{ car.brand }}汽车,价值{{ car.price }}万</h2>
  153. <h2>游戏列表:</h2>
  154. <ul>
  155. <li v-for="g in games" :key="g.id">{{ g.name }}</li>
  156. </ul>
  157. <h2>测试:{{obj.a.b.c.d}}</h2>
  158. <button @click="changeCarPrice">修改汽车价格</button>
  159. <button @click="changeFirstGame">修改第一游戏</button>
  160. <button @click="test">测试</button>
  161. </div>
  162. </template>
  163. <script lang="ts" setup name="Person">
  164. import { reactive } from 'vue'
  165. // 数据
  166. let car = reactive({ brand: '奔驰', price: 100 })
  167. let games = reactive([
  168. { id: 'ahsgdyfa01', name: '英雄联盟' },
  169. { id: 'ahsgdyfa02', name: '王者荣耀' },
  170. { id: 'ahsgdyfa03', name: '原神' }
  171. ])
  172. let obj = reactive({
  173. a:{
  174. b:{
  175. c:{
  176. d:666
  177. }
  178. }
  179. }
  180. })
  181. function changeCarPrice() {
  182. car.price += 10
  183. }
  184. function changeFirstGame() {
  185. games[0].name = '流星蝴蝶剑'
  186. }
  187. function test(){
  188. obj.a.b.c.d = 999
  189. }
  190. </script>
  191. ```
  192. ## 【ref 创建:对象类型的响应式数据】
  193. - 其实`ref`接收的数据可以是:**基本类型**、**对象类型**。
  194. - 若`ref`接收的是对象类型,内部其实也是调用了`reactive`函数。
  195. ```vue
  196. <template>
  197. <div class="person">
  198. <h2>汽车信息:一台{{ car.brand }}汽车,价值{{ car.price }}万</h2>
  199. <h2>游戏列表:</h2>
  200. <ul>
  201. <li v-for="g in games" :key="g.id">{{ g.name }}</li>
  202. </ul>
  203. <h2>测试:{{obj.a.b.c.d}}</h2>
  204. <button @click="changeCarPrice">修改汽车价格</button>
  205. <button @click="changeFirstGame">修改第一游戏</button>
  206. <button @click="test">测试</button>
  207. </div>
  208. </template>
  209. <script lang="ts" setup name="Person">
  210. import { ref } from 'vue'
  211. // 数据
  212. let car = ref({ brand: '奔驰', price: 100 })
  213. let games = ref([
  214. { id: 'ahsgdyfa01', name: '英雄联盟' },
  215. { id: 'ahsgdyfa02', name: '王者荣耀' },
  216. { id: 'ahsgdyfa03', name: '原神' }
  217. ])
  218. let obj = ref({
  219. a:{
  220. b:{
  221. c:{
  222. d:666
  223. }
  224. }
  225. }
  226. })
  227. console.log(car)
  228. function changeCarPrice() {
  229. car.value.price += 10
  230. }
  231. function changeFirstGame() {
  232. games.value[0].name = '流星蝴蝶剑'
  233. }
  234. function test(){
  235. obj.value.a.b.c.d = 999
  236. }
  237. </script>
  238. ```
  239. ## 【ref 对比 reactive】
  240. 宏观角度看:
  241. > 1. `ref`用来定义:**基本类型数据**、**对象类型数据**;
  242. >
  243. > 2. `reactive`用来定义:**对象类型数据**。
  244. - 区别:
  245. > 1. `ref`创建的变量必须使用`.value`(可以使用`volar`插件自动添加`.value`)。
  246. >
  247. > <img src="C:/Users/Administrator/Desktop/资料/images/自动补充value.png" alt="自动补充value" style="zoom:50%;border-radius:20px" />
  248. >
  249. > 2. `reactive`重新分配一个新对象,会**失去**响应式(可以使用`Object.assign`去整体替换)。
  250. - 使用原则:
  251. > 1. 若需要一个基本类型的响应式数据,必须使用`ref`。
  252. > 2. 若需要一个响应式对象,层级不深,`ref`、`reactive`都可以。
  253. > 3. 若需要一个响应式对象,且层级较深,推荐使用`reactive`。
  254. ## 【toRefs 与 toRef】
  255. - 作用:将一个响应式对象中的每一个属性,转换为`ref`对象。
  256. - 备注:`toRefs`与`toRef`功能一致,但`toRefs`可以批量转换。
  257. - 语法如下:
  258. ```vue
  259. <template>
  260. <div class="person">
  261. <h2>姓名:{{person.name}}</h2>
  262. <h2>年龄:{{person.age}}</h2>
  263. <h2>性别:{{person.gender}}</h2>
  264. <button @click="changeName">修改名字</button>
  265. <button @click="changeAge">修改年龄</button>
  266. <button @click="changeGender">修改性别</button>
  267. </div>
  268. </template>
  269. <script lang="ts" setup name="Person">
  270. import {ref,reactive,toRefs,toRef} from 'vue'
  271. // 数据
  272. let person = reactive({name:'张三', age:18, gender:'男'})
  273. // 通过toRefs将person对象中的n个属性批量取出,且依然保持响应式的能力
  274. let {name,gender} = toRefs(person)
  275. // 通过toRef将person对象中的gender属性取出,且依然保持响应式的能力
  276. let age = toRef(person,'age')
  277. // 方法
  278. function changeName(){
  279. name.value += '~'
  280. }
  281. function changeAge(){
  282. age.value += 1
  283. }
  284. function changeGender(){
  285. gender.value = '女'
  286. }
  287. </script>
  288. ```
  289. ## 【computed】
  290. 作用:根据已有数据计算出新数据(和`Vue2`中的`computed`作用一致)。
  291. <img src="C:/Users/Administrator/Desktop/资料/images/computed.gif" style="zoom:20%;" />
  292. ```vue
  293. <template>
  294. <div class="person">
  295. 姓:<input type="text" v-model="firstName"> <br>
  296. 名:<input type="text" v-model="lastName"> <br>
  297. 全名:<span>{{fullName}}</span> <br>
  298. <button @click="changeFullName">全名改为:li-si</button>
  299. </div>
  300. </template>
  301. <script setup lang="ts" name="App">
  302. import {ref,computed} from 'vue'
  303. let firstName = ref('zhang')
  304. let lastName = ref('san')
  305. // 计算属性——只读取,不修改
  306. /* let fullName = computed(()=>{
  307. return firstName.value + '-' + lastName.value
  308. }) */
  309. // 计算属性——既读取又修改
  310. let fullName = computed({
  311. // 读取
  312. get(){
  313. return firstName.value + '-' + lastName.value
  314. },
  315. // 修改
  316. set(val){
  317. console.log('有人修改了fullName',val)
  318. firstName.value = val.split('-')[0]
  319. lastName.value = val.split('-')[1]
  320. }
  321. })
  322. function changeFullName(){
  323. fullName.value = 'li-si'
  324. }
  325. </script>
  326. ```
  327. ## 【watch】
  328. - 作用:监视数据的变化(和`Vue2`中的`watch`作用一致)
  329. - 特点:`Vue3`中的`watch`只能监视以下**四种数据**:
  330. > 1. `ref`定义的数据。
  331. > 2. `reactive`定义的数据。
  332. > 3. 函数返回一个值(`getter`函数)。
  333. > 4. 一个包含上述内容的数组。
  334. 我们在`Vue3`中使用`watch`的时候,通常会遇到以下几种情况:
  335. ### * 情况一
  336. 监视`ref`定义的【基本类型】数据:直接写数据名即可,监视的是其`value`值的改变。
  337. ```vue
  338. <template>
  339. <div class="person">
  340. <h1>情况一:监视【ref】定义的【基本类型】数据</h1>
  341. <h2>当前求和为:{{sum}}</h2>
  342. <button @click="changeSum">点我sum+1</button>
  343. </div>
  344. </template>
  345. <script lang="ts" setup name="Person">
  346. import {ref,watch} from 'vue'
  347. // 数据
  348. let sum = ref(0)
  349. // 方法
  350. function changeSum(){
  351. sum.value += 1
  352. }
  353. // 监视,情况一:监视【ref】定义的【基本类型】数据
  354. const stopWatch = watch(sum,(newValue,oldValue)=>{
  355. console.log('sum变化了',newValue,oldValue)
  356. if(newValue >= 10){
  357. stopWatch()
  358. }
  359. })
  360. </script>
  361. ```
  362. ### * 情况二
  363. 监视`ref`定义的【对象类型】数据:直接写数据名,监视的是对象的【地址值】,若想监视对象内部的数据,要手动开启深度监视。
  364. > 注意:
  365. >
  366. > * 若修改的是`ref`定义的对象中的属性,`newValue` 和 `oldValue` 都是新值,因为它们是同一个对象。
  367. >
  368. > * 若修改整个`ref`定义的对象,`newValue` 是新值, `oldValue` 是旧值,因为不是同一个对象了。
  369. ```vue
  370. <template>
  371. <div class="person">
  372. <h1>情况二:监视【ref】定义的【对象类型】数据</h1>
  373. <h2>姓名:{{ person.name }}</h2>
  374. <h2>年龄:{{ person.age }}</h2>
  375. <button @click="changeName">修改名字</button>
  376. <button @click="changeAge">修改年龄</button>
  377. <button @click="changePerson">修改整个人</button>
  378. </div>
  379. </template>
  380. <script lang="ts" setup name="Person">
  381. import {ref,watch} from 'vue'
  382. // 数据
  383. let person = ref({
  384. name:'张三',
  385. age:18
  386. })
  387. // 方法
  388. function changeName(){
  389. person.value.name += '~'
  390. }
  391. function changeAge(){
  392. person.value.age += 1
  393. }
  394. function changePerson(){
  395. person.value = {name:'李四',age:90}
  396. }
  397. /*
  398. 监视,情况一:监视【ref】定义的【对象类型】数据,监视的是对象的地址值,若想监视对象内部属性的变化,需要手动开启深度监视
  399. watch的第一个参数是:被监视的数据
  400. watch的第二个参数是:监视的回调
  401. watch的第三个参数是:配置对象(deep、immediate等等.....)
  402. */
  403. watch(person,(newValue,oldValue)=>{
  404. console.log('person变化了',newValue,oldValue)
  405. },{deep:true})
  406. </script>
  407. ```
  408. ### * 情况三
  409. 监视`reactive`定义的【对象类型】数据,且默认开启了深度监视。
  410. ```vue
  411. <template>
  412. <div class="person">
  413. <h1>情况三:监视【reactive】定义的【对象类型】数据</h1>
  414. <h2>姓名:{{ person.name }}</h2>
  415. <h2>年龄:{{ person.age }}</h2>
  416. <button @click="changeName">修改名字</button>
  417. <button @click="changeAge">修改年龄</button>
  418. <button @click="changePerson">修改整个人</button>
  419. <hr>
  420. <h2>测试:{{obj.a.b.c}}</h2>
  421. <button @click="test">修改obj.a.b.c</button>
  422. </div>
  423. </template>
  424. <script lang="ts" setup name="Person">
  425. import {reactive,watch} from 'vue'
  426. // 数据
  427. let person = reactive({
  428. name:'张三',
  429. age:18
  430. })
  431. let obj = reactive({
  432. a:{
  433. b:{
  434. c:666
  435. }
  436. }
  437. })
  438. // 方法
  439. function changeName(){
  440. person.name += '~'
  441. }
  442. function changeAge(){
  443. person.age += 1
  444. }
  445. function changePerson(){
  446. Object.assign(person,{name:'李四',age:80})
  447. }
  448. function test(){
  449. obj.a.b.c = 888
  450. }
  451. // 监视,情况三:监视【reactive】定义的【对象类型】数据,且默认是开启深度监视的
  452. watch(person,(newValue,oldValue)=>{
  453. console.log('person变化了',newValue,oldValue)
  454. })
  455. watch(obj,(newValue,oldValue)=>{
  456. console.log('Obj变化了',newValue,oldValue)
  457. })
  458. </script>
  459. ```
  460. ### * 情况四
  461. 监视`ref`或`reactive`定义的【对象类型】数据中的**某个属性**,注意点如下:
  462. 1. 若该属性值**不是**【对象类型】,需要写成函数形式。
  463. 2. 若该属性值是**依然**是【对象类型】,可直接编,也可写成函数,建议写成函数。
  464. 结论:监视的要是对象里的属性,那么最好写函数式,注意点:若是对象监视的是地址值,需要关注对象内部,需要手动开启深度监视。
  465. ```vue
  466. <template>
  467. <div class="person">
  468. <h1>情况四:监视【ref】或【reactive】定义的【对象类型】数据中的某个属性</h1>
  469. <h2>姓名:{{ person.name }}</h2>
  470. <h2>年龄:{{ person.age }}</h2>
  471. <h2>汽车:{{ person.car.c1 }}、{{ person.car.c2 }}</h2>
  472. <button @click="changeName">修改名字</button>
  473. <button @click="changeAge">修改年龄</button>
  474. <button @click="changeC1">修改第一台车</button>
  475. <button @click="changeC2">修改第二台车</button>
  476. <button @click="changeCar">修改整个车</button>
  477. </div>
  478. </template>
  479. <script lang="ts" setup name="Person">
  480. import {reactive,watch} from 'vue'
  481. // 数据
  482. let person = reactive({
  483. name:'张三',
  484. age:18,
  485. car:{
  486. c1:'奔驰',
  487. c2:'宝马'
  488. }
  489. })
  490. // 方法
  491. function changeName(){
  492. person.name += '~'
  493. }
  494. function changeAge(){
  495. person.age += 1
  496. }
  497. function changeC1(){
  498. person.car.c1 = '奥迪'
  499. }
  500. function changeC2(){
  501. person.car.c2 = '大众'
  502. }
  503. function changeCar(){
  504. person.car = {c1:'雅迪',c2:'爱玛'}
  505. }
  506. // 监视,情况四:监视响应式对象中的某个属性,且该属性是基本类型的,要写成函数式
  507. /* watch(()=> person.name,(newValue,oldValue)=>{
  508. console.log('person.name变化了',newValue,oldValue)
  509. }) */
  510. // 监视,情况四:监视响应式对象中的某个属性,且该属性是对象类型的,可以直接写,也能写函数,更推荐写函数
  511. watch(()=>person.car,(newValue,oldValue)=>{
  512. console.log('person.car变化了',newValue,oldValue)
  513. },{deep:true})
  514. </script>
  515. ```
  516. ### * 情况五
  517. 监视上述的多个数据
  518. ```vue
  519. <template>
  520. <div class="person">
  521. <h1>情况五:监视上述的多个数据</h1>
  522. <h2>姓名:{{ person.name }}</h2>
  523. <h2>年龄:{{ person.age }}</h2>
  524. <h2>汽车:{{ person.car.c1 }}、{{ person.car.c2 }}</h2>
  525. <button @click="changeName">修改名字</button>
  526. <button @click="changeAge">修改年龄</button>
  527. <button @click="changeC1">修改第一台车</button>
  528. <button @click="changeC2">修改第二台车</button>
  529. <button @click="changeCar">修改整个车</button>
  530. </div>
  531. </template>
  532. <script lang="ts" setup name="Person">
  533. import {reactive,watch} from 'vue'
  534. // 数据
  535. let person = reactive({
  536. name:'张三',
  537. age:18,
  538. car:{
  539. c1:'奔驰',
  540. c2:'宝马'
  541. }
  542. })
  543. // 方法
  544. function changeName(){
  545. person.name += '~'
  546. }
  547. function changeAge(){
  548. person.age += 1
  549. }
  550. function changeC1(){
  551. person.car.c1 = '奥迪'
  552. }
  553. function changeC2(){
  554. person.car.c2 = '大众'
  555. }
  556. function changeCar(){
  557. person.car = {c1:'雅迪',c2:'爱玛'}
  558. }
  559. // 监视,情况五:监视上述的多个数据
  560. watch([()=>person.name,person.car],(newValue,oldValue)=>{
  561. console.log('person.car变化了',newValue,oldValue)
  562. },{deep:true})
  563. </script>
  564. ```