Skip to content

Latest commit

 

History

History
474 lines (359 loc) · 11.4 KB

21.React.js开发旺财记账.md

File metadata and controls

474 lines (359 loc) · 11.4 KB

旺财记账开发实录

  1. 项目搭建

    1. 使用create-react-app命令搭建项目框架,并用yarn start开启网页

    2. 使用 css reset

      1. src/index.css中加上@import-normalize;默认样式
    3. 配置 sass

      1. 想让React支持sass
      2. 需要node-sass,它有两个缺点,下载速度慢、本地编译慢
      3. 于是我想改用 dart-sass 替换 node-sass
      4. 但是React只支持 node-sass,不支持 dart-sass
      5. 经过我的努力搜索和研究
      6. 我发现 npm 6.9 支持一个新功能,叫 package alias
      7. yarn add node-sass@npm:dart-sass即可偷梁换柱、瞒天过海、暗度陈仓
      8. 最后我完成了我的目标
    4. 使用 styled components

      1. App.tsx添加如下代码并安装yarn add styled-components

        import styled from 'styled-components';
        
        const Button = styled.button`
          color: grey;
        `;
    5. 安装react-router

      yarn add react-router-domyarn add --dev @types/react-router-dom

    6. React Router 初体验

      • import React from "react";
        import {
          BrowserRouter as Router,
          Switch,
          Route,
          Link
        } from "react-router-dom";
        
        export default function App() {
          return (
            <Router>
              <div>
                <nav>
                  <ul>
                    <li>
                      <Link to="/">Home</Link>
                    </li>
                    <li>
                      <Link to="/about">About</Link>
                    </li>
                    <li>
                      <Link to="/users">Users</Link>
                    </li>
                  </ul>
                </nav>
        
                {/* A <Switch> looks through its children <Route>s and
                    renders the first one that matches the current URL. */}
                <Switch>
                  <Route path="/about">
                    <About />
                  </Route>
                  <Route path="/users">
                    <Users />
                  </Route>
                  <Route path="/">
                    <Home />
                  </Route>
                </Switch>
              </div>
            </Router>
          );
        }
        
        function Home() {
          return <h2>Home</h2>;
        }
        
        function About() {
          return <h2>About</h2>;
        }
        
        function Users() {
          return <h2>Users</h2>;
        }
    7. 添加重定向和404界面

      • <Redirect exact from="/" to="/money/"/>
        <Route path="*">
            <NoMatch />
        </Route>
        function NoMatch(){
          return <h2>页面不存在,你丫输错地址了吧!</h2>
        }
    8. Router的两种模式

      1. Hash

        import { HashRouter as Router } from 'react-router-dom';
      2. History

        import { BrowserRouter as Router } from 'react-router-dom';
    9. 制作底部导航栏

      1. 新建index.scss用于样式的初始化

        • @import-normalize;
          * { margin: 0;padding: 0;}
          * { box-sizing: border-box; }
          *::before { box-sizing: border-box;}
          *::after { box-sizing: border-box;}
          ul, ol {list-style: none;}
        • Wrapper组件撑满整个屏幕

          const Wrapper = styled.div`
            border: 1px solid red;
            min-height: 100vh; //重点技巧!!!!
          `;
        • 使用flex布局,尽量避免使用fixed布局

        • 让显示页面尽量高flex-grow: 1;

    10. 解决跨平台字体问题

      1. https://zenozeng.github.io/fonts.css/
    11. Nav组件抽离并封装成新的组件

    12. 使用svg sprite loader加载图片

      1. 先提交完代码,然后运行yarn eject,此步骤不可回退,类似生孩子。
      2. 运行yarn add --dev svg-sprite-loaderyarn add --dev svgo-loader
      3. 通过require('icons/tag.svg');引入图片,而不是import,因为import会产生tree shaking效果,必须添加console.log(...)才能使用
    13. 将icon抽离出组件,避免重复

      import React from 'react';
      
      type Props = {
        name: string
      }
      
      const Icon = (props: Props) => {
        return (
          <svg className="icon">
            <use xlinkHref={'#' + props.name}/>
          </svg>
        );
      };
      
      export default Icon;
    14. 解决有许多图标而导致太多引入的问题

      在Icon组件中 添加如下代码 并安装yarn add --dev @types/webpack-env

      let importAll = (requireContext: __WebpackModuleApi.RequireContext) => requireContext.keys().forEach(requireContext);
      try {importAll(require.context('icons', true, /\.svg$/));} catch (error) {console.log(error);}
    15. 点击导航栏高亮选项

      添加selected样式

      &.selected {
      	color: #f60;
      	.icon {
               fill: #f60;
           }
      }
      <NavLink to="/faq" activeClassName="selected">
        FAQs
      </NavLink>
    16. svgo-loader工具批量去除svg图标的默认填充颜色

      {
      	loader: 'svgo-loader', options: {
      		plugins: [
      			{	
      				name: 'removeAttrs',
      				params: { attrs: '(fill|stroke)'}
      			}
       		]
       	}
      }
    17. 让input充满整个框,并消除选中样式

      > label {
          display: flex;
          align-items: center;
      
          > span {
            padding-right: 16px;
            white-space: nowrap;
          }
      
          > input {
            display: block;
            width: 100%;
            height: 72px;
            background: none;
            border: none;
          }
        }
      :focus{
        outline: none;
      }
    18. 添加选中而不增加div高度

      > li {
            width: 50%;
            text-align: center;
            padding: 16px 0;
            position: relative;
      
            &.selected::after {
              content: '';
              display: block;
              height: 3px;
              background: #333;
              position: absolute;
              left: 0;
              bottom: 0;
              width: 100%;
      
            }

      image-20220322163344131

    19. 给numberpad加入清除浮动样式

      .clearfix:after{
      	content:'';
      	display:block;
      	clear:both;
      }
    20. 将money组件进行模块化到money文件夹中,避免代码过多而导致阅读困难

    21. 将 Styled Component 改造为 Function Component

      const TagsSection: React.FunctionComponent = () => {...}
    22. 标签设置点击事件

      <li key={tag} onClick={
      	() => {onToggleTag(tag);}  //错误写法:onClick={onToggleTag(tag)}
          // 箭头函数() => {onToggleTag(tag)使得函数不会立即执行
      }>{tag}</li>
    23. 标签设置为单选

      const onToggleTag = (tag: string) => {
          const index = selectedTags.indexOf(tag);
          if (index >= 0) {
            setSelectedTags(selectedTags.filter(t => t !== tag));
            //  如果tag 已经选中,就复制所有没有被选中的tag,作为新的selectTag
          } else {
            setSelectedTags([tag]);
            // setSelectedTags([...selectedTags,tag]);
          }
          console.log(selectedTags);
        };
    24. 在备注中使用受控组件

      const NoteSection: React.FunctionComponent = () => {
        const [note, setNote] = useState('');
        console.log(note);
        return (
          <Wrapper>
            <label>
              <span>备注</span>
              <input
                type="text" placeholder="在这里添加备注"
                value={note}
                onChange={
                  (e) =>
                    setNote(e.target.value)}
              />
            </label>
          </Wrapper>
        );
      };
    25. 在备注中使用非受控组件

      const NoteSection: React.FunctionComponent = () => {
        const [note, setNote] = useState('');
        const refInput = useRef<HTMLInputElement>(null);
          
        const onBlur = () => {
          if (refInput.current !== null) {
            console.log(refInput.current.value);
            setNote(refInput.current.value)
          }
        };
        console.log(note);
        return (
          <Wrapper>
            <label>
              <span>备注</span>
              <input
                type="text" placeholder="在这里添加备注"
                defaultValue={note}
                onBlur={onBlur}
                ref={refInput}
              />
            </label>
          </Wrapper>
        );
      };
    26. 关于React onChange 的触发时机

      React onChange 会在你输入每一个只字的时候触发

      HTML onChange 是焦点移出后再触发,早于onBlur

    27. router路由的精准匹配之 exact

      <Route exact path="/tags/:tag">
            <Tag/>
      </Route>
    28. 牛逼的程序员不仅会写代码,还会改代码

      将原来的useState<string[]> 改为useState<id:number;name:string>

      const useTags = () => {//封装一个自定义hook
        const [tags, setTags] = useState<string[]>(['衣', '食', '住', '行',]);
        return {tags, setTags};
      };
      const [tags, setTags] = useState<{ id: number; name: string }[]>([
          {id: 1, name: '衣'},
          {id: 2, name: '食'},
          {id: 3, name: '住'},
          {id: 4, name: '行'},
        ]);
    29. react router通过useParams获取参数

      import {useParams} from 'react-router-dom';
      type Params = {
        id: string
      }
      let {id} = useParams<Params>();
    30. 用空Icon进行占位,便于布局

      <Topbar>
      	<Icon name="left"/>
      	<span>编辑标签</span>
      	<Icon/>
      </Topbar>
      ---------------------------------------------------------------------------
      const Icon = (props: Props) => {
        return (
          <svg className="icon">
            {props.name && <use xlinkHref={'#' + props.name}/>}
          </svg>
        );
      };
    31. 更新和删除Tag

      const updateTag = (id: number, obj: { name: string }) => {
          setTags(tags.map(tag => tag.id === id ? {id, name: obj.name} : tag));
        };
      
        const deleteTag = (id: number) => {
          setTags(tags.filter(tag => tag.id !== id));
        };
    ######