<listing id="r7f1v"></listing>
<listing id="r7f1v"><var id="r7f1v"></var></listing><listing id="r7f1v"><cite id="r7f1v"><i id="r7f1v"></i></cite></listing>
<listing id="r7f1v"><cite id="r7f1v"></cite></listing>
<listing id="r7f1v"></listing>
<listing id="r7f1v"></listing>
<thead id="r7f1v"><cite id="r7f1v"></cite></thead>
<listing id="r7f1v"></listing>
<listing id="r7f1v"><cite id="r7f1v"></cite></listing>
<listing id="r7f1v"></listing>
<listing id="r7f1v"></listing>
<progress id="r7f1v"><var id="r7f1v"></var></progress>
<listing id="r7f1v"></listing>
APP開發平臺 > Blog > React框架搭建過程,具體步驟是什么?

React框架搭建步驟:

1、使用create-react-app創建項目。

2、CSS reset。

3、配置SCSS。

4、配置styled-component。

5、使用React-Router。

6、引入SVG Icon。

1、使用creact-react-app創建項目:

先創建項目目錄,把目錄放拖進VSCode里面,在終端輸入create-react-app . --template typescript,然后輸入yarn start

如果不喜歡每次yarn start都自動打開瀏覽器的話,就在項目下新建一個文件,命名為.env,然后加上BROWSER=none

如果用 WebStorm,就在 .gitignore 添加 /.idea

如果用 VSCode,就在 .gitignore 添加 /.vscode

如果使用 Git status發現.idea或者.vscode有改動,則添加一下以下代碼:

git rm -rf --cached .idea
git rm -rf --cached .vscode

然后提交代碼,則創建項目完成了。

在創建好的項目當中,index.tsx文件里面有一個React.StrictMode,作用是檢查代碼哪里有用錯的地方,可以刪掉。

react項目創建好以后,是沒有內置Router,Redux和SCSS的,需要的話,看文檔自己去安裝。

2、CSS Reset:樣式重置,把所有默認樣式全部去掉

normalize:讓各個瀏覽器的樣式都趨于相同。(讓默認樣式基本一樣)。

在 index.css 添加 @import-normalize; 即可。但是默認樣式一般會被覆蓋掉的,所以用處不大。

3、配置SCSS:(node-sass比較難用,所以不建議使用,使用dart--sass代替)

終端輸入:yarn add --dev node-sass@npm:dart-sass,然后把css文件后綴改為scss,就可以成功使用sass了。

react早就支持直接在SRC目錄下直接引用了,所以為了讓css@import引用更方便,直接絕對用就可以了。

而對于js的引用,首先在tsconfig.json里面compilerOptions低下加上"baseUrl": "src",這句話,然后要保證外面要有"include": ["src"]這一句話。然后js就可以直接引用src低下的文件了。

然后創建helper.scss文件,用來存放變量,函數等公用的東西。至此,css和js就配置完了。

使用css,可以在標簽那里用calssnName寫上標簽名,然后導入css文件,就可以使用了。

在index.scss文件夾里面把全局樣式設置好。

4、配置styled-component:

除了可以把css單獨放在一個文件里面的方法,還可以使用CSS-in-JS的方案:styled-component。

在終端輸入 yarn add styled-components以及yarn add --dev @types/styled-components(這是typescript的支持文件,一般支持文件都是加--dev的)

5、使用React-Router:

首先添加react router以及它的typescript支持文件:

終端運行:yarn add react-router-dom以及yarn add --dev @types/react-router-dom,然后直接看文檔使用即可: React Router: Declarative Routing for React

v6做了升級,具體改動可以參考:react-router-dom v6 移除Redirect后的解決方案

需要配置默認路由"/"以及錯誤路由"*"

如果沒有后臺服務器,就只能使用Hash模式,如果有后臺服務器,配置所有路徑都到首頁才能History模式。

配置Hash模式:

只要把Router的類型改為HashRouter就可以了。

6、引入SVG Icon(直接引入svg的話不能改顏色,所以建議使用Svg symbols的方式):這個可引入可不引入。

首先把需要使用的svg文件下載好,然后在終端輸入yarn eject(注意,eject以后,就不會把webpack文件隱藏回去了),eject成功以后,記得提交一下代碼。接下來在終端輸入yarn add --dev svg-sprite-loader。成功以后,打開config/webpack.config.js,return-module-rules-oneOF,在這個數組的最上面,添加代碼:

 {
 test: /\.svg$/,
 use: [
            { loader: "svg-sprite-loader", options: {} },
            { loader: "svgo-loader", options: {} },
          ],
        }, 

然后終端添加“svgo-loader":yarn add --dev svgo-loader

然后重新運行yarn start,就可以使用了。

使用方法:(因為TreeShaking不適于require,適于Import,所以用import需要調用一下,svg才會顯示,而rquire則不需要,所以使用require更方便。)

require("icons/money.svg");

 <svg fill="red" className="icon">
 <use xlinkHref="#money" />
 </svg>

然后就可以設置icon的樣式了。

最后,require一個目錄/一個文件夾,便于導入svg。

輸入一以下代碼:

let importAll = (requireContext: __WebpackModuleApi.RequireContext) =>
 requireContext.keys().forEach(requireContext);
try {
 importAll(require.context("icons", true, /\.svg$/));
} catch (error) {
 console.log(error);
}

然后終端輸入:yarn add--dev @types/webpack-env,消除__WebpackModuleApi警告。

然后在context后面輸入svg路徑,再關掉重啟。就可以了。這時候,就可以自定義一個icon組件,去自由使用了。

假如一個應用里面,有許多頁面重復,那可以寫一個腳本去自動生成代碼,方便,快捷。


這里貼一些公共類的代碼:

1、index.scss:

@import-normalize;
@import "heplper.scss";
* {
 margin: 0;
 padding: 0;
}
* {
 box-sizing: border-box;
}
*::before {
 box-sizing: border-box;
}
*::after {
 box-sizing: border-box;
}
ul,
ol {
 list-style: none;
}
a {
 text-decoration: none;
 color: inherit;
}
body {
 font-family: $font-hei;
 font-size: 16px;
 line-height: 1.2;
}
.icon {
 width: 1em;
 height: 1em;
}

index.tsx:

import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import "index.scss";
ReactDOM.render(<App />, document.getElementById("root"));

這樣子,index.scss定義的全局樣式就可以在全局使用了。

2、helper.scss:

$font-hei: -apple-system, "Noto Sans", "Helvetica Neue", Helvetica,
 "Nimbus Sans L", Arial, "Liberation Sans", "PingFang SC", "Hiragino Sans GB",
 "Noto Sans CJK SC", "Source Han Sans SC", "Source Han Sans CN",
 "Microsoft YaHei", "Wenquanyi Micro Hei", "WenQuanYi Zen Hei", "ST Heiti",
  SimHei, "WenQuanYi Zen Hei Sharp", sans-serif;

3、icon.tsx:

import React from "react";
//require一個目錄/文件夾
let importAll = (requireContext: __WebpackModuleApi.RequireContext) =>
 requireContext.keys().forEach(requireContext);
try {
 importAll(require.context("icons", true, /\.svg$/));
} catch (error) {
 console.log(error);
}

type Props = {
 name: string;
};
const icon = (props: Props) => {
 return (
 <svg className="icon">
 <use xlinkHref={"#" + props.name} />
 </svg>
  );
};
export default icon;


細節點注意:

1、在Router里面,使用<NavLink to="" activeClassName="selected">,然后加上css,那么點擊就會改變樣式,適合做導航欄icon點擊效果。

6.0版本官方已經不再支持activeClassName這種寫法了,換成動態設置className即可:

<NavLink className={({ isActive })=>"list-group-item"+(isActive ?" mactive":"")} to="/about">               About 		</NavLink><NavLink className={({ isActive })=>"list-group-item"+(isActive ?" mactive":"")} to="/home">               Home          </NavLink>

也可以不加className,6.0版本使用NavLink 默認點擊就會有class="active"這個標簽。

2、在處理導航欄點擊改變樣式的時候,有些SVG圖片自帶顏色,改變不了,這時候可以使用svgo來處理。在SVGO Loader里面設置如下:

                {
                  loader: "svgo-loader",
                  options: {
                    plugins: [
                      {
                        name: "removeAttrs",
                        params: {
                          attrs: "(fill|stroke)",
                        },
                      },
                    ],
                  },
                },

3、styled-component可以給自定義的組件再做一層封裝,如下:

import Layout from '../components/Layout';
const MyLayout = styled(Layout)`
  display:flex;
  flex-direction: column;
`
function Money() {
  return (
    <MyLayout>
      <TagsSection>
        <ol>
          <li>衣</li>
          <li>食</li>
          <li>住</li>
          <li>行</li>
        </ol>
        <button>新增標簽</button>
      </TagsSection>
    </MyLayout>
  );
}

export default Money;

4、在react里面的函數傳值,可以使用匿名函數,不能出現onClick={onToggleTag(tag)}這樣的代碼,因為這代表的是馬上調用行數,函數,然后把得到的值給onClick,這是錯誤的,我們需要的是點擊的時候才調用函數,而不是馬上執行函數,所以應該這樣調用:onClick={()=>{onToggleTag(tag)}}。完整代碼如下:

const onAddTag = () => {
    console.log("object");
    const tagName = window.prompt("新標簽的名稱");
    if (tagName !== null) {
      setTags((t) => [...tags, tagName]);
    }
  };
  const onToggleTag = (tag: string) => {
    console.log(tag);
  };
  return (
    <Wrapper>
      <ol>
        {tags.map((tag) => (
          <li
            key={tag}
            onClick={() => {
              onToggleTag(tag);
            }}
          >
            {tag}
          </li>
        ))}
      </ol>
      <button onClick={onAddTag}>新增標簽</button>
    </Wrapper>

5、判斷選中的元素是否在數組里面,是的話刪除,不是的話添加的方法:

const onToggleTag = (tag: string) => {
 const index = selectedTags.indexOf(tag);
 console.log("index:", index);
 if (index >= 0) {
 setSelectedTags(selectedTags.filter((t) => t !== tag));
 //如果tag已被選中,就復制所有沒有被選中的tag,作為新的selectedTag
    } else {
 setSelectedTags((t) => [...selectedTags, tag]);
    }
  };

6、在index.tsx里面用到的嚴格模式,有時候會引起一些問題(目前暫時遇到log問題),可以刪除嚴格模式。

嚴格模式:

ReactDOM.render(
 <React.StrictMode>
 <App />
 </React.StrictMode>,
 document.getElementById('root')
);

非嚴格模式:

ReactDOM.render(<App />, document.getElementById("root"));

7、input的受控模式:

const NoteSection: React.FC = () => {
 const [note, setNote] = useState<string>("");
 console.log(note);
 return (
 <Wrapper>
 <label>
 <span>備注</span>
 <input
 type="text"
 placeholder="在這里添加備注"
 value={note}
 onChange={(e) => setNote((t) => e.target.value)}
 />
 </label>
 </Wrapper>
  );
};

Input的非受控模式:(不管中間過程,只要鼠標移除,就獲取結果)

const NoteSection: React.FC = () => {
 const [note, setNote] = useState<string>("");
 const refInput = useRef<HTMLInputElement>(null);
 const onBlur = () => {
 if (refInput.current !== null) {
 setNote(refInput.current.value);
    }
  };
 return (
 <Wrapper>
 <label>
 <span>備注</span>
 <input
 type="text"
 placeholder="在這里添加備注"
 ref={refInput}
 defaultValue={note}
 onBlur={onBlur}
 />
 </label>
 </Wrapper>
  );
};

注意:

React onChange和HTML onChange 是不一樣的。

React onChange會在輸入一個字的時候觸發,

HTML onChange在鼠標移走的時候觸發,早于onBlur。

8、當出現Element implicitly has an 'any' type這個錯誤的時候,說明類型使用有誤,這時候可以把類型收縮一下范圍:

const [categoryList] = useState<("-" | "+")[]>(["-", "+"]);

9、哈希類型的使用案例:

const CategorySection: React.FC = () => {
 const [categoryList] = useState<("-" | "+")[]>(["-", "+"]);
 const [category, setCategory] = useState("-"); //+表示收入 -表示支出
 const categoryMap = { "-": "支出", "+": "收入" };
 return (
 <Wrapper>
 <ul>
 {categoryList.map((e) => (
 <li
 key={e}
 className={category === e ? "selected" : ""}
 onClick={() => {
 setCategory(e);
            }}
 >
 {categoryMap[e]}
 </li>
        ))}
 </ul>
 </Wrapper>
  );
};
export { CategorySection };

另外一個變式:

 const categoryMap = { "-": "支出", "+": "收入" };
 type Keys = keyof typeof categoryMap;//通過這個方法,獲取到哈希對象里面的值,然后作為類型。
 const [categoryList] = useState<Keys[]>(["-", "+"]);
 const [category, setCategory] = useState("-"); //+表示收入 -表示支出

10、內部數據讀寫如下:

 const [selectedTags, setSelectedTags] = useState<string[]>([]);//直接使用useState進行內部數據讀和寫。

接收外部數據,然后進行讀寫操作,組件可以讀外部傳進來的數據,但是要改變數據的話,則需要通過通知外部父組件修改才可以,不能在內部把外部數據改了。

讀:

父組件: 
const [selected, setSelected] = useState({
 tags: [] as string[],
 note: "",
 category: "-" as Category,
 amount: 0,
  });
...
 <TagsSection selected={selected.tags} />

子組件:

type Props = { selected: string[] };
const TagsSection: React.FC<Props> = (props) => {
  const selectedTags = props.selected
  const getClass = (tag: string) =>
    selectedTags.indexOf(tag) >= 0 ? "selected" : "";//通過props讀取外部數據
  return (
    <Wrapper>
     ...
    </Wrapper>
  );
};

寫:

父組件:

<TagsSection
        selected={selected.tags}
        onChange={(tags) => setSelected({ ...selected, tags: tags })}
      />

子組件:
type Props = { selected: string[]; onChange: (selected: string[]) => void };//設置props的類型
...
 props.onChange([...selectedTags, tag]);//通知外部父組件修改數據

11、在typescript設置類型的時候,如果要表示我的對象是某個對象一部分的話,可以使用Partial<>來表示

 const [selected, setSelected] = useState({
 tags: [] as string[],
 note: "",
 category: "-" as Category,
 amount: 0,
  });
 type Selected = typeof selected;//通過typeof獲取一個值的類型
 const onChange = (obj: Partial<Selected>) => {//通過Partial<Selected>,得到的類型是傳入的類型的部分類型。
 setSelected({
      ...selected,
      ...obj,
    });
  };

12、注意,自定義hook都是要用use開頭。

一個自定義hook的例子:

import React, { useState } from "react";

const useTags = () => {
 const [tags, setTags] = useState<string[]>(["衣", "食", "住", "行"]);
 return { tags, setTags };
};

export { useTags };


以上這種抽離出useState,并把讀寫接口放到外面去,這種操作就叫做自定義hook。

13、router可以使用精確匹配exact,這樣子就不用考慮路由的順序問題,但是在6.0版本里面,因為優化了,所以不用精確匹配,也不會因路由的順序問題產生跳轉失敗的情況。另外,需要匹配子路由,可以使用一下寫法:

 <Route path="/tags/:tag" element={<Tag />} />//匹配/tags/:*開頭的任意路由。


在子頁面獲取路由的參數:

路由:
 <Route path="/tags/:id" element={<Tag />} />
頁面:
import React from "react";
import { useParams } from "react-router-dom";
import { useTags } from "useTags";
type Params = { id: string };
const Tag: React.FC = () => {
 const { tags } = useTags();
 let { id } = useParams<Params>();//獲取路由參數
 console.log("id:", id);
 const tag = tags.filter((tag) => tag.id === parseInt(id!))[0];
 return <div>{tag.name}</div>;
};

export { Tag };

14、自定義組件類型繼承父類屬性的寫法:

type Props = {
 label: string;
} & React.InputHTMLAttributes<HTMLInputElement>;
const Input: React.FC<Props> = (props) => {
 const { label, children, ...rest } = props;
 return (
 <Label>
 <span>{props.label}</span>
 <input {...rest} />
 </Label>
  );
};


15、如果需要合并className,可以添加classname庫,終端輸入:

yarn add classnames

yarn add --dev @types/classnames

然后代碼如下:

<svg className={cs("icon", className)} {...rest}>//會自動合并'icon'和className
 {props.name && <use xlinkHref={"#" + props.name} />}
 </svg>


16、后退操作:

window.history.back();//當我們采用哈希模式的時候,后退是沒有新的頁面刷新的,只是頁面的狀態切換而已。

注意 window.history.back();有個Bug,就是當用戶復制地址在瀏覽器打開的話,再點擊后退的話,會回到瀏覽器那里,而不是應用的上一頁。


17、如果要實現某變量更新以后調用函數,除第一次以外,可以用如下代碼:(自定義hook)

//
import { useEffect, useRef } from "react";

const useUpdate = (fn: () => void, deps: any[]) => {
 const count = useRef(0);
 useEffect(() => {
 count.current += 1;
  });
 useEffect(() => {
 if (count.current > 1) {
 fn();
    }
  }, deps);
};

export { useUpdate };

使用:
import { useUpdate } from "hooks/useUpdate";
 useEffect(() => {
    setTags(JSON.parse(window.localStorage.getItem("tags") || "[]"));
  }, []);
  useUpdate(() => {
    window.localStorage.setItem("tags", JSON.stringify(tags));
  }, [tags]);

18、創建兩個相似的類型:

type RecordItem = {
 tagIds: number[];
 note: string;
 category: "+" | "-";
 amount: number;
 createAt: string; //ISO 8601
};

// type newRecordItem = {
//   tagIds: number[];
//   note: string;
//   category: "+" | "-";
//   amount: number;
// };

type newRecordItem = Omit<RecordItem, "createAt">;//Omit的意思是,作用于函數,忽略RecordItem里面的createAt屬性。如果有兩項,可以使用|來分割:type newRecordItem = Omit<RecordItem, "createAt"|"uodatedAt">;



19、給組件的屬性添加默認值:

type Props = {
 className: string;
 scrollTop?: number;
};
Layout.defaultProps = {
 scrollTop: 0,
};//給組件屬性添加默認值
const Layout: React.FC<Props> = (props) => {
 const mainRef = useRef<HTMLDivElement>(null);
 useEffect(() => {
 if (!mainRef.current) {
 return;
    }
 mainRef.current.scrollTop = props.scrollTop!;//因為有默認呢只,所以不可能為空的,所以加!。
  }, [props.scrollTop]);
 return (
 <Wrapper>
 <Main ref={mainRef} className={props.className}>
 {props.children}
 </Main>
 <Nav />
 </Wrapper>
  );
};
export default Layout;

20、使用setTimeout的時候,后面秒數為0不是說0秒就執行,而是盡快。

 useEffect(() => {
 setTimeout(() => {
 if (!mainRef.current) {
 return;
      }
 mainRef.current.scrollTop = props.scrollTop!;
    }, 0);
  }, [props.scrollTop]);


21、自動部署:

在scripts文件下,添加腳本deploy.sh,然后編輯即可。


來APICloud移動應用開發平臺學習更多APP開發知識:app開發,app制作,app開發源碼下載,app開發框架,app制作模板等免費獲取。


高效的App定制平臺,標準化、便宜、快!

提交APP定制開發需求
欧美激情一区二区,国产精品区免费视频,欧美激情视频在线播放,久久久亚洲综合久久98,久久国产精品99精品国产