一、创建一个Vue3项目
1.使用 vue-cli 创建
1.安装或者升级 npm install -g @vue/cli
2.保证 vue cli 版本在 4.5.0 以上 vue --version
3.创建项目 vue create my-project
4.然后的步骤 Please pick a preset -
选择 Manually select features
Check the features needed for your project -
Choose a version of Vue.js that you want to start the project with -
选择 3.x
选项配置如下
2.使用 vite 创建
vite 是一个由原生 ESM 驱动的 Web 开发构建工具。在开发环境下基于浏览器原生 ES imports 开发,它做到了本地快速开发启动, 在生产环境下基于 Rollup 打包。快速的冷启动,不需要等待打包操作; 即时的热模块更新,替换性能和模块数量的解耦让更新飞起; 真正的按需编译,不再等待整个应用编译完成,这是一个巨大的改变。
1.执行 npm init vue@latest
2.然后选项如下:
3.根据提示执行
cd 文件名
npm i
4.等待下载完成
5.项目构建完成
二、Composition API
1.setup
(1)新的option, 所有的组合API函数都在此使用, 只在初始化时执行一次
(2)函数如果返回对象, 对象中的属性或方法, 模板中可以直接使用
2.ref
(1)作用: 定义一个数据的响应式
(2)语法: const xxx = ref(initValue):
(3)创建一个包含响应式数据的引用(reference)对象;
js中操作数据: xxx.value;
模板中操作数据: 不需要.value;
(4)一般用来定义一个基本类型的响应式数据
<template>
<h2>{{count}}</h2>
<hr>
<button @click="update">更新</button>
</template>
<script>
import {
ref
} from 'vue'
export default {
/* 在Vue3中依然可以使用data和methods配置, 但建议使用其新语法实现 */
// data () {
// return {
// count: 0
// }
// },
// methods: {
// update () {
// this.count++
// }
// }
/* 使用vue3的composition API */
setup () {
// 定义响应式数据 ref对象
const count = ref(1)
console.log(count)
// 更新响应式数据的函数
function update () {
// alert('update')
count.value = count.value + 1
}
return {
count,
update
}
}
}
</script>
3.reactive
(1)作用: 定义多个数据的响应式
(2)const proxy = reactive(obj): 接收一个普通对象然后返回该普通对象的响应式代理器对象
(3)响应式转换是“深层的”:会影响对象内部所有嵌套的属性
(4)内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据都是响应式的
<template>
<h2>name: {{state.name}}</h2>
<h2>age: {{state.age}}</h2>
<h2>wife: {{state.wife}}</h2>
<hr>
<button @click="update">更新</button>
</template>
<script>
/*
reactive:
作用: 定义多个数据的响应式
const proxy = reactive(obj): 接收一个普通对象然后返回该普通对象的响应式代理器对象
响应式转换是“深层的”:会影响对象内部所有嵌套的属性
内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据都是响应式的
*/
import {
reactive,
} from 'vue'
export default {
setup () {
/*
定义响应式数据对象
*/
const state = reactive({
name: 'tom',
age: 25,
wife: {
name: 'marry',
age: 22
},
})
console.log(state, state.wife)
const update = () => {
state.name += '--'
state.age += 1
state.wife.name += '++'
state.wife.age += 2
}
return {
state,
update,
}
}
}
</script>
三、 比较Vue2与Vue3的响应式
1. vue2的响应式
核心:
对象: 通过defineProperty对对象的已有属性值的读取和修改进行劫持(监视/拦截);
数组: 通过重写数组更新数组一系列更新元素的方法来实现元素修改的劫持;
问题:
对象直接新添加的属性或删除已有属性, 界面不会自动更新;
直接通过下标替换元素或更新length, 界面不会自动更新 arr[1] = {};
//核心方法 -
Object.defineProperty(obj, prop, descriptor)
// obj:要在其上定义属性的对象。
// prop:要定义或修改的属性的名称。
// descriptor:将被定义或修改的属性描述符。
Object.defineProperty(对象, 属性, {
get () {},
set () {}
})
Vue3的响应式
核心:
(1)通过Proxy(代理): 拦截对data任意属性的任意(13种)操作, 包括属性值的读写, 属性的添加, 属性的删除等;
(2)通过 Reflect(反射): 动态对被代理对象的相应属性进行特定的操作
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'
<!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
};
/*
proxyUser是代理对象, user是被代理对象
后面所有的操作都是通过代理对象来操作被代理对象内部属性
*/
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); // (2)
},
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>
四、计算属性与监视
computed函数:
与computed配置功能一致;
有getter和setter;
watch函数
与watch配置功能一致;
只有getter;
监视指定的一个或多个响应式数据, 一旦数据变化, 就自动执行监视回调;
默认初始时不执行回调, 但可以通过配置immediate为true, 来指定初始时立即执行第一次;
通过配置deep为true, 来指定深度监视;
watchEffect函数
不用直接指定要监视的数据, 回调函数中使用的哪些响应式数据就监视哪些响应式数据;
默认初始时就会执行第一次, 从而可以收集需要监视的数据;
监视数据发生变化时回调;
五、Vue3与Vue2的生命周期对比
与 2.x 版本生命周期相对应的组合式 API
beforeCreate --> 使用 setup()
created --> 使用 setup()
beforeMount --> onBeforeMount
mounted --> onMounted
beforeUpdate --> onBeforeUpdate
updated --> onUpdated
beforeDestroy --> onBeforeUnmount
destroyed --> onUnmounted
errorCaptured --> onErrorCaptured
新增的钩子函数
组合式 API 还提供了以下调试钩子函数:
onRenderTracked
onRenderTriggered
六、toRefs
把一个响应式对象转换成普通对象,该普通对象的每个 property 都是一个 ref
应用: 当从合成函数返回响应式对象时,toRefs 非常有用,这样消费组件就可以在不丢失响应式的情况下对返回的对象进行分解使用
问题: reactive 对象取出的所有属性值都是非响应式的
解决: 利用 toRefs 可以将一个响应式 reactive 对象的所有原始属性转换为响应式的 ref 属性
<template>
<h2>App</h2>
<h3>foo: {{foo}}</h3>
<h3>bar: {{bar}}</h3>
<h3>foo2: {{foo2}}</h3>
<h3>bar2: {{bar2}}</h3>
</template>
<script lang="ts">
import { reactive, toRefs } from 'vue'
/*
toRefs:
将响应式对象中所有属性包装为ref对象, 并返回包含这些ref对象的普通对象
应用: 当从合成函数返回响应式对象时,toRefs 非常有用,
这样消费组件就可以在不丢失响应式的情况下对返回的对象进行分解使用
*/
export default {
setup () {
const state = reactive({
foo: 'a',
bar: 'b',
})
const stateAsRefs = toRefs(state)
setTimeout(() => {
state.foo += '++'
state.bar += '++'
}, 2000);
const {foo2, bar2} = useReatureX()
return {
// ...state,
...stateAsRefs,
foo2,
bar2
}
},
}
function useReatureX() {
const state = reactive({
foo2: 'a',
bar2: 'b',
})
setTimeout(() => {
state.foo2 += '++'
state.bar2 += '++'
}, 2000);
return toRefs(state)
}
</script>
八、ref获取元素
利用ref函数获取组件中的标签元素
注意:声明一个 ref 来存放该元素的引用;
必须和模板里的 ref 同名;
让输入框自动获取焦点:
<template>
<h2>App</h2>
<input type="text">---
<input type="text" ref="inputRef">
</template>
<script lang="ts">
import { onMounted, ref } from 'vue'
/*
ref获取元素: 利用ref函数获取组件中的标签元素
功能需求: 让输入框自动获取焦点
*/
export default {
setup() {
//声明一个 ref 来存放该元素的引用
//必须和模板里的 ref 同名
const inputRef = ref<HTMLElement|null>(null)
onMounted(() => {
inputRef.value && inputRef.value.focus()
})
return {
inputRef
}
},
}
</script>