目录
一.Vue3简介
1.性能提升
2.源码升级
3.拥抱ts
4.新特性
(1)Composition API(组合API):
(2)新的内置组件:
(3)其他改变:
二.创建vue3工程
1.使用vue/cli创建(基于webpack)
2.使用vite创建(推荐)
优势:
3.分析工程结构
编辑
4.编写App组件
5.OptionsAPI和CompositionAPI
6.setup
7.setup和OptionsAPI(面试题)
8.setup语法糖
三、vue3中的响应式
1.ref函数
(1)简单数据类型:
(2)复杂数据类型:
2.reactive函数
处理对象数据类型:
处理数组数据类型:
3.reactive和ref的对比
(1)定义的角度
(2)原理的角度
(3)使用的角度
4.toRefs和toRef
(1)toRefs
(2)toRef
编码规范:采用ts+组合式API+setup语法糖
一.Vue3简介
1.性能提升
打包速度快、内存小、渲染快
2.源码升级
使用proxy代替defineProperty实现响应式
重写虚拟Dom实现和Tree-Shaking
3.拥抱ts
4.新特性
(1)Composition API(组合API):
setup
ref与reactive
computed与watch
(2)新的内置组件:
Fragment
Teleport
Suspense
(3)其他改变:
新的生命周期钩子
data选项应始终被声明为一个函数
移出keyCode支持作为v-on的修饰符
二.创建vue3工程
1.使用vue/cli创建(基于webpack)
首先查一下vue / cli的版本:
vue -V或者vue --version
在桌面上添加工程(cd desktop)
苹果电脑需要加上sudo : sudo vue create vue3_test
2.使用vite创建(推荐)
在前端中,webpack是构建工具里的老大哥
vite:新一代的前端构建工具,跟webpack等价,后端的maven跟npm差不多
优势:
开发环境中,无需打包操作,可快速的冷启动。
轻量快速的热重载(HMR)。
真正的按需编译,不再等待整个应用编译完成。
对ts、jsx、css开箱即用
上图是webpack,下面是vite
创建工程:sudo npm create vue@latest
暂时不需要vue router,之后学到了再去配置
运行速度是大大滴快啊,但是暂时不关注这个
3.分析工程结构
extensions.json显示安装的插件
src:源代码文件,前端的所有工作
gitignore:git 的忽略文件
env.d.ts:让程序能认识jpg、txt等文件格式,如果飘红那就是因为没有安装依赖
入口文件不再是main.js,是index.html
拿到手一个前端项目怎么去启动?
找到package.json的dev :npm run dev
依赖管理包:
ts的配置文件:
vite.config.ts:整个工程的配置文件
可以安装插件和配置代理
4.编写App组件
我们在入口文件index.html里引入了main.ts,所以接下来就去写这个
//引入createApp用于创建应用
//createApp类似花盆
import {createApp} from 'vue'//引入App根组件
//App类似花的根
import App from './App.vue'
//createApp(App)把花插在花盆里
createApp(App).mount('#app')
//mount('#app')把花摆在app这个容器里,这个app在index里
所以index.html里面必须得引入main.ts和写一个容器(app)
Vue3中是通过createApp函数创建一个应用实例。
5.OptionsAPI和CompositionAPI
vue2采用的是OptionsAPI,选项式的,name、data、methods等等的都是配置项
vue3采用的是CompositionAPI,组合式的
在vue2中,如果你想去改一个功能,你就得去各个配置项中都去修改很麻烦
vue3中,把这些分散的配置项合并为单独的函数,每个功能就是一个独立的函数
6.setup
setup是Vue3中一个新的配置项,值是一个函数。
data和methods、computed、、、、都不要了,直接都写在setup里
如果return返回的是对象的话,可以直接在template中使用
注意:setup前面不能加async,加上之后返回值就不是一个对象了,是被promise包裹的对象
<template><div class="person"><h2>{{ sex }}</h2><button @click="showTel">点击展示号码</button><button @click="changeSex">点击改变性别</button></div>
</template><script>
export default {
name:'person',
setup(){//以前是写在data中的,现在这里的数据都不是响应式数据let sex='男'let tel=183function showTel(){alert(tel)}function changeSex(){sex='女'console.log(sex)//女//这里性别确实被改变了,但是页面没有变化}return {sex,tel,showTel,changeSex}
}
}
</script>
下面不return,上面是不能用的
注意:(1)vue3中的this是不奏效的
(2)这里的let中写的数据不能实现响应式,函数更改性别之后页面没有变化但是sex已经更改
(3)setup的返回值也可以是一个渲染函数
返回的是函数,最终渲染到页面上,不管你写的什么函数和变量最终返回的都是‘哈哈’
(4)setup要发生在beforeCreated之前的
7.setup和OptionsAPI(面试题)
也就是说setup可不可以和传统的配置项同时写,同时写了要以谁为主?
<template><div class="person"><h2>{{ sex }}</h2><h2>{{ name }}</h2><h2>{{ c }}</h2><button @click="showTel">点击展示号码</button><button @click="changeSex">点击改变性别</button></div>
</template><script>
export default {
name:'person',
data(){return {sex:'nv',tel:183,c:this.name}
},
methods:{
showTel(){
alert(this.tel+'hhh')
}
},
setup(){//以前是写在data中的,现在这里的数据都不是响应式数据let sex='男'let tel=183let name='aa'function showTel(){alert(tel)}function changeSex(){sex='女'console.log(sex)//女//这里性别确实被改变了,但是页面没有变化}return {
```
(1)首先看data中this调用setup中的name,发现name和c都可以显示在页面上,说明data可以通过this调用setup中的数据,虽然vue3的this不奏效。(setup在beforeCreated之后,所以this 的时候可以取到setup中的了)
反过来的话,在setup中是不能调用到data中的数据的。
(2)然后我尝试用data、methods和setup中的数据与方法重命名,发现data中的不管放在setup前后都不奏效,但是函数是谁在后面谁奏效
8.setup语法糖
每次写setup()还要return把数据交出去很费事,一个简单方式:
在script标签里加上setup,这样它自动给你return数据了,直接在script里写setup函数的东西(不用return就行)
但是这么写有一个问题,没有export和name之后我想设置名字咋办呢,再写一个script专门为他写名字多麻烦,下载插件:pm i vite-plugin-vue-setup-extend -D
然后去vite.config.ts里引用
import VueSetupExtend from 'vite-plugin-vue-setup-extend'
,,,
plugins: [vue(),VueSetupExtend(),],//在插件里追加一句调用
然后就可以直接在script标签里写name了,但是使用情况不多,一般就是文件名和组件名一致
三、vue3中的响应式
vue2中写在data中的数据就是响应式的,因为默认做了数据代理和数据劫持。
vue2中实现响应式都是通过Object.defineProperty去监听getter和setter有没有被调用,调用之后去返回数据或者把新数据更新到页面上,这样来实现读或修改,但是你要是去删除一个已经存在的属性或者添加一个没有的属性都不行,都捕获不到的。
就得用this.$set/Vue.set 添加 用 this.$delete/Vue.delete删除
数组的话还可以用this.splice来修改数据
vue2里面,只赋值数组下标不能实现响应式。
vue3中有两种方式定义响应式数据:ref、reactive
通过Proxy(代理):拦截对象中任意属性的变化,包括:属性值的读写、属性的添加、属性的删除等。
通过Reflect(反射):对源对象的属性进行操作。
1.ref函数
作用:定义一个响应式的数据
首先引入:import {ref} from 'vue'
(1)简单数据类型:
只用普通的let name=‘’ 是不能进行后续修改的,ref可以实现响应式对象,加工完之后的name变成ref引用对象,就把它变成一个响应式的了
<template><h1>hhh</h1><h2>{{ name }}</h2><h2>{{ age }}</h2><button @click="changeData">点击改数据</button><!-- test2() -->
</template><script>
import {ref} from 'vue'
export default {name: "App",setup() {let name = ref('tt');let age = ref(18);function changeData() {name.value='h',age.value=48console.log(name,age)}
带下划线的(_value,_rawValue)等等都不用你管,不带下划线的value是我们应该看的。
响应式依然是靠Object.defineProperty()的get与set完成的。(js里面)修改值的话就.value,但是在template用的时候却不用.value,因为它检测到你是ref引用对象,就自动读取value。
数据响应式:ref
修改数据/Js中用:.value
模版上呈现(template):直接写
(2)复杂数据类型:
let job=ref({salary:'30k',mingzi:'前端'})
复杂数据的value里有type(mingzi)和salary,但是它俩不是靠ref实现的,而是变成proxy的代理对象(内部求助reactive函数),所以修改值的时候不用写成job.value.salary.value
function jiaxin(){// job.salary.value='40k'job.value.salary='40k'}
为什么.value要写在中间而不是后面呢,准确来说.value应该紧跟let后面的名字,因为xx.value之后才能摸到这个对象/数组,如果遇到数组的话就是a.value[0].name。
2.reactive函数
作用:定义一个对象类型的响应式数据(基本类型不要用reactive,用ref)
语法:const代理对象=reactive源对象,返回一个代理对象(proxy实例对象,简称proxy对象)
首先引入:import {reactive} from 'vue'
处理对象数据类型:
let car=reactive({price:100,brand:'奔驰'})
调用的时候不用再value了,直接调用car输出的是proxy对象
function add() {console.log(car);car.price +=10;}
而且reactive可以深层次的处理数据。
处理数组数据类型:
let hobby=reactive(['抽烟','喝酒','打麻将'])
修改:
hobby[0]='学习'
vue2中修改马冬梅的时候,不能直接用索引号赋值修改,得用push啥啥的方法来实现响应式
3.reactive和ref的对比
(1)定义的角度
ref经常定义基本数据类型,reactive定义数组、对象数据类型
注:ref也可以定义数组、对象数据类型,内部通过reactive转为代理对象
(2)原理的角度
ref通过 object.defineProperty()的get与set来实现响应式(数据劫持)。
reactive通过使用Proxy来实现响应式(数据劫持),并通过Reflect操作源对象内部的数据。
(3)使用的角度
ref定义的数据:操作数据需要.value,读取数据时模板中直接读取不需要.value
reactive定义的数据:操作数据与读取数据:均不需要.value
注:(1)ref里的value可以通过插件添加,就不用自己加了:
打开设置里的vue,勾上这个就自动添加
(2)如果想整体改变reactive写好的car对象时,用Object.assign
function changeCar(){Object.assign(car,{brand:'qq',price:'1'})}
想整体改变ref定义的car对象的话
function changeCar(){// Object.assign(car,{brand:'qq',price:'1'})car.value={brand:'qq',price:1}}
4.toRefs和toRef
(1)toRefs
作用:把响应式对象里的数据拿出来并且这些数据仍然是响应式的
先来看下面的代码
<template><div class="person"><h2>{{ person.name }}</h2><h2>{{ person.age }}</h2><h2>{{ name }}</h2><h2>{{ age }}</h2><button @click="changeName">修改名字</button><button @click="changeAge">修改年龄</button></div>
</template><script setup lang="ts">
import {reactive} from 'vue'
let person=reactive({name:'tt',age:18
})
let {name,age}=personfunction changeName(){name+='~'console.log(name,person.name)}function changeAge(){age++console.log(age,person.age)}
</script>
name和age+都是改的你解构出来的name和age,而不是person的,所以person 的数据都不变
后台数据变化但是页面上的数据都没变,因为解构出来的不是响应式的数据
let {name,age}=toRefs(person) function changeName(){name.value+='~'console.log(name.value,person.name)}function changeAge(){age.value++console.log(age.value,person.age)}
加上toRefs之后就是从reactive定义的person对象里拿ref定义的所有属性(name和age),现在的name和age也是person 的name和age了,而且也实现了响应式
(2)toRef
toRef就是一个一个拆出来
let ll=toRef(person,'age')