Day38:vue3入门

36 阅读8分钟

Vue3版本已经升级为了Vue的默认版本 ,新版本的特性和优化进一步提高了开发效率

1.Vue3简介

vue3相较于vue2,性能提升了,打包效率提升,渲染效率提升,内存减少

源码重构(使用Proxy代替defineProperty实现响应式),还有全新特性

github上的tags地址:github.com/vuejs/vue-n…

vue2使用配置选项写法

vue3中不再使用配置选项写法,而是使用组合式api——Composition API

全新特性

  • Composition API(组合API)
    • setup配置
    • ref与reactive
    • watch与watchEffect
    • provide与inject
    • ......
  • 新的内置组件
    • Fragment
    • Teleport
    • Suspense
  • 其他改变
    • 新的生命周期钩子
    • data 选项应始终被声明为一个函数
    • 移除keyCode支持作为 v-on 的修饰符
    • ......

支持TypeScript

  • Vue3可以更好的支持TypeScript

2.Vite

vue3不再使用Vue-CLI构建项目而是使用新的脚手架工具

Vite工具构建项目的速度比Vue-CLI要更加快捷。

使用命令来创建项目:

npm init vite@latest

选择创建Vue项目后,进入项目目录安装依赖:

npm install

进入生成的目录,npm i

记得在package中npm 一个scss

npm i sass -d

成功后也会生成对应的项目目录,和Vue-CLI的目录结构基本一致。输入命令来启动项目:

npm run dev

在vue3中,默认的本地端口代码改为了5173

在package.json中可以查看版本号

在vue控件中,可以通过插件提供的快捷键直接生成vue3模板

在vite中,可以修改服务器发送请求的了路径

在Vue3中,构建项目的语法有所改变,所以将项目的入口文件(src/main.js)来单独分析一下

// vue3的入口文件
import { createApp } from 'vue'
import App from './App.vue'

createApp(App).mount('#app') // mount等价于el
//vue2的入口文件
import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

new Vue({
  render: (h) => h(App),
}).$mount('#app')

vue2入口中,通过构造函数得到应用实例。其中render其实是一个方法,最后通过$mount挂载到#app中

在Vue3中,不再使用构造函数,变成了利用工厂函数createApp(方法)来创建(生产应用实例)

Vue3遵从函数式编程思想,任何方法都需要手动引入。

3.组合式API

在Vue2中,我们绝大部分逻辑代码都写在配置选项中,数据放到data中,方法放到methods中,请求发送放到mounted中,这种做法看起来很"合理",在存在一个很大问题,它将逻辑割裂了,例如要完成一个A功能,希望能将实现这个功能的所有代码集合在一起,然后完成B功能,再将B功能的代码集合到一起,显然之前的配置选项API让一切变得有点混乱,于是在Vue3中,选项式API进化成了组合式API,这也是Vue3新特性的重点。

  • 使用传统OptionsAPI中,新增或者修改一个需求,就需要分别在data,methods,computed里修改 。
  • 在组合式CompositionAPI中,我们可以更加优雅的组织我们的代码,函数。让相关功能的代码更加有序的组织在一起。

4.setup() 入口

setup可以理解为一个入口,之前的data,methods,watch,computed,生命周期函数等等最常见的逻辑代码都会放到这个函数中来编写。

在根实例App.vue中完成一个简单的点击按钮 数字自增1的效果:

<template>
  <button @click="add">{{ count }}</button>
</template>

<script>
  import { ref } from 'vue'
  export default {
    setup() {
      let count = ref(1)
      let add = () => {
        count.value++ // count被ref包裹后,需要把count改为count.value,才能获取到值
      }
      return {
        count,
        add,
      }
    },
  }
</script>
<style></style>

比较一下Vue3和Vue2中的语法区别,可以总结出来setup的一些语法要求:

  • setup可以理解为Vue3.0中一个新的配置项,值为一个函数(后面还有更优的写法)。
  • 组件中所用到的:数据、方法、计算属性、监听属性等等,均要配置在setup中
  • setup的生命周期在beforeCreate之前,因此获取不到this(Vue3中需要忘记this)。
  • 定义响应式数据需要使用ref函数包裹,需要从vue中导入
  • 需要访问通过ref包裹的响应式数据必须通过.value的形式访问(但是在模板中不需要.value,vue会自动抽取value放入模板)(ref为vue获取dom元素的工具)
  • 定义的数据和函数均需要return才能使用。setup返回的对象可以直接用来视图模板中。
  • Vue3仍然可以兼容Vue2的语法,但不要这么做。
  • script setup语法糖可以简化写法,不需要再写return

5.响应式数据

5.1 ref

在刚才的示例中,有这么一行代码:

let count = ref(1)

这样做就相当于在data中定义了一个count,初始值为1。

ref是Vue3中的一个方法,如果要使用需要引入它。

在Vue3中,如果希望一个数据具有响应式,必须手动调用方法来实现

Vue2的data配置项实现数据的响应式是需要消耗性能的,静态展示不需要使用响应式

Vue3中将这项权利交给了开发者

let add = () => {
  count.value++
}

这行代码是用来更改count数据的

ref的本质是,在原有传入数据的基础上,在外层包了一层对象,包成复杂类型,来借助reactive实现响应式。

因此更改通过ref定义的响应式数据,必须通过.value的形式来访问,在模板中可以直接使用(扒掉了一层对象)。

ref既可以定义基本数据类型,也可以定义引用值

5.2 reactive

用ref定义的响应式数据必须通过.value的形式才能访问,reactive正好解决了这个问题,reactive同样是Vue提供的用来定义响应式数据的方法,reactive方法定义的是一个引用类型数据(对象,数组等复杂数据类型),但它只能定义引用数据类型(不支持简单类型数据)

<template>
  <p>{{ person.name }}</p>
  <p>{{ person.age }}</p>
  <p><button @click="add2">点我年纪加一</button></p>
</template>
<script>
  import { reactive,} from 'vue'
  export default {
    setup() {
      let person = reactive({
        name: '张三',
        age: 18,
      })
      let add2 = () => {
        person.age++
      }
      return {
        person,
        add2,
      }
    },
  }
</script>
<style></style>

5.3 ref和reactive的比较

  • ref和reactive都是用来定义响应式数据的方法
  • ref既可以定义基本数据,也可以定义引用数据
  • reactive只能定义引用数据
  • ref定义的数据在setup中需要通过.value来进行操作,在模板中可以直接访问
  • reactive定义的数据可以直接操作和访问,不用使用.value。
  • 更推荐使用ref来定义数据

6.props和自定义事件

vue2中使用props和this.$emit进行组件通讯

vue3中父组件给子组件传递一些数据,并且希望子组件中的按钮能触发父组件中的数据自增,这是一个父子通信的典型场景

在父组件中定义内容,向外传递,通过子组件在父组件中的接口传递内容,在子组件中双括号接收

setup的小括号中为reactive定义的一个对象形式的参数,如果我们想要使用父组件传递的对象,则使用props作为形参来代替this

//父组件中 组件的使用流程和Vue2中保持一致 仍然需要引用注册使用
  <template>
    <Child @add-count="count++" :msg="msg" />
    <p>{{ count }}</p>
  </template>
<script>
  import { ref } from 'vue'
  import Child from './components/Child.vue'
  export default {
    setup() {
      let count = ref(1)
      let msg = ref('这是传递给子组件的信息')
      return {
        count,
        msg,
      }
    },
    components: {
      Child,
    },
  }
</script>
<style></style>
//子组件中
  <template>
    <p>{{ props.msg }}</p>
    <button @click="add">点我父组件加一</button>
  </template>

<script>
  export default {
    props: ['msg'],
    emits: ['add-count'],//Vue3中需要接受自定义事件 这是和Vue2的显著区别
    setup(props, ctx) {
      const add = () => {
        ctx.emit('add-count')
      }
      return {
        props,
        add,
      }
    },
  }
</script>
<style></style>

setup会接受2个参数,第一个即是父组件传递过来的props对象,第二个参数可以理解为一个上下文对象,这个对象中包含了触发自定义事件的emit方法。当然,仍然需要用选项的形式来接受传递过来的属性和自定义事件。

vue3父类向子类传递方法的流程:

定义方法,

return方法,

在子组件中传入方法,

子组件接收方法,

使用ctx形参定位方法使用的对象(充当this),

重新定义方法来使用父类传入的方法,

return方法,

在子组件视图中加入重新定义的方法

7.计算属性

在Vue3中计算属性变成了一个setup入口中的函数来使用。

姓名案例的Vue3实现(如果没写语法糖记得return)

import {computed} from 'vue'
setup(){
  //计算属性——简写
  let fullName = computed(()=>{
    return person.firstName + '-' + person.lastName
  })
  //计算属性——完整
  let fullName = computed({
    get(){
      return person.firstName + '-' + person.lastName
    },
    set(value){
      const nameArr = value.split('-')
      person.firstName = nameArr[0]
      person.lastName = nameArr[1]
    }
  })
}

计算属性注意点:

  • 计算属性中不应该有“副作用”——比如异步请求,修改dom等
  • 要避免直接修改计算属性的值——计算属性是只读的,特殊情况可以配置get set

双向绑定的姓名写法

直接修改姓名也可以改变单独的姓和名

8.侦听属性

和计算属性类似,侦听属性也变成了一个函数,第一个参数是需要监听的响应式数据(ref对象),第二个参数是变化时执行的回调函数。

<template>
  <button @click="count++">{{ count }}</button>
  <h2>{{ text }}</h2>
</template>

<script>
  import { ref, watch } from 'vue'
  export default {
    setup() {
      let count = ref(0)
      let text = ref('偶数')
      watch(count, () => {
        if (count.value % 2 === 0) {
          text.value = '偶数'
        } else {
          text.value = '奇数'
        }
      })
      return {
        count,
        text,
      }
    },
  }
</script>
<style></style>

  • 对多个响应式数据变化的侦听,不管哪个数据都需要触发回调函数。
  • 其中第一个参数可以传递数组([ref1,ref2]),第二个值newValue,oldValue可以对第一个参数的每一次变化都进行监听
  • const数据如果是复杂类型(比如对象),普通监听监听不到,必须要写deep:true 。
  • deep:true和immediate:true在watch的第三个参数中单独开启 {deep:true}
  • 如果只想对对象中的一个属性进行监视,则watch(() => count.value.a),就能仅对age进行监视
  • 对多个但不是全部对象进行监视,写成数组
  • watchEffect方法,不用手动指定监听对象,只要里面的数据变化就会自动监听

9 生命周期

Vue3中的生命周期一般都直接写在setup入口当中,其中销毁阶段的生命周期名称做了一些修改,而setup本身也可以理解为创建阶段的生命周期,Vue3中的生命周期和Vue2的对应关系如下:

选项式API组合式API
beforeCreatesetup()
createdsetup()
beforeMountonBeforeMount
mountedonMounted
beforeUpdateonBeforeUpdate
updatedonUpdated
beforeDestroyonBeforeUnmount
destroyedonUnmounted
<script>
  import { onMounted } from 'vue'
  export default {
    setup() {
      onMounted(()=>{
        发送一个请求
      })
    },
  }
</script>
  • 使用函数写法调用生命周期可以调用多次,并不会冲突,而是按照顺序依次执行