使用nuxt3 + vue3 :其中nuxt3集成了vite速度更快,并且提供了SSR渲染。
SwiperJS 4~5:提供整页滚动效果,以及轮播图效果。很强!
VueScorll:可以设置自定义的滚动条。
Less:CSS预编译。
NodeJS:不必多说。
MongoDB:和Node和前端相形最好的文档型Nosql数据库。
1.在首屏的div块中现了一个名为 fp-page 的类:
此处的 fp 代表 first paint ,首次绘制,是衡量用户体验的指标之一。fp-page就代表首次绘制的页面。没有什么特殊的,只是告知开发者哪一个页面首先绘制。
2.优化图片加载
将一些相对较小的图片,可以直接存在项目的img目录里,一起打包在发布包中,作为优先载入,其他的大图就还是使用CDN或者静态资源服务器提供。
在nuxt3中,我们使用自带的useState()方法管理页面状态,主要为当前页面是哪一页的状态,state的状态可以在window.NUXT.state中查看。
就是从上一页进入about页面时,元素从下往上移动一段距离到指定位置,当从下一页进入about页面时,元素从上往下移动一段距离,通过swiper-slide-next,swiper-slide-prev和swiper-slide-active类根据页面状态进行位置切换,然后通过transition过渡,实现动画效果。
.swiper-slide {
transition: transform 500ms ease-out; // 设置过渡效果
}
.swiper-slide-active {
// 滑动到当前页面后的移动终点位置
.home-about-cates,
.home-about-container {
transform: translateY(0);
}
}
.swiper-slide-prev {
// 元素在上一页时所处的位置
.home-about-cates {
transform: translateY(-1rem);
}
.home-about-container {
transform: translateY(-1.5rem);
}
}
.swiper-slide-next {
// 元素在下一页时所处的位置
.home-about-cates {
transform: translateY(1.5rem);
}
.home-about-container {
transform: translateY(2rem);
}
}这个需求不仅仅是在about页面用到,其他页面和小组件也有,是很常用的方法,需要牢记。
最初的起始偏移量为186px,即transform: translateX(186px),后续所有子块的滑动距离都是在这个基础上计算的。计算方式为:当前偏移量-子块自身宽度 = X - offsetX
在 Nuxt 中你仍然可以使用 vue 中的transition组件,来帮助页面转换中的过渡,使用方式和vue中一样,将需要过渡的元素包裹在 组件之间,然后设置以下四个样式:
.v-enter-active,
.v-leave-active {
transition: opacity 0.5s ease;
}
.v-enter-from,
.v-leave-to {
opacity: 0;
}使用 vue 的 transition 组件,将官网 about 页面的子组件过渡做了优化,在从发展历程块切换到别的块的时候不会再闪现第一个swiper-slide块了(实际表现是Fly me 2 the moon那个大图)。
旧版:
我的改良版:
现在会正确的从当前发展历程正在展示的图片直接过渡到其他块,而不会再闪现一下第一张图。
在display:flex布局中,如果我们希望元素排在一行,可以使用这个布局,然后如果子元素设置了position: absolute且父元素设置了 position: relative(子绝父相),那么这个元素将不参与 flex 布局的宽度/高度分配,但是仍然享有 flex 的特性,即浮动效果。
图中,黄块本来应该在箭头指示位置,因为他是 flex 布局中的第二个子元素,但是设置了 绝对定位后,就不占有原来的空间了,会分配给其他flex子元素。
使用这个特性,可以很方便的给 flex 布局添加顶部导航,侧边栏,底部按钮等功能:
图中,框框里的就是 flex 布局中的绝对定位做的。
关于这一点是一个很麻烦的过程,但其实搞懂了顺序之后就还好。首先,要明确整个过渡发生了什么,简单概括一下一个元素的过渡:
- 先为元素添加 过渡中的样式类 .active,这里一般是设置过渡属性transition的时机,根据情况不同还可以分别设置 .leave-active 和 .enter-active 分别对应离开或进入时的过渡效果;
- 为元素添加过渡后的样式类 .leave-to/enter-to,进入或者出去后的最终样式;
- 与第1,2步间隔合适的时间后(可以使用setTimeout)等待过渡结束后再删除 .leave-to/.enter-to 类 ,比如过渡时间为500ms,则设置500ms后的定时器来删除类。
- 创建定时器,等待第3步的过渡时间,间隔一段时间后,删除 .active 类,(可选)此处也可按情况分为 .leave-active 和 .enter-active,如果分开了的话就删除当前的 active类,并添加另一个类,比如删除 .leave-active 添加 .enter-active,然后再创建定时器,删除当前 active 类。
_原理:_由active设置过渡类型,然后添加样式类,此时元素因获得新样式产生过渡,然后删除过渡类,元素因失去类导致变回原样式,又会产生过渡,最后删除 active 即可。
关于如何添加或删除类:可以用原生方法,或者vue,只要能改都可以。
大部分的过渡都是这个过程,只要理清楚就行。
1.public目录下的文件会被一起打包进.output/_nuxt中,也就是可以直接在生产环境中使用,而assets中的文件则不会被一起打包,不能直接在生产环境使用。所以,包括字体,图片,音频等需要由浏览器自动下载的文件,最好就放在 public/ 目录下!
2.通过调用useFetch()方法获取的数据,在页面加载后可以在devtools中的,window.NUXT.data 中看到:
这个问题可能是swiper版本过高导致的,我在nuxt项目中使用swiper6/7/8的时候都遇到了这个问题,鼠标滚动失效并且很多功能都用不了,初步猜测是因为高版本中的一些样式或逻辑规范和vue产生了冲突,或者就是其本身有问题。
降低版本到swiper4~5之间,即可解决问题。将package.json中的swiper版本改为4.5.0,然后重新npm install一下。
刷新浏览器的时候由于swiper是在dom渲染完成后才修改的,所以第一个slide始终会闪现一下,这种时候可以用老方法遮罩。但是我在nuxt中发现了一个很奇妙的方法。只需要在当前布局文件中添加任意一个项目中不存在的的组件标签即可:
<template>
<!--注意以下这个组件nux是不存在的-->
<nux />
<div class="home">
<!-- <div style="width: 100%;height: 100%;background-color: white;z-index: 999;" v-if="isReady"></div> -->
<div class="swiper-container">
<!-- 第二层一个包裹块 -->
<div class="swiper-wrapper">
<div class="swiper-slide">
<slot></slot>
</div>
<div class="swiper-slide">
<slot name="product"></slot>
</div>
<div class="swiper-slide">
<slot name="news"></slot>
</div>
</div>
</div>
<!-- <div class="swiper-pagination"></div> -->
</div>
</template>这样就不会频闪,而且静态资源的DOM也不会发生突发性位移了。原因不明,初步推断是因为无法识别这个组件,Nuxt并没有做全部更新,而是部分更新所以DOM没有刷新,也就不会频闪。但是这样的坏处是会报非常多的警告:
这时就可以用掩耳盗铃的方法,将console 的warn方法重写掉让他没法报警告。这是很取巧的方法,而且原理不明,谨慎使用。
console.warn = ()=>{}在不使用严格等号 === 的时候,即双等号 ==的时候,null==undefined为true,而用三等号===时则是false。他们的值相同但是类型不同。
null可以和数字类型运算,运算中null相当于0,结果被隐式转换为数字, 但undefined不可以和数字类型进行运算,其结果为 NaN。
判断一个对象的属性是否存在:
错误部分:
let { query } = this.$route
if (query.page === null) {
this.mySwiper.slideTo(0, 0);
}报错,即使query.page不存在,其返回值也不是null,而是undefined,在===中null是不等于undefined的所以错误。解决方法可以将===改为==,或者将null改为undefined,也可以用对象自身的hasOwnProperty方法,或者in判断符。具体可以看这个:
修改后:
let { query } = this.$route
if (query.page === undefined) {
this.mySwiper.slideTo(0, 0);
}** flex-shrink是flex子元素的属性**,不是容器的属性。 用来设置,当父元素的宽度小于所有子元素的宽度的和时(即子元素会超出父元素),子元素如何缩小自己的宽度的。 flex-shrink的默认值为1,当父元素的宽度小于所有子元素的宽度的和时,子元素的宽度会减小。值越大,减小的越厉害。如果值为0,表示不减小。
在横向使用swiper的时候,如果将wrapper设置为 display:flex 时子slide将会平分整个宽度,也就是说每个slide的宽度都会被缩小,以在一整行放下。但是我们想一个 slide 独占一整个页面宽度,然后滑动后是下一个slide,这种时候就涉及到了flex-shrink属性。通过给子元素slide-wrapper类设置flex-shrink的值为0,则表示不缩小 flex 元素的宽度/高度。这样就能让一个swiper-slide拥有给定的宽度了。
.swiper-wrapper {
position: relative;
width: 100%;
z-index: 1;
display: flex;
transition-property: transform, -webkit-transform;
box-sizing: content-box;
}
.swiper-slide {
width: 100%;
position: relative;
transition-property: transform, -webkit-transform;
}.swiper-slide {
flex-shrink: 0;
width: 100%;
position: relative;
transition-property: transform, -webkit-transform;
}在vue2中,我们可以通过给子组件设置 ref 然后 this.$refs.name 的方式获取子组件实例,然后通过其调用内部方法:
但是vue3的 setup 语法糖中内部变量对外是默认关闭的,也就是子组件不对外导出任何变量,如果直接获取子组件实例会发现,其内部没有提供任何方法:
使用 vue3提供的 defineExpose() 编译器宏,手动导出需要的变量或者方法:
// 子组件代码
<script setup>
const foo = () => {
console.log("foo");
}
// 子组件内设置对外公开的变量
defineExpose({
foo
});
</script>父组件内调用:
// 调用子组件方法 vue2写法
this.$refs.childRef.foo(); // foo()
// vue3写法
const childRef = ref(null);
childRef.value.foo();可以使用 defineProps 编译器宏,用一个变量接收,然后通过这个变量获取props中的值:
<script setup>
const props = defineProps({
delay: {
type: Number,
default: 3420
}
})
onMounted(() => {
console.log(props.delay) // 3420
})
</script>在设置了siwper的自动播放中的delay延迟时间后,本应该按照既定时间切换到下一张的时候可以明显的感觉到间隔时间比上一次切换更长了,也就是说越来越卡了。
个人觉得,swiper实现自动播放功能的原理可能还是setTimeout或者setInterval,但是这两者一旦设置过多就是会造成越来越卡的现象,尤其是如果一次创建了很多setTimeout任务后,延迟尤为明显。使用setInterval也有问题。所以建议要不就手动实现自动播放,不要使用autoplay,要不就都使用autoplay,个人选择前者。
每隔一段时间就调用一次 swiper.slideNext()方法,并重新调用一次自身,创建新的定时器。
<script setup>
function autoplay() {
timeOut = setTimeout(() => {
if (swiper.isEnd) {
// 如果是结尾 就回到开头
swiper.slideTo(0);
} else {
swiper.slideNext()
}
autoplay()
}, props.delay)
}
onMounted(() => {
swiper = new Swiper('.home-product-swiper', {});
autoplay(); //调用一下
})
</script>一、可能是父级没有获得宽/高的原因,导致设置绝对定位后无法找到位置,元素消失。这种情况需要检查祖辈元素,找到设置了相对定位的元素看看其是否有具体的宽高,如果没有,设置即可。
官网的例子就是一坨shit,就写一个简单的代码,tm细节一点没说,吐了。
https://www.swiper.com.cn/api/effects/194.html
使用fade,渐入渐出效果,可以在slide变换的时候实现前一个淡出后一个淡入的效果,也就是米哈游官网的样子。但是其中有一个细节官网一点没说。
首先,它是通过让swiper-slide的不透明度在0和1之间切换实现的,也就是说让下一张的透明度变成1,上一张变成0,但是**如果你没有设置css过渡效果(即transition),则其变化会非常生硬,并且这个属性swiper并不会!!自动添加,需要手动给swiper-slide类添加transition-property: opacity。**也就是说,仅仅写了以下代码是不够的!!!
var mySwiper = new Swiper('.swiper',{
effect : 'fade',
fadeEffect: {
crossFade: true,
}
})很简单,只需要给 .swiper-slide 添加过渡属性即可。使用transition或者transition-property都可。
.swiper-slide {
transition-property: opacity;
}
一般写完方法后,我们悬停方法可以看到一些参数、返回值的介绍,在VSCode中如果需要为自己的方法/函数添加悬停的内容,可以在方法名上面输入 // 编辑器会自动生成注释内容,注释的内容即为悬停时的介绍内容。**
悬停效果:
其中,所有的注释参数都需要以@开头,有固定的几个可选选项:
等等很多,主要用的只有 param 和 return 即可。
参数@prarm 后的大括号 { } 表示需要参数的类型。
还可以自定义参数的表达,只需要在 @ 后添加任意文字即可。
或者在第一行介绍函数:
所有的关于html的设置都可以在 nuxt.config.js 文件中配置,包括head中的meta,link,script等,还包括开发环境,自动导入等等配置。其配置将作用与整个项目。
所有配置项可以在这里浏览:
import { defineNuxtConfig } from 'nuxt'
// https://v3.nuxtjs.org/api/configuration/nuxt.config
export default defineNuxtConfig({
app: {
head: {
meta: [
// <meta name="viewport" content="width=device-width, initial-scale=1">
{ name: 'viewport', content: 'width=device-width, initial-scale=1' }
],
link: [
// 下句等同于:<link rel="icon" href="https://www.mihoyo.com/favicon.ico">
{ rel: 'icon', href: 'https://www.mihoyo.com/favicon.ico' }
],
}
}
}).anim {
background-image: linear-gradient(-135deg, #3778e5, #e98bc0, #3778e5, #e98bc0, #3778e5);
background-clip: text; // 从前景内容的形状(比如文字)作为裁剪区域向外裁剪,如此即可实现使用背景作为填充色之类的遮罩效果。
}background-image属性除了可以接受一个url地址或者文件地址外,还可以创建渐变色(gradient)来确定图像。
css中有多个gradient函数,根据参考手册了解用法。
= linear-gradient([ [ | to ] ,]? [, ]+)
= [left | right] || [top | bottom]
= [ | ]?
下述值用来表示渐变的方向,可以使用角度或者关键字来设置:
:
用角度值指定渐变的方向(或角度)。
to left:
设置渐变为从右到左。相当于: 270deg
to right:
设置渐变从左到右。相当于: 90deg
to top:
设置渐变从下到上。相当于: 0deg
to bottom:
设置渐变从上到下。相当于: 180deg。这是默认值,等同于留空不写。
用于指定渐变的起止颜色:
:
指定颜色。
:
用长度值指定起止色位置。不允许负值
:
用百分比指定起止色位置。
**border-box: **
从border区域(含border)开始向外裁剪背景。
**padding-box: **
从padding区域(含padding)开始向外裁剪背景。
**content-box: **
从content区域开始向外裁剪背景。
text:
从前景内容的形状(比如文字)作为裁剪区域向外裁剪,如此即可实现使用背景作为填充色之类的遮罩效果。遮罩效果See with Webkit
很简单,只需要使用 window.open() 方法即可,有两个参数,第一个参数指定 url ,第二参数指定打开的位置,参数和 a 标签的 target 属性一样,如果要打开新页面就是 _blank
window.opne("https://www.baidu.com","_blank");
// 等效于 <a href="https://www.baidu.com" target="_blank"></a>swiper在初始化的时候就在swiper-wrapper下生成了已确定的swiper-slide的个数,(而异步加载会动态的决定swiper-slide的个数),如果数据不是初始化的时候就确定的,是异步获取的,就会导致无法滑动。
即在swiper初始化后如果直接动态修改 swiper-slide 的个数就会导致swiper卡死。
以下,以jquery为例:
var reportHtml = '';
$.ajax({
type: "get",
url: url,
dataType: "json",
success:function(data){
$("#reportList").empty();
for(var i=0;i<data.data.length;i++){
reportHtml+='<div class="swiper-slide">'
+'<img src="' + data.data[i].img + '">'
+'</div>';
}
$("#reportList").append(reportHtml);
//在为所有swiper-slide添加完毕后,在new 初始化swiper
var swiper = new Swiper('.swiper-container', {
slidesPerView : 3
});
}
});添加 observer、observeParents
在 Swiper 的上启用动态检查器(Mutation Observer),如果你更改swiper 的样式(隐藏/显示)或修改其子元素(添加/删除幻灯片),Swiper 会更新(重新初始化)并触发 observerUpdate 事件。
默认 false ,不开启动态检查器,此时可以使用** update() **方法手动更新。
注意:隐藏swiper 和删除slide 会触发两次事件,因为slide 不是swiper 真正的一级子元素。
var mySwiper = new Swiper ('.swiper-container', {
observer:true, // 修改swiper自己或子元素时,自动初始化swiper
observeParents:true, // 修改swiper的父元素时,自动初始化swiper
loop: true, // 循环模式选项
})background的复合写法从左到右可以依次为:
注意点:其中 size 在 position 后面写的时候要加一个斜杠 / 类似:
background: url(test1.jpg) no-repeat 10px 20px/50px 60px其中 10px 20px的斜杠后 / 的 50px 60px 即为background-size属性
background: url(test1.jpg) no-repeat scroll 10px 20px/50px 60px content-box padding-box,
url(test2.jpg) no-repeat scroll 10px 20px/70px 90px content-box padding-box,
url(test3.jpg) no-repeat scroll 10px 20px/110px 130px content-box padding-box #aaa;background-image: url(test1.jpg), url(test2.jpg), url(test3.jpg);
background-repeat: no-repeat, no-repeat, no-repeat;
background-attachment: scroll, scroll, scroll;
background-position: 10px 20px, 10px 20px, 10px 20px;
background-size: 50px 60px, 70px 90px, 110px 130px;
background-origin: content-box, content-box, content-box;
background-clip: padding-box, padding-box, padding-box;
background-color: #aaa;一个有意思的现象,即使你看到相对定位的父元素顶部已经占满了页面,但是子元素用绝对定位设置 top 时,有时会不在我们预想的位置上,特别如果父元素设置了overflow:hidden。分析原因可能是 相对定位的父元素其超出浏览器可视窗口的部分虽然不可见,但是子元素仍然是以超出部分的边界来定位的,高度/宽度增加或减少后都会影响父元素四边界的位置,进而导致与我们预想的绝对定位的位置不匹配,此时就要考虑父元素的宽高问题了。
.father {
position: relative;
width: 1000px; // 如果父元素的宽高不同,则子元素的定位位置可能也不同
height: 1000px;
overflow: hideen;
}
.child {
position: absolute;
top: 0;
right: 0
}在Nuxt2/Nuxt3中可以直接通过 vue 的 watch 方法监听路由的 query 变化:
<script setup>
const route = useRoute()
const { data, refresh } = await useFetch('/api/user')
watch(() => route.query, () => refresh())
</script>也可以简写成:
<script setup>
watch(() => useRoute().query, (newRoute, oldRoute) => {
console.log(newRoute);
console.log(oldRoute);
})
</script>但是!官网说Nuxt3中不支持这个方法,但是实际还是可以的,不知道为什么。
翻译:Nuxt 3 不支持此功能。作为代替,您可以直接使用 watcher 来触发重新获取数据。
在css中通过类给元素添加动画,如果动画播放结束了,可以通过删除这个动画类名,然后重新给予类名的方式来刷新动画,在vue中可以通过三元运算符或者 if 判断来为元素删除并重新添加类名,这样动画就会重新播放了。
<template>
<img :src="item.bannerimg" alt="miHoYo_imitate" class="mibanner-game"
:class="index == activeIndex ? 'animate' : ''" :style="style">
</template>
<script setup>
// wathc监听路由的变化,在离开当前页面后轮播图不再播放,当回到该页面后重新播放并重新加载动画类,让动画也重新播放
watch(() => useRoute().query, newRoute => {
if (newRoute.page != 'product') {
activeIndex.value = -1 // activeIndex的值控制动画是否加载,只需要变化这个值即可重新加载动画。
pause(true)
} else {
activeIndex.value = swiper.activeIndex // activeIndex的值控制动画是否加载,只需要变化这个值即可重新加载动画。
pause(false)
}
})
</script>话说这个之前好像做过吧:
b {
width: 0;
height: 0;
margin-left: .14rem;
border: .1rem solid transparent;
border-left: .16rem solid #3d424d;
transition: all 500ms;
position: relative;
z-index: 1;
}因为CSS中边框的交界处就是用三角形衔接的,所以只要元素的宽高都为0,就是一等腰三角形啦。
如果我们希望子元素在某一时刻按照一定规则过渡动画,那么可以通过先设定一个预设类名,在该类名下设定子元素的最终样式,然后再给子元素单独设定一个起始样式和过渡规则(transition),之后只需要给父类添加这个预设的类名,这样子元素自然会获得该类下的样式,进而改变当前样式,并且由于设置了过渡,那么子元素就会动起来了,其实有点像手动实现css动画的效果,其实用animation实现也是可以的。
我们预先设定一个 .animated 类,在该类下 .foo .bar 有着最终的样式:
.animated .foo {
width: 1000px;
height: 1000px;
}
.animated .bar {
color: red;
}并且还要给他们设定原始的样式和过渡规则:
.foo {
width: 500px;
height: 500px;
transition: all 300ms
}
.bar {
color: green;
transition: color 300ms;
}他们有着共同的父元素 .father
<div class="father">
<div class="foo"></div>
<div class="bar"></div>
</div>此时由于 animated 类不存在,所以子元素不会产生变化,所以我们只需要给父元素动态添加 animated 类,即可影响所有的子元素,使他们进行过渡动画。
<div class="father" :class="show==true?:'animated':''">
<div class="foo"></div>
<div class="bar"></div>
</div>这里我们让 show 这个变量管理是否添加animated类,只要将show的值变为true,那么所有的子元素就会动起来了。并且我们还可以给子类设置不同的过渡延迟,这样会更有层次感:
.foo {
width: 500px;
height: 500px;
transition: all 300ms;
transition-delay: 300ms;
}
.bar {
color: green;
transition: color 300ms;
transition-delay: 500ms;
}此时添加 animated 类后 .foo 块先动, .bar 后动。
swiper中会给当前正在活动的块自动添加一个 .swiper-slide-active 类,但如果我们像18中那样给 slide 动态添加类名,则有可能导致 swiper-slide-active 的自动添加被卡住,也就是说无法正常添加这个类,进而导致整个页面上一些部分无法启效果,比如 hover 等。
初步分析应该,添加 animated类之后,页面上的元素正在播放,此时css不会继续添加类,而是会等到动画结束后才添加,但是由于动画结束后刚好到了siwper自动滑动的时间,就导致滑动到下一个块后,仍是先播放动画再添加 swiper-slide-active 类,恶性循环,导致类名一直无法添加。
<div class="swiper-slide" v-for="(item, index) in datasets" :class="index == activeIndex ? 'animated' : ''">
<div class="minbanner">
可以看到 本应该处于 .swiper-slide-active 的块此时并没有这个类,只有animated。
此时需要手动点击导航,才能让他重新获取这个类。
说明 .swiper-slide-active 类的添加,被 .animated 类卡住了。
1.不要动态给swiper-slide添加类名,给其子块,比如随便一个div添加animated。
2.将animate的动画时间缩短,缩短到比轮播图自动播放的时间间隔更短,或许能解决问题。
<div class="swiper-slide" v-for="(item, index) in datasets">
<div class="minbanner" :class="index == activeIndex ? 'animated' : ''">
<!--任意内容-->
</div>
</div>
这样 swiper-slide 就能正确获取 swiper-slide-active 类,并且子元素也能通过animate带来做一些动画过渡效果了。

前提:容器高度为0.9rem。三个子元素高度都为0.3rem。测试时,发现设置其中一个子元素的背景色后,在继续用transfrom:translateY进行Y轴移动时子元素发生塌陷。
可以看到。左侧的子元素高度变窄了。
在完善代码后,在点击后取消translate操作后,高度恢复正常,如图一所示。
初步判断可能的原因是块级元素span的行高和高度在active状态下与原高度.3rem之间还有一些差距,所以导致塌陷问题。
这个问题困扰我很久了,每次都只能重启项目才会恢复。
原因:在vue 的mounted生命周期中,页面上的DOM元素可能没有完全渲染完毕,所以同步任务document.querySelector()可能获取不到DOM元素。
注意:该现象也会出现在created中,解决方法相同。
注意:以下方法不要直接写在scirpt setup下,因为底下的东西最后都会被编译小时,无法获取dom元素,最好写在onMounted中。
解决办法:
- 使用setTImeout,setTimeout为宏任务,会在vue的renderer执行完毕后再执行,此时一定能获得dom元素
- 使用vue提供的 nextTick() 方法,该方法传入一个回调函数,该函数会被添加到下一次事件循环中。当当前任务队列全部执行完毕后执行该回调函数,此时上一队列任务全部完成即DOM元素也一定渲染完成。所以也一定能获取dom元素。(在vue2中使用this.$nextTick(),在vue3中script setup可以直接使用 nextTick() )
以下,只以Vue3作为例子:
<script setup>
onMounted(){
setTimeout(()=>{
document.getElementById("demo")
},1)
}
</script><script setup>
onMounted(){
nextTick(()=>{
document.getElementById("demo")
})
}
</script>































