0%

今天晚上和高中同学约好了晚上要去一家牛肉馆吃牛肉火锅,由于路途遥远,4点钟就出发了。

坐的公交路线仿佛让我感觉回到了乡下,路比较窄,四周没啥人,晚上走的话估计我会被吓到,公交车都不敢下了。不过惊喜的是,居然经过了一篇稻田,想想我是多久没看到那样的绿色了,心里的恐惧好像就淡点了。想想自己本来就不喜欢宅的人,明明以前很喜欢出去玩的,现在好像不知不觉变得宅了,可能是身边逐渐没了和自己一起出去玩的人。

我想我不应该忘记高中时期和小伙伴们一起发现的那个小天地,第一次看到那地方,那种激动,那种雀跃,那种不可描述的喜悦,我想都不应该忘记,虽然记忆中比较遥远了,但是那是最开始的初心。那种美好,不应该被这高楼大厦和灯红酒绿所遮掩,被岁月所冲蚀。

这是一张古老的照片哈哈哈,当然这只是一小部分,我只知道,但是看到那的时候,我想到了课本里面的小石谭记忆,我只知道如果有幸能够再见到这地方能让我很开心,让心里平静下来,可惜这是不可能的事情了。

刚要吃完饭的时候,同学说她听别人说青山有个地方,买酸汤粉很好吃,也要卖凉虾的,吃完饭要不要过去买,我脑壳抽,就说去,哈哈哈,这才符合我之前的风格嘛,跟我表妹学的哈哈。之前就觉得她是一个很洒脱的人,说去哪就去哪,好像从来都不会觉得累,也不觉得没意思,相反兴致勃勃,十分有探险精神,之前还和她大晚上去抓蝗虫。可惜她嫁人了,以后这种日子想必也是少有了。

以前我们总计划长大了要一起去旅游,想想都觉得很美好,现在因为各种各样的原因,想象中的计划也没有实现。我之前一直计划着今年要去深圳和堂姐去看一次海,去看一次海上的日落,到山顶上看一次日出,跟杭州的大学同学约着一起去南京找另一个大学同学,想了好久好久,但是最后还是没有去。我想我堂妹之前说的话是对的,想要做什么就应该想到了就去做,别总想着以后。

最近好像心里变得有点烦躁,我自己也不知道为什么,好像专心不下来去认真做一件事情,我不知道自己在想什么,亦或是再等什么。我有时候会去翻阅一些技术文章,想着收藏起来,以后会去看,想着看一本书,一天看多少,多少天要看完。我想这种东西就不应该去计划,想到了就得去做,别把它放在以后。想得多,可能也是变得浮躁得一个理由吧,当自己专注于做某一件事情时,就不应该去想另外的事情了,当下才是最重要的。

好了,差不多就这样吧,看点书准备睡觉嘿嘿。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<template>
<div :data-text="text">123</div>
<div :style="{ backgroundColor: bgColor }">123</div>
<div>123</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
const text = ref('hello')
const bgColor = ref('red')
</script>
<style scoped lang="scss">
/* 这个例子是用来学习如何绑定css变量的,
在css里面是用var(变量名字)
在vue中使用css变量可用attr(变量名)
*/
div {
color: #fff;
}
div:first-of-type:before {
content: attr(data-text);
font-size: 16px;
color: lightpink;
}
</style>

昨天写到一半,婶婶就打电话过来让我去东站接她,急急忙忙关了电脑今天才发现我之前写的都没保存,啊啊啊啊啊!!!我不想重写了,就这样吧,粗略总结一下。

父子之间传值我就不想写了,比较不熟悉的是父孙之间的传值。

父传孙

在父组件文件中,有子组件,在这个子组件中传入一个属性,然后在子组件文件中,用一个孙组件,在这个孙组件中用v-bind= “$attr”直接实现透传,最后在孙组件文件中,通过props的形式去获取。

孙传父

整体思路和子传夫比较像,都是通过触发点击事件传值的。首先,孙组件触发父组件的函数将想要传递过去的值传过去。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//孙组件文件GrandSon.vue
<template>
<div>
<h1>我是孙组件</h1>
<button @click="sendToFather">点我传值</button>
</div>
</template>

<script setup lang="ts">
import { ref } from 'vue'

const hello = ref('okok')
const emit = defineEmits(['sendToFather'])
const sendToFather = () => {
emit('sendToFather', hello.value)
}
</script>

<style scoped lang="scss">
div {
margin: 10px 0;
}
</style>

作为中间组件的子组件,用v-bind=”$attrs” 做中间管道,在vue2中孙传父是用v-on=“$listeners”作为中间管道,但是在vue3中废除了这个语法,改成不管是孙传父,还是父传孙,子组件都用v-bind=”$attrs” 做透传。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//子组件 Son.vue
<template>
<div>
<h1>我是子组件</h1>
<GrandSon v-bind="$attrs" />
</div>
</template>

<script setup lang="ts">
import GrandSon from './GrandSon.vue'
</script>

<style scoped lang="scss">
div {
margin: 10px 0;
}
</style>

父组件用函数属性接受

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<template>
<div>
<h1>我是父组件</h1>
<Son @sendToFather="sendToFather" />
<div>子组件传过来的值{{ hello }}</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import Son from './Son.vue'
const hello = ref('')
const sendToFather = (val: string) => {
hello.value = val
console.log(val, '子组件传过来的值')
}
</script>
<style scoped></style>

一天有一天的事情,想想自己也是很久没写博客了,越来越懒了,周五就很想躺着追剧,想想还是克制了,得把我这篇写写。

最近这个taro整得我有点难过,已经各执一段时间的东西又被捡起来了,要搞啥适老化,调样式调得有点烦,昨天晚上用自己的电脑安装了下taro,想运行下项目,结果出现了一堆问题,果然人生就是不断地制造问题然后不断地解决问题的,解决了挺有成就感的,解决不了就摆烂,到了某个时间段答案总会浮出水面。

安装Taro

1
2
3
4
5
6
7
8
# 使用 npm 安装 CLI
$ npm install -g @tarojs/cli

# OR 使用 yarn 安装 CLI
$ yarn global add @tarojs/cli

# OR 安装了 cnpm,使用 cnpm 安装 CLI
$ cnpm install -g @tarojs/cli

我在安装的时候提示已经安装成功了,但是用yarn dev:weapp编译运行的时候出现了错误,后来搜索了一些解决方案,反正就是不断地尝试,因为你并不知道答案对不对,有时候它没用,有时候它就莫名奇妙地对了,结果都是摸索出来的。 网上有人说yarn全局安装位置全局bin位置不同导致错误,我查看了自己的yarn全局安装位置和全局bin位置,果然不同,具体看这篇npm/yarn修改全局安装路径和缓存路径

修改之后将yarn全局bin路径添加到电脑的环境变量。

在执行安装taro指令的时候尽量不要在项目目录里面执行,不然很容易全局安装不了,我也不知道是不是有这个原因,当时我是设置好环境变量,然后再C盘根路径下打开终端执行安装命令,它就安装成功了。

好了,安装好Taro,编译项目,提示Taro已经运行起来了,版本号是。。。,记不清了,然后又开始报错,说找不到啥啥的,昨天晚上没截图,不好意思。。。倒腾了一会,最后想到了可能是taro版本的问题,因为观察到项目里用的taro版本和我安装的不一样,我安装的版本要高些,然后重新安装项目对应版本的,重新编译运行,项目跑起来啦!!

打开微信开发者工具,哦豁,提示app.json文件找不到!!!,救命,为什么的在公司打开这个项目的时候没遇到这么多问题,此时已是晚上11点多,我在想我还有时间改点东西吗,我真的是会,好吧,开始解决。其实之前没接触过taro ,该咋样咋样把。最后的答案,在project.private.config.json里面加入以下代码

1
"miniprogramRppt":"./路径" 双引号的内容是你项目文件app.json所在的路径,路径里面不要加入app.json,指明到它的上一级路径即可

ok ,终于出来了,然后今天改完了样式,准备全局注入,结果无法像pc端那样全局加入类,我真的是。。。。郁闷了一个下午,网上的回答也是少得可怜,基本没啥答案,幸运的是今天h5正常在浏览器打开了,最重要得事情就是想想怎么像在浏览器那样获取DOM添加类名,这个周末注定是要和taro过了,我的css视频还没刷完呢。。。。不过研究下taro也不错,希望不会让人觉得太烦躁。

最后,附上无意中听到的一首周杰伦的《手写的从前》里的一段rap歌词,真的爱了,希望能唤醒我已多年未陶冶的情操。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
微风需要竹林 溪流需要蜻蜓
乡愁般的离开 需要片片浮萍
记得那年的雨季 回忆里特安静
哭过后的决定 是否还能进行
我傻傻等待 傻傻等春暖花开
等终等于等明等白 等爱情回来
青春属于表白 阳光属于窗台
而我想我属于一个拥有你的未来
纸上的彩虹 用素描画的钟
我还在修改 回忆之中你的笑容
该怎么去形容 为思念酝酿的痛
夜空霓虹 都是我不要的繁荣
或许去趟沙滩 或许去看夕阳
或许任何一个可以想心事的地方
情绪在咖啡馆 被调成一篇文章
彻底爱上你如诗一般透明的泪光

Taro的页面通信机制

h5和小程序的页面通信区别

Taro在写h5和小程序时还是存在一定的差异的,最近踩了好多坑

首先Taro提拱了判断小程序和h5环境的api

1
2
3
4
5
6
7
8
9
10
Taro.getEnv() 返回值可有以下几种

"WEAPP"
"WEB"
"RN"
"SWAN"
"ALIPAY"
"TT"
"JD"

小程序页面通信

小程序如果从当前页面跳入下一个页面,进行一系列操作后返回当前页面并刷新当前页面,可以不采用页面通信的方法,因为小程序每次回到页面的时候都会重新刷新页面数据的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//发起事件的页面
const pages = Taro.getCurrentPages();
const current = pages[pages.length - 1];
const eventChannel = current.getOpenerEventChannel();
eventChannel.emit("modifyEvent");

//监听事件的页面在跳转到发起事件页面之后将事件注册一下
Taro.navigateTo({
url: '发起事件的页面',
events: {
modifyEvent: () => {
//执行一些操作
},
},
});
Taro.navigateBack();

h5页面通信

h5跳转到下一个页面进行一系列的操作后跳回之前的页面并刷新跳回来的页面,它是不会自动刷新的,所以只能用这种页面通信的方法。

1
2
3
4
5
发起事件的页面
Taro.eventCenter.trigger('modifyEvent') //modifyEvent为事件名称
监听事件的页面接听之后执行相关的操作
Taro.eventCenter.on('modifyEvent', (arg) => { 一些操作 })
Taro.navigateBack();

在写这篇文章之前记录下自己的刚发现的图床工具PicGO,在附上github地址用vue和electron开发的桌面应用,之前我一直很苦恼写markdown的时候上传图片很麻烦,现在有了这个工具,一切都变得很easy。

不过这个工具直接下载安装安装不了,我是用scoop安装的,今天还看到了一个界面十分优美得类似markdown的工具,但是奈何电脑直接安装是装不了的,也没有scoop的安装方式,真的好可惜,哪天再去试试。

目前工作中用到reduce的情况也还好,先备着

结合concat实现数组扁平化

1
2
3
4
5
6
7
8
const arr = [
[1,2],
[3,4],
[5,6]
].reduce((acc, cur) => {
return acc.concat(cur)
}, [])
console.log(arr)

多维数组扁平化

1
2
3
4
5
6
7
8
9
10
const arr = [
[1, 2,[2, 3, 4, 5]],
[3, 4, [2, 3,[2, 3, 1]]],
[5, 6]
]
const flatten = (arr) => {
return arr.reduce((pre, cur) =>
pre.concat(Array.isArray(cur)? flatten(cur) : cur),[])
}
console.log(flatten(arr))

数组分块

根据传入限制大小,对数组进行分块,小于限制长度就往里面添加,否则直接将其加入res

1
2
3
4
5
6
7
8
9
10
11
12
13
const chunk = (arr, size) => {
return arr.reduce(
(res, cur) => (
res[res.length - 1].length < size
? res[res.length - 1].push(cur)
: res.push([cur]),
res
),
[[]]
);
};
const arr4 = [1, 2, 3, 4, 5, 6];
console.log(chunk(arr4, 3)); // [ [ 1, 2, 3 ], [ 4, 5, 6 ] ]

字符串统计

1
2
3
4
5
6
7
8
9
const countChar = text => {
text = text.split("");
return text.reduce((record, c) => {
record[c] = (record[c] || 0) + 1;
return record;
}, {});
};
const text = "划水水摸鱼鱼";
console.log(countChar(text)); // { '划': 1, '水': 2, '摸': 1, '鱼': 2 }

vscode常用命令

1
2
3
code .用vscode打开当前文件
code -v 查看当前vscode的版本
未完待续~

volar在vue中不生效的问题

最近几天发现我的vscode在编写vue文件的时候没有代码提示了,我以为是我的vscode配置有问腿,一直在vscode配置里面找问题,想想应该是volar插件是不是有问题,但是我卸载了根本没啥反应,思索着我也没改什么地方,怎么可能突然就没提示了呢。

后面在volar插件里面无聊般光了一下,无意中发现了一个东西,volar插件有一个Runtime Status选项,在那可以看到这个插件是否是正常运行的,点开看后提示当前版本的vscode和volar的版本并不匹配,安装了最新版本的vscode,问题解决

好像没到一周呵呵,就上了三天班。。。不过这三天做得最多的事情就是新增需求,该需求,改bug。

什么时候markdowm能支持导入本地图片正常显示,真的每次写总结的时候想放图片不想还要自己找一个图床放上去,再从图床上面粘贴链接过来,真的很麻烦呀。

不想放图片,图片搞下来我都不想在写总结了,自己看懂就好啦。。。

1、第一个bug: (附上产品经理的描述,毕竟人家比我专业点)1. 一屏管重点——在区域图和点位图来回切换会导致风场叠加,取消风场的情况下,页面还显示风场,页面卡顿(bug)

这个地方我差不多看代码看了2个多三个多小时,其中一部分时间在沉迷在页面卡顿中无法自拔,一直在那调呀测呀,页面有时候还更新的不及时,我其实对公司的地图技术栈并不是很熟,但是不该能知道它写这个是什么意思,但是要一个文件一个文件地跳,很难得自己有这个耐心。最后理通了代码逻辑,注释掉了一行代码,解决了。

2、echart需求实现:现在暂时是不能对echart的配置了然于心,个性化的配置真的伤脑筋,费时间,很想把那天搜索的链接地址放进来,不过是在公司电脑上搜的,现在没啥心情重新去搜,上班了把它放进我的收藏夹里。

3、图片预览切换需求:图片点开后增加左右切换按钮,点击可在图片之间切换

这个地方可以附下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

<div class="pre btn" @click="showPre">
<span>&lt;</span>
</div>
<div class="content">
<el-carousel ref="carousel" arrow="never" :autoplay="false" height="90px" indicator-position="none">
<el-carousel-item v-for="(item, index) in list" :key="index">
<el-image
style="width:100%; height: auto"
:src="item"
:preview-src-list="[item]"
:key="index">
</el-image>

</el-carousel-item>
</el-carousel>
</div>
<div class="next btn" @click="showNext">
<span>&gt;</span>
</div>

原来的代码时长这个样子的,在走马灯里面放一个图片预览的组件,我去element ui看过Image组件的例子,发现原来的代码传参有点问题,导致图片切换按钮不能正常显示,后来我把:preview-src-list=”[item]”换成了:preview-src-list=”list”, :src=”item”换成了:src=”list[0]”,发现只能显示第一张图片,有去看了Carousel组件的例子,自己思考了一下,之前这个代码想要实现的功能是在图片未被点开之前,利用走马灯的切换效果,通过前后两个按钮切换走马灯里内容的变化,点开图片后不能显示切换按钮是因为传入的参数只有一张图片的链接,虽然我改变了参数,但是再用外面的按钮切换的时候,由于我传入的是第一张照片的链接,所以怎么切都显示第一张,后面把index动态传入 :src=”list[index]”,问题解决,感觉之前能想到走马灯和图片预览的同学也是可以哦,发现了新大陆

这几天改改改,写写写,我好像明白了一些事情,有些东西,有些事情,有的时候突然就有了答案,当时自己想不明白的,未知的,有一天你突然就知道了,明白了,或许有些莫名奇妙吧。人的一生不就是一直在找寻答案吗,迷茫,不解时不时充斥着生活,答案可能不是是与否,也不是为了求知,而是突然某一天或许是一件事,亦或是一个人,自己突然就明白了,所以不要为了位置的东西情绪低落,一切都会有答案的。

渐渐将我的blog变成了日记。。。罪过罪过

写在前面的话

虽然哈,很多人绝得html和css很简单,但是作为一个合格的前端开发工程师,还是要对美有一定的追求,个人觉得我的css并扎实,然我随手实现一个炫酷动画,不好意思,我不会。。。所以从今天开始,我要开始积累实现动画的思路啦,更重要的是,学习写代码的思路,怎么把代码写得既简单又好维护,这也是我目前比较想达到的。

我比较想纯CSS实现动画,不想去操作dom

后面会陆续更新一些自己在学习动画的例子和心得,希望自己可以不断地坚持。

这个例子中style=”–clr: #04fc43”是我之前没看到的一种写法,不过用起来真香,可以省去很多多余的代码,例子简单或者复杂并不重要,重要的是构建思维,这也是我比较喜欢在youtube上国up主写代码的原因,你总是能get到各种各样新颖的写法。

嗯嗯。。。。好久都没有更新博客了,之前一直在忙找工作的事情,最近新入职了新公司比较忙,博客就搁置了。

清明放假和小伙伴出去玩了,花了一天在赶需求和改代码,今天晚上终于有点时间来写了嘿嘿。

最近也算是把工作落实了下来了吧,回顾自己一年以来的前端自学历程,中途真的有过很多次想过放弃,遇到问题的时候就会让自己的自信心大受打击,过程是艰辛但同时也伴随着喜悦。

工作只是前端学习路程的小开端,我也明白自己目前还有很多地方需要提升,所以后面的路该怎么走才是最最最最重要的。

首先我并不提倡熬夜,自学前端的那一年真的是每天差不多熬到一点多两点,然后早上6点50左右就要起床,每天晚上都有在跑步。所以嘞,锻炼这件事情不能落下,从辞职到找工作这段时间宅着的时间太长了,体重又增加了,有的裤子都穿不上了,太伤心了,今年的目标是体重绝对不能超过95斤,这真的是底线了救命。

关于下班之后的时间和上下班坐公交的时间我也得好好规划下,今年的目标除了体重之外,怎么能够让自己在这一年之内快速成长起来也很值得自己去思考,我并不希望自己在前端这条路上只为满足工作需求,更多的是能够去到更多的领域,去看到更大的世界,开拓更多的领域,这是前端吸引我的一个地方,当然我更希望自己会成为一个优秀的创造者。

嗯嗯嗯。。。写到这差不多了,我得去规划下我之后工作时间之外的生活,心动不如行动嘛嘿嘿

使用createContext创建全局状态

1、创建

1
2
3
4
5
UserContext.js文件

import {createContext} from "react"
const UserContext = createContext(null)
export default UserContext

2 使用UseContex.Prviderc在父组件中传入到子组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

APP.jsx文件

import UserContext from './UserContext'
function App() {
let [user, setUser] = useState({
isLoggin: false,
currentUserId: null,
CurrentUserName: null,
CurrentUserRole: null
})
return (
<UserContext.Provider value={{user, setUser}}>
<HashRouter>
<NavBar/>
<div className="container-fluid">
<Switch>
<Route path="/" exact={true} component={Login}/>
<Route path="/*" exact={true} component={NoMatchPage}/>
</Switch>

</div>
</HashRouter>
</UserContext.Provider>
)
}

3、在子组件中用useContext接收

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
import React, { useState, useEffect, useContext } from 'react'
import UserContext from './UserContext'

const Login = (props) => {
const [email, setEmail] = useState('123@qq.com')
const [password, setPassword] = useState('123a3442')
let userContext = useContext(UserContext) ///////在这里传入

useEffect(() => {
document.title = '登录'
})

let [dirty, setDirty] = useState({
email: false,
password: false,
})

const [errors, setErrors] = useState({
email: [],
password: [],
})
const [message, setMessage] = useState('')

const validate = () => {
const errorsData = {}
errorsData.email = []
if (!email) {
errorsData.email.push('邮箱不能为空')
}
let emailReg = /[^@ \t\r\n]+@[^@ \t\r\n]+\.[^@ \t\r\n]+/
if (email) {
if (!emailReg.test(email)) {
errorsData.email.push('邮箱输入格式有误')
}
}

errorsData.password = []
if (!password) {
errorsData.password.push('密码不能为空')
}
let passwordReg = /^(?=.*?[a-z])(?=.*?[0-9]).{8,16}$/
if (password) {
if (!passwordReg.test(password)) {
errorsData.password.push('密码输入格式有误')
}
}
setErrors(errorsData)
}
useEffect(validate, [password, email, setErrors])

let onClickLogin = async () => {
let dirtyData = dirty
Object.keys(dirtyData).forEach((control) => {
dirtyData[control] = true
})
setDirty(dirtyData)
validate()
if (isValid()) {
let response = await fetch(
`http://localhost:5000/users?email=${email}&password=${password}`,
{ method: 'GET' }
)
if (response.ok) {
let body = await response.json()
if (body.length > 0) {
setMessage(<span className="text-success">登录成功</span>)
userContext.setUser({
...userContext.user,
isLoggin: true,
currentUserName: body[0].fullName,
currentUserId: body[0].id,
currentUserRole: body[0].role,
})
if (body[0].role === 'user') {
props.history.replace('/dashboard')
} else if(body[0].role==="admin") {
props.history.replace('./products')
}
} else {
setMessage(<span className="text-danger">无效登录,请重新尝试</span>)
}
}
}
}

let isValid = () => {
let valid = true
for (let control in errors) {
if (errors[control].length > 0) {
valid = false
}
}
return valid
}
return (
<div className="row">
<div className="col-lg-5 col-md-7 mx-auto">
<div className="card boder-success shadow-lg my-2">
<div className="card-header border-bottom border-success">
<h4 style={{ fontSize: '40px' }} className="text-success">
Login
</h4>
</div>

<div className="card-body boder-bottom boder-success">
<div className="form-group">
<label htmlFor="email">Email</label>
<input
value={email}
type="text"
className="form-control"
id="email"
placeholder="Email"
onChange={(e) => {
setEmail(e.target.value)
}}
onBlur={() => {
setDirty({ ...dirty, email: true })
validate()
}}
/>
<div className="text-danger">
{dirty.email && errors.email.length > 0 ? errors.email : ''}
</div>
</div>

<div className="form-group">
<label htmlFor="password">Password</label>
<input
type="password"
className="form-control"
id="password"
placeholder="Password"
value={password}
onChange={(e) => {
setPassword(e.target.value)
}}
onBlur={() => {
setDirty({ ...dirty, password: true })
validate()
}}
/>
<div className="text-danger">
{dirty.password && errors.password.length > 0
? errors.password
: ''}
</div>
</div>
<div className="card-footer text-right">
<div className="">{message}</div>
<div>
<button
className="btn btn-primary mycolor"
onClick={onClickLogin}
>
登录
</button>
</div>
</div>
</div>
</div>
</div>
</div>
)
}

export default Login

使用useReducer