Vue系列文章目录
提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
提示:Vue2过渡到Vue3的写法
提示:以下是本篇文章正文内容,下面案例可供参考
Vue3
vue2的升级版本,做了一些内部优化,从性能上,还有写法上都有改变。还有就是对vue2做了兼容。
文档: https://cn.vuejs.org/
Vue3 新特性
数据双向绑定的原理改变了, 改成proxy (原来: Object.defineProperty() )
增加了setup函数 (以前都是选项式,现在呢就是可以支持hooks函数式)
增加了hooks函数
支持ts
支持jsx/tsx
性能有提高: 大概 1.2 - 2 倍
对vue2做了兼容(放弃了一些api)
开始vue3
感受一下setup
<template>
<div class="home">
{{num}}
<div><button @click="add">add</button></div>
</div>
</template>
<script>
// 导入一个创建响应式对象的hooks ref
import { ref } from 'vue'
export default {
name: 'HomeView',
setup(){ // setup选项
const num = ref(0); // 创建一个响应式对象
function add(){ // 定义的一个方法
num.value++
}
return { // 在setup函数里面,返回出去的东西,就可以外面使用了
num,
add
}
}
}
</script>
vscode的插件
Volar vue3的插件
Vetur vue2的插件
做vue3开发的时候,要把vue2的插件禁用。
setup函数
这是vue3里面新加的钩子
接收两个参数: props, context
可以返回一个对象,返回的这个对象就可以在外面调用了,比如: 数据,方法…
是在实例创建挂载前执行的。(在setup里面拿不到this; 里面要使用hooks去完成 )
reactive/shallowReactive
reactive:深层次的响应式对象,接受一个数据, 通常是复杂类型数据
shallowReactive::非深层次的响应式对象
<template>
<div class="home">
<div> {{ count.num }} </div>
<div> <button @click="change">count++</button> </div>
<hr/>
user:{{user.name}} --{{ user.hobby.h1 }},{{ user.hobby.h2 }}
<div>
<button @click="changeName">changename</button>
<button @click="changeHobby">改变爱好</button>
</div>
<hr/>
obj: {{ obj.name}}--{{obj.hobby.h1}},{{obj.hobby.h2}}
<div><button @click="changeObj">change obj</button></div>
</div>
</template>
<script>
// 创建响应式对象
import { reactive, shallowReactive } from 'vue';
export default {
name: 'HomeView',
setup(){
// 深层次的响应式对象
const count = reactive({num: 1}) // 接受一个数据, 通常是复杂类型数据
// const count = {num:1}
function change(){
count.num++
console.log(count)
}
// 非深层次的响应式对象
// 只有第一层数据发生改变,才会触发视图的更新
const user = shallowReactive({
name:'young',
hobby:{
h1: 'code',
h2: 'sing'
}
})
function changeName(){
user.name = 'yyds'
}
function changeHobby(){
user.hobby.h1 = 'jump';
console.log(user)
}
const obj = reactive({
name:'young',
hobby:{
h1: 'code',
h2: 'sing'
}
})
function changeObj(){
obj.hobby.h1 = 'jump!!!'
}
return {
count ,
change,
user,
changeName,
changeHobby,
obj,
changeObj
}
}
}
</script>
ref
使用ref创建响应式独享,可以接收一个基本类型的数据
在js中访问到数据,要通过.value
在模板中可以直接取
<template>
<h2>app</h2>
<div>
{{state}}
</div>
<div><button @click="add">count++</button></div>
</template>
<script>
// ref
import { ref } from 'vue';
export default {
setup(){
// 使用ref创建响应式独享,可以接收一个基本类型的数据
// 在js中访问到数据,要通过.value
// 在模板中可以直接取
const state = ref(0)
function add(){
// state.value++
// console.log(ref)
state.value++
}
return {
state,
add
}
}
}
</script>
toRefs
将数据转换成ref创建的数据
<template>
<h2>app</h2>
<div>
state: {{state}}
</div>
<div>
count: {{ count }}
</div>
<div>
obj: {{count}}
</div>
<div>
<button @click="add">count++</button>
<button @click="add2">obj.count++</button>
</div>
</template>
<script>
import { ref, reactive, toRefs } from 'vue';
export default {
setup(){
const state = ref(0)
const count = reactive({count: 0})
const obj = toRefs(count) // 转换成ref
function add(){
count.count++
// console.log(count)
}
function add2(){
// obj.count++
// console.log(obj)
count.count++
}
return {
state,
add,
...count, // 用扩展运算符展开后再返回的reactive对象就失去了响应式.
...obj, // ref对象展开后返回,也还是有响应式的.
add2
}
}
}
</script>
isRef/isReactive
判断是否是一个由Ref/Reactive创建的数据
<template>
<h2>app</h2>
<div>
state: {{state}}
</div>
<div>
count: {{ count }}
</div>
<div>
obj: {{obj.count}}
</div>
<div>
<button @click="add">test</button>
<button @click="add2">test2 </button>
</div>
</template>
<script>
import { ref, reactive, toRefs, isRef, isReactive } from 'vue';
export default {
setup(){
const state = ref(0)
const count = reactive({count: 0})
const obj = toRefs(count) // 转换成ref
function add(){
console.log(isRef(state)) // true
console.log(isRef(count)) // false
console.log(isRef(obj)) // false
}
function add2(){
console.log(isReactive(state)) // false
console.log(isReactive(count)) // true
console.log(isReactive(obj)) // false
}
return {
state,
count,
obj,
add,
add2
}
}
}
</script>
readonly(只读)/shallowReadonly(表面只读)
设置数据为只读属性
<template>
<h2>app</h2>
<div>
count: {{ count }}
</div>
<div>
obj: {{obj}}
</div>
<div>
couter:{{couter}}
</div>
<div>
<button @click="add">test</button>
<button @click="add2">test2</button>
</div>
</template>
<script>
import { reactive, readonly, shallowReadonly } from 'vue';
export default {
setup(){
const count = reactive({count: 0, hobby:{ h1:'code'}})
const obj = readonly(count) // 所有层都不能改
const couter = shallowReadonly(count) // 第二层往后就可以改了
function add(){
//[Vue warn] Set operation on key "count" failed: target is readonly.
// 不能修改只读对象,修改会报警告
// obj.count++
obj.hobby.h1 = 'sing'
}
return {
count,
add,
obj,
couter,
add2(){
// couter.count++
couter.hobby.h1 = 'sing'
}
}
}
}
</script>
生命周期
链接:生命周期钩子
链接:生命周期选项
选项式生命周期钩子和,setup(hooks)生命周期钩子对比
选项式 API | Hook inside setup |
---|---|
beforeCreate | Not needed* |
created | Not needed* |
beforeMount | onBeforeMount |
mounted | onMounted |
beforeUpdate | onBeforeUpdate |
updated | onUpdated |
beforeUnmount | onBeforeUnmount |
unmounted | onUnmounted |
errorCaptured | onErrorCaptured |
renderTracked | onRenderTracked |
renderTriggered | onRenderTriggered |
//下面两个是keep-alive包裹的组件增加的两个生命周期钩子 | |
activated | onActivated |
deactivated | onDeactivated |
setup就是之前vue2的选项都可以写在这里面.
对应的选项就有对应的hooks来去完成
包括生命周期钩子也是用到hooks来去做
<template>
<h2>app</h2>
<div>{{num}}</div>
</template>
<script>
// 1. 导入生命周期钩子
import { onMounted, ref } from 'vue';
export default {
setup(){
// setup就是之前vue2的选项都可以写在这里面.
// 对应的选项就有对应的hooks来去完成
// 包括生命周期钩子也是用到hooks来去做
let apphtml = document.getElementById('app').innerHTML;
console.log(apphtml)
const num = ref(99)
// 2. 使用
// 挂载后的生命周期钩子 onMounted
// 需要接受一个函数, 这个生命周期钩子要执行的函数
onMounted(()=>{
// console.log('挂载后')
apphtml = document.getElementById('app').innerHTML;
console.log(apphtml)
console.log(num.value)
})
return {
num
}
},
}
</script>
计算属性
在Vue3中计算属性也变成了钩子
<template>
<h2>app</h2>
<div>num:{{num}}</div>
<div>numCache:{{numCache}}</div>
<div><button @click="changenum">修改num</button></div>
<br />
<hr />
</template>
<script>
// 计算属性
// 也变成了钩子
import { ref, reactive, computed } from 'vue';
export default {
setup(){
const num = ref(99)
// 得到了一个计算属性
// 1.回调函数
const numCache = computed(()=>{
console.log('aaaa')
return num // 当前计算属性的依赖
})
// 2.对象的形式
// 传一个对象进来
// get() 取值 的时候触发
// set() 被设置值的时候触发
const count = ref(0)
// 可写的计算属性
// 他的的返回值是一个ref类型的对象
let countPlus = computed({
get:()=>{
return count.value*2
},
set:(val)=>{ // countPlus.value 修改后就会触发这个set
// console.log('countPlus修改了:'+val)
count.value = val
}
})
return {
num,
numCache,
count,
countPlus,
changenum(){
num.value++
},
}
},
}
</script>
侦听器
也是一个hooks, 有返回值,是一个函数,这个函数被执行了就停止监听了
两种情况:
监听一个值:
参数1:是一个回调函数 返回什么就监听那个值的变化
参数2: 是改变后要触发的函数, 有两个参数,修改后,前的值
监听多个值:
参数1: 就变成一个数组,里面的元素都是要监听的对象
参数2: 是改变后要触发的函数, 有两个参数,修改后,前的值
<template>
<h2>app</h2>
<div>num:{{num}}</div>
<div><button @click="changenum">修改num</button></div>
<br />
<hr />
<div>age:{{age}}</div>
<div>score:{{score}}</div>
<div>
<button @click="changeage">changeage</button>
<button @click="changescore">changescore</button>
</div>
<div>
<button @click="stop">停止 age-score的监听</button>
</div>
</template>
<script>
// 侦听器
import { ref, watch } from 'vue';
export default {
setup(){
const num = ref(99)
const age = ref(18)
const score = ref(100)
// 也是一个hooks
// 适合监听一个值的情况
// 参数1:是一个回调函数 返回什么就监听那个值的变化
// 参数2: 是改变后要触发的函数, 有两个参数,修改后,前的值
// 有返回值,是一个函数,这个函数被执行了就停止监听了
watch(
()=>{
return num.value
},
(newval,oldval)=>{
console.log('num改变了')
console.log(newval)
console.log(oldval)
}
)
// 监听多个值
// 参数1: 就变成一个数组,里面的元素都是要监听的对象
// 有返回值,是一个函数,这个函数被执行了就停止监听了
const stopwatch = watch([score,age],(newval,oldval)=>{
// console.log('num/age变化了')
console.log(newval) // 和参数1的顺序是对应的, 都是修改后的值
console.log(oldval) // 修改前的值
})
return {
num,
age,
score,
changenum(){
num.value++
},
changeage(){
age.value++
// score.value++
},
changescore(){
score.value++
},
stop(){
stopwatch()
}
}
},
}
</script>
setup语法(完全的Vue3写法)
只要给script加上 setup属性; 里面的代码就相当于写在setup里面
不需要return 了.
顶层变量就可以直接在外面用
<template>
<h2>app</h2>
<div>{{ num }}</div>
<div> {{ a }} </div>
<div> <button @click="changenum">change-num</button> </div>
</template>
<script setup>
// 只要给script加上 setup属性; 里面的代码就相当于写在setup里面
// 不需要return 了.
// 顶层变量就可以直接在外面用
import { ref, onMounted } from 'vue';
const num = ref(0)
function changenum(){
num.value++
}
onMounted(()=>{
console.log('aaaaaaaaaaaaaaaaa')
})
// {
// const a = 10; //不是顶层变量就不能在外面用.
// }
const a = 10;
</script>
provide/inject(依赖注入)
HomeView.vue
<template>
<div>
<div>home</div>
<Parent></Parent>
</div>
</template>
<!-- 当做是祖先组件 -->
<!-- 祖先向后代传值 -->
<script>
/*
home money
Parent
Son 拿到money
*/
import { provide } from 'vue';
import Parent from './Parent.vue';
export default {
setup(){
// 参数1: 传的名字
// 参数2: 传的值
provide('money',100) // 定义向下传的值.
},
components: {
Parent
}
}
</script>
Parent.vue
<template>
<div>parent</div>
<hr/>
<Son></Son>
</template>
<script>
import Son from './Son.vue'
export default {
components:{
Son
}
}
</script>
Son.vue
<template>
<div>
son
</div>
</template>
<script>
import { inject } from 'vue';
export default {
setup(){
const box = inject('money'); // 拿到传下来的值
console.log(box)
}
}
</script>
router4.x
因为在setup里面没有this(this不代表当前实例), 不能使用this. r o u t e r / t h i s . router/ this. router/this.route 对象
所以就提供了对应的hooks来帮助我们拿到这两个对象
useRouter // 执行了可以返回一个路由对象 $router
useRoute // 返回一个路由信息对象
<template>
<button @click="goabout">跳转到about</button>
<button @click="gomy">跳转到my</button>
<hr>
<h2> count: {{ this.$store.state.count }}</h2>
<h2> {{ count }} </h2>
<button @click="getCount">获取到vuex -- count</button>
</template>
<script>
import { computed } from 'vue';
import { useRouter, useRoute } from 'vue-router';
import { useStore, mapState } from 'vuex';
export default {
setup(){
const $router = useRouter();
const $store = useStore();
// 在setup里面就不要使用辅助函数, 在这里没有优势,代码还比较复杂
const count = computed(()=>$store.state.count)
const aa = mapState(['age'])
// 因为在sutup里面没有 this, 所以不能拿到我们要的属性值.
console.log(aa.age) // 100
// console.log(aa.age.bind({$store:this.$store})) // 以前的
console.log(aa.age.bind({$store:$store})()) //现在要通过bind()改变
return {
count,
goabout(){
// 在setup中this不代表实例对象,所以就不能通过this.$router/ this.$route 获取到和路由相关的对象
// 如果要获取就要使用vue-routert提供的hooks
// this.$router.push('/about')
// console.log(this)
// useRouter() 获取路由对象 (可以操作)
// useRoute() 获取到路由信息对象 (只有信息, params query ..)
// console.log($router)
// $router.push('/about')
$router.push({name: 'about'})
},
gomy(){
$router.push({
name: 'my',
params: {
id: 1234
},
query: {
age: 18,
name: 'yyds'
}
})
/*
// 用path 跳转,不能传 params
$router.push({
path: '/my',
params: {
id: 1234
},
query: {
age: 18,
name: 'yyds'
}
})
*/
},
}
}
}
</script>
Vuex4
也是因为在setup中拿不到this, 所以也是通过hooks ,useStore(); 来拿到仓库对象
<template>
<button @click="goabout">跳转到about</button>
<button @click="gomy">跳转到my</button>
<hr>
<h2> count: {{ this.$store.state.count }}</h2>
<h2> {{ count }} </h2>
<button @click="getCount">获取到vuex -- count</button>
</template>
<script>
import { computed } from 'vue';
import { useRouter, useRoute } from 'vue-router';
import { useStore, mapState } from 'vuex';
export default {
setup(){
const $store = useStore();
// 在setup里面就不要使用辅助函数, 在这里没有优势,代码还比较复杂
const count = computed(()=>$store.state.count)
const aa = mapState(['age'])
// 因为在sutup里面没有 this, 所以不能拿到我们要的属性值.
console.log(aa.age) // 100
// console.log(aa.age.bind({$store:this.$store})) // 以前的
console.log(aa.age.bind({$store:$store})()) //现在要通过bind()改变
return {
count,
getCount(){
console.log($store.state.count)
}
}
}
}
</script>
自定义指令
局部定义
全局定义
总结(连载中…)
提示:这里对文章进行总结:本文仅仅简单介绍了Vue3的使用,提供了大量能使我们快速便捷地处理数据的函数和方法