Vue3-ElementPlus
Vue3-ElementPlus
Vue3
Vue3 带来了什么
- 性能的提升
- 打包减少了
14%
- 初次渲染快
55%
,更新渲染快133%
- 内存减少
54%
- 打包减少了
- 源码的升级
- 使用
Proxy
代替 defineProperty
实现响应式 - 重写虚拟
DOM
的实现和 Tree-Shaking
...
- 使用
- 拥抱
TypeScript
Vue3
可以更好的支持 TypeScript
- 新的特性
Composition API(组合
API) setup 配置
ref
与 reactive watch
与 watchEffect
- 新的内置组件
Fragment
Teleport
Suspense
- 其他改变
- 新的生命周期钩子
data
选项应始终被生命为一个函数 - 移除
keyCode
支持作为 v-on
的修饰符
Vue3 项目创建
-
使用
vue-cli
创建 1
2
3
4
5
6
7
8
9## 查看 @vue/cli 版本,
确保 @vue/cli 版本在 4.5.0 以上
vue --version
## 安装或升级@vue/cli
npm install -g @vue/cli
## 创建
vue create vue_demo
## 启动
cd vue_demo
npm run serve-
查看
版本查看并创建项目 项目运行
-
-
使用
vite
创建项目 -
优势
- 开发环境中,无需打包操作,
可快读的冷启动 - 轻量快速的热重载
HMR
- 真正的按需编译,
不再等待整个应用编译完成
- 开发环境中,无需打包操作,
-
项目创建
1
2
3
4
5
6
7
8## 创建工程 npm init vite-app(固定写法) <project-name>: 创建的项目名称
npm init vite-app <project-name>
## 进入工程目录
cd <project-name>
## 安装依赖
npm install
## 运行
npm run dev-
运行测试
vite
项目创建与启动
-
-
分析工程结构
-
main.js
1
2
3
4
5
6
7
8
9// 引入的不在是 Vue 构造函数了,
引入的是一个名为 createApp 的工厂函数
import { createApp } from 'vue'
import App from './App.vue'
// 创建应用实例对象-app(类似于之前 vue2中的 vm, 但 app 比 vm 更 “轻”)
const app = createApp(App)
// mount(挂载)
app.mount('#app')1
2
3
4
5<template>
<img alt="Vue logo" src="./assets/logo.png">
<HelloWorld msg="Welcome to Your Vue.js App"/>
</template> -
安装
vue3
对应的开发者工具 logo
(存在一些问题,vue2依然好用)
常用 CompositionAPI
拉开序幕的 setup
-
理解:
Vue3.0
中一个新的配置项, 值为一个函数 -
setup
是所有 Composition API(组合
API) -
组件中所用到的: 数据、方法等,
均要配置在 setup
中 -
setup
函数的两种返回值 - 若返回一个对象,
则对象中的属性、方法在模板中均可以直接使用 ( 重点关注
) - 若返回一个渲染函数: 则可以自定义渲染内容
- 若返回一个对象,
-
注意点
-
尽量不要与
Vue2.x
配置混用 Vue2.x
配置 ( data、methods、computed
)中可以访问到 setup
中的属性、方法 - 但在
setup
中不能访问到 Vue2.x
配置 ( data、methods、computed....
) - 如果有重名,
setup
优先
-
setup
不能是一个 async
函数, 因为返回值不再是 return
的对象, 而是 promise
,模板中看不到return
对象中的属性 -
实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28<template>
<h2 @click="sayUserInfo">我是 APP 的根组件 </h2>
</template>
<script>
export default {
name: 'App',
// Vue3 新的配置项: 将 data,methods,watch,computed 等都配置在其中....
setup() {
// 数据
let username = 'coder-itl'
let age = 18
// 方法
function sayUserInfo() {
console.log(username, age)
}
return {
username,
age,
// 返回方法不加 小括号
sayUserInfo,
}
},
}
</script>
-
ref 函数
-
作用: 定义一个响应式的数据
-
语法
const xxx=ref(initValue)
- 创建一个包含响应式数据的
引用对象
(reference 对象) JS
中操作数据: xxx.value
- 模板中读取数据: 不需要
.value
,直接<div>{{xxx}}</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43<template>
<h2 @click="sayUserInfo">我是 APP 的根组件 </h2>
<p>用户名: {{ username }}</p>
<p>年龄: {{ age }}</p>
<ul>
<li v-for="(v, k) in job">{{ k }}: {{ v }}</li>
</ul>
</template>
<script>
import { ref } from 'vue'
setup(){
// 定义数据为响应式
let username = ref('coder-itl')
let age = ref(18)
let job = ref({
type: '前端工程师',
salary: '13k',
info: { a: 12, b: 22 },
})
// 方法
function sayUserInfo() {
// 更新数据值
username.value = 'coder'
age.value = 19
console.log('修改之后: ', username, age)
// 更新对象数据值
console.log(job)
job.value.type = 'aa'
job.value.salary = '20k'
}
return {
username,
age,
// 返回方法不加 小括号
sayUserInfo,
job,
}
}
</script> - 创建一个包含响应式数据的
-
备注
- 接受的数据可以是: 基本类型、也可以是对象类型
- 基本类型的数据: 响应式依然是靠
Object.defineProperty()
的 get
与 set 完成的 - 对象类型的数据: 内部
求助
了Vue3.0
中的一个新函数 reactive
函数
reactive 函数
-
ref
与 reactive
比较 ref
与 reactive
比较 -
作用: 定义一个
对象类型
的响应式数据( 基本类型不要用它,
)要用 ref 函数 -
语法
const 代理对象 = refactive(源对象)
接收一个对象 (或数组), 返回一个 代理对象
(Proxy 的实例对象, 简称 proxy 对象) -
reactive
定义的响应式数据是 深层次的
-
内部基于
ES6
的 proxy
实现, 通过代理对象操作源对象内部数据进行操作 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44<template>
<h1 @click="sayUserInfo">修改对象的值 </h1>
<ul>
<li v-for="(v, k) in job1">{{ k }}: {{ v }}</li>
</ul>
<ul>
<li v-for="(v, k) in job2">{{ k }}: {{ v }}</li>
</ul>
</template>
<script>
import { reactive, ref } from 'vue'
export default {
name: 'App',
// Vue3 新的配置项: 将 data,methods,watch,computed 等都配置在其中....
setup() {
let job1 = ref({
type: '前端工程师-job1',
salary: '13k',
info: { a: 12, b: 22 },
})
let job2 = reactive({
type: '前端工程师-job2',
salary: '13k',
info: { a: 12, b: 22 },
})
// 方法
function sayUserInfo() {
console.log(job1)
console.log(job2)
console.log('修改 job2: ', (job2.type = 'java工程师'))
}
return {
// 返回方法不加 小括号
sayUserInfo,
job1,
job2,
}
},
}
</script>
Vue3.0 中的响应式原理
Vue2.x 的响应式原理
-
实现原理
- 对象类型: 通过
Object.defineProperty()
对属性的读取, 修改进行拦截 (数据劫持) - 数组类型: 通过重写更新数组的一系列方法来实现拦截
(对数组的变更方法进行了包裹)
1
2
3
4
5
6
7
8
9
10
11Object.defineProperty(data,'count',{
get(){},
set(){}
})
// vue2 对象新增属性动态(两种) 写法
this.$set(Object,key,value)
Vue.set(Object,key,value)
// 移除
this.$delete(Object,key)
Vue.delete(Object,key)- 存在的问题
- 新增属性、删除属性,
界面不会更新 - 直接通过下标修改数组,
界面不会自动更新
- 新增属性、删除属性,
- 对象类型: 通过
Vue3.x 的响应式原理
-
解决
Vue2.x
存在的问题 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48<template>
<h1 @click="sayUserInfo">修改对象的值 </h1>
<ul>
<li v-for="(v, k) in job1">{{ k }}: {{ v }}</li>
</ul>
<button @click="addSex">添加一个属性 </button>
<button @click="deleteType">删除一个属性 </button>
<button @click="updateHobby">修改数组第一个值 </button>
</template>
<script>
import { reactive, ref } from 'vue'
export default {
name: 'App',
// Vue3 新的配置项: 将 data,methods,watch,computed 等都配置在其中....
setup() {
let job1 = reactive({
type: '前端工程师-job1',
salary: '13k',
info: { a: 12, b: 22 },
hobby: ['a', 'ab', 'abc'],
})
// 方法
function sayUserInfo() {
console.log(job1)
}
function addSex() {
job1.sex = '女'
}
function deleteType() {
delete job1.type
}
function updateHobby() {
job1.hobby[0] = 'coder-itl'
}
return {
// 返回方法不加 小括号
sayUserInfo,
job1,
addSex,
deleteType,
updateHobby,
}
},
}
</script> -
实现原理
-
通过
Proxy(代理)
:拦截对象中任意属性的变化, 包括: 属性值的读写、属性的添加、属性的删除等 -
通过
Reflect(反射)
:对被代理对象的属性进行操作 -
实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16<script>
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.delete(target, prop)
}
})
</script>
-
ref 与 reactive 的对比
- 从定义数据角度对比
ref
用来定义: 基本数据类型
reactive
用来定义: 对象
(或数组) 类型数据 - 备注:
ref
也可以用来定义 对象
,它内部会自动通过(或数组) 类型数据 reactive
转换为 代理对象
- 从原理角度对比
ref
通过 Object.defineProperty()
的 get
与 set
来实现响应式 ( 数据劫持
)reactive
通过使用 Proxy
来实现响应式 ( 数据劫持
),并通过 Reflect
操作 源对象
内部的数据
- 从使用角度对比
ref
定义的数据: 操作数据需要 .value
读取数据时模板中直接读取不需要 ( .value
)reactive
定义的数据: 操作数据与读取数据, 均不需要 .value
setup 的两个注意点
-
setup
执行的时机 - 在
beforeCreate
之前执行一次, this
是 undefined
- 在
-
setup
的参数 props
: 值为对象,包含: 组件外部传递过来, 且组件内部声明接受了的属性 context
: 上下文对象attrs
: 值为对象,包含: 组件外部传递过来, 但没与在 props
配置中声明的属性, 相当于 this.$attrs
slots
:收到的插槽内容, 相当于 this.$slots
emit
:分发自定义事件的函数, 相当于 this.$emit
-
实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53App.vue:
<template>
// 自定义事件
<demo @hello="showMessage" msg="props-传递数据"></demo>
</template>
<script>
import Demo from '@/components/Demo.vue'
export default {
name: 'App',
components: {
Demo,
},
// Vue3 新的配置项: 将 data,methods,watch,computed 等都配置在其中....
setup() {
return {
showMessage(value) {
console.log('hello事件已经触发, 参数: ', value)
},
}
},
}
</script>
Demo.vue:
<template>
<h1>用户名: {{ username }}</h1>
<button @click="showInfo">显示参数信息 </button>
</template>
<script>
import { reactive, ref } from 'vue'
export default {
name: 'Demo',
// 自定义事件
emits: ['hello'],
props: ['msg'],
// Vue3 新的配置项: 将 data,methods,watch,computed 等都配置在其中....
setup(props, context) {
return {
username: 'coder-itl',
showInfo() {
console.log(props, context.slots)
// 触发自定义事件
context.emit('hello', { data: 666 })
},
}
},
}
</script>
计算属性
-
与
Vue2.x
中 computed
配置功能一致 -
写法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50<template>
<p>
firstName:
<input type="text" v-model="person.firstName" />
</p>
<p>
lastName:
<input type="text" v-model="person.lastName" />
</p>
<p>全名: {{ fullName1 }}</p>
<p>全名: {{ fullName2 }}</p>
</template>
<script>
import { reactive, computed } from 'vue'
export default {
name: 'Demo',
// Vue3 新的配置项: 将 data,methods,watch,computed 等都配置在其中....
setup() {
let person = reactive({
firstName: 'coder',
lastName: 'itl',
})
// 计算属性-简写
let fullName1 = computed(() => {
return person.firstName + ' ' + person.lastName
})
// 计算属性完整写法
let fullName2 = computed({
get() {
return person.firstName + ' ' + person.lastName
},
set(value) {
const nameArr = value.split(' ')
person.firstName = nameArr[0]
person.lastName = nameArr[1]
},
})
return {
person,
fullName1,
fullName2,
}
},
}
</script>
<style></style>
watch
-
与
Vue2.x
中 watch
配置功能一致 -
两个小坑
- 监视
reactive
定义的响应式数据时: oldValue
无法正确获取, 强制开启了深度监视 ( deep
配置失效) - 监视
reactive
定义的响应式数据中某个属性时, deep
配置有效
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74<script>
import { reactive, ref, watch } from 'vue'
export default {
name: 'Demo',
// Vue3 新的配置项: 将 data,methods,watch,computed 等都配置在其中....
setup() {
let sum = ref(0)
let msg = ref('info....')
let person = reactive({
type: 'Java工程师',
salary: 20,
info: {
type: 'Java工程师',
salary: 20,
},
})
// 情况一: 监视 ref 定义的响应式数据
watch(
sum,
(newValue, oldValue) => {
console.log('sum变化了', newValue, oldValue)
},
{ immediate: true }
)
// 情况二: 监视多个 ref 定义的响应式数据
watch([sum, msg], (newValue, oldValue) => {
console.log('sum | msg 变化了', newValue, oldValue)
})
/**
* 情况三: 监视 reactive 定义的响应式数据
* 若 watch 监视的是 reacctive 定义的响应式数据,则无法正确获得 oldValue
* 若 watch 监视的是 reacctive 定义的响应式数据,则强制开启了深度监视
*/
watch(
person,
(newValue, oldValue) => {
console.log('person变化了', newValue, oldValue)
}, // deep 配置不在奏效
{ immediate: true, deep: false }
)
// 情况四: 监视 reactive 所定义的一个响应式数据中的某个属性
watch(
() => person.type,
(newValue, oldValue) => {
console.log('person的 type 变化了', newValue, oldValue)
}
)
// 情况五: 监视 reactive 所定义的一个响应式数据中的某些属性
watch(
[() => person.type, () => person.salary],
(newValue, oldValue) => {
console.log('person的 type|salary 变化了', newValue, oldValue)
}
)
// 特殊情况
watch(
() => person.salary,
(newValue, oldValue) => {
console.log('person的 salary 变化了', newValue, oldValue)
},
// 此处由于监视的是 reactive 定义对象中的某些属性,所以 deep 配置有效
{ deep: true }
)
return {
sum,
msg,
person,
}
},
}
</script>
<style></style> - 监视
watchEffect 函数
-
watch
的套路是: 既要指明监视的属性, 也要指明监视的回调 -
watchEffect
: 不用指明监视那个属性,监视的回调中用到那个属性, 那就监视那个属性 -
watchEffect
有点像computed
:-
但
computed
注重计算出来的值 (回调函数的返回值), 所以必须要写返回值 -
而
watchEffect
更注重的是过程 (回调函数的函数体), 所以不用写返回值 1
2
3
4
5
6import {watchEffect} from "vue"
watchEffect(() => {
const x1 = sum.value
const x2 = person.salary
console.log("watchEffect配置的回调执行了.......''")
})
-
Vue3 声明周期
- 无较大变化
自定义 hook 函数
-
什么是
hook?
本质是一个函数, 把 setup
函数中使用的 CompositionAPI
进行了封装 -
类似于
Vue2.x
中的 mixin
-
自定义
hook
的优势:复用代码, 让 setup
中的逻辑更清楚易懂 -
案例: 获取鼠标点击
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29// 定义 hook 函数
import { onBeforeMount, onMounted, reactive } from 'vue'
export default function () {
// 实现鼠标打点相关数据
let point = reactive({
x: 0,
y: 0,
})
// 实现鼠标打点相关的方法
function savePoint(event) {
point.x = event.pageX
point.y = event.pageX
}
// 实现鼠标打点相关的生命周期钩子
onMounted(() => {
window.addEventListener('click', savePoint)
})
onBeforeMount(() => {
window.removeEventListener('click', savePoint)
})
}
// 使用 hook
import usePoint from "../hooks/savePoint"
setup(){
let savePoint = usePoint()
}
toRef
-
作用: 创建一个
ref
对象, 其 value
值指向另一个对象中的某个属性 -
语法:
const name= toRef(person,'name')
-
应用: 要将响应式对象中的某个属性单独提供给外部使用
-
扩展:
toRefs
与 toRef
功能一致, 但可以批量创建多个 ref
对象, 语法: toRefs(person)
-
实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41<template>
<h1>Test:</h1>
<p>{{ person.name }}</p>
<p>{{ person.age }}</p>
<p>{{ person.job.type }}</p>
<p>{{ person.job.sa }}</p>
<hr />
<p>{{ name }}</p>
<p>{{ age }}</p>
<p>{{ type }}</p>
<p>{{ salary }}</p>
</template>
<script>
import { reactive, toRef } from 'vue'
export default {
name: 'Test',
// Vue3 新的配置项: 将 data,methods,watch,computed 等都配置在其中....
setup() {
let person = reactive({
name: 'coder-itl',
age: 18,
job: {
type: 'student',
salary: -1000,
},
})
return {
person,
name: toRef(person, 'name'),
age: toRef(person, 'age'),
type: toRef(person.job, 'type'),
salary: toRef(person.job, 'salary'),
}
},
}
</script>
<style></style>
其他 Composition API
shallowReactive 与 shallowRef
shallowReactive
:只处理对象最外层属性的响应式(浅响应式) shallowRef
: 只处理基本数据类型的响应式,不进行对象的响应式处理 - 什么时候使用
- 如果有一个对象数据,
结构层级比较深, 但变化时指示外层属性变化 =>shallowReactive
- 如果有一个对象数据,
后续功能不会修改该对象中的属性, 而是生新的对象来替换 =>shallowRef
- 如果有一个对象数据,
readonly 与 shallowReadonly
readonly
:让一个响应式数据变为只读的 ( 深只读
)shallowReadonly
: 让一个响应式数据变为只读的( 浅只读
)- 应对场景: 不希望数据被修改时
toRaw 与 markRaw
toRaw
- 作用: 将一个由
reactive
生成的 响应式对象
转为普通对象
- 使用场景: 用于读取响应式对象对应的普通对象,
对这个普通对象的所有操作, 不会引起页面更新
- 作用: 将一个由
markRaw
- 作用: 标记一个对象,
使其永远不会再成为响应式对象 - 应用场景
- 有些值不应被设置为响应式,
例如复杂的第三方类库等 - 当渲染具有不可变数据源的大列表时,
跳过响应式转换可以提高性能
- 有些值不应被设置为响应式,
- 作用: 标记一个对象,
Element-Plus
-
安装
1
npm install element-plus --save