IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> 【记账本项目】前端(React Hook)(含代码) -> 正文阅读

[JavaScript知识库]【记账本项目】前端(React Hook)(含代码)

第一章 项目框架搭建
yarn create @vitejs/app react-vite-h5 --template react
yarn
yarn run dev

为了在项目过程中不出现千奇百怪问题,这里贴出项目的包版本。

{
  "name": "react-vite-h5",
  "private": true,
  "version": "0.0.0",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview"
  },
  "dependencies": {
    "axios": "^0.26.1",
    "classnames": "^2.3.1",
    "dayjs": "^1.11.0",
    "global": "^4.4.0",
    "lib-flexible": "0.3.2",
    "pm2": "^5.2.0",
    "postcss-pxtorem": "6.0.0",
    "pushstate-server": "^3.1.0",
    "query-string": "^7.1.1",
    "rc-form": "^2.4.12",
    "react": "^17.0.2",
    "react-captcha-code": "^1.0.7",
    "react-dom": "^17.0.2",
    "react-router-dom": "5.2.0",
    "zarm": "^2.9.13"
  },
  "devDependencies": {
    "@vitejs/plugin-react": "^1.0.7",
    "@vitejs/plugin-react-refresh": "^1.3.6",
    "less": "4.1.1",
    "vite": "^2.8.0",
    "vite-plugin-style-import": "0.10.1"
  }
}

  1. 引入路由插件react-router-dom
yarn add react-router-dom@5 -S
cd src
mkdir container && cd container
mkdir Index About
cd Index && touch index.jsx
import React from "react";

const Index = () => {
    return <div>
        <h1>Index</h1>
    </div>
}

export default Index;
cd About && touch index.jsx
import React from "react";

const About = () => {
    return <div>
        <h1>About</h1>
    </div>
}

export default About;
cd src && mkdir router && touch index.jsx
import Index from '../container/Index'
import About from '../container/About'

const routes = [
  {
    path: "/",
    component: Index
  },{
    path: "/about",
    component: About
  }
];

export default routes;
vim App.jsx
import React, { useState } from 'react'
import {
  BrowserRouter as Router,
  Switch,
  Route
} from "react-router-dom"
import routes from '../src/router'
function App() {
  return <Router>
    <Switch>
      {
        routes.map(route => <Route exact key={route.path} path={route.path}>
          <route.component />
        </Route>)
      }
    </Switch>
  </Router>
}

export default App

启动项目 npm run dev

  1. 引入Zarm UI组件库
yarn add zarm -S
vim App.jsx
  import React, { useState } from 'react'
  import {
    BrowserRouter as Router,
    Switch,
    Route
  } from "react-router-dom"

+	import { ConfigProvider } from 'zarm'
+ import zhCN from 'zarm/lib/config-provider/locale/zh_CN'
+ import 'zarm/dist/zarm.css'

  import routes from '../src/router'
  function App() {
    return <Router>
+     <ConfigProvider locale={zhCN} primaryColor={'#007fff'}>
      <Switch>
        {
          routes.map(route => <Route exact key={route.path} path={route.path}>
            <route.component />
          </Route>)
        }
      </Switch>
+     </ConfigProvider>
    </Router>
  }

  export default App

此时 zarm 的样式,已经全局引入了,我们先查看在 /container/Index/index.jsx 添加一个按钮是否生效:

image-20220424091738839

  1. 使用less
yarn add less -D
vim vite.config.js
{
  plugins: [...]
  css: {
    modules: {
      localsConvention: 'dashesOnly'
    },
    preprocessorOptions: {
      less: {
        // 支持内联 JavaScript
        javascriptEnabled: true,
      }
    }
  },
}
  1. 移动端项目适配rem
yarn add lib-flexible@0.3.2 -S
vim main.jsx
  import React from 'react'
  import ReactDOM from 'react-dom'
+ import 'lib-flexible/flexible'
  import './index.css'
  import App from './App'

  ReactDOM.render(
    <React.StrictMode>
      <App />
    </React.StrictMode>,
    document.getElementById('root')
  )
yarn add postcss-pxtorem@6.0.0
cd / && vim postcss.config.js
// postcss.config.js
// 用 vite 创建项目,配置 postcss 需要使用 post.config.js,之前使用的 .postcssrc.js 已经被抛弃
// 具体配置可以去 postcss-pxtorem 仓库看看文档
module.exports = {
  "plugins": [
    require("postcss-pxtorem")({
      rootValue: 37.5,
      propList: ['*'],
      selectorBlackList: ['.norem'] // 过滤掉.norem-开头的class,不进行rem转换
    })
  ]
}
  1. 按需引入css
yarn add vite-plugin-style-import@0.10.1 -D
vim vite.config.js
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import styleImport from 'vite-plugin-style-import'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react(),styleImport({
    libs:[{
      libraryName:'zarm',
      esModule:true,
      resolveStyle:(name)=>{
        return `zarm/es/${name}/style/css`;
      }
    }]
  })],
  css: {
    modules: {
      localsConvention: 'dashesOnly'
    },
    preprocessorOptions: {
      less: {
        // 支持内联 JavaScript
        javascriptEnabled: true,
      }
    }
  }
})
yarn run build

image-20220423125727116

  1. 二次封装axios
后端接口:http://dualseason.com:7001
yarn add axios -S
cd src && mkdir utils && vim axios.js 
import axios from 'axios'
import { Toast } from 'zarm'

const MODE = import.meta.env.MODE // 环境变量

axios.defaults.baseURL = 'http://dualseason.com:7001'
axios.defaults.withCredentials = true
axios.defaults.headers['X-Requested-With'] = 'XMLHttpRequest'
axios.defaults.headers['Authorization'] = `${localStorage.getItem('token') || null}`
axios.defaults.headers.post['Content-Type'] = 'application/json'

axios.interceptors.response.use(res => {
  if (typeof res.data !== 'object') {
    Toast.show('服务端异常!')
    return Promise.reject(res)
  }
  if (res.data.code != 200) {
    if (res.data.msg) Toast.show(res.data.msg)
    if (res.data.code == 401) {
      window.location.href = '/login'
    }
    return Promise.reject(res.data)
  }
  return res.data
})

export default axios;

代码解释:

const MODE = import.meta.env.MODE

MODE 是一个环境变量,通过 Vite 构建的项目中,环境变量在项目中,可以通过 import.meta.env.MODE 获取,环境变量的作用就是判断当前代码运行在开发环境还是生产环境。

axios.defaults.baseURL = 'http://dualseason.com:7001'

baseURLaxios 的配置项,它的作用就是设置请求的基础路径,后续我们会在项目实战中有所体现。配置基础路径的好处就是,当请求地址修改的时候,可以在此统一配置。

axios.defaults.headers['X-Requested-With'] = 'XMLHttpRequest'
axios.defaults.headers['Authorization'] = `${localStorage.getItem('token') || null}`
axios.defaults.headers.post['Content-Type'] = 'application/json'

上述三个配置是用于请求头的设置,Authorization 是我们在服务端鉴权的时候用到的,我们在前端设置好 token,服务端通过获取请求头中的 token 去验证每一次请求是否合法。最后一行是配置 post 请求是,使用的请求体,这里默认设置成 application/json 的形式。

axios.interceptors.response.use(res => {
  if (typeof res.data !== 'object') {
    Toast.show('服务端异常!')
    return Promise.reject(res)
  }
  if (res.data.code != 200) {
    if (res.data.msg) Toast.show(res.data.msg)
    if (res.data.code == 401) {
      window.location.href = '/login'
    }
    return Promise.reject(res.data)
  }

  return res.data
})

interceptors 为拦截器,拦截器的作用是帮你拦截每一次请求,你可以在回调函数中做一些“手脚”,再将数据 return 回去。上述代码就是拦截了响应内容,统一判断请求内容,如果非 200,则提示错误信息,401 的话,就是没有登录的用户,默认跳到 /login 页面。如果是正常的响应,则 retrun res.data

最后我们将这个 axios 抛出,供页面组件请求使用。

cd src && mkdir utils && vim index.js
import axios from './axios'

export const get = axios.get
export const post = axios.post
  1. 代理配置(项目不需要代理,后端已处理跨域)
vim vite.config.js
server: {
  proxy: {
    '/api': {
      // 当遇到 /api 路径时,将其转换成 target 的值
      target: 'http://api.chennick.wang/api/',
      changeOrigin: true,
      rewrite: path => path.replace(/^\/api/, '') // 将 /api 重写为空
    }
  }
}
  1. resolve.alias别名设置
vim vite.config.js
  import { defineConfig } from 'vite'
  import react from '@vitejs/plugin-react'
  import styleImport from 'vite-plugin-style-import'
+ import path from 'path'

  // https://vitejs.dev/config/
  export default defineConfig({
    plugins: [react(),styleImport({
      libs:[{
        libraryName:'zarm',
        esModule:true,
        resolveStyle:(name)=>{
          return `zarm/es/${name}/style/css`;
        }
      }]
    })],
    css: {
      modules: {
        localsConvention: 'dashesOnly'
      },
      preprocessorOptions: {
        less: {
          // 支持内联 JavaScript
          javascriptEnabled: true,
        }
      }
    },
+   resolve: {
+     alias: {
+       '@': path.resolve(__dirname, 'src'), // src 路径
+       'utils': path.resolve(__dirname, 'src/utils') // src 路径
+     }
+   }
  })

此时我们便可以修改之前的代码如下:

vim router/index.jsx
import Index from '@/container/Index'
import About from '@/container/About'
vim App.jsx
import routes from '@/router'

行文至此,我们的基础开发环境已经搭建完毕,涉及构建工具、前端框架、UI 组件库、HTTP 请求库、CSS 预加载器、跨域代理、移动端分辨率适配,这些知识都是一个合格的前端工程师应该具备的,所以请大家加油,将他们都通通拿下,下载代码

第二章 底部导航栏实现
  1. 预期效果

image-20220423212900887

cd src && mkdir components && cd components && mkdir NavBar && cd NavBar && touch index.jsx style.module.less

zarm文档这里要做底部导航栏最适合的就是TabBar(众安科技移动端组件库例子

image-20220423223327330

我们从架构的层面来看,这三个按钮分别对应跳转三个页面,分别是homedata以及user,所以我们应该先创建三个路由,不过创建路由前需要创建对应的组件,用于跳转。

cd src/container && mkdir Home && cd Home && touch index.jsx style.module.less
vim src/container/Data/index.jsx
import React from "react";

const Data = () => {
    return <div>
        <h1>Data</h1>
    </div>
}

export default Data;
cd src/container && mkdir Data && cd Data && touch index.jsx style.module.less
vim src/container/Home/index.jsx
import React from "react";

const Home = () => {
    return <div>
        <h1>Home</h1>
    </div>
}

export default Home;
cd src/container && mkdir User && cd User && touch index.jsx style.module.less
vim src/container/User/index.jsx
import React from "react";

const User = () => {
    return <div>
        <h1>User</h1>
    </div>
}

export default User;
vim src/router/index.js
- import Index from "@/container/Index";
  import About from "@/container/About";
  import Home from "@/container/Home";
  import Data from "@/container/Data";
  import User from "@/container/User";
  const router = [
-     {
-         path: '/',
-         component: Index,
-     }, {
          path: '/about',
          component: About,
      }, {
          path: '/user',
          component: User,
      }, {
          path: '/data',
          component: Data,
      }, {
          path: '/',
          component: Home,
      }
  ];

  export default router;

image-20220424092324993

确保路由能够正常跳转后再进行其他操作,接下来我们对NavBar组件进行编写,编写前我们查看文档,先确保能够显示静态再做动态。

image-20220424093245260

vim NarBar/index.jsx
import React from "react";
import { TabBar } from "zarm";

const NavBar = () => {
    return <TabBar>
        <TabBar.Item itemKey={'/'} title={'账单'}/>
        <TabBar.Item itemKey={'/data'} title={'统计'}/>
        <TabBar.Item itemKey={'/user'} title={'我的'}/>
    </TabBar>
}

export default NavBar;
vim App.jsx
  import React from "react";

  import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
  import router from "@/router";

  import { ConfigProvider } from 'zarm'
  import zhCN from 'zarm/lib/config-provider/locale/zh_CN'
  import 'zarm/dist/zarm.css'
+ import NavBar from "@/components/NavBar";

  const App = () => {
    return <Router>
      <ConfigProvider primaryColor={'#007fff'} locale={zhCN}>
        <Switch>
          {router.map(route => <Route exact key={route.path} path={route.path}><route.component /></Route>)}
        </Switch>
      </ConfigProvider>
+     <NavBar/>
    </Router>;
  }

  export default App;

image-20220424093918153

image-20220424094149182

到此为止我们已经可以使用组件进行代码编写了,可以写一个静态页面了,代码我已经放在了gitee上,跑不起来这些代码的可以去获取,直接下载下来运行即可。

接下来我们要实现点击NavBar进行路由跳转,只需要在上一步基础上加上如下代码,如果你不能出现跳转可能是环境没配置好,我们要明确目标,环境只是为了写代码的,我们不必在环境上花太多时间,所以我建议直接下载代码直接使用即可,接下来每一步我都会把对应代码放上来,出现问题先不要管,删掉自己的代码然后用我提供的代码进行替换即可,后面有基础后再自己搞。

vim NavBar/index.jsx
  import React from "react";
  import { TabBar } from "zarm";
  import { useHistory } from 'react-router-dom'
  const NavBar = () => {

+     const history = useHistory();

+     const changeTab = (path) => [
+         history.push(path)
+     ]

+     return <TabBar onChange={changeTab}>
          <TabBar.Item itemKey={'/'} title={'账单'} />
          <TabBar.Item itemKey={'/data'} title={'统计'} />
          <TabBar.Item itemKey={'/user'} title={'我的'} />
      </TabBar>
  }

  export default NavBar;

useHistory()react-router-dom提供的一个路由组件,可以使用push()方法进行路由插入history中。

接下来我们实现功能:当点击NavBar时,我们的蓝色active会标亮选中。

  import React, { useState } from "react";
  import { TabBar } from "zarm";
  import { useHistory } from 'react-router-dom'
  const NavBar = () => {

+     const [activeKey, setActiveKey] = useState("/");
      const history = useHistory();

      const changeTab = (path) => {
+         setActiveKey(path)
          history.push(path)
      }

      return <TabBar activeKey={activeKey} onChange={changeTab}>
          <TabBar.Item itemKey={'/'} title={'账单'} />
          <TabBar.Item itemKey={'/data'} title={'统计'} />
          <TabBar.Item itemKey={'/user'} title={'我的'} />
      </TabBar>
  }

  export default NavBar;
  1. 添加底部导航栏图标

image-20220424120331611

cd src/components && mkdir CustomIcon && touch index.jsx
import { Icon } from "zarm";
// 官网:https://www.iconfont.cn/home/index
export default Icon.createFromIconfont('//at.alicdn.com/t/font_2236655_w1mpqp7n1ni.js');
  import React, { useState } from "react";
  import { TabBar } from "zarm";
  import { useHistory } from 'react-router-dom'
+ import CustomIcon from "@/components/CustomIcon";
  const NavBar = () => {

      const [activeKey, setActiveKey] = useState("/");
      const history = useHistory();

      const changeTab = (path) => {
          setActiveKey(path)
          history.push(path)
      }

      return <TabBar activeKey={activeKey} onChange={changeTab}>
+         <TabBar.Item itemKey={'/'} title={'账单'} icon={<CustomIcon type="zhangdan"/>}/>
+         <TabBar.Item itemKey={'/data'} title={'统计'} icon={<CustomIcon type="tongji"/>}/>
+         <TabBar.Item itemKey={'/user'} title={'我的'} icon={<CustomIcon type="wode"/>}/>
      </TabBar>
  }

  export default NavBar;
  1. 底部导航栏的显示与隐藏

我们要做到动态隐藏显示NavBar可以使用文档里面提到的visible属性。

我们要实现的功能:当用户强行在地址栏输入我们不存在的路由时,我们不仅不显示页面的内容,还要把底部的NavBar给屏蔽掉。

image-20220424122210632

如图,我们就没有/detail路径,也就是说当用户来到这个路径时会产生bug,只显示底部导航栏但没内容,这是不行的,而且以后我们还要做登陆页面也是没有NavBar的,改!

vim App.jsx
import React, { useState, useEffect } from 'react'
import {
  BrowserRouter as Router,
  Switch,
  Route,
  useLocation
} from "react-router-dom"

import { ConfigProvider } from 'zarm'

import routes from '@/router'
import NavBar from '@/components/NavBar'
const App = () => {
  const location = useLocation() // 拿到 location 实例
  const { pathname } = location // 获取当前路径
  const needNav = ['/', '/data', '/user'] // 需要底部导航栏的路径
  const [showNav, setShowNav] = useState(false) // 是否展示 Nav
  
  useEffect(() => {
    setShowNav(needNav.includes(pathname))
  }, [pathname]) // [] 内的参数若是变化,便会执行上述回调函数

  return <Router>
    <ConfigProvider primaryColor={'#007fff'}>
      <Switch>
        {
          routes.map(route => <Route exact key={route.path} path={route.path}>
            <route.component />
          </Route>)
        }
      </Switch>
    </ConfigProvider>
    <NavBar showNav={showNav}/>
  </Router>
}

export default App

我们按照修改思路进行编写代码,结果发现报错了,那问题出在哪呢?

image-20220424145602619

我们来看报错信息Cannot read properties of undefined (reading 'location')

这是因为想要在函数组件内执行 useLocation,该组件必须被 Router 高阶组件包裹,我们做如下改动,将 App.jsxRouter 组件,前移到 main.jsx 内。

vim App.jsx
import React, { useState, useEffect } from 'react'
import {
  Switch,
  Route,
  useLocation
} from "react-router-dom"

import { ConfigProvider } from 'zarm'

import routes from '@/router'
import NavBar from '@/components/NavBar'
const App = () => {
  const location = useLocation() // 拿到 location 实例
  const { pathname } = location // 获取当前路径
  const needNav = ['/', '/data', '/user'] // 需要底部导航栏的路径
  const [showNav, setShowNav] = useState(false) // 是否展示 Nav
  
  useEffect(() => {
    setShowNav(needNav.includes(pathname))
  }, [pathname]) // [] 内的参数若是变化,便会执行上述回调函数

  return <>
    <ConfigProvider primaryColor={'#007fff'}>
      <Switch>
        {
          routes.map(route => <Route exact key={route.path} path={route.path}>
            <route.component />
          </Route>)
        }
      </Switch>
    </ConfigProvider>
    <NavBar showNav={showNav}/>
  </>
}

export default App
vim main.jsx
import React from 'react'
import ReactDOM from 'react-dom'
import 'lib-flexible/flexible'
import './index.css'
import App from './App'
import { BrowserRouter as Router } from 'react-router-dom'

ReactDOM.render(
  <React.StrictMode>
    <Router>
      <App />
    </Router>
  </React.StrictMode>,
  document.getElementById('root')
)
vim NavBar/index.jsx
import React, { useState } from "react";
import { TabBar } from "zarm";
import { useHistory } from 'react-router-dom'
import CustomIcon from "@/components/CustomIcon";
const NavBar = ({showNav}) => {

    const [activeKey, setActiveKey] = useState("/");
    const history = useHistory();

    const changeTab = (path) => {
        setActiveKey(path)
        history.push(path)
    }
        
    return <TabBar activeKey={activeKey} onChange={changeTab} visible={showNav}>
        <TabBar.Item itemKey={'/'} title={'账单'} icon={<CustomIcon type="zhangdan"/>}/>
        <TabBar.Item itemKey={'/data'} title={'统计'} icon={<CustomIcon type="tongji"/>}/>
        <TabBar.Item itemKey={'/user'} title={'我的'} icon={<CustomIcon type="wode"/>}/>
    </TabBar>
}

export default NavBar;

这里说明一下传入参数showNav为什么要加一个{},这是ES6语法中的结构,App.jsx<NavBar showNav={showNav}/>这里的showNav={}传给NavBar组件的其实是一个props,所以原本的写法是:

const NavBar = (props) = >{
  const {showNav} = props
	...
}

这里我们使用简写方式,能偷懒就不多写任何东西。

导航栏可以用在很多地方,映射到 PC 网页就是左侧侧边导航,道理都是相通的。移动端放在下面控制,PC 端放在左边或者右边控制罢了。所以再次强调不要学完了一个知识点,就思维定势地认为只能用在某一个需求上,能做到融会贯通,才是判断一个好程序员的标准,下载代码

第三章 登陆注册页面

image-20220424151919830

cd src/container && mkdir Login && cd Login && touch index.jsx style.module.less
vim Login/index.jsx
import React from 'react';

const Login = () => {
    return <div>
        <h1>Login</h1>
    </div>
}

export default Login;
vim router/index.js
  import Data from '@/container/Data'
  import Home from '@/container/Home'
  import User from '@/container/User'
+ import Login from '@/container/Login'

  const routes = [
    {
      path: "/",
      component: Home
    },
    {
      path: "/user",
      component: User
    },
    {
      path: "/data",
      component: Data
    },
+   {
+     path:'/login',
+     component: Login
+   }
  ];

  export default routes

image-20220424193758798

通过上面的学习我们可以得出一个开发顺序的思路:先路由后页面,先静态后动态。首先要确保这个页面可以正常跳转了,能够出来基础页面了,最后我们才去对页面进行丰富以及美化,否则做了一堆结果页面没出来,一切都是无效的。

vim Login/index.jsx
import React from 'react'
import { Button, Cell, Checkbox, Input } from 'zarm';
import CustomIcon from '@/components/CustomIcon';

const Login = () => {
    return <div>
        <div />
        <div>
            <span>注册</span>
        </div>
        <div>
            <Cell icon={<CustomIcon type={'zhanghao'} />}><Input clearable type='text' placeholder='账号'/></Cell>
            <Cell icon={<CustomIcon type={'mima'} />}><Input clearable type='text' placeholder='密码'/></Cell>
            <Cell icon={<CustomIcon type={'mima'} />}><Input clearable type='text' placeholder='验证码'/></Cell>
        </div>
        <div>
            <div>
                <Checkbox />
                <label>阅读并同意 <a>《掘掘记账本条款》</a> </label>
            </div>
            <Button block theme='primary'>注册</Button>
        </div>
    </div>
}

export default Login;

image-20220425083432085

上述代码中,关键部分是账号输入、密码输入、验证码输入,这三个输入框是需要获取数据作为接口的参数提交上去的。

很多时候,服务端没有开发好接口的时候,我们前端要做的任务就是先还原 UI 稿,把该切的页面都切出来,并且预留好需要给接口提交的数据交互,比如上述三个输入框。

接下来我们来让页面变好看:

vim ./style.module.less  							#以后样式部分的这行我就省略了
.index {
    min-height: 100vh;// 确保全屏
    background-image: linear-gradient(217deg, #6fb9f8, #3daaf85e, #49d3fc1a, #3fd3ff00);// 蓝白渐变色

    .head {
        height: 200px;// 撑大顶部图片位置
        background: url('//s.yezgea02.com/1616032174786/cryptocurrency.png') no-repeat center;// 放入图片
        background-size: 120%;// 放大图片
        border-bottom-left-radius: 12px;
        border-bottom-right-radius: 12px;

        img {
            width: 34px;
            margin: 15px 0 0 15px;
        }
    }

    .tab {
        color: #597fe7;// 让登陆注册变蓝
        padding: 30px 24px 10px 24px;// 和边框隔开

        >span {
            margin-right: 10px;// 登陆	注册	隔开
            font-size: 14px;
            font-weight: bold;

            &.active {// 选中后样式
                font-size: 20px;
                border-bottom: 2PX solid #597fe7;
                padding-bottom: 6px;
            }
        }
    }

    .form {
        padding: 0 6px;

        :global {
            .za-cell {// zarm组件的cell的类名就叫.za-cell,不信自己去浏览器看
                background-color: transparent;// 背景色采用底层背景,换句话说就是变透明

                &::after {
                    border-top: none;// 消除顶部的黑线
                }
            }
        }
    }

    .operation {
        padding: 10px 24px 0 24px;

        .agree {
            display: flex;
            align-items: center;
            margin-bottom: 10px;

            label {
                margin-left: 10px;
                font-size: 14px;
            }
        }
    }
}

由于我们采用的是 CSS Module 的形式进行开发,也就是你在页面中声明的类名都会根据当前页面,打一个唯一的 hash 值,比如我们页面中声明的 className={s.form},最终在浏览器中显示的是这样的:

image-20220425091641354

_form_1h5us_30是已经被编译过的样式,这样做的目的是避免和别的页面的样式重名,这是目前样式管理的一个诟病,当多人参与项目开发的时候,很难做到不污染全局样式名称,除非很小心的命名样式名称。

所以经过编译之后,想要修改 .form 下的 .za-cell,如下写法,将无法修改成功:

.form {
  .za-cell {
    color: red;
  }
}

原因是,上述写法,.za-cell 会被编译加上 hash,组件库 Zarm 内的 dom 类名还是叫 za-cell,如上图所示。所以为了不加 hash,就需要这样操作:

.form {
  :global {
    .za-cell {
      color: red;
    }
  }
}

这样 .za-cell 就不会被加上 hash,如上图的.za-cell所示。

接下来我们添加验证码:

yarn add react-captcha-code -S

由于代码太长加的又少,就贴图了。

image-20220425093230724

image-20220425093515816

我们给页面加上相应的逻辑,首先是账号、密码、验证码:

vim Login/index.jsx
import React, { useState, useCallback } from 'react'
import { Button, Cell, Checkbox, Input } from 'zarm';
import CustomIcon from '@/components/CustomIcon';
import Captcha from "react-captcha-code"
import s from './style.module.less'

const Login = () => {

    const [userName, setUserName] = useState('');
    const [passWord, setPassWord] = useState('');
    const [verify, setVerify] = useState('');
    const [captcha, setCaptcha] = useState(''); 

    const handleChange = useCallback((captcha) => {
        console.log(captcha);//打印出验证码内容,用于判断输入验证码是否正确
        setCaptcha(captcha);
    },[]);

    return <div className={s.index}>
        <div className={s.head} />
        <div className={s.tab}>
            <span>登陆</span>
            <span>注册</span>
        </div>
        <div className={s.form}>
            <Cell icon={<CustomIcon type={'zhanghao'} />}><Input clearable type='text' placeholder='账号' onChange={value => setUserName(value)} /></Cell>
            <Cell icon={<CustomIcon type={'mima'} />}><Input clearable type='text' placeholder='密码' onChange={value => setPassWord(value)} /></Cell>
            <Cell icon={<CustomIcon type={'mima'} />}><Input clearable type='text' placeholder='验证码' onChange={value => setVerify(value)} /><Captcha charNum={4} onChange={handleChange}/></Cell>
        </div>
        <div className={s.operation}>
            <div className={s.agree}>
                <Checkbox />
                <label>阅读并同意 <a>《掘掘记账本条款》</a> </label>
            </div>
            <Button block theme='primary'>注册</Button>
        </div>
    </div>
}

export default Login;

image-20220425143932764

到此为止,我们可以获取的参数有用户输入的账号、密码、验证码以及验证码图片上的验证码。

接下来我们该写一下注册方法了,前端只需要把参数传给后端即可注册:

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2022-04-26 11:33:45  更:2022-04-26 11:35:22 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 2:18:14-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码