78、vue3介绍、创建项目、组合式和配置项api、setup、ref函数、reactive函数、计算、监听属性、生命周期、toRef、vue3 setup写法

96 阅读6分钟

vue3介绍

vue3变化

1.性能的提升
  打包大小减少41%
  初次渲染快55%, 更新渲染快133%
  内存减少54%

2.源码的升级
  使用Proxy代替defineProperty实现响应式
  重写虚拟DOM的实现和Tree-Shaking

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

组合式API和配置项API

Options API 存在的问题

使用传统OptionsAPI(配置项API)中,新增或者修改一个需求,就需要分别在data,methods,computed里修改 。

optionapi.png

组合式API

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

组合式.png

创建vue3项目

1.创建vue3项目有两种方式
	1.vue-cli
  	 1.终端输入:vue create vue_cli_test
     2.路由选择,选择vue3即可
     3.跟之前创建vue2是一样的
  2.vite
		npm init vue@latest
2.文档:https://cn.vuejs.org/guide/quick-start.html#creating-a-vue-application

vite创建

1.什么是vite?—— 新一代前端构建工具。
2.优势如下:
  开发环境中,无需打包操作,可快速的冷启动。
  轻量快速的热重载(HMR)。
  真正的按需编译,不再等待整个应用编译完成。
3.创建工程
  1.npm init vue@latest
 	2.进入工程目录
  	cd <project-name>
 	3.安装依赖
  npm install
  4.运行
  	npm run dev
    
4.工具链,构建工具
  工具链(Toolchain)是指一系列相互关联的工具集合,用于支持软件开发和构建过程中的各个环节。它包括了编译器、解释器、链接器、调试器等一系列工具,用于将源代码转换为可执行文件或库文件。

  构建工具(Build tool)是工具链中的一部分,用于自动化构建过程,管理源代码的编译、链接和打包等操作。构建工具的主要目标是简化和加速软件构建过程,提高开发效率

npm init vue@latest

vue3创建.png

setup

1.setup是个函数,以后vue的代码,都写在这里面
2.使用总结
  1.里面可以定义变量
  2.可以定义函数,可以定义匿名函数
  3.如果想在templage中使用,必须return
  4.如果要对变量加入响应式,需要使用ref包裹变量
  5.data,methods都可以用,但是是setup函数先执行,才走其它
  6.template中使用函数,变量,都优先用setup中的
  7.setup最先执行,是再生命周期的beforeCreate前执行的,内部没有this,也不能用this了

在setup中定义函数、变量

 1.总结: 
    1.里面可以定义变量
    2.可以定义函数,可以定义匿名函数
    3.如果想在templage中使用,必须return
 		4.直接定义的变量和函数,在页面中使用,失去了响应式
    
2.var:老语法 let:新语法 const:定义常量
<template>
    <div class="home">
        <p>姓名:{{ name }}</p>
        <p>年龄:{{ age }}</p>

        <ul v-for="item in hobby">
            <li>{{ item }}</li>
        </ul>
        <p>购物车:</p>
        物品名:{{goods_obj.name}}
        价格:{{goods_obj.price}}
        <br>
        <button @click="ClickAdd">点我,价格、年龄加一</button>


    </div>
</template>

<script>

export default {
    name: 'HomeView',
    setup() {
        //以后所有vue3的代码,都写在这里面

        //原来写在data中定义变量,若果想在view中使用定义的变量,必须return出去
        //var:老语法 let:新语法 const:定义常量
        let name = 'nana'
        let age = 18
        let hobby = ['read', 'shopping']
        let goods_obj = {'name': '钢笔', 'price': 19}

        //定义函数
        const ClickAdd=()=>{
            goods_obj.price +=1
            age += 1
            console.log(goods_obj.price,age)
        }
        return {name, age, hobby, goods_obj,ClickAdd}
        
        //定义的变量和函数,在页面中使用,失去了响应式

    }
}
</script>

ref函数

1.作用: 定义一个响应式的数据
2.导入:import {ref} from 'vue'
3.语法: const xxx = ref(initValue)
4.创建一个包含响应式数据的引用对象(reference对象,简称ref对象)。
5.响应数据
  JS中操作数据: xxx.value
  模板中读取数据: 不需要.value,直接:<div>{{xxx}}</div>
ps:
 1. 接收的数据可以是:基本类型、也可以是对象类型。
 2.基本类型的数据:响应式依然是靠Object.defineProperty()的get与set完成的
 3.对象类型的数据:内部 求助 了Vue3.0中的一个新函数—— reactive函数

使用

<template>
    <div class="home">
        <p>姓名:{{ name }}</p>
        <p>年龄:{{ age }}</p>

        <ul v-for="item in hobby">
            <li>{{ item }}</li>
        </ul>
        <p>购物车:</p>
        物品名:{{goods_obj.name}}
        价格:{{goods_obj.price}}
        <br>
        <button @click="ClickAdd">点我,年龄加一</button>
        <button @click="ClickChange">点我,名字改变</button>

    </div>
</template>

<script>
import {ref} from 'vue' //导入
export default {
    name: 'HomeView',
    setup() {
        let name = ref('nana')  //变成了响应式

        let age = ref(18)
        let hobby = ['read', 'shopping']
        let goods_obj = {'name': '钢笔', 'price': 19}

        const ClickAdd=()=>{
            console.log(age,typeof age)  //RefImpl ,obj
            age.value +=1  //需要 对象.value才能取出来
            console.log(age)  //RefImpl
        }

        const ClickChange=()=>{
            name.value = 'cx'
        }
        return {name, age, hobby, goods_obj,ClickAdd,ClickChange}
    }
}
</script>

Data、menthods等都可以使用

1..data,methods都可以用,但是是setup函数先执行,才走其它
2.template中使用函数,变量,都优先用setup中的
3..setup最先执行,是再生命周期的beforeCreate前执行的,内部没有this,也不能用this了
<template>
    <div class="home">
        名字是:{{ name }},年龄是:{{ age }},爱好:{{ hobby }}
        <br>
        <button @click="handleAdd">点我涨年龄</button>
        <br>
        <button @click="handleChange">点击变名字</button>
    </div>
</template>

<script>
import {ref} from 'vue'

export default {
    name: 'HomeView',
    data(){
        return {
            hobby: this.name + '爱打篮球'
        }
    },
    methods: {
    handleAdd() {
      console.log('methods中的handleAdd')
    }
  },
    setup() {
        // 原来的data,method能不能用
        let name = 'nana'
        let age = 19

        const handleAdd = () => {
            console.log('setup中的handleAdd')
        }
        console.log('---',this)
        return {name, age,handleAdd}
    },


}
</script>

reactive

1.作用: 定义一个对象类型的响应式数据(基本类型不要用它,要用ref函数)
2.语法:const 代理对象= reactive(源对象)接收一个对象(或数组),返回一个代理对象(Proxy的实例对象,简称proxy对象)
3.reactive定义的响应式数据是“深层次的”
4.内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据进行操作

4.reactive对比ref
  1.从定义数据角度对比:
      ref用来定义:基本类型数据
      reactive用来定义:对象(或数组)类型数据
  2.从原理角度对比:
      ref通过Object.defineProperty()的get与set来实现响应式(数据劫持)。
      reactive通过使用Proxy来实现响应式(数据劫持), 并通过Reflect操作源对象内部的数据。

  3.从使用角度对比:
      ref定义的数据:操作数据需要.value,读取数据时模板中直接读取不需要.value。
      reactive定义的数据:操作数据与读取数据:均不需要.value。
    
5.使用方式

  let data = reactive({'name': 'nana', 'age': 19, 'hobby': '篮球'})
  const handelAdd = () => {
        data.age += 1  // 使用reactive包裹的对象,直接当对象用即可
        console.log(data)
      }
   return {data, handelAdd}

使用

<template>
    <div class="home">
                <p>姓名:{{ name }}</p>
                <p>年龄:{{ age }}</p>

                <ul v-for="item in hobby">
                    <li>{{ item }}</li>
                </ul>
                <p>购物车:</p>
                物品名:{{goods_obj.name}}
                价格:{{goods_obj.price}}
                <br>
                <button @click="ClickAdd">点我,年龄加一</button>
                <button @click="ClickChange">点我,名字改变</button>
    </div>
</template>

<script>

import {ref,reactive} from 'vue'

export default {
    name: 'HomeView',
    setup() {

        let name = ref('nana')

        let age = ref(18)
        let hobby = ['read', 'shopping']
        let goods_obj = reactive({'name': '钢笔', 'price': 19})

        //定义函数
        const ClickAdd=()=>{
            age.value +=1
            goods_obj.price +=1
            console.log(goods_obj.price,typeof goods_obj.price)
        }

        const ClickChange=()=>{
            name.value = 'cx'
        }
        return {name, age, hobby, goods_obj,ClickAdd,ClickChange}
}}
</script>

计算、监听属性

computed函数

与Vue2.x中computed配置功能一致

原来写法照常使用

<template>
    <div class="home">
        <h1>首页</h1>
        <input type="text" v-model="name">  -->{{newName}}
        <br>

    </div>
</template>

<script>
import {ref,computed} from "vue";

export default {
    name: 'HomeView',
    setup(){
        let name = ref('')
        //原来写法照常使用
        const newName=computed(()=>{
            return name.value +'nb'
        })

        return {name,newName}

    }
}
</script>

计算属性可以取值用,还可以改值

<template>
    <div class="home">
        <h1>首页</h1>
        <input type="text" v-model="name">  -->{{newName}}
        <br>
        <input type="text" v-model="newName">

    </div>
</template>

<script>
import {ref,computed} from "vue";

export default {
    name: 'HomeView',
    setup(){
        let name = ref('')

        //计算属性可以取值用,还可以改值
        const newName = computed({
            get(){
                // 使用计算属性,会触发这里
                return name.value +'=nb'
            },
            set(value){
               // 只要计算属性发生变化,就会执行这里
                // 只要newName变了,name理应该也变,需要我们写代码变
                console.log(value)
                let res = value.split('=')
                name.value=res[0]
                console.log('计算属性变了,变成', value)
            }
        })
        return {name,newName}
    }
}
</script>

watch函数

与Vue2.x中watch配置功能一致

1.两个小“坑”:
  1.监视reactive定义的响应式数据时:oldValue无法正确获取、强制开启了深度监视(deep配置失效)。
  2.监视reactive定义的响应式数据中某个属性时:deep配置有效。

使用

配置api
<template>
    <div class="home">
        <h1>首页</h1>
        姓名:{{name}}
        年龄:{{age}}
        <button @click = 'handleChange'>点我,年龄+1</button>

    </div>
</template>

<script>

import {ref,reactive} from 'vue'

export default {
    name: 'HomeView',
    data(){
        return {
            name:'nana',
            age:18
        }
    },
    methods:{
        handleChange(){
            this.age +=1
        }

    },
    watch:{
        age(){
            console.log('age变了')
        }
    }
}
</script>

组合api

监听属性--普通

<template>
    <div class="home">
        <h1>首页</h1>
        姓名:{{name}}
        年龄:{{age}}
        <button @click = 'handleAdd'>点我,年龄+1</button>
        <button @click = 'handleChange'>点我,名字变</button>

    </div>
</template>

<script>

import {ref,watch} from 'vue'

export default {
    name: 'HomeView',
    setup(){
        let name = ref('nana')
        let age = ref(19)

        const handleAdd=()=>{
            age.value +=1
        }
        const handleChange=()=>{
            name.value='cx'
        }
        // 1 监听属性--监听普通变了
        // watch(name,()=>{
        //     console.log('name变了')
        // })
        //2.监听属性--同时监听多个
        watch([name,age],(newValue,oldValue)=>{
            console.log('name或age变了',newValue,oldValue) //name或age变了 (2) ['cx', 20] (2) ['nana', 20]
        })
        return {name,age,handleAdd,handleChange}
    }
}
</script>

监听对象的某个属性

<template>
    <div class="home">
        <h1>首页</h1>
        购物详情:{{good_info}}
        商品名:{{good_info.title}}
        商品价格:{{good_info.price}}
        <button @click = 'handleAdd'>点我,价格+1</button>
    </div>
</template>

<script>

import {ref,watch,reactive} from 'vue'

export default {
    name: 'HomeView',
    setup(){

        // 监听属性之监听对象中的某个属性
        let good_info = reactive({'title':'钢笔','price':99})
        const handleAdd=()=>{
            good_info.price +=1
        }
        watch(()=>good_info.price,()=>{
            console.log('good_info中的price变了')
        })
        return {good_info,handleAdd}
        // return {name,age,handleAdd,handleChange}
    }
}
</script>

生命周期

1.Vue3.0中可以继续使用Vue2.x中的生命周期钩子,但有有两个被更名:
  beforeDestroy改名为 beforeUnmount
  destroyed改名为 unmounted
  
2.Vue3.0也提供了 Composition API 形式的生命周期钩子,与Vue2.x中钩子对应关系如下:
    1.beforeCreate===>setup()
    2.created=======>setup()
    3.beforeMount ===>onBeforeMount
    4.mounted=======>onMounted
    5.beforeUpdate===>onBeforeUpdate
    6.updated =======>onUpdated
    7.beforeUnmount ==>onBeforeUnmount
    8.unmounted =====>onUnmounted
    
ps:
	1.vue3不建议使用 配置项api,建议把所有代码都写在setup函数中
	2.可以写配置项api,但是不建议了(以后写组合式aip)

vue3生命周期.png

练习

HomeView.vue

<template>
    <div class="home">
        <h1>首页</h1>
        <button @click="handleShow">点我显示</button>
        <HelloWorld v-if="show"></HelloWorld>


    </div>
</template>

<script>
import HelloWorld from "@/components/HelloWorld.vue";
import {ref} from 'vue'

export default {
    name: 'HomeView',
    setup() {
        let show = ref(true)
        const handleShow = () => {
            show.value = !show.value
        }
        return {show,handleShow}
    },
    components:{
        HelloWorld
    }
}
</script>

配置项api

HelloWorld.vue

<template>
    <div class="hello">
        <h1>HelloWorld组件</h1>
    </div>
</template>

<script>
export default {
    name: 'HelloWorld',
    data(){
       return{
           t:null
       }
    },

    beforeCreate() {
        console.log('beforeCreate触发了')
    },
    created(){
        this.t = setInterval(()=>{
            console.log('ok')
        },3000)
        console.log('created触发了')
    },
    beforeMount() {
        console.log('beforeMount触发了')
    },
    mounted() {
        console.log('mounted触发了')
    },
    beforeUpdate() {
        console.log('beforeUpdate触发了')
    },
    updated(){
        console.log('updated触发了')
    },
    beforeUnmount(){

        console.log('beforeUnmount触发了')
        clearInterval(this.t)
        this.t=null
    },
    unmounted(){
        console.log('unmounted触发了')
    }


}
</script>


组合式api

<template>
    <div class="hello">
        <h1>HelloWorld组件</h1>


    </div>
</template>

<script>
import {onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted} from "vue";

export default {
    name: 'HelloWorld',
    setup() {
        console.log('vue组件创建了(包含beforeCreate,created)')
        //原来写在created中的,直接写在下面即可,直接在这开启定期器
        var t = setInterval(() => {
            console.log('ok')
        }, 3000)

        onBeforeMount(() => {
            console.log('onBeforeMount')
        })
        onMounted(() => {
            console.log('onMounted')
        })
        onBeforeUpdate(() => {
            console.log('onBeforeUpdate')
        })
        onUpdated(() => {
            console.log('onUpdated')
        })

        onBeforeUnmount(() => {
            console.log('onBeforeUnmount')
            clearInterval(t)
            t = null
        })
        onUnmounted(() => {
            console.log('onUnmounted')
        })

    }

}
</script>


toRef

1.作用:创建一个ref对象,其value值指向另一个对象中的某个属性。
2.语法:const name = toRef(person,'name')
3.应用: 要将响应式对象中的某个属性单独提供给外部使用时。
4.扩展:toRefs 与toRef功能一致,但可以批量创建多个 ref 对象,语法:toRefs(person)
5.文档:https://www.jianshu.com/p/0c6ad50a9055

使用

...{对象}--->相当于解压

<template>
    <div class="home">
        <h1>首页</h1>
        姓名{{ data.name }}--->年龄{{ data.age }}
        <br>
    </div>
</template>

<script>
export default {
    name: 'HomeView',
    setup() {
        let data = {name: 'nana', age: 19}
        return {data}

    },
}
</script>

<template>
    <div class="home">
        <h1>首页</h1>
        姓名{{ name }}--->年龄{{ age }}
        <br>

    </div>
</template>

<script>

export default {
    name: 'HomeView',
    setup() {
        let data = {name: 'nana', age: 19}
        let b = {...data,gender:'female'}
        console.log(b)  //{name: 'nana', age: 19, gender: 'female'}
        return {...data}

    },

}
</script>

在setup函数中return时,使用return {...toRefs(data)},以后再templte中直接使用内层的变量即可

<template>
    <div class="home">
        <h1>首页</h1>
        姓名{{ name }}--->年龄{{ age }}
        <br>
        <button @click ='handleClick'>点我看美女</button>
        <button @click = 'handleAdd'>点我改年龄</button>
    </div>
</template>

<script>

import {reactive,toRefs} from "vue";

export default {
    name: 'HomeView',
    setup() {
        let data = reactive({name: 'nana', age: 19})

        const handleClick=()=>{
            alert('美女')
        }
        const handleAdd=()=>{
            data.age +=1
            console.log(data.age)
        }
        //...toRefs(data)  等同于  {name:data.name,age:data.age}
        return {...toRefs(data),handleClick,handleAdd}
    },
}
</script>

vue3 setup写法

1.<script setup>表示:这个script里所有东西是setup函数,原来写在setup中的,现在定格写即可
2.组件导入,自动注册
3.不需要return
4.照样写:watch,computed,生命周期钩子,组件导入,自动注册
<script setup>

//组件导入,自动注册
import {ref} from 'vue'
import HelloWorld from "../components/HelloWorld.vue";

let name = ref('nana')
//不需要return
const handelClick=()=>{
    name.value='cx'
}

</script>

<template>
<div>
    <h1>首页</h1>
    {{name}}
    <br>
    <button @click="handelClick">点我,变名字</button>
    <HelloWorld msg="nana"> </HelloWorld>
    
</div>
</template>