• 从入门到入职(不是),React开发之——Tesla界面开发教程


    前言

    React是FaceBook于2013开源的项目,一经问世就在前端占据一席之地,随着近几年不断发展壮大,已成为前端主流框架。而它之所以能如此成功,主要是因为它的声明式组件化高效(虚拟DOM、Diff)便捷(React Native),下面通过项目实战详细地介绍组件的设计思路与流程,如发现各种问题,请看官巨佬们轻喷并指正~🙏

    准备阶段

    首先我们需要创建一个React项目,我使用的是 ViteJS 来初始化本项目,当然也可以使用官方脚手架 create-react-app 来初始化,前者相对来说更快,两者各有优缺点,大家可以根据喜好选择。在VSCode终端输入:

     npm init @vitejs/app 
    
    • 1

    接着给项目命名为 Tesla --> 连续两次选择 react --> 最后根据提示进入创建的项目文件夹 --> 安装项目依赖 --> 运行下我们的项目

    cd Teslanpm install
    npm run dev 
    
    • 1
    • 2

    紧接着我们会在3000端口看到项目已经运行起来了,最后再来介绍下本项目依赖的包:

    • axios: 是一个基于 promise 的一个用于发送ajax请求的HTTP库,本质上是对AJAX的封装,用于前端获取数据。
    • styled-components:是一个针对React的 css in js 类库。
    • antd-mobile:提供可直接使用的前端模板,如本项目用到的Modal模态框、PopUp弹出层等。
    • iconfont:图标字体库。
    • propTypes:对props中数据类型进行检测及限制。

    在终端命令行输入以下命令,将上面提到的包全部下载安装:

     npm i axiosnpm i styled-componentsnpm i antd-mobilenpm i prop-types 
    
    • 1

    另外,项目的数据存储在fastmock,这是一个在线接口工具,可以让你在没有后端程序的情况下能真实地在线模拟 ajax 请求,实现前后端分离。

    项目实现

    首先来看下界面实现的效果图: 项目预览

    设计思路

    通过观察Tesla 官网,可以了解到我们需要实现以下功能:(该项目仅为1.0版本,更多功能后续后慢慢添加

    • 基础界面开发:完成静态页面编写(需要有一定html+css基础
    • 模态框:点击查看详情按钮、底栏箭头以及中间部分,弹出模态框
    • tab 切换:底栏弹出的模态框内实现切换效果
    • 弹出层:点击CLTC综合工况按钮、底栏的对话图标,从底部出现弹出层

    下面来看看项目文件结构:

    • api 目录:顾名思义,用来存放数据请求。
    • assets 资源目录:用来存放一些静态资源,比如:图片、字体、全局样式…
    • components 组件目录:存放通用组件,如顶栏、底栏,以及一些可复用的组件等。
    • page 页面目录:存放页面组件,如首页、定制等。 (由于本项目为初级阶段,页面级组件暂时只有Cuntom定制界面)

    采用Layout布局,将

    组件分别固定在头部与尾部,页面级别组件 放在中间,简化App.jsx根组件。

    App 根组件

    根组件负责调用api获取数据,并通过props向子组件传递参数。(由于只有一个页面组件,所以没有将路由分离到单独的文件,后期页面组件多了则需要分离)

    import React, { useState, useEffect } from 'react'
    import './App.css'
    import { Routes, Route, Link } from 'react-router-dom'
    import Custom from './pages/Custom'
    import Header from './components/Header'
    import Footer from './components/Footer'
    import { getCarParams } from './api/request'
    
    function App() {// 设置一个汽车参数状态并设初始值为空数组,carParams为读操作,setCarParams为写操作const [carParams, setCarParams] = useState([]);// 设置一个显示版本状态并设初始值为1,showEdition为读操作,setShowEdition为写操作const [showEdition, setShowEdition] = useState('1')// useEffect生命周期函数,它会在组件加载完成时运行,这里作用是异步(async + await)获取数据useEffect(() => {(async () => {let { data: carParamsData } = await getCarParams()setCarParams(carParamsData)})()}, [])return (
    }>
    ) } export default App
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    | api接口

    引入axios 远程获取数据,这是采用了fastmock创建的伪接口,后续根组件通过引入并调用getCarParams方法拿到数据。

    import axios from 'axios'
    
    export const getCarParams = () =>axios.get('https://www.fastmock.site/mock/7c2b4d662d21c3311479338632d3faec/tesla/design') 
    
    • 1
    • 2
    • 3

    Custom 定制组件

    定制页面组件将根组件传来的参数props继续传给 Main.jsx 子组件。(由于时间有限,只写了主组件,定制界面的其他组件后期添加)

    import React, { useState, useEffect } from 'react'
    import { Wrapper } from './style'
    import Main from './Main'
    
    export default function Custom(props) {return (
    ) }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    Main 定制界面内的主组件

    获取组件传来的数据,通过判断当前组件是否选中,是则渲染数据,不是则不渲染,并引入 组件,同时引入propTypesprops中数据类型进行检测,判断是否为数组,且必需传值

    import propTypes from 'prop-types'
    ...
    {/* 汽车图片 */}...{/* 汽车参数 */}{carParams.map(item => {return ({/* 通过判断当前组件是否选中,是则渲染数据,不是则不渲染 */}showEdition == item.id &&
    ...
    )})}{/* 电机驱动部分 */}...{/* 模态框部分 */}
    ... // 判断是否为数组,且必需传值 Main.propTypes = {carParams: propTypes.array.isRequired }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    Header 头组件

    头组件只用来放置 Logo,通过定位,固定在头部。

    import React from 'react'
    import { Wrapper } from './style'
    import IMG from '../../assets/img/tesla.png'
    import { Link } from 'react-router-dom'
    
    export default function Header() {return ()
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    Footer 尾组件

    尾组件存放基础结构、计算器弹层组件与弹出层组件,通过定位,固定在尾部。

    {/* 点击显示模态框 */}
    setVisible(true)}>
    setVisible(true)}>
    {carParams.map(item => {return (showEdition == item.id && ¥ {item.price})})}实际价格
    {carParams.map(item => {return (showEdition == item.id && ¥ {item.another_price})})}减去节省的燃油费
    {/* 对话框弹出层组件 */}{/* 计算器弹层 */}
    • 1
    • 2

    Dialogue 对话框组件

    引入了上面提到的antd-mobile 组件库中的Popup弹出层组件

    // 引入 antd-mobile 组件库
    import { Popup, Button } from 'antd-mobile'
    ...
    {filter: 'blur(5px)',opacity: 0.5,}}// 主体样式bodyStyle={{borderTopLeftRadius: '12px',borderTopRightRadius: '12px',minHeight: '40vh',}}// 点击关闭图标关闭弹出层onClose={() => {setVisible1(false)}}// 点击遮罩层关闭弹出层onMaskClick={() => {setVisible1(false)}}>// 弹出层内容{mockContent()}
    
    ...
    const mockContent = () => {return (...)
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    ModalCalculator 组件

    引入了上面提到的antd-mobile 组件库中的Modal弹层组件,通过结构父组件传递的参数props,实现tab 切换。

    import React from 'react'
    import Modal from '@/components/common/Modal/ModalCalculator/modal'
    import './index.css'
    import { useState } from 'react'
    
    export default function ModalCalculator(props) {// 将三个参数从父组件传递的参数props 中解构出来const { visible, setVisible, onModalClose } = props// 设置一个 active 状态,并设初始值为1,用来给下面tab 切换做准备const [active, setActive] = useState('1') return (
    setVisible(false)}>
    {* tab 切换实现 */}
    setActive('1')}>现金
    setActive('2')}>合作金融机构贷款
    setActive('3')}>特斯拉融资租赁
    {active == '1' &&
    ...
    }{active == '2' &&
    ...
    }{active == '3' &&
    ...
    }
    ) }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    Modal组件

    由于样式不同,模态框无法复用 (应该是我的原因),导致组件需要与组件分开写,但是即使分开,如果className相同,也会相互影响,所以我干脆 结构分离 + 类名分离,Modal组件的 {title}{children} 则对应组件与组件的 标题内容组件 (该组件对应组件) 如下:

    import React, { useState, useEffect } from "react";
    import './modal.css'
    
    const Modal = (props) => {const [visible, setVisible] = useState(false)const { visible: show, children, title, onClose } = props;useEffect(() => {setVisible(show)}, [show])const maskClick = () => {setVisible(false)onClose && onClose()}return (visible && 
    {title}
    {children}
    ) } export default Modal;
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    剩下的组件以及对应的组件就不放图了,与上面类似,只是改了下类名。

  • 相关阅读:
    SwiftUI SQLite教程之带有历史的搜索栏List App (教程含完整代码)
    VSCode 建议你启用 gopls, 它到底是个什么东东?
    【ZooKeeper】zookeeper源码8-Follower和Leader状态同步
    基于java+springboot+vue实现的美食信息推荐系统(文末源码+Lw)23-170
    [LeetCode周赛复盘] 第 115 场双周赛20231014
    [Android开发学iOS系列] ViewController
    网络安全笔记-TCP/IP
    【玩转腾讯混元大模型】怎么说?我用混元AI大模型开发了个IDEA插件
    基本排序算法
    Linux服务器安装MYSQL
  • 原文地址:https://blog.csdn.net/pfourfire/article/details/126541689