vue3 快速上手,基础知识(一)

110 阅读3分钟

前言

  • 创建一个vue3的项目
  • vue3相对于vue2都有哪些优化
  • vue3中组合式API的基础用法
  • 依赖注入

1、创建项目

 # 安装脚手架
 npm install -g @vue/cli
 # OR
 yarn global add @vue/cli
 ​
 # 查看脚手架版本号
 vue --version
 # 图形化工具
 vue ui
 ​
 # 创建项目
 vue create vue3
 # 启动项目
 cd vue3
 npm run serve

2、vue3都有哪些升级

2-1. 性能

vite的优点
  • 打包大小减少
  • 启动服务变快,热更新渲染更快(存在一些缓存机制)
  • 内存占用减少
vite的缺点
  • 生态不足
  • prod环境构建,使用的 Rollup
  • 使用还不广泛,可能存在很多问题未被发现

2-2. 源码

  • 组合式API,Components API让组件抽离、逻辑代码复用更加灵活

  • 使用ES 6新增Proxy代替Object.defineProperty实现响应式

  • 更好的支持TypeScript

  • 移除一些冷门API

  • 引入 Tree shaking 按需编译,体积比vue2更小

     // 如果你的项目没有用到 watch,编译时就会 tree shaking 掉
     import { computed, watch } from 'vue';
    

3、生命周期

lifecycle.16e4c08e.png

组合式API 生命周期引用及调用时机
 // setup == beforeCreate & created
 <scripte setup>
     import { onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted, onErrorCaptured, onRenderTracked, onRenderTriggered, onActivated, onDeactivated, onServerPrefetch } from 'vue';
 ​
     // 在组件DOM实际渲染安装之前调用,在这一步中,根元素还不存在
     onBeforeMount() {}
     // 在组件的第一次渲染后调用,该元素现在可用,允许直接DOM访问
     onMounted() {}
     
     // 数据更新时调用,发生在虚拟 DOM 打补丁之前,在更新前访问现有的DOM
     onBeforeUpdate() {}
     // DOM更新后
     onUpdated() {}
     
     // 在卸载组件实例之前调用。在这个阶段,实例仍然是完全正常的。可以卸载一些定时器等   
     onBeforeUnmount() {}
     // 卸载组件实例后调用。
     onUnmounted() {}
     
     // 被 keep-alive 缓存的组件激活时调用。
     onActivated() {}
     // 被 keep-alive 缓存的组件停用时调用。
     onDeactivated() {}
     
     // 在捕获了后代组件传递的错误时调用。 
     onErrorCaptured() {}
     
     // 跟踪虚拟 DOM 重新渲染时调用 (只在开发环境起作用)
     onRenderTracked() {}
     // 在一个响应式依赖被组件触发了重新渲染之后调用 (只在开发环境起作用)
     onRenderTriggered() {}
     
     // 需要做SSR的生命周期,当组件实例在服务器上被渲染之前要完成的异步函数。
     onServerPrefetch() {}
 <scripte>

4、响应式API

4-1. ref 函数

接受任意类型值返回一个响应式的数据

语法
 let count = ref(0)
例子
 <template>
     /* 在模板中使用ref响应式数据时不需要 .value 取值 */
     <div>{{count}}</div>
 </template>
 ​
 <script setup>
     import { ref } from 'vue'
     import type { Ref } from 'vue'
 ​
     let count = ref(0)
     // 通过.value的方式取值
     console.log(count.value) // 0
     // 通过.value的方式修改ref返回的响应式的值
     count.value ++
     console.log(count.value) // 1
     
     // 为 ref() 标注类型
     let year: Ref<string | number> = ref('2022')
     year.value = 2022
 </script>

4-2. reactive 函数

定义一个对象类型响应式数据;返回一个响应式对象(Proxy 代理对象)

语法
 const obj = reactive({ age: 18, sex: '男' })
例子
 <template>
     /* 此处用法与 ref 一致 */
     <div>{{obj.age}}</div>
 </template>
 ​
 <script setup>
     import { reactive } from 'vue'
     
     const obj = reactive({ age: 18, sex: '男' })
     
     obj.age = 20
     obj.sex = '女'
     // 与 ref 不同可以直接访问和修改响应式数据的值 不需要通过 .value 获取
     console.log(obj) // { age: 20, sex: '女' }
 </script>

4-3. reactive 和 ref 区别

  • ref 一般用于定义基础类型数据,如果使用 ref 定义对象或数组时 源码实际时调用 reactive
  • reactive 定义一个对象或数组返回一个响应式的 Proxy 实例
  • ref基于object.defineProperty实现响应式,reactive基于Proxy实现

4-4. computed函数

与vue2中的computed使用一致

4-5. watch函数

watch函数接受三个参数,第一个是响应式数据 第二个是更改后的回调函数 第三个是可选配置参数

语法
 watch(
   () => state, // 需要监听的响应式数据
   (newValue, oldValue) => {
     // newValue === oldValue
   }, // 修改值后的回调
   { deep: true } // 执行时机,debugger 等配置
 )
例子
 <script setup>
     import { reactive, watch } from 'vue'    
     const state = reactive({ count: 0 })
     watch(() => state.count, (count, prevCount) => {
         /* ... */
     }, {
         immediate?: boolean, // 默认:false 是否立即监听。
         deep?: boolean, // 默认:false 是否深度监听。
         flush?: 'pre' | 'post' | 'sync', // 调用时机 pre > dom更新前;post > dom;更新后 sync > 同步调用
         onTrack?: (event: DebuggerEvent) => void,  // 会在某个响应式 property 或 ref 作为依赖被追踪时调用。
         onTrigger?: (event: DebuggerEvent) => void // 会在侦听回调被某个依赖的修改触发时调用。
     })
     
     console.log(state.count) // 访问 state.count,触发onTrack
     state.count ++ // 修改 state.count,触发onTrigger
 </script>

4-6. watchEffect

用于自动收集响应式数据的依赖

语法
 // 传入的函数会立即执行一次,在执行的过程中收集依赖,只有收集的依赖发生变化时 函数才会执行
 // 第二个参数的三个配置项与 watch 的配置项用法一致
 watchEffect(() => {
     /*  */
 }, {
   flush: 'post',
   onTrack(e) {
     debugger
   },
   onTrigger(e) {
     debugger
   }
 })
例子
 <script setup>
 import { reactive, ref, watchEffect } from "vue";
 let num = ref(0)
 let obj = reactive({ name: '张三' })
 ​
 const stop = watchEffect(() => {
   console.log(obj.name);
 })
 ​
 setTimeout(() => {
   num.value ++ // watchEffect 不执行
 }, 1000)
     
 setTimeout(() => {
   obj.name = '李四' // watchEffect 执行
   stop() // 停止监听器
 }, 3000)
     
 setTimeout(() => {
   obj.name = '可鲁可' // watchEffect 不再执行
 }, 5000)
 </script>
清除副作用

开发中需要在侦听函数中执行网络请求,但是在网络请求还没有达到的时候停止了侦听器,或者侦听器侦听函数被再次执行了,那么上一次的网络请求应该被取消掉,这个时候可以清除上一次的副作用;

 <template>
   <div>
     <p>{{name}}</p>
     <button @click="changeName">改变</button>
   </div>
 </template>
 ​
 <script setup>
     import { ref, watchEffect } from 'vue';
 ​
     const name = ref('张三')
     const num = ref(0)
     
     watchEffect(onInvalidate => {
         // 模拟值发生变化是需要发的一些网络请求
         const timer = setTimeout(() => {
             console.log("请求成功");
         }, 2000);
         // 上一次请求未完成再次发生变化时,取消上一次的请求
         onInvalidate(() => {
             clearTimeout(timer);
         });
         console.log('name === ', name.value);
         console.log('num === ', num.value)
     });
     const changeName = () => {
       name.value = '李四'
       num.value ++ 
     };
 </script>

4-7. readonly()

接受一个对象(响应式/非响应式对象),返回一个只读状态的 Proxy 代理对象

语法
 const copy = readonly(obj) 
例子
 import { reactive, watchEffect, readonly } from "vue";
 ​
 const original = reactive({ count: 0 })
 const copy = readonly(original)
 ​
 watchEffect(() => {
   // 用来做响应性追踪
   console.log(original.count)
 })
 watchEffect(() => {
   // 用来做响应性追踪
   console.log(copy.count)
 })
 setTimeout(() => {
     // 更改源属性会触发其依赖的侦听器
     original.count++
 ​
     // 更改该只读副本将会失败,并会得到一个警告
     copy.count++ // warning!
 }, 2000)

5、依赖注入

实现跨层级传值

  • provide 接受两个值,第一个参数是要注入的key<string | symbol>,第二个是需要注入的值。
  • inject 接受三个值,第一个是注入的 key,第二个参数是可选参,如果没有匹配到key时使用默认值,如果默认值本身时一个函数则需要将第三个参数设置为false,表明第二个默认值就是函数而不是工厂函数。
语法
 provide('foo', 'bar')
 inject('foo', () => '如果没有找到foo,则使用默认值')
例子
 // 父组件
 <script setup>
 import { ref, provide } from 'vue'
 ​
 // 提供静态值
 provide('foo', 'bar')
 ​
 // 提供响应式的值
 const count = ref(0)
 provide('count', count)
 </script>
 ​
 // 子孙组件
 <script setup>
 import { inject } from 'vue'
 ​
 // 接收静态数据,当foo没有被找到时使用 new foo
 const inject = inject('foo', 'new foo')
 ​
 // 接收响应式的值
 const count = inject('count')
 // 修改接收的响应式值
 count.value ++
 </script>