尚硅谷Vue3学习笔记

编码规范

TypeScript+组合式API+setup语法糖

环境配置

npm create vue@latest
npm i
npm run dev

实例

初始

import { createApp } from 'vue'   // 引入createApp函数
import App from './App.vue'       // 从App.vue中导入组件,并将其命名为App

createApp(App).mount('#app')      // 调用createApp函数,将根组件App作为参数传入,并挂载在名为"app"的DOM节点上

一个.vue文件中应有三种标签:template,script(<script lang="ts">),style
Vite项目中,index.html是项目的入口文件,在项目的最外层。
加载index.html后,Vite解析(<script type="module" src="xxx">)指向的ts/js。
Vue3中是通过createApp函数创建一个应用实例。

核心语法

1.OptionsAPI与CompositionAPI

Vue2的API设计是Options风格的,Vue3的API设计是Composition风格的。

OptionsAPI的弊端

Options类型的API,数据、方法、计算属性等,是分散在:data、methods、computed中的,若想新增或者修改一个需求,就需要分别修改:data、methods、computed,不便于维护和复用。

CompositionAPI的优势

可以用函数的方式,更加优雅的组织代码,让相关功能的代码更加有序的组织在一起。

2.setup

1:setup函数中的this是undefined,不可使用。setup执行优先级高于beforeCreate
2:

<template>
    <div class="person">
        <h2>name:{{ name }}</h2>
        <h2>age:{{ age }}</h2>
        <button @click="changeName">change name</button>
        <button @click="changeAge">change age</button>
        <button @click="showTel">look up the phone</button>
    </div>
</template>

<script lang="js">
    export default{
        name:"Person",
        setup(){
            // 数据
            let name = 'zhangsan'   // 不是响应式的
            let age = 18
            let tel ='12345678901'

            // 方法
            function changeName(){
                name = 'lisi'
            }
            function changeAge(){
                age += 1
            }
            function showTel(){
                alert(tel)
            }
            return {age,name,tel,changeAge,changeName,showTel}
        },
    }
</script>

这样子将无法改变name、age

3:setup函数的返回值也可以是渲染函数

return ()=>'haha'

4:data、methods中可以使用this读到setup函数中的数据,反之不行

5:为了解决setup函数中时常存在的问题:已定义数据与函数,但是忘记return。setup中被设计了语法糖。

<script lang="ts">
    export default{
        name:"Person",
    }
</script>

<!-- 或者是 <script lang="ts" setup> -->
<!-- 或者是 <script setup lang="ts" name="Person">  这个安装vite-plugin-vue-setup-extend插件后,可以更改网页视图中的标签名字 -->
<script setup lang="ts">  
    // defineOptions({
    //     name: 'Person'
    // })
    let a=666
</script>

扩展:上述代码,还需要编写一个不写setup的script标签,去指定组件名字,比较麻烦,我们可以借助vite中的插件简化

  1. 第一步:npm i vite-plugin-vue-setup-extend -D

  2. 第二步:vite.config.ts


import { defineConfig } from 'vite'

import VueSetupExtend from 'vite-plugin-vue-setup-extend'

  

export default defineConfig({

  plugins: [ VueSetupExtend() ]

})

3.ref创建:基本类型的响应式数据

作用:定义响应式变量。
语法:import { ref } from 'vue' let xxx = ref(初始值)。
返回值: 一个RefImpl的实例对象,简称ref对象或ref,ref对象的value 属性是响应式的。
注意点:

  • JS中操作数据需要:xxx.value,但模板中不需要.value,直接使用即可。
  • 对于let name = ref('张三')来说,name不是响应式的,name.value是响应式的。

4.reactive创建:对象类型的响应式数据

  • 作用:定义一个响应式对象(基本类型不要用它,要用ref,否则报错)
  • 语法:let 响应式对象= reactive(源对象)。
  • 返回值:一个Proxy的实例对象,简称:响应式对象。
  • 注意点:reactive定义的响应式数据是“深层次”的。
let obj = {a:1,b:1}
console.log(obj)

显示:{a:1,b:1}

import { reactive } from 'vue'
let obj1 = reactive({a:1,b:1})
let obj2 = reactive({a:1},{b:2})
console.log(obj1)
console.log(obj2)

显示:
Proxy(Object){a:1,b:1}
Proxy(Array){0:{...},1:{...}}

5.ref创建:对象类型的响应式数据

  • 其实ref接收的数据可以是:基本类型、对象类型。
  • 若ref接收的是对象类型,内部其实也是调用了reactive函数。
  • 嵌套后的响应式数据依旧是响应式的
let car = reactive({brand:"Benz",price:100})
car.price += 1
-->
let car = ref({brand:"Benz",price:100})
car.value.price += 1


let games = reactive([
        {id:"ad1",name:"LOL"},
        {id:"ad2",name:"CSGO"},
        {id:"ad3",name:"DOTA"},
    ])
games[0].name = 'lol'
-->
let games = ref([
        {id:"ad1",name:"LOL"},
        {id:"ad2",name:"CSGO"},
        {id:"ad3",name:"DOTA"},
    ])
games.value[0].name = 'lol'

6:ref对比reactive

宏观角度看:

  • 1.ref用来定义:基本类型数据、对象类型数据
  • 2.reactive用来定义:对象类型数据

区别:

    1. ref创建的变量必须使用.value(可以使用volar插件自动添加.value)
  • 2.reactive重新分配一个新对象,会失去响应式(可以使用Object.assign去整体替换)。Object.assign(a,b,c...)就是将b,c...依次分配给a

使用原则:

  • 1.若需要一个基本类型的响应式数据,必须使用ref
  • 2.若需要一个响应式对象,层级不深,ref、reactive都可以
  • 3.若需要一个响应式对象,且层级较深,推荐使用reactive
let car = ref({brand:"Benz",price:100})
function changeCar(){
    car.value = {brand:"Mercades",price:150} //correct
}
-->
let car = reactive({brand:"Benz",price:100})
function changeCar(){
	car = {brand:"Mercades",price:150}              // wrong
	car = reactive({{brand:"Mercades",price:150}})  // wrong
	Object.assign(car,{brand:"Mercades",price:150}) // correct
}

这样的原因:ref类型的.value一定是响应式的,所以可以直接全部更改

let sum = ref (0)
sum.value += 1  // correct
sum = ref (1)   // wrong

7:toRefs和toRef

  • 作用:将一个响应式对象中的每一个属性,转换为ref对象
  • 备注:toRefs与toRef功能一致,但toRefs可以批量转换
let person = reactive({
    name:"zhangsan",
    age:18
})
let {name,age} = toRefs(person)
let nl = toRef(person,'age')
function changeName(){
    name.value += '~'
}
function changeAge(){
    age.value += 1
}

name就相当于&person.name,且为ref类型。必须同名

8:computed

<script lang="ts" setup name="Person">
    import { computed , ref } from 'vue'
    let lastName  = ref("zhang")
    let firstName = ref("san")
    let fullName = computed({
        get(){ // 读取
            return lastName.value.slice(0,1).toUpperCase() + lastName.value.slice(1,) + ' ' + firstName.value.slice(0,1).toUpperCase() + firstName.value.slice(1,)
	    },
        set(val){ // 修改
            let [str1='',str2=''] = val.split(' ')
            lastName.value = str1
            firstName.value = str2
        }
    })
    function changeFullName(){
        fullName.value='Li Si'
    }
</script>

1.computed类型具有记忆属性,而function()不具有
2.computed.value是只读的,不可修改。只能通过修改computed中需要计算的值来改变computed

9:watch

  • 作用:监视数据的变化(和Vue2中的watch作用一致)
  • 特点:Vue3中的watch只能监视以下四种数据:
  1. ref定义的数据。
  2. reactive定义的数据。
  3. 函数返回一个值(getter函数)。
  4. 一个包含上述内容的数组。

情况一:

监视ref定义的【基本类型】数据:直接写数据名即可,监视的是其value值的改变

<script lang="ts" setup name="Person">
    import { ref,watch } from 'vue'
    let sum = ref (0)
    function addSum(){
        sum.value += 1
    }
    let stopWatch = watch(sum,(newValue)=>{
    // 此处命名无需一定是newvalue,默认该括号的的第一、二个值为新、旧的前面        的变量
        console.log('newValue:'+newValue)
        if( newValue >= 10)
            stopWatch() // 执行函数即可停止监视。可以任意名字
    }) // 配置对象 
  //},{once:True} )
</script>

情况二:

监视ref定义的【对象类型】数据:直接写数据名,监视的是对象的【地址值】,若想监视对象内部的数据,要手动开启深度监视。

注意:

  • 若修改的是ref定义的对象中的属性,newValue 和 oldValue 都是新值,因为它们是同一个对象。
  • 若修改整个ref定义的对象,newValue 是新值, oldValue 是旧值,因为不是同一个对象了。
  • 本质上就是两者是否是对于同一个地址值取值
<script lang="ts" setup name="Person">
    import { ref,watch } from 'vue'
    let person = ref ({name:"zhangsan",age:18})
    function changeName(){
        person.value.name += '~'
    }
    function changeAge(){
        person.value.age += 1
    }
    function changeAll(){
        person.value = {name:"lisi",age:20}
    }
    watch(person,(newv,oldv)=>{
        console.log("something changed:",newv,"  the old:",oldv)
    },{deep:true,once:false,immediate:true})
</script>

情况三:

监视reactive定义的【对象类型】数据,且默认开启了深度监视。

<script lang="ts" setup name="Person">
    import { reactive,watch } from 'vue'
    let person = reactive ({name:"zhangsan",age:18})
    function changeName(){
        person.name += '~'
    }
    function changeAge(){
        person.age += 1
    }
    function changeAll(){
        Object.assign(person,{name:"lisi",age:20})
        // 对于该函数,只有在修改前后存在变化时,才能被监测到改变,ref则不需要
    }
    watch(person,(newv,oldv)=>{
        console.log("something changed:",newv,"  the old:",oldv)
    },{deep:true,once:false,immediate:true})
    // deep:false 也不会报错

</script>

情况四:

监视ref或reactive定义的【对象类型】数据中的某个属性,注意点如下:
1.若该属性值不是【对象类型】,需要写成函数形式。
2.若该属性值是依然是【对象类型】,可直接编,也可写成函数,建议写成函数。

结论:监视的要是对象里的属性,那么最好写函数式,注意点:若是对象监视的是地址值,需要关注对象内部,需要手动开启深度监视。

<script lang="ts" setup name="Person">
    import { reactive,watch } from 'vue'
    let person = reactive({
        name:"zhangsan",
        age:18,
        car:{
            c1:"benz",
            c2:"BMW"
        }
    })
    function changeName(){
        person.name += '~'
    }
    function changeAge(){
        person.age += 1
    }
    function changeC1(){
        person.car.c1 = 'wolksvagen'
    }
    function changeC2(){
        person.car.c2 = 'frella'
    }
    function changecar(){
        person.car = {c1:'byd',c2:'lixiang'}
    }    
    watch(()=>{return person.name},()=>{ // 此处的return可省略
        console.log("person1 changed")
    })
    watch(()=>{return person.car},()=>{
        console.log("person2 changed")
    },{deep:true})
    watch(()=>{return person.car},()=>{
        console.log("person3 changed")
    })
    watch(()=>{return person.car.c1},()=>{
        console.log("person4 changed")
    })
    watch(person.car,()=>{  // 这样写也可以,不过当整个被改变时,
                            // 就不会显示,所以建议写成函数形式
        console.log("person5 changed")
    })
</script>

情况五:

监视上述的多个数据

<script lang="ts" setup name="Person">
    import { reactive,watch } from 'vue'
    let person = reactive({
        name:"zhangsan",
        age:18,
        car:{
            c1:"benz",
            c2:"BMW"
        }
    })
    function changeName(){
        person.name += '~'
    }
    function changeAge(){
        person.age += 1
    }
    function changeC1(){
        person.car.c1 = 'wolksvagen'
    }
    function changeC2(){
        person.car.c2 = 'frella'
    }
    function changecar(){
        person.car = {c1:'byd',c2:'lixiang'}
    }
    watch([()=>person.car.c1,()=>person.name],()=>{  
        console.log("person changed")
    })
</script>

10. watchEffect

  • 官网:立即运行一个函数,同时响应式地追踪其依赖,并在依赖更改时重新执行该函数。
  • watch对比watchEffect
  1. 都能监听响应式数据的变化,不同的是监听数据变化的方式不同
  2. watch:要明确指出监视的数据
  3. watchEffect:不用明确指出监视的数据(函数中用到哪些属性,那就监视哪些属性)。
  • 示例代码:

    <template>
      <div class="person">
        <h1>需求:水温达到50℃,或水位达到20cm,则联系服务器</h1>
        <h2 id="demo">水温:{{temp}}</h2>
        <h2>水位:{{height}}</h2>
        <button @click="changePrice">水温+1</button>
        <button @click="changeSum">水位+10</button>
      </div>
    </template>
    
    <script lang="ts" setup name="Person">
      import {ref,watch,watchEffect} from 'vue'
      // 数据
      let temp = ref(0)
      let height = ref(0)
      // 方法
      function changePrice(){
        temp.value += 10
      }
      function changeSum(){
        height.value += 1
      }
      // 用watch实现,需要明确的指出要监视:temp、height
      watch([temp,height],(value)=>{
        // 从value中获取最新的temp值、height值
        const [newTemp,newHeight] = value
        // 室温达到50℃,或水位达到20cm,立刻联系服务器
        if(newTemp >= 50 || newHeight >= 20){
          console.log('联系服务器')
        }
      })
      // 用watchEffect实现,不用
      const stopWtach = watchEffect(()=>{
        // 室温达到50℃,或水位达到20cm,立刻联系服务器
        if(temp.value >= 50 || height.value >= 20){
          console.log(document.getElementById('demo')?.innerText)
          console.log('联系服务器')
        }
        // 水温达到100,或水位达到50,取消监视
        if(temp.value === 100 || height.value === 50){
          console.log('清理了')
          stopWtach()
        }
      })
    </script>
    

11.标签的ref属性

作用:用于注册模板引用

  • 用在普通DOM(HTML)标签上,获取的是DOM节点
  • 用在组件标签上,获取的是组件实例对象

用在普通DOM标签上:

  <div class="person">
    <h1 ref="title1">尚硅谷</h1>
    <h2 ref="title2">前端</h2>
    <h3 ref="title3">Vue</h3>
    <input type="text" ref="inpt"> <br><br>
    <button @click="showLog">点我打印内容</button>
  </div>
</template>

<script lang="ts" setup name="Person">
  import {ref} from 'vue'
  let title1 = ref()
  let title2 = ref()
  let title3 = ref()
  function showLog(){
    // 通过id获取元素
    const t1 = document.getElementById('title1')
    // 打印内容
    console.log((t1 as HTMLElement).innerText)
    console.log((<HTMLElement>t1).innerText)
    console.log(t1?.innerText)
		/************************************/
    // 通过ref获取元素
    console.log(title1.value)
    console.log(title2.value)
    console.log(title3.value)
  }
</script>

用在组件标签上:

<!-- 父组件App.vue -->
<template>
  <Person ref="ren"/>
  <button @click="test">测试</button>
</template>

<script lang="ts" setup name="App">
  import Person from './components/Person.vue'
  import {ref} from 'vue'
  let ren = ref()
  function test(){
    console.log(ren.value.name)
    console.log(ren.value.age)
  }
</script>

<!-- 子组件Person.vue中要使用defineExpose暴露内容 -->
<script lang="ts" setup name="Person">
  import {ref,defineExpose} from 'vue'
	// 数据
  let name = ref('张三')
  let age = ref(18)
  /****************************/
  /****************************/
  // 使用defineExpose将组件中的数据交给外部
  defineExpose({name,age})
</script>
posted @ 2026-08-04 11:55  fatelism  阅读(4)  评论(0)    收藏  举报