vue3相关内容

151 阅读6分钟

1.动机与目的:

1.  更好的逻辑复用 与 代码组织 (composition组合式api)
optionsAPI(旧) => compositionAPI(新), 效果: 代码组织更方便了, 逻辑复用更方便了 非常利于维护!!

2.  更好的类型推导 (typescript支持)
    vue3 源码用 ts 重写了, vue3 对 ts 的支持更友好了 (ts 可以让代码更加稳定, 类型检测! )

2.vue3新特性:

1.  数据响应式原理重新实现 (ES6 proxy 替代了 ES5Object.defineProperty)
Object.defineProperty(user,'username',{set(){},get(){}})递归监听对象中的属性
new Proxy(user,{set,get})监听对象
解决了: 例如数组的更新检测等bug, 大大优化了响应式监听的性能
(原来检测对象属性的变化, 需要一个个对属性递归监听) **proxy 可以直接对整个对象劫持**
2.  虚拟DOM - 新算法 (更快 更小)
3.  提供了composition api, 可以更好的逻辑复用
4.  模板可以有多个根元素
5.  源码用 typescript 重写, 有更好的类型推导 (类型检测更为严格, 更稳定)
小结: vue3 性能更高, 体积更小, 更利于复用, 代码维护更方便

3.vue2与vue3的区别:

vue2用的是选项式optionsAPI  ,  vue3用的是组合式compositionAPI
数据响应式原理不同:vue2用的是Object.defineProperty   vue3用的是Proxy

4.vite的优势

  极速的服务启动,使用原生 ESM 文件,无需打包
  丰富的功能,对 TypeScript、JSX、CSS 等支持开箱即用

传统方式

      基于打包器的方式启动,必须优先抓取并构建你的整个应用,然后才能提供服务。
      更新速度会随着应用体积增长而直线下降。     

vite 方式

 Vite 以 [原生 ESM] 方式提供源码。这实际上是让浏览器接管了打包程序的部分工作。
 Vite 只需要在浏览器请求源码时进行转换并 **按需提供源码**。
 根据情景动态导入代码,即只在当前屏幕上实际使用时才会被处理。 
 vite =>应用(main.js)  =>   浏览器直接运行main.js
 

image.png

5.使用vite创建一个vue3的项目

 1.在文件夹里面输入cmd打开黑窗口,然后输入命令 yarn create vite
 2.输入文件夹名字vite-demo
 

image.png

3.选择创建的项目类型,选择vue即可

image.png

4.选择创建的vue项目类型, 不选ts

image.png

5.启动项目,在黑窗口依次输入以下命令

image.png

6.完成后生成地址

image.png

6.如何快速创建一个vue3项目

创建普通vue项目
yarn create vite vite-demo --template vue

image.png

创建基于ts模板的项目
yarn create vite vite-demo-ts --template vue-ts

7.vue2和vue3的导入方式

image.png

8.vscode插件说明

 vue2中需要安装插件`vetur`,可以实现组件高亮。但是vue3的一些语法在vetur中报错。
 vue3中需要安装插件`volar`,提供了更加强大的功能。
 所以,使用功能vue3,需要卸载`vetur`插件,安装`volar`插件
 

image.png

9.组合式API

composition API 和 options API

1. vue2 采用的就是 optionsAPI
(1) 优点:**`易于学习和使用`**, 每个代码有着明确的位置 (例如: 数据放 data 中, 方法放 methods中)
(2) 缺点: 相似的逻辑, 不容易复用, 在大项目中尤为明显
(3) 虽然 optionsAPI 可以通过mixins 提取相同的逻辑, 但是也并不是特别好维护
2. vue3 新增的就是 compositionAPI
(1) compositionAPI 是基于 **逻辑功能** 组织代码的, 一个功能 api 相关放到一起
(2) 即使项目大了, 功能多了, 也能快速定位功能相关的 api
(3) 大大的提升了 `代码可读性``可维护性`
3. vue3 推荐使用 composition API, 也保留了options API
 即就算不用composition API, 用 vue2 的写法也完全兼容!!

10. 体验 options API

  <template>
      <div>鼠标位置</div>
      <div>x:{{mouse.x}}</div>
      <div>y:{{mouse.y}}</div>
      <div>次数:{{count}}</div>
      <button @click="add">+1</button>
 </template>

 <script>
    export default {
        data() {
              return {
             //鼠标数据位置
              mouse:{
                     x:0,
                     y:0
                   },
      //计算器数据
    count:0
          }
        },
  methods:{
      move(e){
            this.mouse.x = e.pageX
            this.mouse.y = e.pageY
          },
    add(){
         this.count++
       }
     },
 mounted(){
        document.addEventListener('mousemove',this.move)
     },
  destroyed(){
        document.removeEventListener('mousemove',this.move)
      }
    }
 </script>

image.png

11.composition API

 <template>
      <div>当前鼠标位置</div>
      <div>x: {{ mouse.x }}</div>
      <div>y: {{ mouse.y }}</div>
      <div>当前点击次数:{{ count }}</div>
      <button @click="add">点击</button>
 </template>

 <script>
        import { onMounted, onUnmounted, reactive, ref } from 'vue'
      function userCount() {
      const count = ref(0)
         const add = () => {
         count.value++
        }
        return {
        count,
        add
      }
    }
  function useMouse() {
 const mouse = reactive({
  x: 0,
  y: 0,
})
   const move = (e) => {
  mouse.x = e.pageX
  mouse.y = e.pageY
}
onMounted(() => {
  document.addEventListener('mousemove', move)
})
onUnmounted(() => {
  document.removeEventListener('mousemove', move)
})
return  mouse
}
      export default {
    setup() {
   const {count,add} = userCount()
   const mouse = useMouse()
 return {
         count,
         add,
         mouse,
       }
     },
   }
</script>

image.png

12.setup 函数

 1.  setup 函数是一个新的组件选项, 作为组件中 compositionAPI 的起点
 2.  从生命周期角度来看, setup 会在 beforeCreate 钩子函数之前执行
 3.  **setup 中不能使用 this, this 指向 undefined**
 4.  在模版中需要使用的数据和函数,需要在 `setup` 返回。
 <template>
   <div>{{count}}</div>
   <button @click="btn">按钮</button>
</template>

 <script>
   export default {
    setup () {
             console.log('setup执行了')
             console.log(this)
   // 定义数据和函数
     const count = 100
     const btn = () => {
     console.log("点击了")
   }

    return { count , btn}
  },
  beforeCreate() {
          console.log('beforeCreate')
       }
    }
</script>

image.png

13.reactive 函数

前置说明:
 1.  setup 需要有返回值, 只有返回的值才能在模板中使用
 2.  默认普通的数据, 不是响应式的

作用: 传入一个复杂数据类型,将复杂类型数据, 转换成响应式数据 (返回该对象的响应式代理)

 <template>
        <div>{{ user }}</div>
        <button @click="add">+</button>
</template>

<script>
        import { reactive } from 'vue'

 export default {
       setup () {
// 1. setup 需要返回值, 返回的值才能在模板中使用
// 2. 默认的普通的值不是响应式的, 需要用 reactive 函数
      let user = reactive({
      name: 'zs',
      age: 18
    })
       const add = ()=>{
       user.age++
    }
    return {
      user,
      add
    }
  }
}
</script>

image.png

14.ref 函数

 reactive 处理的数据, 必须是复杂类型, 如果是简单类型无法处理成响应式, 所以有 ref 函数!
 作用: 对传入的数据(一般简单数据类型),包裹一层对象, 转换成响应式。
  1.  ref 函数接收一个的值, 返回一个ref 响应式对象, 有唯一的属性 value
  2.  在 setup 函数中, 通过 ref 对象的 value 属性, 可以访问到值
  3.  在模板中, ref 属性会自动解套, 不需要额外的 .value
  4.  ref函数也支持传入复杂类型,传入复杂类型,也会做响应式处理
ref 和 reactive 的最佳使用方式:
明确的对象,明确的属性,用reactive,其他用 ref
 <template>
        <div>{{ money }}</div>
        <button @click="money++">改值</button>
</template>

<script>
       import { reactive, ref } from 'vue'
       export default {
       setup() {
                let money = ref(100)
                money.value++
                return {
                money
              }
            }
         }
 </script>

15.setup语法

image.png

16.计算属性computed

<script setup>
import { computed, ref } from 'vue'
const age = ref(10)
// 不带set的计算属性
const nextAge = computed(() => {
return +age.value + 1
})

// 带set的计算属性
const nextAge2 = computed({
get() {
return +age.value + 2
},
set(value) {
age.value = value - 2
},
})
</script>
<template>
        <div>我今年的年纪 <input type="text" v-model="age" /></div>
        <div>我明年的年龄 {{ nextAge }}</div>
        <div>我后年的年龄 <input type="text" v-model="nextAge2" /></div>
</template>

image.png

17.侦听器watch函数

watch监视, 接收三个参数
 1. 参数1: 监视的数据源
 2. 参数2: 回调函数
 3. 参数3: 额外的配置

image.png

image.png

18.生命周期钩子函数的使用

image.png

image.png

import { onMounted, onUpdated, onUnmounted } from 'vue'

 const MyComponent = {
   setup() {
    onMounted(() => {
    console.log('mounted!')
   })
     onUpdated(() => {
      console.log('updated!')
   })
     onUnmounted(() => {
     console.log('unmounted!')
  })
 }
}

19.组件通讯-父传子

步骤:
    1.  父组件提供数据
    2.  父组件将数据传递给子组件
    3.  子组件通过defineProps进行接收
    4.  子组件渲染父组件传递的数据
    

image.png

image.png

20.组件通讯-子传父

步骤:
    1.  子组件通过defineEmit获取emit对象(因为没有this)
    2.  子组件通过emit触发事件,并且传递数据
    3.  父组件提供方法
    4.  父组件通过自定义事件的方式给子组件注册事件
    

image.png

image.png

21.依赖注入 - provide 和 inject

image.png

image.png

image.png

22.toRefs 函数

使用场景: 如果对一个响应数据, 进行解构 或者 展开, 会丢失他的响应式特性
 原因: vue3 底层是对 对象 进行监听劫持
 作用: 对一个响应式对象的所有内部属性, 都做响应式处理
 1.  reactive/ref的响应式功能是赋值给对象的, 如果给对象解构或者展开, 会让数据丢失响应式的能力
 2.使用 toRefs 可以保证该对象展开的每个属性都是响应式的
 <template>
       <div>{{ name}}</div>
       <div>{{ age }}</div>
       <button @click="age++">改值</button>
</template>

<script setup>
       import { reactive, ref, toRefs } from 'vue'
       const user = ref({
        name: 'zs',
        age: 18,
     })
    const { name, age } = toRefs(user.value)
</script>