Vue3
创建
基于vue-cli
## 查看@vue/cli版本,确保@vue/cli版本在4.5.0以上
vue --version
## 安装或者升级你的@vue/cli
npm install -g @vue/cli
## 执行创建命令
vue create vue_test
## 随后选择3.x
## Choose a version of Vue.js that you want to start the project with (Use arrow keys)
## > 3.x
## 2.x
## 启动
cd vue_test
npm run serve
基于Vite
## 1.创建命令
npm create vue@latest
## 2.具体配置
## 配置项目名称
√ Project name: vue3_test
## 是否添加TypeScript支持
√ Add TypeScript? Yes
## 是否添加JSX支持
√ Add JSX Support? No
## 是否添加路由环境
√ Add Vue Router for Single Page Application development? No
## 是否添加pinia环境
√ Add Pinia for state management? No
## 是否添加单元测试
√ Add Vitest for Unit Testing? No
## 是否添加端到端测试方案
√ Add an End-to-End Testing Solution? » No
## 是否添加ESLint语法检查
√ Add ESLint for code quality? Yes
## 是否添加Prettiert代码格式化
√ Add Prettier for code formatting? No
文件结构
main.js
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
createApp(App) 返回的变量就是轻量的vm,并且很多方法不带$
vue3中template中,不用再写根div了
setup
<template>
<div>
<h2>hhh</h2>
<button @click="sayHello">点击</button>
</div>
</template>
<script>
export default {
name: 'App',
components: {
},
setup() {
let name = 'jjking';
function sayHello() {
alert('Hello' + name);
}
return {
name,sayHello
}
}
}
</script>
<style></style>
setup函数返回的对象中的内容,可以在模版中直接使用
setup中this访问是undefined
setup函数会在beforeCreate之前调用
setup返回值,如果是一个渲染函数,则可以自定义渲染内容
这个东西,就是vue2中的main.js中的那个渲染函数
<template>
<div>
<h2>hhh</h2>
<button @click="sayHello">点击</button>
</div>
</template>
<script>
import { h } from 'vue';
export default {
name: 'App',
components: {
},
setup() {
let name = 'jjking';
function sayHello() {
alert('Hello' + name);
}
// return {
// name,sayHello
// }
return () => h('h1','jjking');
}
}
</script>
<style></style>
不管你写什么,都会渲染成h1
- setup的参数
- props:值为对象,包含:组件外部传递过来,且组件内部声明接收了的属性。
- context:上下文对象
- attrs: 值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性, 相当于
this.$attrs
。 - slots: 收到的插槽内容, 相当于
this.$slots
。 - emit: 分发自定义事件的函数, 相当于
this.$emit
。
- attrs: 值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性, 相当于
ref
基本数据类型
使用ref包裹的返回的对象
是一个响应式的对象,如果要修改ref中的对象,需要修改value,而不是直接改
并且,看这个对象,是在原型对象中有get,set方法,有点类似于vue2的_data里边的数据
模版字符串{{}}里边不用.value的原因是,模版字符串会自动加上.value
<template>
<div>
<h2>{{name}}</h2>
<button @click="change">点击change</button>
</div>
</template>
<script>
import { ref } from 'vue';
export default {
name: 'App',
components: {
},
setup() {
let name = ref('jjking');
function change() {
console.log(name);
name.value = '改变后';
}
return {
name,change
}
}
}
</script>
对象数据类型
setup() {
let job = ref({
type:'前端工程师',
salary: 30
})
function changeSalary() {
console.log(job);
job.value.salary = 40;
}
return {
name,job,changeSalary
}
}
我们打印可以看到他实际上是Proxy对象,他的响应式是借助了Vue3中的 新函数,reactive函数
我们更改的话,就如上代码
reactive
一般用于对象和数组,基本类型用ref,不然报错
返回值是Proxy对象,且reactive定义的是深层次的响应式对象
注意,如果reavtive重新分配一个新对象,会失去响应式,必须用Object.assign来做整体的替换
<template>
<div class="person">
<h2>汽车信息:一辆{{car.brand}}车,价值{{car.price}}万</h2>
<button @click="changeBrand">修改汽车的品牌</button>
<button @click="changePrice">修改汽车的价格</button>
<button @click="changeCar">修改汽车</button>
<hr>
<h2>当前求和为:{{sum}}</h2>
<button @click="changeSum">点我sum+1</button>
</div>
</template>
<script lang="ts" setup name="Person">
import {ref,reactive} from 'vue'
// 数据
let car = reactive({brand:'奔驰',price:100})
let sum = ref(0)
// 方法
function changeBrand(){
car.brand = '宝马'
}
function changePrice(){
car.price += 10
}
function changeCar(){
// car = {brand:'奥拓',price:1} //这么写页面不更新的
// car = reactive({brand:'奥拓',price:1}) //这么写页面不更新的
// 下面这个写法页面可以更新
Object.assign(car,{brand:'奥拓',price:1})
}
function changeSum(){
// sum = ref(9) //这么写页面不更新的
sum.value += 1
}
</script>
Vue3中的响应式原理
Vue2中,如果我们想修改对象或者数组中的数据,Vue是不会监测变化的,换句话说,也就是响应式没用,我们新增属性,删除属性,修改数组中的值
实现原理
通过Proxy代理对象
Proxy和Object.defineProperty很类似,不过他添加了删除属性,和追加属性的方法
追加属性的方法集成到了set方法里边
为什么要用Reflect呢,就是因为Reflect操作对象可以返回布尔值,如果像之前那样修改代码的话,报错是直接抛出的,我们必须用try catch来捕获
computed函数
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]
}
})
}
watch
//情况一:监视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监视的是reactive定义的响应式数据,则无法正确获得oldValue!!
若watch监视的是reactive定义的响应式数据,则强制开启了深度监视
*/
watch(person,(newValue,oldValue)=>{
console.log('person变化了',newValue,oldValue)
},{immediate:true,deep:false}) //此处的deep配置不再奏效
//情况四:监视reactive定义的响应式数据中的某个属性
watch(()=>person.job,(newValue,oldValue)=>{
console.log('person的job变化了',newValue,oldValue)
},{immediate:true,deep:true})
//情况五:监视reactive定义的响应式数据中的某些属性
watch([()=>person.job,()=>person.name],(newValue,oldValue)=>{
console.log('person的job变化了',newValue,oldValue)
},{immediate:true,deep:true})
//特殊情况
watch(()=>person.job,(newValue,oldValue)=>{
console.log('person的job变化了',newValue,oldValue)
},{deep:true}) //此处由于监视的是reactive素定义的对象中的某个属性,所以deep配置有效
监视reactive定义的响应式数据时:oldValue无法正确获取、强制开启了深度监视(deep配置失效)。
watchEffect
watch的套路是:既要指明监视的属性,也要指明监视的回调。
watchEffect的套路是:不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性。
watchEffect有点像computed:
- 但computed注重的计算出来的值(回调函数的返回值),所以必须要写返回值。
- 而watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值。
//watchEffect所指定的回调中用到的数据只要发生变化,则直接重新执行回调。
watchEffect(()=>{
const x1 = sum.value
const x2 = person.age
console.log('watchEffect配置的回调执行了')
})
生命 周期钩子
- ue3.0中可以继续使用Vue2.x中的生命周期钩子,但有有两个被更名:
beforeDestroy
改名为beforeUnmount
destroyed
改名为unmounted
- Vue3.0也提供了 Composition API 形式的生命周期钩子,与Vue2.x中钩子对应关系如下:
beforeCreate
===>setup()
created
=======>setup()
beforeMount
===>onBeforeMount
mounted
=======>onMounted
beforeUpdate
===>onBeforeUpdate
updated
=======>onUpdated
beforeUnmount
==>onBeforeUnmount
自定义钩子
什么是hook?—— 本质是一个函数,把setup函数中使用的Composition API进行了封装。
类似于vue2.x中的mixin。
自定义hook的优势: 复用代码, 让setup中的逻辑更清楚易懂。
就是封装一个函数,为了复用
toRefs toRef
<template>
<div class="person">
<h2>姓名:{{person.name}}</h2>
<h2>年龄:{{person.age}}</h2>
<h2>性别:{{person.gender}}</h2>
<button @click="changeName">修改名字</button>
<button @click="changeAge">修改年龄</button>
<button @click="changeGender">修改性别</button>
</div>
</template>
<script lang="ts" setup name="Person">
import {ref,reactive,toRefs,toRef} from 'vue'
// 数据
let person = reactive({name:'张三', age:18, gender:'男'})
// 通过toRefs将person对象中的n个属性批量取出,且依然保持响应式的能力
let {name,gender} = toRefs(person)
// 通过toRef将person对象中的gender属性取出,且依然保持响应式的能力
let age = toRef(person,'age')
// 方法
function changeName(){
name.value += '~'
}
function changeAge(){
age.value += 1
}
function changeGender(){
gender.value = '女'
}
</script>
toRef有点类似于,桥接,他指向的还是源数据,只不过他为了少写点代码