前言:使用Vue3(Vue.js - 渐进式 JavaScript 框架 | Vue.js)的 组合式API 编程风格来进行基础转换。
基础变动:
- setup语法糖 Vue3.2之后才能放到script里面
- data methods 都不再需要,也不再需要return出变量或是函数
- 路由相关知识,只注意useRoute() useRouter()其余的,和之前自己写的lyrics前端路由就够用了
- Vue3 异步编程
- provide 和 inject
- ······
语法转换过程中,主要分几个大块:
- 语法:框架自行的一些语法迁移(官网里面的基础)
- 组件相关:尤其是组件之间的各类通信方式,以及封装组件的传参等,单独一篇文章总结。
- 编程风格/封装相关
- ······
数据
普通数据
<script setup lang="ts">
interface SpecialFunDataItem {
title: string
description: string
}
const apply_data: SpecialFunDataItem[] = [
{
title: '知识库/项目文档/产品手册',
description: '适合',
},
{
title: '帮助中心/FAQ/在线问答',
description: '适合。',
},
]
</script>
响应数据
ref
<template>
<a-modal
v-model:visible="visible"、
···
>
</template>
<script setup lang="ts">
import {ref} from 'vue'
const visible = ref<boolean>(false)
</script>
reactive
reactive这个更多使用在对象和数组类型的响应式定义数据,因为在vue2中,对于数组和对象是不能够完美响应式的,需要watch来监听实现,而在vue3中,以此作为优化来定义响应式数据。
但是直接使用ref也可以定义响应式数组或对象数据
- reactive定义的数组不能实时渲染,用ref就完事了
Vue3异步请求获取数据在渲染时不显示的问题_s-010101的博客-CSDN博客_vue异步请求数据没渲染
并不是生命周期的问题,而是数据定义的问题
除了上述两种方式解决办法,还可以使用ts语法,接口,参照antdv中的表单相关实例代码
- reactive不能渲染,更新名称子组件不能渲染
- FIXME: 重命名组件中,输入框值变化不能响应式
同时对数据定义用ref不用reactive
Vue2中的data
如何在setup中使用data中的数据库?
按照以前vue2,直接使用this可以访问到data中定义的数据,但在vue3不行,因为setup()函数的执行要比created,oncrated函数都要早。
具体使用以下小技巧可以访问到:
<div v-on:click="fun(testdata)"></div>
setup(){
const fun=(i)=>{
alert(i)
}
},
data(){
return{
testdata:1,
}
}
也可以使用getCurrentInstance方法获取data内的数据(未验证)
<script lang="ts">
import { getCurrentInstance } from "vue";
export default {
data() {
return {
b: "data数据",
};
},
setup() {
const datab = getCurrentInstance();
async function getdata(){
let dataa=datab.data.b;
console.log(dataa)
}
},
};
</script>
计算属性+方法+侦听器
计算属性(computed)和方法(methods)的区别
- 计算属性是基于他们的依赖来进行缓存的
“他们的依赖”指的是data数据域中的data数据,如果其变动,计算属性值才会变
- 方法不存在缓存
计算属性(computed)和侦听器(watch)的使用
- 侦听器更用于异步或是开销较大的操作
watch(侦听器)+computed(计算属性)+watchEffect
- watchEffect:只要在该函数引用声明式变量,那么就会执行该函数
为了根据响应式状态自动应用和重新应用副作用,我们可以使用 watchEffect 函数。它立即执行传入的一个函数,同时响应式追踪其依赖,并在其依赖变更时重新运行该函数。
- watch:vue2中监听新值和旧值的一样使用(ref reactive 各有不同)
- computed
- 注意Vue computed 的类型声明方式https://bytenote.net/article/121073339505573889 https://v3.cn.vuejs.org/api/computed-watch-api.html#computed
- 不确定暂时先用any
- 动态监听数组,返回的新值打印出来都是数组元素逗号隔开,因为监听的是内部每一个数组元素
<template>
<button type="button" @click="count++">[watch watchEffect]ref 定义的 count is: {{ count }}</button><hr/>
<button type="button" @click="numbers[0]++">[watch watchEffect]reactive 定义的 numbers is: {{ numbers }}</button><hr/>
<button type="button" >[computed]ref 定义的 person_ref is: {{ person_ref }}</button><hr/>
<button type="button" >[computed]reactive 定义的 person_reactive is: {{ person_reactive }}</button><hr/>
<span>动态参数绑定:</span><a :href="getHrefValue()">{{getHrefValue()}}</a><hr/>
</template>
<script setup lang="ts">
import { ref, watchEffect, watch, reactive, computed, Ref } from 'vue'
const count = ref(0)
const numbers = reactive([1, 2, 3, 4])
let person_ref = ref('wuzutao')
let person_reactive = reactive({firstName:'wu', lastName:'zutao', fullName: 'wu-zutao'})
let getHrefValue = () => {
return 'www.wztlink1013.com'
}
// [watchEffect ref reactive]
watchEffect(()=>{
const x1 = count.value
const x2 = numbers[0]
console.log('因为watchEffect里面调用了count/numbers[0] 所以watchEffect所指定的回调执行了', x1, x2)
})
// [watch ref]
watch(count, (count, prevCount) => {
console.log('新值', count)
console.log('旧值', prevCount)
})
// [watch reactive]
watch(
() => [...numbers],
(numbers, prevNumbers) => {
console.log('numbers新值',numbers)
console.log('numbers旧值',prevNumbers)
}
)
// [computed ref]
// const computed_ref = computed(() => { // 简单式
// person_ref.value = person_ref.value + '+'
// })
const computed_ref = computed<any>({ // 复合式
get: () => {
console.log('[ref]使用(get)该变量,就会调用')
},
set: (val) => {
console.log('[ref]改变(set)该变量,就会调用')
person_ref.value = val + '-内部处理值-原始值' + person_ref.value
}
})
// console.log(computed_ref.value) // 会打印undefined因为上面的get没有传参
computed_ref.value = '修改的目的值'
// [computed reactive]
// person['firstName'] = computed(()=>{
// return person.firstName + '-' + person.lastName
// })
const computed_reactive= computed<any>({ // 完整写法
get(){
// return person_reactive.firstName + '-' + person_reactive.lastName
console.log('[reactive]使用(get)该变量,就会调用')
},
set(value){
console.log('[reactive]改变(set)该变量,就会调用')
const nameArr = value.split('-')
person_reactive.fullName = value
person_reactive.firstName = nameArr[0]
person_reactive.lastName = nameArr[1]
}
})
computed_reactive.value = 'wu+-zutao+'
</script>
<style scoped>
a {
color: #42b983;
}
label {
margin: 0 0.5em;
font-weight: bold;
}
code {
background-color: #eee;
padding: 2px 4px;
border-radius: 4px;
color: #304455;
}
</style>
- vue3的监听器报重载错误
Vue2中侦听器(watch)注意事项
实际开发过程中:
- 非的确必要,尽量不要使用watch监听,实际过程中,会造成许多问题,比如渲染顺序等,会给后续添加功能带来难以维护的问题
- 当监听的数据不是一个简单的基本类型,比如一个对象,一个数组,此时应该使用深度监听:deep:true;当想让监听器一启动就触发一次watch,应该使用: immediate: true。
直接watch
监听对象内的是检测不到变化的,因为对象的指向并没有发生改变。Vue中的watch监听对象内属性的变动方案
使用deep属性
new Vue({
data: {
count: 10,
blog:{
title:'my-blog',
categories:[]
}
},
watch: {
blog:{
handler(newVal,oldVal){
console.log(`new: ${newVal}, old: ${oldVal}`);
},
deep:true
}
}
})
里面的deep
设为了true
,这样的话,如果修改了这个blog
中的任何一个属性,都会执行handler
这个方法。不过这样会造成更多的性能开销,尤其是对象里面属性过多,结构嵌套过深的时候。而且有时候我们就只想关心这个对象中的某个特定属性,这个时候可以这样
用字符串来表示对象的属性调用
new Vue({
data: {
count: 10,
blog:{
title:'my-blog',
categories:[]
}
},
watch: {
'blog.categories'(newVal, oldVal) {
console.log(`new:${newVal}, old:${oldVal}`);
},
}
})
使用计算属性(computed)
new Vue({
data: {
count: 10,
blog:{
title:'my-blog',
categories:[]
}
},
computed: {
categories() {
return this.blog.categories;
}
},
watch: {
categories(newVal, oldVal) {
console.log(`new:${newVal}, old:${oldVal}`);
},
},
})
参考:https://segmentfault.com/a/1190000018080301
生命周期
- 生命周期需要注意,没有created阶段了,直接在setup里面了,也就是setup阶段是没有挂载真实DOM的,如果需要操作真实dom需要在onMounetd里面进行相应逻辑
onMounted(() => {
if ((document.getElementById('test') as HTMLElement) === null) {
console.log('--------------');
} else {
console.log('+++++++++++++++++++++');
}
});
- https://v3.cn.vuejs.org/guide/instance.html#生命周期图示
- https://juejin.cn/post/6942030120383168542
- https://segmentfault.com/a/1190000038426588
- https://juejin.cn/post/7108206884867276831
- Vue 3 生命周期完整指南
- bug:刚进入页面,下面的地方不会自动摊开宽度,获取dom的宽度失败
代码放到onMounted中即可
vue2当中:
- beforeCreate:在实例初始化之后,数据观测和事件配置之前被调用
- created:在实例创建完成后被立即调用
- beforeMount:在挂载开始之前被调用
- mounted:el被新创建的vm.$el替换,并挂载到实例上去之后调用该钩子
- beforeUpdate:数据更新时调用,发生在虚拟DOM打补丁之前
- updated:由于数据更改导致的虚拟DOM重新渲染和打补丁,在这之后会调用该钩子
- beforeDestroy:实例销毁之前调用
- destroyed:实例销毁后调用
<script>
setup() {
console.log('----setup第一个执行----')
// vue3.x生命周期写在setup中
onBeforeMount(() => {
console.log('------vue3中onBeforeMount-----')
})
onMounted(() => {
console.log('------vue3中onMounted-----')
})
onRenderTriggered((event) => {
console.log('------vue3中onRenderTriggered-----', event)
})
},
// vue2当中的
beforeCreate() {
console.log('----vue2中beforeCreate第二个执行----')
},
// vue2当中的
created() {
console.log('----vue2中created第三个执行----')
},
</script>
执行结果如下:
具体使用
router和route区别及使用
vue3中的useRoute()和useRouter(); - 掘金
Vue3中使用Async Await
- vue3的setup本就是一个async,所以可以直接使用await(不可以)
- vue3.0 中 如何在setup中使用async await - 阿臻 - 博客园
const handleLoginOut = async () => {
emit('wsClosed');
const { ret } = await logout();
if (ret > -1) {
localStorage.clear();
onRedirect();
}
};
emit使用
slot使用
vue2:
<template>
<template slot="content">
···
</template>
</template>
vue3:
<template>
<slot name="content">
···
</slot>
</template>
ref获取元素节点
[取]函数式编程模式[弃]Mixin逻辑
- cooperation的Mixin代码较难抽离出来
使用到较多的cooperation文件代码,Mixin逻辑,不再像Home组件中的上传逻辑一样直接放到一块,改用引用函数的方式,因为变量不多。
ylCooperationSdkInstance变量在组件中使用较少,但是如果直接引用ts文件中的变量,是否会存在不再响应的问题。使用vue3推荐的组合式函数编程方式:
https://cn.vuejs.org/guide/reusability/composables.html
- 上面的mixin文件代码全部放在ts文件中,然后在vue中导入ts文件,其仍然保留生命周期
- 单独ts文件是用不了全局变量
直接导入router文件,使用根源API
https://cn.vuejs.org/guide/reusability/composables.html#vs-mixins
- 将上传逻辑js代码转为ts代码,然后再全量导入Home/index.vue中
- data数据迁移
- 里面应该只有uploadedNum变量再vue组件中使用到了
- iconType变量两者都有
- uploadHandler.ts上传逻辑代码进行ts转换。并且将代码放到组件中
在Home组件中,混合代码较多,其中,上传逻辑使用的是vue2.x的Minix混入方式,但是在vue3不再推荐该模式写代码,所以单独将上传逻辑js文件单独整理其逻辑,文件逻辑中,上传js代码和Home.vue组件两者相互又使用一些变量···
- 上传js代码中有6处使用vue中的变量
- vue中有2处使用js代码中变量
尝试方法1:因为两者多出混入变量使用,尝试将上传的ts代码全部放到Home组件中,虽然代码变长了,但是逻辑性提高了,先做出如下备份
vue3中不要命名冲突
会导致取值和预期取值不一样
pagination对象类型,在首页的分页逻辑有误
vue3全局路由配置
- vue3路由全局匹配
js/ts文件使用相应vue中函数
- vue2:传递“this”:https://www.cnblogs.com/taohuaya/p/10765731.html
- 但是vue3没有this,利用传参
全局变量的使用
vue3注册全局变量失去响应性
- 点击登录按钮,相应一个loading的状态,这个在之前项目中是利用全局注册的一个变量,vue3的全局注册变量方法
- https://blog.csdn.net/weixin_43090018/article/details/117222606
- https://blog.csdn.net/XKFC1/article/details/123715354
- vue3 怎么创建全局的响应式对象 创建简单的vuex_最有才的河南大汉的博客-CSDN博客
- fetch文件中的axiosLoadingObj变量,在main.ts中注册为全局变量,Login.vue中使用,当fetch中axiosLoadingObj变量的值改变,Login.vue中使用该全局变量不会发生改变。
- 接口 - loading 的全局变量迁移
不能使用v3全局变量api getCurrentInstance 不能响应式,同时打包情况下
直接导入ts文件,在ts文件中做变量导出,
v-if/else branches must use unique keys.vue(29)
- 官方解释:https://github.com/vuejs/core/issues/1712#issuecomment-665206103
- 解决参考:https://blog.csdn.net/Fine_Cui/article/details/124906140
<template v-for="(item, index) in fileList">
<a-menu-divider v-if="item.isDivider" :key="index" />
<a-menu-item
v-else
:key="item.value"
class="create-button-menu-item"
@click="handleMenuClick(item.value)"
>
将第五行代码中的index换成item.value,一是因为index在这里并没有传值的实际作用,而是解决key值相同问题
具名插槽
- vue3插槽使用方式和vue2不一样,不能使用template,而是slot标签,name属性
- 具名插槽在vue2和vue3两者使用有差别
注释导致页面不能渲染
上述箭头所标注的地方不要有注释,有注释会渲染不出来
样式只在当前页面有效
组件中style标签后面加上scoped
就可以
<style lang="less" scoped></style>
vue2获取dom节点自定义属性值
vue如何获取自定义元素属性参数值_lotSeed_5的博客-CSDN博客_vue获取元素的属性值
根据dom属性来动态改变css
当给dom添加自定义属性,vue2和vue3实际渲染出来的不一样(如果是标签自身属性,vue3和vue2是一样的)
- vue3
<div :selected="true">selected</div>
<div :selected="false">UnSlected</div>
- vue2
<div :selected="true">selected</div>
<div :selected="false">UnSlected</div>
- 根据以上不同,在css选择器使用有区别
- vue3
// vue3
div[selected=true] {
color: red;
}
- vue2
// vue2
div[selected] {
color: red;
}
评论区