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 |
|---|---|
| beforeCreate | setup() |
| created | setup() |
| beforeMount | onBeforeMount |
| mounted | onMounted |
| beforeUpdate | onBeforeUpdate |
| updated | onUpdated |
| beforeDestroy | onBeforeUnmount |
| destroyed | onUnmounted |
<script>
import { onMounted } from 'vue'
export default {
setup() {
onMounted(()=>{
发送一个请求
})
},
}
</script>
- 使用函数写法调用生命周期可以调用多次,并不会冲突,而是按照顺序依次执行