[Vue]Vue3学习笔记(尚硅谷)(三)

简介: [Vue]Vue3学习笔记(尚硅谷)(三)

🥽 自定义hook函数

  • 什么是hook?—— 本质是一个函数,把setup函数中使用的Composition API进行了封装。
  • 类似于vue2.x中的mixin。
  • 自定义hook的优势: 复用代码, 让setup中的逻辑更清楚易懂。

src\hooks\usePoint.js

import {reactive,onMounted,onBeforeUnmount} from 'vue'
export default function() {
  //实现鼠标“打点”相关的数据
  let point = reactive({
    x:0,
    y:0
  })
  //实现鼠标“打点”相关的方法
  function savePoint(event){
    point.x = event.pageX
    point.y = event.pageY
    console.log(event.pageX,event.pageY)
  }
  //实现鼠标“打点”相关的生命周期钩子
  onMounted(()=>{
    window.addEventListener('click',savePoint)
  })
  onBeforeUnmount(()=>{
    window.removeEventListener('click',savePoint)
  })
  return point
}

Demo.vue

<template>
  <h2>当前求和为:{{sum}}</h2>
  <button @click="sum++">点我+1</button>
  <hr>
  <h2>当前点击时鼠标的坐标为:x:{{point.x}},y:{{point.y}}</h2>
</template>
<script>
  import {ref} from 'vue'
  import usePoint from '../hooks/usePoint'
  export default {
    name: 'Demo',
    setup(){
      //数据
      let sum = ref(0)
      let point = usePoint()
      //返回一个对象(常用)
      return {sum,point}
    }
  }
</script>

🥽 toRef

  • 作用:创建一个 ref 对象,其value值指向另一个对象中的某个属性。
  • 语法:const name = toRef(person,'name')
  • 应用: 要将响应式对象中的某个属性单独提供给外部使用时。
  • 扩展:toRefstoRef功能一致,但可以批量创建多个 ref 对象,创建一个对象中所有属性对应的 ref 对象,语法:toRefs(person)
<template>
  <h4>{{person}}</h4>
  <h2>姓名:{{name}}</h2>
  <h2>年龄:{{age}}</h2>
  <h2>薪资:{{job.j1.salary}}K</h2>
  <button @click="name+='~'">修改姓名</button>
  <button @click="age++">增长年龄</button>
  <button @click="job.j1.salary++">涨薪</button>
</template>
<script>
  import {ref,reactive,toRef,toRefs} from 'vue'
  export default {
    name: 'Demo',
    setup(){
      //数据
      let person = reactive({
        name:'张三',
        age:18,
        job:{
          j1:{
            salary:20
          }
        }
      })
      // const name1 = person.name
      // console.log('%%%',name1)
      // const name2 = toRef(person,'name')
      // console.log('####',name2)
      const x = toRefs(person)
      console.log('******',x)
      //返回一个对象(常用)
      return {
        person,
        // name:toRef(person,'name'),
        // age:toRef(person,'age'),
        // salary:toRef(person.job.j1,'salary'),
        ...toRefs(person)
      }
    }
  }
</script>

🥽 shallowReactive 与 shallowRef

  • shallowReactive:只处理对象最外层属性的响应式(浅响应式)。
  • shallowRef:只处理基本数据类型的响应式, 不进行对象的响应式处理,对于对象类型数据直接将该对象作为ref对象的value的值。
  • 什么时候使用?
  • 如果有一个对象数据,结构比较深, 但变化时只是最外层属性变化 ===> shallowReactive。
  • 如果有一个对象数据,后续功能不会修改该对象中的属性,而是生成新的对象来替换,即对于该对象数据在后续功能中不会修改其属性,而是会将该对象整个进行替换 ===> shallowRef。

🥽 readonly 与 shallowReadonly

  • readonly 与 shallowReadonly 均接收一个响应式数据为参数。
  • readonly: 让一个响应式数据变为只读的(深只读),传入的响应式数据不管有几层,都不能进行修改。
  • shallowReadonly:让一个响应式数据变为只读的(浅只读),传入的响应式数据只有最外层数据不能进行修改。
  • 应用场景: 不希望数据被修改时。将数据交给其他组件并且不希望这个组件对数据进行更改。

🥽 toRaw 与 markRaw

  • toRaw:
  • toRaw 接收一个响应式对象为参数,只能接收reactive生成的响应式对象,不能处理ref生成的响应式数据
  • 作用:将一个由reactive生成的响应式对象转为普通对象
  • 使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新。
  • markRaw:
  • 接收一个对象类型数据为参数
  • 作用:标记一个对象,使其永远不会再成为响应式对象。向一个已经是响应式对象的数据追加一个属性,该属性的值为对象类型数据,vue会为其自动添加响应式,当不希望该属性的值为响应式时可以使用该函数,减小开销。
  • 应用场景:
  1. 有些值不应被设置为响应式的,例如复杂的第三方类库等,如果向响应式对象追加一个第三方类库对象(一般属性多且层次多),开销会很大。
  2. 当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能。

🥽 customRef

  • 作用:创建一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制。
  • 实现防抖效果:
<template>
    <input type="text" v-model="keyword">
    <h3>{{keyword}}</h3>
  </template>
  <script>
    import {ref,customRef} from 'vue'
    export default {
      name:'Demo',
      setup(){
        // let keyword = ref('hello') //使用Vue准备好的内置ref
        //自定义一个myRef
        function myRef(value,delay){
          let timer
          //通过customRef去实现自定义
          return customRef((track,trigger)=>{
            return{
              get(){ // 读取数据时调用
                track() //告诉Vue这个value值是需要被“追踪”的,告诉vue追踪数据的变化
                return value
              },
              set(newValue){ // 修改数据时调用
                clearTimeout(timer) // 触发就清除原先的定时器不执行之前定时器的回调
                timer = setTimeout(()=>{
                  value = newValue // 修改数据
                  trigger() //告诉Vue去更新界面,重新解析模板
                },delay)
              }
            }
          })
        }
        let keyword = myRef('hello',500) //使用程序员自定义的ref
        return {
          keyword
        }
      }
    }
  </script>

🥽 provide 与 inject

  • 作用:实现祖与后代组件间通信
  • 套路:父组件有一个 provide 选项来提供数据,后代组件有一个 inject 选项来开始使用这些数据
  • 具体写法:
  1. 祖组件中:
setup(){
  ......
    let car = reactive({name:'奔驰',price:'40万'})
    // 给后代组件传递数据 
    // 第一个参数为对传递数据的命名,第二个参数为传递的数据
    provide('car',car)
    ......
}
  1. 后代组件中:
setup(props,context){
  ......
  // 获取祖组件传递过来命名为car的数据
    const car = inject('car')
    return {car}
  ......
}

🥽 响应式数据的判断

  • isRef: 检查一个值是否为一个 ref 对象
  • isReactive: 检查一个对象是否是由 reactive 创建的响应式代理
  • isReadonly: 检查一个对象是否是由 readonly 创建的只读代理
  • isProxy: 检查一个对象是否是由 reactive 或者 readonly 方法创建的代理

🥽 Composition API 的优势

🌊 Options API (配置式API)存在的问题

使用传统OptionsAPI中,新增或者修改一个需求,就需要分别在data,methods,computed里修改 。同一个需求的数据、方法等较分散不集中。

🌊 Composition API (组合式API)的优势

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

🥽 Fragment

  • 在Vue2中: 组件必须有一个根标签
  • 在Vue3中: 组件可以没有根标签, 内部会将多个标签包含在一个Fragment虚拟元素中
  • 好处: 减少标签层级, 减小内存占用

🥽 Teleport

  • 什么是Teleport?—— Teleport 是一种能够将我们的组件html结构移动到指定位置的技术。
<!-- 移动位置可以标签元素或标签选择器 -->
<teleport to="移动位置">
  <div v-if="isShow" class="mask">
    <div class="dialog">
      <h3>我是一个弹窗</h3>
      <button @click="isShow = false">关闭弹窗</button>
    </div>
  </div>
</teleport>
<template>
  <div>
    <button @click="isShow = true">点我弹个窗</button>
    <!-- 将html结构移动到body中 -->
    <teleport to="body">
      <div v-if="isShow" class="mask">
        <div class="dialog">
          <h3>我是一个弹窗</h3>
          <h4>一些内容</h4>
          <h4>一些内容</h4>
          <h4>一些内容</h4>
          <button @click="isShow = false">关闭弹窗</button>
        </div>
      </div>
    </teleport>
  </div>
</template>
<script>
  import {ref} from 'vue'
  export default {
    name:'Dialog',
    setup(){
      let isShow = ref(false)
      return {isShow}
    }
  }
</script>
<style>
  .mask{
    position: absolute;
    top: 0;bottom: 0;left: 0;right: 0;
    background-color: rgba(0, 0, 0, 0.5);
  }
  .dialog{
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%,-50%);
    text-align: center;
    width: 300px;
    height: 300px;
    background-color: green;
  }
</style>

🥽 Suspense

// 静态引入只要组件不引入成功,当前整个组件都不会进行显示
// import Child from './components/Child'//静态引入
// 异步引入引入的组件等加载完成再进行显示,当前组件可以不用等待引入成功即可先进行显示
import {defineAsyncComponent} from 'vue' 
const Child = defineAsyncComponent(()=>import('./components/Child')) //异步引入
  • 当异步引入的组件引入完成,组件会突然显示,造成用户体验效果不佳
  • Suspense 标签可以在等待异步组件时渲染一些额外内容,让应用有更好的用户体验
  • 如果组件为异步引入且使用了Suspense,则组件的setup可以返回Promise实例对象,即当Suspense和异步组件配合时,setup可以是一个async函数。
  • 使用步骤:
  • 异步引入组件
import {defineAsyncComponent} from 'vue'
const Child = defineAsyncComponent(()=>import('./components/Child.vue'))
  • 使用Suspense包裹组件,并配置好defaultfallback
<template>
  <div class="app">
    <h3>我是App组件</h3>
    <Suspense>
        <!-- 用于放置真正需要显示的组件 -->
      <template v-slot:default>
        <Child/>
      </template>
      <!-- 用于放置等待异步组件时渲染的内容 -->
      <template v-slot:fallback>
        <h3>加载中.....</h3>
      </template>
    </Suspense>
  </div>
</template>

🥽 Vue3全局API的转移

  • Vue 2.x 有许多全局 API 和配置。
  • 例如:注册全局组件、注册全局指令等。
//注册全局组件
Vue.component('MyButton', {
  data: () => ({
    count: 0
  }),
  template: '<button @click="count++">Clicked {{ count }} times.</button>'
})
//注册全局指令
Vue.directive('focus', {
  inserted: el => el.focus()
}
  • Vue3.0中对这些API做出了调整:
  • 将全局的API,即:Vue.xxx调整到应用实例(app)上
2.x 全局 API(Vue 3.x 实例 API (app)
Vue.config.xxxx app.config.xxxx
Vue.config.productionTip 移除
Vue.component app.component
Vue.directive app.directive
Vue.mixin app.mixin
Vue.use app.use
Vue.prototype app.config.globalProperties

🥽 Vue3其他改变

  • data选项应始终被声明为一个函数。
  • 过度类名的更改:
  • Vue2.x写法
.v-enter,
.v-leave-to {
  opacity: 0;
}
.v-leave,
.v-enter-to {
  opacity: 1;
}
  • Vue3.x写法
.v-enter-from,
.v-leave-to {
  opacity: 0;
}
.v-leave-from,
.v-enter-to {
  opacity: 1;
}
  • 移除keyCode作为 v-on 的修饰符,同时也不再支持config.keyCodes,不支持自定义按键别名。
  • 移除v-on.native修饰符
  • 父组件中绑定事件
<my-component
  v-on:close="handleComponentEvent"
  v-on:click="handleNativeClickEvent"
/>
  • 子组件中声明自定义事件
<script>
  export default {
    emits: ['close'] // 子组件接收的事件为自定义事件,没有接收的事件为原生事件
  }
</script>
  • 移除过滤器(filter)

过滤器虽然这看起来很方便,但它需要一个自定义语法,打破大括号内表达式是 “只是 JavaScript” 的假设,这不仅有学习成本,而且有实现成本!建议用方法调用或计算属性去替换过滤器。


相关文章
|
1天前
|
JavaScript
如何在 Vue 项目中选择合适的模块格式
【10月更文挑战第20天】选择合适的模块格式需要综合考虑多个因素,没有一种绝对正确的选择。需要根据项目的具体情况进行权衡和分析。在实际选择过程中,要保持灵活性,根据项目的发展和变化适时调整模块格式。
14 7
|
1天前
|
JavaScript 前端开发 编译器
在 Vue 项目中使用 ES 模块格式的优点
【10月更文挑战第20天】在 Vue 项目中使用 ES 模块格式具有众多优点,这些优点共同作用,使得项目能够更高效、更可靠地开发和运行。当然,在实际应用中,还需要根据项目的具体情况和需求进行合理的选择和配置。
14 6
|
1天前
|
JavaScript 前端开发 开发者
Vue 的优缺点
【10月更文挑战第16天】Vue 具有众多优点,使其成为前端开发中备受青睐的框架之一。尽管它也存在一些局限性,但通过合理的应用和技术选型,这些问题可以得到一定程度的解决。在实际项目中,开发者可以根据项目的需求和特点,权衡 Vue 的优缺点,选择最适合的技术方案。同时,随着 Vue 不断的发展和完善,相信它将在前端开发领域继续发挥重要作用。
11 6
|
1天前
Vue3 中使用 Event Bus
【10月更文挑战第16天】Event Bus 是 Vue3 中一种简单而实用的通信方式,在一些简单的场景中可以发挥重要作用。但在实际应用中,要根据项目的具体需求和复杂度,选择合适的通信方式,以实现最佳的性能和可维护性。同时,要遵循最佳实践,合理使用 Event Bus,避免出现问题。
10 5
|
1天前
|
JavaScript 前端开发 Java
《vue3第五章》新的组件,包含:Fragment、Teleport、Suspense
《vue3第五章》新的组件,包含:Fragment、Teleport、Suspense
10 2
|
1天前
|
Java
vue3知识点:Teleport组件
vue3知识点:Teleport组件
10 1
|
3天前
|
JavaScript
在 Vue 中处理组件选项与 Mixin 选项冲突的详细解决方案
【10月更文挑战第18天】通过以上的分析和探讨,相信你对在 Vue 中使用 Mixin 时遇到组件选项与 Mixin 选项冲突的解决方法有了更深入的理解。在实际开发中,要根据具体情况灵活选择合适的解决方案,以确保代码的质量和可维护性。
27 7
|
3天前
|
存储 JavaScript
vue——store全局存储
【10月更文挑战第18天】Vuex 是 Vue.js 应用中非常重要的一个工具,它为我们提供了一种有效的状态管理方式。通过合理地使用 Vuex,我们可以更好地组织和管理应用的状态,提高应用的开发效率和质量。
15 1
|
2天前
|
缓存 JavaScript UED
Vue 的动态组件与 keep-alive
【10月更文挑战第19天】总的来说,动态组件和 `keep-alive` 是 Vue.js 中非常实用的特性,它们为我们提供了更灵活和高效的组件管理方式,使我们能够更好地构建复杂的应用界面。深入理解和掌握它们,以便在实际开发中能够充分发挥它们的优势,提升我们的开发效率和应用性能。
29 18
|
1天前
|
JavaScript 索引
vue 在 v-for 时给每项元素绑定事件
在 Vue 中使用 v-for 渲染列表时,可以通过给每项元素绑定事件来实现交互功能。通常使用 `@click` 等事件修饰符,结合方法或内联表达式来处理事件。例如:`&lt;li v-for=&quot;item in items&quot; @click=&quot;handleClick(item)&quot;&gt;{{ item }}&lt;/li&gt;`。