Vue3视图渲染技术(2)

我是南城余!阿里云开发者平台专家博士证书获得者!

欢迎关注我的博客!一同成长!

一名从事运维开发的worker,记录分享学习。

专注于AI,运维开发,windows Linux 系统领域的分享!

本章节对应知识库

南城余 — Java全栈 · 语雀

本内容来自尚硅谷课程,此处在知识库做了个人理解
————————————————
在这里插入图片描述

6.4 双向绑定

单项绑定和双向绑定

  • 单向绑定: 响应式数据的变化会更新dom树,但是dom树上用户的操作造成的数据改变不会同步更新到响应式数据
  • 双向绑定: 响应式数据的变化会更新dom树,但是dom树上用户的操作造成的数据改变会同步更新到响应式数据
    • 用户通过表单标签才能够输入数据,所以双向绑定都是应用到表单标签上的,其他标签不行
    • v-model专门用于双向绑定表单标签的value属性,语法为 v-model:value='',可以简写为 v-model=''
    • v-model还可以用于各种不同类型的输入,<textarea><select> 元素。
<script type="module" setup>

  //引入模块
  import { reactive,ref} from 'vue' 
  let hbs = ref([]); //装爱好的值
  let user = reactive({username:null,password:null,introduce:null,pro:null})   
  function login(){
    alert(hbs.value);
    alert(JSON.stringify(user));
  }
  function clearx(){
    //user = {};// 这中写法会将数据变成非响应的,应该是user.username=""
    user.username=''
    user.password=''
    user.introduce=''
    user.pro=''
    hbs.value.splice(0,hbs.value.length);;
  }
</script>

<template>
  <div>
      账号: <input type="text" placeholder="请输入账号!" v-model="user.username"> <br>
      密码: <input type="text" placeholder="请输入账号!" v-model="user.password"> <br>
      爱好: 
        吃 <input type="checkbox" name="hbs" v-model="hbs" value=""><input type="checkbox" name="hbs" v-model="hbs" value=""><input type="checkbox" name="hbs" v-model="hbs" value=""><input type="checkbox" name="hbs" v-model="hbs" value="">
      <br>
      简介:<textarea v-model="user.introduce"></textarea>
      <br>
      籍贯:
          <select v-model="user.pro">
            <option value="1"></option>
            <option value="2"></option>
            <option value="3"></option>
            <option value="4"></option>
            <option value="5"></option>
            <option value="6"></option>
          </select> 
      <br>
      <button @click="login()">登录</button> 
      <button @click="clearx()">重置</button>
      <hr>
      显示爱好:{{ hbs }}
      <hr>
      显示用户信息:{{ user }}
  </div> 
</template> 

<style scoped>
</style>

6.5 属性计算

模板中的表达式虽然方便,但也只能用来做简单的操作。如果在模板中写太多逻辑,会让模板变得臃肿,难以维护。比如说,我们有这样一个包含嵌套数组的对象:

<script type="module" setup>
  //引入模块
  import { reactive,computed} from 'vue'
  const author = reactive({
    name: 'John Doe',
    books: [
      'Vue 2 - Advanced Guide',
      'Vue 3 - Basic Guide',
      'Vue 4 - The Mystery'
    ]
  })
 
</script>

<template>
  <div>
    <p>{{author.name}} Has published books?:</p>
    <span>{{ author.books.length > 0 ? 'Yes' : 'No' }}</span>
  </div>
</template> 

<style scoped>
</style>
  • 这里的模板看起来有些复杂。我们必须认真看好一会儿才能明白它的计算依赖于 author.books。更重要的是,如果在模板中需要不止一次这样的计算,我们可不想将这样的代码在模板里重复好多遍。

因此我们推荐使用计算属性来描述依赖响应式状态的复杂逻辑。这是重构后的示例:

<script type="module" setup>
  //引入模块
  import { reactive,computed} from 'vue'
  const author = reactive({
    name: 'John Doe',
    books: [
      'Vue 2 - Advanced Guide',
      'Vue 3 - Basic Guide',
      'Vue 4 - The Mystery'
    ]
  })
  // 一个计算属性 ref
  const publishedBooksMessage = computed(() => {
    console.log("publishedBooksMessage")
    return author.books.length > 0 ? 'Yes' : 'No'
  })
  // 一个函数
  let hasBooks = ()=>{
    console.log("hasBooks")
    return author.books.length > 0?'Yes':'no'
  }

</script>

<template>
  <div>
    <p>{{author.name}} Has published books?:</p>
    <span>{{ author.books.length > 0 ? 'Yes' : 'No' }}</span>
    <span>{{ hasBooks() }}</span><!-- 调用方法,每个标签都会调用一次 -->
    <span>{{ hasBooks() }}</span>

    <p>{{author.name}} Has published books?:</p>
    <span>{{ publishedBooksMessage }}</span><!-- 属性计算,属性值不变时,多个个标签只会调用一次 -->
    <span>{{ publishedBooksMessage }}</span>
  </div>
</template> 

<style scoped>
</style>

  • 我们在这里定义了一个计算属性 publishedBooksMessagecomputed() 方法期望接收一个 getter 函数,返回值为一个计算属性 ref。和其他一般的 ref 类似,你可以通过 publishedBooksMessage.value 访问计算结果。计算属性 ref 也会在模板中自动解包,因此在模板表达式中引用时无需添加 .value

  • Vue 的计算属性会自动追踪响应式依赖。它会检测到 publishedBooksMessage 依赖于 author.books,所以当 author.books 改变时,任何依赖于 publishedBooksMessage 的绑定都会同时更新。

计算属性缓存 vs 方法

  • 若我们将同样的函数定义为一个方法而不是计算属性,两种方式在结果上确实是完全相同的,然而,不同之处在于计算属性值会基于其响应式依赖被缓存。一个计算属性仅会在其响应式依赖更新时才重新计算。这意味着只要 author.books 不改变,无论多少次访问 publishedBooksMessage 都会立即返回先前的计算结果!

6.6 数据监听器

计算属性允许我们声明性地计算衍生值。然而在有些情况下,我们需要在状态变化时执行一些“副作用”:例如更改 DOM,或是根据异步操作的结果去修改另一处的状态。我们可以使用 watch 函数在每次响应式状态发生变化时触发回调函数:

  • watch主要用于以下场景:
    • 当数据发生变化时需要执行相应的操作
    • 监听数据变化,当满足一定条件时触发相应操作
    • 在异步操作前或操作后需要执行相应的操作

监控响应式数据(watch):

<script type="module" setup>
  //引入模块
  import { ref,reactive,watch} from 'vue'
 
  let firstname=ref('')
  let lastname=reactive({name:''})
  let fullname=ref('')

  //监听一个ref响应式数据
  watch(firstname,(newValue,oldValue)=>{
    console.log(`${oldValue}变为${newValue}`)
    fullname.value=firstname.value+lastname.name
  })
  //监听reactive响应式数据的指定属性
  watch(()=>lastname.name,(newValue,oldValue)=>{
    console.log(`${oldValue}变为${newValue}`)
    fullname.value=firstname.value+lastname.name
  })
  //监听reactive响应式数据的所有属性(深度监视,一般不推荐)
  //deep:true 深度监视
  //immediate:true 深度监视在进入页面时立即执行一次
  watch(()=>lastname,(newValue,oldValue)=>{
    // 此时的newValue和oldValue一样,都是lastname
    console.log(newValue)
    console.log(oldValue)
    fullname.value=firstname.value+lastname.name
  },{deep:true,immediate:false})
</script>

<template>
  <div>
    全名:{{fullname}} <br>
    姓氏:<input type="text" v-model="firstname"> <br>
    名字:<input type="text" v-model="lastname.name" > <br>
  </div>
</template> 

<style scoped>
</style>

监控响应式数据(watchEffect):

  • watchEffect默认监听所有的响应式数据
<script type="module" setup>
  //引入模块
  import { ref,reactive,watch, watchEffect} from 'vue'
 
  let firstname=ref('')
  let lastname=reactive({name:''})
  let fullname=ref('')

  //监听所有响应式数据
  watchEffect(()=>{
    //直接在内部使用监听属性即可!不用外部声明
    //也不需要,即时回调设置!默认初始化就加载!
    console.log(firstname.value)
    console.log(lastname.name)
    fullname.value=`${firstname.value}${lastname.name}`
  })
</script>

<template>
  <div>
    全名:{{fullname}} <br>
    姓氏:<input type="text" v-model="firstname"> <br>
    名字:<input type="text" v-model="lastname.name" > <br>
  </div>
</template> 

<style scoped>
</style>

watch vs. watchEffect

  • watchwatchEffect 都能响应式地执行有副作用的回调。它们之间的主要区别是追踪响应式依赖的方式:
    • watch 只追踪明确侦听的数据源。它不会追踪任何在回调中访问到的东西。另外,仅在数据源确实改变时才会触发回调。watch 会避免在发生副作用时追踪依赖,因此,我们能更加精确地控制回调函数的触发时机。
    • watchEffect,则会在副作用发生期间追踪依赖。它会在同步执行过程中,自动追踪所有能访问到的响应式属性。这更方便,而且代码往往更简洁,但有时其响应性依赖关系会不那么明确。

6.7. Vue生命周期

6.7.1 生命周期简介

每个 Vue 组件实例在创建时都需要经历一系列的初始化步骤,比如设置好数据侦听,编译模板,挂载实例到 DOM,以及在数据改变时更新 DOM。在此过程中,它也会运行被称为生命周期钩子的函数,让开发者有机会在特定阶段运行自己的代码!

  • 周期图解:

  • 常见钩子函数
    • onMounted() 注册一个回调函数,在组件挂载完成后执行。
    • onUpdated() 注册一个回调函数,在组件因为响应式状态变更而更新其 DOM 树之后调用。
    • onUnmounted() 注册一个回调函数,在组件实例被卸载之后调用。
    • onBeforeMount() 注册一个钩子,在组件被挂载之前被调用。
    • onBeforeUpdate() 注册一个钩子,在组件即将因为响应式状态变更而更新其 DOM 树之前调用。
    • onBeforeUnmount() 注册一个钩子,在组件实例被卸载之前调用。
6.7.2 生命周期案例
<script setup>

    import {ref,onUpdated,onMounted,onBeforeUpdate} from 'vue'
    
    let message =ref('hello')
   
    // 挂载完毕生命周期
    onMounted(()=>{
      console.log('-----------onMounted---------')
      let span1 =document.getElementById("span1")
      console.log(span1.innerText)
    })
    // 更新前生命周期
    onBeforeUpdate(()=>{
      console.log('-----------onBeforeUpdate---------')
      console.log(message.value)
      let span1 =document.getElementById("span1")
      console.log(span1.innerText)
    })
    // 更新完成生命周期
    onUpdated(()=>{
      console.log('-----------onUpdated---------')
      let span1 =document.getElementById("span1")
      console.log(span1.innerText)
    })
</script>

<template>
  <div>
    <span id="span1" v-text="message"></span> <br>
    <input type="text" v-model="message">
  </div>
  
</template>

<style scoped>
</style>

6.8 Vue组件

6.8.1 组件基础

组件允许我们将 UI 划分为独立的、可重用的部分,并且可以对每个部分进行单独的思考。组件就是实现应用中局部功能代码和资源的集合!在实际应用中,组件常常被组织成层层嵌套的树状结构:

  • 这和我们嵌套 HTML 元素的方式类似,Vue 实现了自己的组件模型,使我们可以在每个组件内封装自定义内容与逻辑。

传统方式编写应用:

组件方式编写应用:

  • 组件化:对js/css/html统一封装,这是VUE中的概念

  • 模块化:对js的统一封装,这是ES6中的概念

  • 组件化中,对js部分代码的处理使用ES6中的模块化

6.8.2 组件化入门案例

案例需求: 创建一个页面,包含头部和菜单以及内容显示区域,每个区域使用独立组建!

1 准备vue项目

npm create vite
cd vite项目
npm install

2 安装相关依赖

npm install sass
npm install bootstrap

3 创建子组件 在src/components文件下 vscode需要安装Vetur插件,这样vue文件有快捷提示

  • Header.vue
<script setup type="module">
</script>

<template>
    <div>
        欢迎: xx <a href="#">退出登录</a>
    </div>
</template>

<style>
</style>
  • Navigator.vue
<script setup type="module">
</script>

<template>
    <!-- 推荐写一个根标签-->
    <div>
       <ul>
          <li>学员管理</li>
          <li>图书管理</li>
          <li>请假管理</li>
          <li>考试管理</li>
          <li>讲师管理</li>
       </ul>
    </div>
</template>

<style>
</style>
  • Content.vue
<script setup type="module">
</script>

<template>
    <div>
        展示的主要内容!
    </div>
</template>

<style>
</style>
  • App.vue 入口组件App引入组件
<script setup>
    import Header  from './components/Header.vue'
    import Navigator  from './components/Navigator.vue'
    import Content  from './components/Content.vue'
</script>

<template>
  <div>
     <Header class="header"></Header>
     <Navigator class="navigator"></Navigator>
     <Content class="content"></Content>
  </div>
</template>

<style scoped>
    .header{
       height: 80px;
       border: 1px solid red;
    }

    .navigator{
      width: 15%;
      height: 800px;
      display: inline-block;
      border: 1px blue solid;
      float: left;
    }

    .content{
      width: 83%;
      height: 800px;
      display: inline-block;
      border: 1px goldenrod solid;
      float: right;
    }

</style>

4 启动测试

npm run dev
6.8.3 组件之间传递数据
6.8.3.1 父传子

Vue3 中父组件向子组件传值可以通过 props 进行,具体操作如下:

  1. 首先,在父组件中定义需要传递给子组件的值,接着,在父组件的模板中引入子组件,同时在引入子组件的标签中添加 props 属性并为其设置需要传递的值。

  2. 在 Vue3 中,父组件通过 props 传递给子组件的值是响应式的。也就是说,如果在父组件中的传递的值发生了改变,子组件中的值也会相应地更新。

  • 父组件代码:App.vue
<script setup>

  import Son from './components/Son.vue'
  import {ref,reactive,toRefs} from 'vue'

  let message = ref('parent data!')
  let title = ref(42)

  function changeMessage(){
    message.value = '修改数据!'
    title.value++
  }
</script>

<template>
  <div>
    <h2>{{ message }}</h2>
    <hr>
    <!-- 使用子组件,并且传递数据! -->
    <Son :message="message" :title="title"></Son>
    <hr>
    <button @click="changeMessage">点击更新</button>
  </div>
</template>

<style scoped>
</style>
  • 子组件代码:Son.vue
<script setup type="module">
    import {ref,isRef,defineProps} from 'vue'
    //声明父组件传递属性值
    defineProps({
        message:String ,
        title:Number
    })
</script>

<template>
    <div>
    <div>{{ message }}</div>
    <div>{{ title }}</div>
    </div>
</template>

<style>
</style>
6.8.3.2 子传父
  • 父组件: App.vue
<script setup>
    import Son from './components/Son.vue'
    import {ref} from 'vue'

    let pdata = ref('')

    const padd = (data) => {
        console.log('2222');
        pdata.value =data;
    }

    //自定义接收,子组件传递数据方法! 参数为数据!
    const psub = (data) => {
        console.log('11111');
        pdata.value = data;
    }
</script>

<template>
    <div>
        <!-- 声明@事件名应该等于子模块对应事件名!调用方法可以是当前自定义!-->
        <Son @add="padd" @sub="psub"></Son>
        <hr>
        {{ pdata }}
    </div>
</template>



<style>
</style>
  • 子组件:Son.vue
<script setup>

    import {ref,defineEmits} from 'vue'

    //1.定义要发送给父组件的方法,可以1或者多个
    let emites = defineEmits(['add','sub']);

    let data = ref(1);

    function sendMsgToParent(){
        console.log('-------son--------');

        //2.出发父组件对应的方法,调用defineEmites对应的属性
        emites('add','add data!'+data.value)
        emites('sub','sub data!'+data.value)

        data.value ++;
    }
</script>

<template>
    <div>
      <button @click="sendMsgToParent">发送消息给父组件</button>
    </div>
</template>
  
6.8.3.3 兄弟传参
  • Navigator.vue: 发送数据到App.vue
<script setup type="module">
    import {defineEmits} from 'vue'
    const emits = defineEmits(['sendMenu']);
    //触发事件,向父容器发送数据
    function send(data){
        emits('sendMenu',data);
    }
</script>

<template>
    <!-- 推荐写一个根标签-->
    <div>
       <ul>
          <li @click="send('学员管理')">学员管理</li>
          <li @click="send('图书管理')">图书管理</li>
          <li @click="send('请假管理')">请假管理</li>
          <li @click="send('考试管理')">考试管理</li>
          <li @click="send('讲师管理')">讲师管理</li>
       </ul>
    </div>
</template>

<style>

</style>
  • App.vue: 发送数据到Content.vue
<script setup>
  
  import Header  from './components/Header.vue'
  import Navigator  from './components/Navigator.vue'
  import Content  from './components/Content.vue'

  import {ref} from "vue"
  //定义接受navigator传递参数
  var navigator_menu = ref('ceshi');

  const receiver = (data) =>{
    navigator_menu.value = data;
  }
</script>

<template>
  <div>
      <hr>
      {{ navigator_menu }}
      <hr>
     <Header class="header"></Header>
     <Navigator @sendMenu="receiver" class="navigator"></Navigator>
     <!-- 向子组件传递数据-->
     <Content class="content" :message="navigator_menu"></Content>
    </div>
</template>

<style scoped>
    .header{
       height: 80px;
       border: 1px solid red;
    }

    .navigator{
      width: 15%;
      height: 800px;
      display: inline-block;
      border: 1px blue solid;
      float: left;
    }

    .content{
      width: 83%;
      height: 800px;
      display: inline-block;
      border: 1px goldenrod solid;
      float: right;
    }

</style>
  • Content.vue
<script setup type="module">
    defineProps({
        message:String
    })
</script>

<template>
    
    <div>
        展示的主要内容!
        <hr>
        {{ message }}
    </div>
</template>

<style>
</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/261860.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

vue中的侦听器和组件之间的通信

目录 一、侦听器 监听基本数据类型&#xff1a; 监听引用数据类型&#xff1a; 计算属性和watch区别&#xff1f; 二、组件通信/传值方式 1.父子组件传值 父组件给子组件传值&#xff1a; &#xff08;1&#xff09;props &#xff08;2&#xff09;provide inject &…

k8s中Helm工具实践

k8s中Helm工具实践 1&#xff09;安装redis-cluster 先搭建一个NFS的SC&#xff08;只需要SC&#xff0c;不需要pvc&#xff09;&#xff0c;具体步骤此文档不再提供&#xff0c;请参考前面相关章节。 下载redis-cluster的chart包 helm pull bitnami/redis-cluster --untar…

Java可变参数(学习推荐版,通俗易懂)

定义 可变参数本质还是一个数组 示例代码 注意事项 1.形参列表中&#xff0c;可变参数只能有一个 2.可变参数必须放在形参列表的最后面 注意是最后面。 name也可以为int类型

做题总结 202. 快乐数

202. 快乐数 思路分析代码实现-Java代码优化 思路分析 本人没有思路 在看题的时候&#xff0c;我不知道如果 不是快乐数怎么处理。我感觉是会死循环&#xff0c;一直加下去。没有考虑到会有重复数字出现。 为什么不会进行死循环&#xff1f;&#xff08;为什么会有重复数字出…

DevOps系列文章 : 使用dpkg命令打deb包

创建一个打包的目录&#xff0c;类似rpmbuild&#xff0c;这里创建了目录deb_build mkdir deb_build目标 我有一个hello的二进制文件hello和源码hello.c, 准备安装到/opt/helloworld目录中 步骤 在deb_build目录创建一个文件夹用于存放我的安装文件 mkdir helloworld在he…

SAP 特殊采购类30简介---标准委外

前面我们已经测试了很多的特殊采购类,今天我们测试一个在SAP系统中非常基本的功能—采购外协,通常采购外协和工序外协经常会被放在一起讨论方案,同时每个PP模块顾问和MM模块顾问所必需的。这个功能技术上讲不是很难,需要理解这个外协的意思。采购外协有时也会被称为标准外协…

[MySQL]用基本的mysql语句写的{商店的数据}和{学生成绩}

文章目录 前言一、题目二、创建2.写入table 三.查看表单结构四.插入数据1.俩种方法2.指定插入 五.查询1.全部和指定查询2.别名查询3.去重4.排序5.条件查询&#xff08;where) 六.修改七.删除八.在table中插入一列总结&#xff1a; 前言 提示&#xff1a;以下是本篇文章正文内容…

Temporary failure in name resolution

报错&#xff1a; 1.打开resolv.conf文件 sudo vim /etc/resolv.conf 2. 确保resolv.conf文件至少包含一个名称服务器。列出名称服务器的行应如下所示&#xff1a; 3. 保存文件并退出。 4. 接下来&#xff0c;重新启动DNS 解析器服务。运行以下命令&#xff1a; sudo syste…

探索未来交通!空客、宝马开启新一轮“量子计算挑战赛”

12月6日&#xff0c;空中客车公司和宝马集团共同发起了一项名为 “量子交通探索”的全球量子计算挑战赛&#xff0c;以应对航空和汽车领域最紧迫的挑战——这些挑战对于传统计算机而言仍然是难以克服的。 这项挑战是首创性的&#xff0c;它将两个全球行业领导者聚集在一起&…

Pytest+Requests+Allure实现接口自动化测试

一、整体结构 框架组成&#xff1a;pytestrequestsallure设计模式&#xff1a; 关键字驱动项目结构&#xff1a; 工具层&#xff1a;api_keyword/参数层&#xff1a;params/用例层&#xff1a;case/数据驱动&#xff1a;data_driver/数据层&#xff1a;data/逻辑层&#xff1a…

windows VMWare 安装虚拟机-保姆级教程,涵盖网络配置

1、虚拟机是什么 虚拟机&#xff08;Virtual Machine&#xff0c;简称 VM&#xff09;是一种运行在物理计算机上的软件实体&#xff0c;它模拟了一台完整的计算机系统。虚拟机可以在一台物理计算机上同时运行多个操作系统和应用程序&#xff0c;每个虚拟机都被分配了独立的计算…

C#攻克反爬虫之代理IP爬取

目录 前言 一、什么是代理IP 二、代理IP的获取 1. 免费代理IP网站 2. 第三方API 三、C#实现代理IP爬取 1. 安装HtmlAgilityPack和HttpClient 2. 获取代理IP 3. 使用代理IP发送请求 四、常见问题及解决方案 1. 代理IP的可用性 2. 频繁更换代理IP 总结 前言 随着互…

坚持提升这个能力,让你越来越强大

哈喽&#xff0c;你好啊&#xff01;我是雷工。 今天在读《张一鸣管理日志》时&#xff0c;看到这么一句话&#xff1a; “产品创新要从根本上解决问题&#xff0c;而不是想办法绕过问题&#xff0c;解决的问题很可能就是将来的核心竞争力。” 这让我想起了亚马逊公司&#x…

还在用QQ拼音输入法吗?赶快卸载吧~!

最近总觉得我的C盘在莫名其妙的减少。之前的电脑C盘只有240G&#xff0c;所以我很在意C盘空间。但是&#xff0c;我发现买了新电脑&#xff0c;C盘空间也在莫名其妙减少。 随挨个文件夹检查。最后发现&#xff0c;QQ拼音的 dict 文件夹很大&#xff0c;居然有 30G多G。 30多~…

图片曝光修正方法(直方图均衡和CNN)

图像过曝或曝光不足时需要曝光处理&#xff0c; 这里以曝光不足举例。 直方图均衡法&#xff1a; 通过RGB通道的直方图均衡达到处理曝光不足的效果。 代码&#xff1a; underexpose cv2.imread("exposure_test.jpg") #underexpose cv2.cvtColor(underexpose, cv2…

leetcode 1314. 矩阵区域和(优质解法)

代码&#xff1a; class Solution {public int[][] matrixBlockSum(int[][] mat, int k) {int mmat.length;int nmat[0].length;int[][]answernew int[m][n]; //要返回的结果矩阵int[][]sumnew int[m1][n1]; //前缀和数组//初始化前缀和数组for(int i1;i<m;i){for(int…

Java泛型-13

泛型的好处 public class Demo01 {public static void main(String[] args) {Person<String> person new Person<String>("韩曙平");} }class Person<E>{ //创建时才定义数据类型 编译时就可以进行约束E str;public Person(E str){this.str str…

Github项目推荐:在线rename

项目地址 GitHub - JasonGrass/rename: 在线文件批量重命名 项目简介 一个开源的在线重命名文件工具。利用了新的浏览器API获取文件句柄&#xff0c;在不上传文件的情况下对文件进行重命名。可以作为前端文件操作api学习范例。 项目截图

PropTypes 在 React 中的使用心得

在 React 开发中&#xff0c;PropTypes 是一个非常有用的库&#xff0c;用于对组件的属性进行类型检查。它可以帮助我们在开发过程中捕获潜在的错误&#xff0c;提高代码的可靠性和可维护性。本文将介绍 PropTypes 的基本用法和一些使用心得。 一、什么是 PropTypes PropTypes…