2020年9月18日,Vue.js发布3.0版本,代号:One Piece
重要的是它带了什么?四字总结:更快更轻。喔~对了,它还更好的支持TypeScript(TS可能以后也会成为主流了吧)
这次笔记重点是vue3.0的新特性!
中文文档
GitHub文档
学习视频,感谢尚硅谷博主
准备
创建vue3.0项目方式:
一、 vue-cli
## 查看@vue/cli版本,确保@vue/cli版本在4.5.0以上
vue --version
## 安装或者升级你的@vue/cli
npm install -g @vue/cli
## 创建
vue create <project-name>
## 启动
cd <project-name>
npm run serve
二、 vite
官方文档:https://v3.cn.vuejs.org/guide/installation.html#vite
vite官网:https://vitejs.cn
## 创建工程
npm init vite-app <project-name>
## 进入工程目录
cd <project-name>
## 安装依赖
npm install
## 运行
npm run dev
Composition API(组合API)
setup
作为vue3.0新配置项,本质是一个函数
注意:
① 不要和vue2.x配置项混合使用
② vue2配置项(data、methos、computed…)能访问setup里的配置,但setup不能访问到vue2.x配置
③ 如果重名,setup优先
④ setup不能是一个async函数,因为返回值不再是return的对象, 而是promise, 模板看不到return对象中的属性。(后期也可以返回一个Promise实例,但需要Suspense和异步组件的配合)
⑤ 执行时机:在beforeCreate之前执行一次,this是undefined
⑥ setup的参数:(props,context)
- props:值为对象,包含:组件外部传递过来,且组件内部声明接收了的属性。
- context:上下文对象
- attrs: 值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性, 相当于
this.$attrs
。- slots: 收到的插槽内容, 相当于
this.$slots
。- emit: 分发自定义事件的函数, 相当于
this.$emit
。
setup(){
//数据
//方法
function sayHello(){}
function test2(){}
//返回一个对象(常用)
return {sayHello,test2}
//返回一个函数(渲染函数)
// return ()=> h('h1','尚硅谷')
}
ref函数
作用:定义响应式数据(常用于基本类型数据)
如果定义数据是对象,内部其实还求助了vue3.0的一个新函数——reactive函数
reactive函数
作用:定义一个对象类型的响应式数据
reactive定义的响应式数据是“深层次的”。
基本类型数据不能用reactive定义!
<template>
<h1>一个人的信息</h1>
<h2>姓名:{{name}}</h2>
<h2>年龄:{{age}}</h2>
<h3>工作种类:{{person.job.type}}</h3>
<h3>工作薪水:{{person.job.salary}}</h3>
<h3>爱好:{{person.hobby}}</h3>
<h3>测试的数据c:{{person.job.a.b.c}}</h3>
<button @click="changeInfo">修改人的信息</button>
</template>
<script>
// 引用对应函数
import {ref,reactive} from 'vue'
export default {
name: 'App',
setup(){
//数据
// ref数据
let name = ref('张三')
let age = ref(18)
// reactive数据
let person = reactive({
job:{
type:'前端工程师',
salary:'30K',
a:{
b:{
c:666
}
}
},
hobby:['抽烟','喝酒','烫头']
})
//方法
function changeInfo(){
// ref数据操作:xxx.value
name.value = '李四'
age.value = 48
// reactive数据不需要.value
person.job.type = 'UI设计师'
person.job.salary = '60K'
person.job.a.b.c = 999
person.hobby[0] = '学习'
}
//返回一个对象(常用)
return {
name,
age,
person,
changeInfo
}
}
}
</script>
响应式原理对比
vue2.0
问题:
- 新增属性、删除属性, 界面不会更新。
- 直接通过下标修改数组, 界面不会自动更新。
// 数据劫持
Object.defineProperty(data, 'count', {
get () {},
set () {}
})
vue3.0 —— proxy(代理)
完美解决vue2的问题
new Proxy(data, {
// 拦截读取属性值
get (target, prop) {
return Reflect.get(target, prop)
},
// 拦截设置属性值或添加新属性
set (target, prop, value) {
return Reflect.set(target, prop, value)
},
// 拦截删除属性
deleteProperty (target, prop) {
return Reflect.deleteProperty(target, prop)
}
})
computed计算属性
setup(){
//数据
let person = reactive({
firstName:'张',
lastName:'三'
})
//计算属性——简写(没有考虑计算属性被修改的情况)
/* person.fullName = computed(()=>{
return person.firstName + '-' + person.lastName
}) */
//计算属性——完整写法(考虑读和写)
person.fullName = computed({
get(){
return person.firstName + '-' + person.lastName
},
set(value){
const nameArr = value.split('-')
person.firstName = nameArr[0]
person.lastName = nameArr[1]
}
})
//返回一个对象(常用)
return {
person
}
}
watch属性
格式:watch(xxx数据,(newvalue,oldvalue)=>{},{immediate:true/false})
//情况一:监视ref所定义的一个响应式数据
watch(sum,(newValue,oldValue)=>{
console.log('sum变了',newValue,oldValue)
},{immediate:true})
//情况二:监视ref所定义的多个响应式数据,数组形式返回
watch([sum,msg],(newValue,oldValue)=>{
console.log('sum或msg变了',newValue,oldValue)
},{immediate:true})
/*
情况三:监视reactive所定义的一个响应式数据的全部属性
1.注意:此处无法正确的获取oldValue
2.注意:强制开启了深度监视(deep配置无效)
*/
watch(person,(newValue,oldValue)=>{
console.log('person变化了',newValue,oldValue)
},{deep:false}) //此处的deep配置无效
//情况四:监视reactive所定义的一个响应式数据中的某个属性
watch(()=>person.name,(newValue,oldValue)=>{
console.log('person的name变化了',newValue,oldValue)
})
//情况五:监视reactive所定义的一个响应式数据中的某些属性
watch([()=>person.name,()=>person.age],(newValue,oldValue)=>{
console.log('person的name或age变化了',newValue,oldValue)
})
//特殊情况
watch(()=>person.job,(newValue,oldValue)=>{
console.log('person的job变化了',newValue,oldValue)
},{deep:true}) //此处由于监视的是reactive定义的对象中的某个属性,所以deep配置有效
watchEffect函数
智能版的watch(谁用就监视谁,不必写明白我在监视谁)
setup(){
//数据
let sum = ref(0)
let msg = ref('你好啊')
let person = reactive({
name:'张三',
age:18,
job:{
j1:{
salary:20
}
}
})
watchEffect(()=>{
const x1 = sum.value //我用了sum的数据,那么我是在监视sum
const x2 = person.job.j1.salary //同理
console.log('watchEffect所指定的回调执行了')
})
//返回一个对象(常用)
return {
sum,
msg,
person
}
}
生命周期
在setup外配置:
beforeDestroy
改名为beforeUnmount
destroyed
改名为unmounted
在setup内配置:
beforeCreate
===>setup()
created
=======>setup()
beforeMount
===>onBeforeMount
mounted
=======>onMounted
beforeUpdate
===>onBeforeUpdate
updated
=======>onUpdated
beforeUnmount
==>onBeforeUnmount
unmounted
=====>onUnmounted
hook
本质是一个函数,把同功能所用的的composition api封装起来
类似于mixin
优势:复用代码,逻辑清晰易管理
toRef和toRefs
作用:要将响应式对象中的某个属性单独提供给外部使用时。
let person = reactive({
name:'张三',
age:18,
job:{
j1:{
salary:20
}
}
})
const name1 = person.name //此时的name1 只是普通的数据,没有响应式了
console.log('%%%',name1)
const name2 = toRef(person,'name') //name2 依然是响应式数据
console.log('####',name2)
// toRefs ——批量创建多个 ref 对象
const p = {...toRefs(person)}
shallowReactive和shallowRef
三个字:浅响应!
- shallowReactive:只处理对象最外层属性的响应式(浅响应式)。
- shallowRef:只处理基本数据类型的响应式, 不进行对象的响应式处理。
- 什么时候使用?
- 如果有一个对象数据,结构比较深, 但变化时只是外层属性变化 ===> shallowReactive。
- 如果有一个对象数据,后续功能不会修改该对象中的属性,而是生新的对象来替换 ===> shallowRef。
readonly和shallowReadonly
两个字:只读
- readonly: 让一个响应式数据变为只读的(深只读)。
- shallowReadonly:让一个响应式数据变为只读的(浅只读)。
- 应用场景: 不希望数据被修改时。
toRaw和markRaw
作用:把reactive定义的响应式对象变成普通对象
setup(){
let person = reactive({
name:'张三',
age:18,
job:{
j1:{
salary:20
}
}
})
function showRawPerson(){
const p = toRaw(person) //此时的p只是一个普通对象
p.age++
console.log(p) //更新了age也不会显示在页面上
}
function addCar(){
let car = {name:'奔驰',price:40}
person.car = markRaw(car) // 新添加的属性不会有响应式效果
}
return {
person, //新添加的属性必须把整个person返回才能读取到
...toRefs(person)
}
}
customRef
作用:自定义ref
例如:有一个需求,输入框的内容我隔1秒再显示出来
<template>
<input type="text" v-model="keyWord">
<h3>{{keyWord}}</h3>
</template>
<script>
import {ref,customRef} from 'vue'
export default {
name: 'App',
setup() {
//自定义一个ref——名为:myRef
function myRef(value,delay){
let timer //防抖
return customRef((track,trigger)=>{
return {
get(){
console.log(`有人从myRef这个容器中读取数据了,我把${value}给他了`)
track() //通知Vue追踪value的变化(页面再显示value值)
return value
},
set(newValue){
console.log(`有人把myRef这个容器中数据改为了:${newValue}`)
clearTimeout(timer)
timer = setTimeout(()=>{
value = newValue
trigger() //通知Vue去重新解析模板
},delay)
},
}
})
}
// let keyWord = ref('hello') //使用Vue提供的ref
let keyWord = myRef('hello',500) //使用程序员自定义的ref
return {keyWord}
}
}
</script>
provide和inject
简单来说:祖孙组件传值
// 祖组件
setup(){
let car = reactive({name:'奔驰',price:'40W'})
provide('car',car) //给自己的后代组件传递数据
return {...toRefs(car)}
}
// 孙组件
setup(){
let car = inject('car')
return {car}
}
响应式数据的判断
- isRef: 检查一个值是否为一个 ref 对象
- isReactive: 检查一个对象是否是由
reactive
创建的响应式代理- isReadonly: 检查一个对象是否是由
readonly
创建的只读代理- isProxy: 检查一个对象是否是由
reactive
或者readonly
方法创建的代理
import {ref, reactive,toRefs,readonly,isRef,isReactive,isReadonly,isProxy } from 'vue'
export default {
name:'App',
setup(){
let car = reactive({name:'奔驰',price:'40W'})
let sum = ref(0)
let car2 = readonly(car)
console.log(isRef(sum))
console.log(isReactive(car))
console.log(isReadonly(car2))
console.log(isProxy(car))
console.log(isProxy(sum))
return {...toRefs(car)}
}
}
新的内置组件
Fragment组件
vue2.0:必须有一个根标签
vue3.0:可以多标签,可以没有根标签
Teleport组件
可以把组件某部分内容移动到别的指定位置
例如:在某个页面的弹窗移到body页
<template>
<div>
<button @click="isShow = true">点我弹个窗</button>
<teleport to="body">
<div v-if="isShow" class="mask">
<div class="dialog">
<h3>我是一个弹窗</h3>
<h4>一些内容</h4>
<button @click="isShow = false">关闭弹窗</button>
</div>
</div>
</teleport>
</div>
</template>
<script>
import {ref} from 'vue'
export default {
name:'Dialog',
setup(){
let isShow = ref(false)
return {isShow}
}
}
</script>
<style>
// 此时的定位是相对于body
.mask{
position: absolute;
top: 0;bottom: 0;left: 0;right: 0;
background-color: rgba(0, 0, 0, 0.5);
}
.dialog{
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
text-align: center;
width: 300px;
height: 300px;
background-color: green;
}
</style>
Suspense组件
异步引入组件时,提高用户体验
// 模板
<template>
<div class="app">
<h3>我是App组件</h3>
<Suspense>
<template v-slot:default>
//我们需要的组件
<Child/>
</template>
<template v-slot:fallback>
// 等待时候显示内容
<h3>加载中.....</h3>
</template>
</Suspense>
</div>
</template>
// 引入组件
import {defineAsyncComponent} from 'vue'
const Child = defineAsyncComponent(()=>import('./components/Child.vue'))
其他改变
将全局的API,即:
Vue.xxx
调整到应用实例(app
)上2.x 全局 API( Vue
)3.x 实例 API ( app
)Vue.config.xxxx app.config.xxxx Vue.config.productionTip 移除 Vue.component app.component Vue.directive app.directive Vue.mixin app.mixin Vue.use app.use Vue.prototype app.config.globalProperties filter 移除 v-on.native 修饰符 移除 过度类名的更改:
// Vue2.x写法 .v-enter, .v-leave-to { opacity: 0; } .v-leave, .v-enter-to { opacity: 1; } // Vue3.x写法 .v-enter-from, .v-leave-to { opacity: 0; } .v-leave-from, .v-enter-to { opacity: 1; }