Vue3

1 认识Vue3
1) 了解相关信息
Vue.js 3.0 "One Piece" 正式版在今年9月份发布
2年多开发, 100+位贡献者, 2600+次提交, 600+次PR
Vue3支持vue2的大多数特性
更好的支持Typescript
2) 性能提升:
打包大小减少41%
初次渲染快55%, 更新渲染快133%
内存减少54%
使用Proxy代替defineProperty实现数据响应式
重写虚拟DOM的实现和Tree-Shaking
3) 新增特性
Composition (组合) API
setup
ref 和 reactive
computed 和 watch
新的生命周期函数
provide与inject
新组件
Fragment - 文档碎片
Teleport - 瞬移组件的位置
Suspense - 异步加载组件的loading界面
其它API更新
全局API的修改
将原来的全局API转移到应用对象
模板语法变化
2 创建vue3项目
2.1 使用 vue-cli 创建
安装
npm install -g @vue/cli
创建项目
vue create my-project
2.2 使用 vite 创建
安装vue-cli到最新版本 (必须高于4.5.0)
npm init vite-app <project-name>
cd <project-name>
npm install
npm run dev
3 Composition API的使用
Options API 选项api
Options API的优点是容易学习和使用,代码有明确的书写位置
Options API的缺点就是相似逻辑不容易复用,在大项目中尤为明显。
Options API可以通过mixins提取相同的逻辑,但是容易发生命名
冲突且来源不清晰
Composition API 组合API
Composition API是根据逻辑功能来组织代码的,一个功能所
有的api放到一起
即便项目很大,功能很多,都能够快速的定位到该功能所有的API
Composition API提高了代码可读性和可维护性
Vue3.0中推荐使用composition API,也保留了options API。
3.1 setup
Setup函数是一个新的组件选项,作为组件中composition
API的起点
从生命周期钩子的角度来看,setup会在beforeCreate
钩子函数之前执行。Setup中不能使用this,this指向undefined
新的option, 所有的组合API函数都在此使用, 只在初始化时
执行一次
函数如果返回对象, 对象中的属性或方法, 模板中可以直接使用
<template>
<div class="app"></div>
</template>
<script>
export default {
setup() {
console.log('setup执行了')
console.log(this)
},
beforeCreate() {
console.log('beforeCreate')
}
}
</script>
<style></style>
3.1.1 setup细节
setup执行的时机
在beforeCreate之前执行(一次), 此时组件对象还没有创建
this是undefined, 不能通过this来访问
data/computed/methods / props
其实所有的composition API相关回调函数中也都不可以
setup的返回值
一般都返回一个对象: 为模板提供数据, 也就是模板中可以直接使
用此对象中的所有属性/方法
返回对象中的属性会与data函数返回对象的属性合并成为
组件对象的属性
返回对象中的方法会与methods中的方法合并成功组件
对象的方法
如果有重名, setup优先
注意:
一般不要混合使用: methods中可以访问setup提供的属性
和方法, 但在setup方法中不能访问data和methods
setup不能是一个async函数: 因为返回值不再是return的对象,
而是promise, 模板看不到return对象中的属性数据
setup的参数
setup(props, context) / setup(props, {attrs, slots, emit})
props: 包含props配置声明且传入了的所有属性的对象
attrs: 包含没有在props配置中声明的属性的对象,
相当于 this.$attrs
slots: 包含所有传入的插槽内容的对象, 相当于 this.$slots
emit: 用来分发自定义事件的函数, 相当于 this.$emit
<template>
<h2>App</h2>
<p>msg: {
{msg}}</p>
<button @click="fn('--')">更新</button>
<child :msg="msg" msg2="cba" @fn="fn"/>
</template>
<script lang="ts">
import {
reactive,
ref,
} from 'vue'
import child from './child.vue'
export default {
components: {
child
},
setup () {
const msg = ref('abc')
function fn (content: string) {
msg.value += content
}
return {
msg,
fn
}
}
}
</script>
<template>
<div>
<h3>{
{n}}</h3>
<h3>{
{m}}</h3>
<h3>msg: {
{msg}}</h3>
<h3>msg2: {
{$attrs.msg2}}</h3>
<slot name="xxx"></slot>
<button @click="update">更新</button>
</div>
</template>
<script lang="ts">
import {
ref,
defineComponent
} from 'vue'
export default defineComponent({
name: 'child',
props: ['msg'],
emits: ['fn'],
data () {
console.log('data', this)
return {
}
},
beforeCreate () {
console.log('beforeCreate', this)
},
methods: {
},
setup (props, {
attrs, emit, slots}) {
console.log('setup', this)
console.log(props.msg, attrs.msg2, slots, emit)
const m = ref(2)
const n = ref(3)
function update () {
m.value += 2
n.value += 2
emit('fn', '++')
}
return {
m,
n,
update,
}
},
})
</script>
3.2 ref
ref函数接受一个简单类型的值,返回一个可改变的
ref对象。返回的对象有唯一的属性 value
在setup函数中,通过ref对象的value属性可以访问到值
在模板中,ref属性会自动解套,不需要额外的.value
如果ref接受的是一个对象,会自动调用reactive
<template>
<h2>{
{count}}</h2>
<hr>
<button @click="update">更新</button>
</template>
<script>
import {
ref
} from 'vue'
export default {
setup () {
const count = ref(1)
console.log(count)
function update () {
count.value = count.value + 1
}
return {
count,
update
}
}
}
</script>
3.2.1 模板refs
为了获得对模板内元素或组件实例的引用,我们可
以像往常一样在 setup() 中声明一个 ref 并返回它
<template>
<div class="app">
<h1 ref="hRef">钩子函数----123</h1>
<Demo ref="dRef"></Demo>
</div>
</template>
<script>
import Demo from './Demo.vue'
import {
ref, provide, onMounted } from 'vue'
export default {
components: {
Demo
},
setup() {
const hRef = ref(null)
const dRef = ref(null)
onMounted(() => {
console.log(hRef.value.innerHTML)
console.log(dRef.value)
})
return {
hRef,
dRef
}
}
}
</script>
<style></style>
3.3 reactive
Reactive函数接受一个普通对象,返回该对象的响应式代理。
<template>
<div class="app">
<div>{
{ car.brand }}----{
{ car.price }}</div>
<button @click="car.brand = '奔驰'">修改</button>
</div>
</template>
<script>
import {
reactive } from 'vue'
export default {
setup() {
const car = reactive({
brand: '宝马',
price: 100
})
return {
car
}
}
}
</script>
<style></style>
3.4 Vue2.0和vue3.0响应式原理对比
3.4.1 Vue2.0响应式原理
Vue2.0中
响应式数据
核心:
对象: 使用ES5中的Object.defineProperty方法实现
对对象的已有属性值的读取和修改进行劫持(监视/拦截)
数组: 通过重写数组更新数组一系列更新元素的方法来实
现元素修改的劫持
缺点
对象直接新添加的属性或删除已有属性, 界面不会自动更新
直接通过下标替换元素或更新length, 界面不会自动更新 arr[1] = {}
解决方案
Vue2.0提供Vue.set方法用于动态给对象添加属性
Vue2.0提供Vue.delete方法用于动态删除对象的属性
重写vue中数组的方法,用于监测数组的变更
Object.defineproperty
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script>
const data = {
name: 'zs',
age: 18
}
for (let k in data) {
let temp = data[k]
Object.defineProperty(data, k, {
get() {
console.log(`我劫持了${
k}的获取`)
return temp
},
set(value) {
console.log(`我劫持了${
k}的设置,值${
value}`)
temp = value
}
})
}
</script>
</body>
</html>
3.4.2 Vue3.0响应式原理
核心:
通过Proxy(代理): 拦截对data任意属性的任意(13种)操作,
包括属性值的读写, 属性的添加, 属性的删除等...
通过 Reflect(反射): 动态对被代理对象的相应属性进行特
定的操作
优点
可以检测到代理对象属性的动态添加和删除
可以监测到数组的下标和length属性的变更
缺点
ES6的proxy语法对于低版本浏览器不支持,IE11
Vue3.0会针对于IE11出一个特殊的版本用于支持ie11
es6的proxy语法.html
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)
}
})
proxy.name = 'tom'
vue3中响应式原理
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Proxy 与 Reflect</title>
</head>
<body>
<script>
const user = {
name: "John",
age: 12
};
const proxyUser = new Proxy(user, {
get(target, prop) {
console.log('劫持get()', prop)
return Reflect.get(target, prop)
},
set(target, prop, val) {
console.log('劫持set()', prop, val)
return Reflect.set(target, prop, val);
},
deleteProperty (target, prop) {
console.log('劫持delete属性', prop)
return Reflect.deleteProperty(target, prop)
}
});
console.log(proxyUser===user)
console.log(proxyUser.name, proxyUser.age)
proxyUser.name = 'bob'
proxyUser.age = 13
console.log(user)
proxyUser.sex = '男'
console.log(user)
delete proxyUser.sex
console.log(user)
</script>
</body>
</html>
3.5 reactive与ref-细节
是Vue3的 composition API中2个最重要的响应式API
ref用来处理基本类型数据, reactive用来处理对象
(递归深度响应式)
如果用ref对象/数组, 内部会自动将对象/数组转换
为reactive的代理对象
ref内部: 通过给value属性添加getter/setter来实
现对数据的劫持
reactive内部: 通过使用Proxy来实现对对象内部所有
数据的劫持, 并通过Reflect操作对象内部数据
ref的数据操作: 在js中要.value, 在模板中不需
要(内部解析模板时会自动添加.value)
<template>
<h2>App</h2>
<p>m1: {
{m1}}</p>
<p>m2: {
{m2}}</p>
<p>m3: {
{m3}}</p>
<button @click="update">更新</button>
</template>
<script lang="ts">
import {
reactive,
ref
} from 'vue'
export default {
setup () {
const m1 = ref('abc')
const m2 = reactive({
x: 1, y: {
z: 'abc'}})
const m3 = ref({
a1: 2, a2: {
a3: 'abc'}})
console.log(m1, m2, m3)
console.log(m3.value.a2)
function update() {
m1.value += '--'
m2.x += 1
m2.y.z += '++'
m3.value = {
a1: 3, a2: {
a3: 'abc---'}}
m3.value.a2.a3 += '=='
console.log(m3.value.a2)
}
return {
m1,
m2,
m3,
update
}
}
}
</script>
3.6 计算属性与监视
computed函数:
与computed配置功能一致
只有getter
有getter和setter
watch函数
与watch配置功能一致
监视指定的一个或多个响应式数据, 一旦数据变化,
就自动执行监视回调
默认初始时不执行回调, 但可以通过配置immediate为true,
来指定初始时立即执行第一次
通过配置deep为true, 来指定深度监视
watchEffect函数
不用直接指定要监视的数据, 回调函数中使用的哪些响应式
数据就监视哪些响应式数据
默认初始时就会执行第一次, 从而可以收集需要监视的数据
监视数据发生变化时回调
<template>
<h2>App</h2>
fistName: <input v-model="user.firstName"/><br>
lastName: <input v-model="user.lastName"/><br>
fullName1: <input v-model="fullName1"/>