Vue3(二):报错调试,vue3响应式原理、computed和watch,ref,props,接口

一、准备工作调试

跟着张天禹老师看前几集的时候可能会遇到如下问题:

1.下载插件:Vue Language Features (Volar)或者直接下载vue-offical

2.npm run serve时运行时出现错误:Error: @vitejs/plugin-vue requires vue (>=3.2.13) or @vue/compiler-sfc to be present in the dependen

解决办法:Error: @vitejs/plugin-vue requires vue (>=3.2.13) or @vue/compiler-sfc to be present in the dependen_error: @vitejs/plugin-vue requires vue (>=3.2.13) -CSDN博客

3.tsconfig.json首行大括号报错飘红,解决办法:解决vscode中导入的vue项目tsconfig.json文件首行标红问题_vue3项目下载下 tsconfig 飘红,代码不校验-CSDN博客

4.引入的import App from './App.vue'飘红,就关闭这个文件,再重新打开一遍即可

(但很恶心的是打开之后有时3和4问题还会飘红,不过控制台没啥报错就行)

5.关闭eslint语法检查。报错内容:Component name "xxx" should always be multi-word...解决办法:在.eslintrc.js中的rules添加如下规则:参考链接解决:Component name "index" should always be multi-word_component name "login" should always be multi-word-CSDN博客

6.Vue+ts报错:Property 'xxx' does not exist on type...解决办法:在tsconfig.json中将strict改为false.参考连接:vue+Ts 报vue ts this报Property 'x' does not exist on type_vue2 tsx文件中this.报类型错误-CSDN博客

二、 Vue2和3(OptionsAPI 与 CompositionAPI)区别

`Vue2`的`API`设计是`Options`(配置)风格的。

`Vue3`的`API`设计是`Composition`(组合)风格的。

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

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

三、setup

1.概述

`setup`是`Vue3`中一个新的配置项,值是一个函数,它是 `Composition API`表演的舞台,组件中所用到的:数据、方法、计算属性、监视......等等,均配置在`setup`中。

  • `setup`函数返回的对象中的内容,可直接在模板中使用。

  • `setup`中访问`this`是`undefined`。

  • `setup`函数会在`beforeCreate`之前调用,它是"领先"所有钩子执行的。

2.用法

javascript 复制代码
//Person.vue
<template>
  <div class="person">
    <h2>姓名:{{name}}</h2>
    <h2>年龄:{{age}}</h2>
    <button @click="changeName">修改名字</button>
    <button @click="changeAge">年龄+1</button>
    <button @click="showTel">点我查看联系方式</button>
  </div>
</template>

<script lang="ts">
  export default {
    name:'Person',
    setup(){
      // 数据,原来写在data中(注意:此时的name、age、tel数据都不是响应式数据)
      let name = '张三'
      let age = 18
      let tel = '13888888888'

      // 方法,原来写在methods中
      function changeName(){
        name = 'zhang-san' //注意:此时这么修改name页面是不变化的
        console.log(name)
      }
      function changeAge(){
        age += 1 //注意:此时这么修改age页面是不变化的
        console.log(age)
      }
      function showTel(){
        alert(tel)
      }

      // 返回一个对象,对象中的内容,模板中可以直接使用
      return {name,age,tel,changeName,changeAge,showTel}
      //return ()=> '你好啊!'若返回一个**函数**:则可以自定义渲染内容
    }
  }
</script>

3. setup 与 Options API 的关系

  • `Vue2` 的配置(`data`、`methos`......)中**可以访问到** `setup`中的属性、方法。

  • 但在`setup`中**不能访问到**`Vue2`的配置(`data`、`methos`......)。

  • 如果与`Vue2`冲突,则`setup`优先。

(我的理解就是2可以用3,3不能用2)

4.setup 语法糖

借助语法糖可实现如下效果:

第一种写法 :

TypeScript 复制代码
<!-- 语法糖 -->
<script>
export default {
    name:'Person',
}
</script> 
<script setup >
    // 数据(注意:此时的name、age、tel都不是响应式数据)
    let name = '张三'
    let age = 18
    let tel = '13888888888'
    
    // 方法
    function changName() {
        name = '李四'//注意:此时这么修改name页面是不变化的
    }
    function changAge() {
        console.log(age)
        age += 1 //注意:此时这么修改age页面是不变化的
    }
    function showTel() {
        alert(tel)
    }
</script>

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

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

  2. 第二步:`vite.config.ts`

```jsx

import { defineConfig } from 'vite'

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

export default defineConfig({

plugins: [ VueSetupExtend() ]

})

```

  1. 第三步:`<script setup lang="ts" name="Person">`

四、ref和reactive

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

TypeScript 复制代码
//Person.vue
<script setup lang="ts" name="Person">
import { ref } from 'vue'
// name和age是一个RefImpl的实例对象,简称ref对象,它们的value属性是响应式的。
let name = ref('张三')
let age = ref(18)
// tel就是一个普通的字符串,不是响应式的
let tel = '13888888888'

function changeName() {
    // JS中操作ref对象时候需要.value
    name.value = '李四'
    console.log(name.value)

    // 注意:name不是响应式的,name.value是响应式的,所以如下代码并不会引起页面的更新。
    // name = ref('zhang-san')
}
function changeAge() {
    // JS中操作ref对象时候需要.value
    age.value += 1
    console.log(age.value)
}
function showTel() {
    alert(tel)
}
</script>

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

作用:**定义一个**响应式对象**(基本类型不要用它,要用`ref`,否则报错)

语法:**`let 响应式对象= reactive(源对象)`。

返回值:**一个`Proxy`的实例对象,简称:响应式对象。

注意点:**`reactive`定义的响应式数据是"深层次"的。

TypeScript 复制代码
//Person.vue
<template>
    <div class="person">
        <h2>汽车信息:一台{{ car.brand }}汽车,价值{{ car.price }}万</h2>
        <h2>游戏列表:</h2>
        <ul>
            <li v-for="g in games" :key="g.id">{{ g.name }}</li>
        </ul>
        <h2>测试:{{ obj.a.b.c.d }}</h2>
        <button @click="changeCarPrice">修改汽车价格</button>
        <button @click="changeFirstGame">修改第一游戏</button>
        <button @click="test">测试</button>
    </div>
</template>

<script lang="ts" setup name="Person">
import { reactive } from 'vue'

// 数据
let car = reactive({ brand: '奔驰', price: 100 })
let games = reactive([
    { id: 'ahsgdyfa01', name: '英雄联盟' },
    { id: 'ahsgdyfa02', name: '王者荣耀' },
    { id: 'ahsgdyfa03', name: '原神' }
])
let obj = reactive({
    a: {
        b: {
            c: {
                d: 666
            }
        }
    }
})

function changeCarPrice() {
    car.price += 10
}
function changeFirstGame() {
    games[0].name = '流星蝴蝶剑'
}
function test() {
    obj.a.b.c.d = 999
}
</script>

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

  • 其实`ref`接收的数据可以是:**基本类型**、**对象类型**。

  • 若`ref`接收的是对象类型,内部其实也是调用了`reactive`函数。

TypeScript 复制代码
<template>
  <div class="person">
    <h2>汽车信息:一台{{ car.brand }}汽车,价值{{ car.price }}万</h2>
    <h2>游戏列表:</h2>
    <ul>
      <li v-for="g in games" :key="g.id">{{ g.name }}</li>
    </ul>
    <h2>测试:{{obj.a.b.c.d}}</h2>
    <button @click="changeCarPrice">修改汽车价格</button>
    <button @click="changeFirstGame">修改第一游戏</button>
    <button @click="test">测试</button>
  </div>
</template>

<script lang="ts" setup name="Person">
import { ref } from 'vue'

// 数据
let car = ref({ brand: '奔驰', price: 100 })
let games = ref([
  { id: 'ahsgdyfa01', name: '英雄联盟' },
  { id: 'ahsgdyfa02', name: '王者荣耀' },
  { id: 'ahsgdyfa03', name: '原神' }
])
let obj = ref({
  a:{
    b:{
      c:{
        d:666
      }
    }
  }
})

console.log(car)

function changeCarPrice() {
  car.value.price += 10
}
function changeFirstGame() {
  games.value[0].name = '流星蝴蝶剑'
}
function test(){
  obj.value.a.b.c.d = 999
}
</script>

4.ref reactive对比,注意点

宏观角度看:

> 1. `ref`用来定义:**基本类型数据**、**对象类型数据**;

> 2. `reactive`用来定义:**对象类型数据**。

-区别:

> 1. `ref`创建的变量必须使用`.value`(可以使用`volar`插件自动添加`.value`)。

> <img src="images/自动补充value.png" alt="自动补充value" style="zoom:50%;border-radius:20px" />

>

> 2. `reactive`重新分配一个新对象,会**失去**响应式(可以使用`Object.assign`去整体替换)。

  • 使用原则:

> 1. 若需要一个基本类型的响应式数据,必须使用`ref`。

> 2. 若需要一个响应式对象,层级不深,`ref`、`reactive`都可以。

> 3. 若需要一个响应式对象,且层级较深,推荐使用`reactive`。

五、toRefs 与 toRef

作用:将一个响应式对象中的每一个属性,转换为`ref`对象,保持响应式的能力。

-备注:`toRefs`与`toRef`功能一致,但`toRefs`可以批量转换。

TypeScript 复制代码
//App.vue
<template>
  <div class="person">
    <h2>姓名:{{person.name}}</h2>
    <h2>年龄:{{person.age}}</h2>
    <h2>性别:{{person.gender}}</h2>
    <button @click="changeName">修改名字</button>
    <button @click="changeAge">修改年龄</button>
    <button @click="changeGender">修改性别</button>
  </div>
</template>

<script lang="ts" setup name="Person">
  import {ref,reactive,toRefs,toRef} from 'vue'

  // 数据
  let person = reactive({name:'张三', age:18, gender:'男'})
	
  // 通过toRefs将person对象中的n个属性批量取出,且依然保持响应式的能力
  let {name,gender} =  toRefs(person)
	
  // 通过toRef将person对象中的gender属性取出,且依然保持响应式的能力
  let age = toRef(person,'age')

  // 方法
  function changeName(){
    name.value += '~'
  }
  function changeAge(){
    age.value += 1
  }
  function changeGender(){
    gender.value = '女'
  }
</script>

六、计算属性和监视属性

1.computed

(1)计算属性------只读取,不修改

TypeScript 复制代码
<script lang="ts" setup name="Person">
import {ref,computed} from 'vue'

    ...
	//计算属性------简写(只读)
    let firstName = ref('zhang')
    let lastName = ref('san')

    let fullName = computed(()=>{
        return person.firstName + '-' + person.lastName
    })
</script>

(2)计算属性------既读取又修改

TypeScript 复制代码
<script lang="ts" setup name="Person">
import {ref,computed} from 'vue'

    let firstName = ref('zhang')
    let lastName = ref('san')

let fullName = computed({
    // 读取
    get() {
        return firstName.value + '-' + lastName.value
    },
    // 修改
    set(val) {
        console.log('有人修改了fullName', val)
        firstName.value = val.split('-')[0]
        lastName.value = val.split('-')[1]
    }
})

    function changeFullName() {
        fullName.value = 'li-si'
    }
</script>
html 复制代码
//Person.vue
<template>
    <div class="person">
        姓:<input type="text" v-model="firstName"><br>
        名:<input type="text" v-model="lastName"><br>
        全名:<span>{{ fullName }}</span><br>
        <button @click="changeFullName">全名改为:li-si</button>
    </div>
</template>

<script lang="ts" setup name="Person">
    import {ref,computed} from 'vue'
    // 数据
    let firstName = ref('zhang')
    let lastName = ref('san')

    //1.计算属性只读取不修改
    // let fullName = computed(()=>{
    //     return firstName.value + '-' + lastName.value
    // })

    // 2.计算属性读取并修改
    let fullName = computed({
    // 读取
    get() {
        return firstName.value + '-' + lastName.value
    },
    // 修改
    set(val) {
        console.log('有人修改了fullName', val)
        firstName.value = val.split('-')[0]
        lastName.value = val.split('-')[1]
    }
})

    function changeFullName() {
        fullName.value = 'li-si'
    }
</script>

2.watch

作用:监视数据的变化(和`Vue2`中的`watch`作用一致)

-特点:`Vue3`中的`watch`只能监视以下**四种数据**:

> 1. `ref`定义的数据。

> 2. `reactive`定义的数据。

> 3. 函数返回一个值(`getter`函数)。

> 4. 一个包含上述内容的数组。

1. watch监视`ref`定义的基本类型数据

直接写数据名即可,监视的是其`value`值的改变

javascript 复制代码
<template>
    <div class="person">
        <h1>sum值{{ sum }}</h1>
        <button @click="changeSum">改变</button>
    </div>
</template>

<script lang="ts" setup name="Person">
    import {ref,watch} from 'vue'
    //数据
    let sum = ref(0)
    //方法
    function changeSum() {
        sum.value += 1
    }
    //watch监视1,ref基本类型对象
    const stopWatch =watch(sum,(newValue,oldValue)=>{
        console.log("sum变化了", newValue, oldValue);
        if(newValue>=10){
            stopWatch()
        }
    })
</script>

2.监视`ref`定义的【对象类型】数据

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

注意:

若修改的是`ref`定义的对象中的属性,`newValue` 和 `oldValue` 都是新值,因为它们是同一个对象。

若修改整个`ref`定义的对象,`newValue` 是新值, `oldValue` 是旧值,因为不是同一个对象了。

javascript 复制代码
<template>
  <div class="person">
    <h1>情况二:监视【ref】定义的【对象类型】数据</h1>
    <h2>姓名:{{ person.name }}</h2>
    <h2>年龄:{{ person.age }}</h2>
    <button @click="changeName">修改名字</button>
    <button @click="changeAge">修改年龄</button>
    <button @click="changePerson">修改整个人</button>
  </div>
</template>

<script lang="ts" setup name="Person">
  import {ref,watch} from 'vue'
  // 数据
  let person = ref({
    name:'张三',
    age:18
  })
  // 方法
  function changeName(){
    person.value.name += '~'
  }
  function changeAge(){
    person.value.age += 1
  }
  function changePerson(){
    person.value = {name:'李四',age:90}
  }
  /* 
    监视,情况一:监视【ref】定义的【对象类型】数据,监视的是对象的地址值,若想监视对象内部属性的变化,需要手动开启深度监视
    watch的第一个参数是:被监视的数据
    watch的第二个参数是:监视的回调
    watch的第三个参数是:配置对象(deep、immediate等等.....) 
  */
  watch(person,(newValue,oldValue)=>{
    console.log('person变化了',newValue,oldValue)
  },{deep:true})
  
</script>

(1)这样写的话监视不到深层的person.value,只有点击修改人才能监视到,效果如下:

javascript 复制代码
watch(person,(newValue,oldValue)=>{
        console.log("person变化了", newValue, oldValue);
    })

(2)加上deep:true,可以监视到深层的,但是newvalue=oldvalue,(修改的是`ref`定义的对象中的属性,`newValue` 和 `oldValue` 都是新值,因为它们是同一个对象。)

javascript 复制代码
 watch(person,(newValue,oldValue)=>{
        console.log("person变化了", newValue, oldValue);
    }, { deep: true, })

(3) 加上immediate,发现newvalue是当前值,oldvalue是undefined

javascript 复制代码
 watch(person,(newValue,oldValue)=>{
        console.log("person变化了", newValue, oldValue);
    }, { deep: true, immediate: true })

3. 监视`reactive`定义的【对象类型】数据

默认开启深度监视,不能通过deep:false关闭

javascript 复制代码
<template>
    <div class="person">
        <h1>watch3</h1>
        <h2>姓名{{ person.name }}</h2>
        <h2>年龄{{ person.age }}</h2>
        <button @click="changeName">修改名字</button>
        <button @click="changeAge">修改年龄</button>
        <button @click="changePerson">修改人</button>
    </div>
</template>

<script lang="ts" setup name="Person">
    import {reactive,watch} from 'vue'
    //数据
    let person = reactive({
        name:'cgp',
        age:18
    })
    //方法
    function changeName() {
        person.name += '~'
    }
    function changeAge() {
        person.age += 1
    }
    function changePerson() {
        Object.assign(person, { name: '李四', age: 80 })
    }
    //watch3,监视reactive对象类型数据
    watch(person,(newValue,oldValue)=>{
        console.log("person变化了", newValue, oldValue);
    })
</script>

4.监视`ref`或`reactive`定义的【对象类型】数据中的某个属性

  1. 若该属性值**不是**【对象类型】,需要写成函数形式。

  2. 若该属性值是**依然**是【对象类型】,可直接编,也可写成函数,建议写成函数。

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

第一种:监视响应式对象中的某个属性(其他的检测不到),且该属性是基本类型的,要写成函数式。而且可以监视到newvalueoldvalue,而且不相同

javascript 复制代码
  // 监视,情况四:监视响应式对象中的某个属性,且该属性是基本类型的,要写成函数式
watch(()=> person.name,(newValue,oldValue)=>{
    console.log('person.name变化了',newValue,oldValue)
  }) 

第二种:监视响应式对象中的某个属性,且该属性是对象类型的,可以直接写(person.car,但是监测的不完整),也能写函数,更推荐写函数 (要加上deep:true监测才完整)

javascript 复制代码
watch(()=>person.car,(newValue,oldValue)=>{
    console.log('person.car变化了',newValue,oldValue)
  },{deep:true})

5.监视上述的多个数据

javascript 复制代码
//监视,情况五:监视上述的多个数据
watch([() => person.name,()=> person.car], (newValue, oldValue) => {
    console.log('person.car变化了', newValue, oldValue)
}, { deep: true })

其实常用的是1和4

3.watchEffect

官网:立即运行一个函数,同时响应式地追踪其依赖,并在依赖更改时重新执行该函数。

`watch`对比`watchEffect

> 1. 都能监听响应式数据的变化,不同的是监听数据变化的方式不同

> 2. `watch`:要明确指出监视的数据

> 3. `watchEffect`:不用明确指出监视的数据(函数中用到哪些属性,那就监视哪些属性)。

javascript 复制代码
// 用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()
    }
})

七、标签的 ref 属性

作用:用于注册模板引用。

> * 用在普通`DOM`标签上,获取的是`DOM`节点。

> * 用在组件标签上,获取的是组件实例对象。

1.用在普通`DOM`标签上

html 复制代码
<template>
  <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获取元素会和父组件相同的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>

2.用在组件标签上

这个听得有点懵,反正好像是看不到子组件里面的具体内容,一个保护措施

javascript 复制代码
<!-- 父组件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>
javascript 复制代码
<!-- 子组件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>

八、回顾ts中的接口,泛型,自定义类型

1.定义一个接口

src/types/index.ts

TypeScript 复制代码
//定义一个接口,用于限制person对象的具体属性
export interface PersonInter{
id:string,
name:string,
age:number
}

Person.vue

TypeScript 复制代码
<template>
    <div class="person">
        ???
    </div>
</template>

<script lang="ts" setup name="Person">
import { type PersonInter } from '@/types'
let person: PersonInter = { id: 'asyud7asfd01', name: '张三', age: 60 }
</script>

2.泛型和自定义类型

泛型限制一个人,自定义类型可以限制一堆人

TypeScript 复制代码
//定义一个接口,用于限制person对象的具体属性
export interface PersonInter{
id:string,
name:string,
age:number
}
//一个自定义接口
// export type Persons = Array<PersonInter>//泛型,因为PeronInter定义的接口是对象不是数组
export type Persons = PersonInter[]
TypeScript 复制代码
<template>
    <div class="person">
        ???
    </div>
</template>

<script lang="ts" setup name="Person">
import { type PersonInter,type Persons } from '@/types'
// let person: PersonInter = { id: 'asyud7asfd01', name: '张三', age: 60 }
// let personList: Array<PersonInter> //泛型,因为PeronInter定义的接口是对象不是数组
    let personList: Persons=[
    { id: 'asyud7asfd01', name: '张三', age: 60 }, //这样写得话如果你的name拼错了,,会立马告诉你
    { id: 'asyud7asfd02', name: '李四', age: 18 },
    { id: 'asyud7asfd03', name: '王五', age: 5 }
    ]
</script>

九、props

1.先在index.ts中定义一个接口

TypeScript 复制代码
// 定义一个接口,限制每个Person对象的格式
 export interface PersonInter {
  id:string,
  name:string,
     age:number
    }
    
 // 定义一个自定义类型Persons
 export type Persons = Array<PersonInter>

2.把父组件的数据传递给子组件

父组件App.vue

TypeScript 复制代码
<template>
  <Person :list="persons" />
</template>

<script lang="ts" setup name="App">
   import Person from './components/Person.vue'
   import { reactive } from 'vue'
     import { type Persons } from './types'
   
 let persons = reactive<Persons>([
 { id: 'e98219e12', name: '张三', age: 18 },
 { id: 'e98219e13', name: '李四', age: 19 },
 { id: 'e98219e14', name: '王五', age: 20 }
     ])
  </script>

3.三种接收方法

Person.vue

TypeScript 复制代码
<template>
     <div class="person">
        <ul>
            <li v-for="item in list" :key="item.id">
                {{ item.name }}--{{ item.age }}
                </li>
            </ul>
        </div>
</template>

<script lang="ts" setup name="Person">
 import { defineProps } from 'vue'
 import { type PersonInter } from '@/types'
   
   // 第一种写法:仅接收
 // const props = defineProps(['list'])
   
   // 第二种写法:接收+限制类型
 // defineProps<{list:Persons}>()
   
   // 第三种写法:接收+限制类型+指定默认值+限制必要性
    let props = withDefaults(defineProps<{ list?: Persons }>(), {
    list: () => [{ id: 'asdasg01', name: '小猪佩奇', age: 18 }]
       })
    console.log(props)
</script>

看来不能一次学太多,后八和九听得有点懵,后面遇到了再补吧

相关推荐
崔庆才丨静觅2 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60613 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了3 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅3 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅4 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅4 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment4 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅4 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊5 小时前
jwt介绍
前端
爱敲代码的小鱼5 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax