First React

First React Program

2021/09/18 (更新內容)
2021/09/22 (新增常見問題)

開發環境與開發工具

Visual Studio Code (VS Code)

Node.js

  • 首先要安裝Node.js,開發react.js時可以使用node.js的javascript runtime,另外,node也提供了Node Package Manager (NPM)來協助開發者管理使用的套件。

    • 目前的Node穩定版本是14.17.1,最新的版本是16.4.0 (2021/06/27)

    • MacOS可利用package manager(如:homebrew)安裝

  • 也可以利用Yarn來管理套件,因為都是使用package.json,所以,都可以使用,只是語法跟效能的不同而已 (詳參: 用Yarn取代npm加速開發),建議各組統一使用同樣的套件管理程式

    • ** create-react-app預設使用yarn,所以,要注意現在使用的是哪個套件管理工具

  • 更新node版本

CodeSandbox

也可以使用CodeSandbox來進行開發,原本只是個讓大家測試程式碼的沙盒(sandbox),但是,功能越來越強大了,CodeSandbox可以直接透過瀏覽器開發程式,也可以多人使用,但是,如果要有比較完整的功能是需要付費的。

react.ppt
  • 安裝

    • Node.js

  • 產生新的react專案

  • 啟動react專案

產生一個react專案

React 是一個專注於 UI(View)的 JavaScript 函式庫(Library),利用純JavaScript在前端產生HTML(而不是由後端產生)。所以,React並沒有提供css及對應的js,所以,還是需要採用如Material-UI、Bootstrap或Semantic-UI的UI架構。

產生react專案的方式有很多種,一般初學者會利用create-react-app快速產生一個react專案。

  • React.js[01] — 建造 React App 的三種方式 (by 簡立學長)

    • 透過 CDN 載入 React.js & babel ,運用 JSX 打造 React.js Hello world!。

    • 透過 create-react-app CLI ,快速建造 React App。

    • 一步步應用 npm & webpack 建造出你的 React App 。

利用create-react-app

如果是一個新的專案,要先開一個新的資料夾,然後啟動vs code,啟動之後,選「檔案」、「開啟資料夾」選擇剛剛新增的資料夾。

** 注意,資料夾名稱不要使用「react」,否則在執行npx時,會得到:

Cannot create a project named "react" because a dependency with the same name exists.

Due to the way npm works, the following names are not allowed:


react

react-dom

react-scripts


Please choose a different project name.

然後,選「檢視」、「整合式終端機」,在終端機下利用create-react-app來產生一個新的react專案在目前的專案路徑裡(詳參官方文件: Getting Started),並且,指定使用npm為套件管理程式

npx create-react-app . --use-npm

因為我們要啟動PWA,所以,使用pwa的樣板

npx create-react-app . --use-npm --template cra-template-pwa

如果不能使用npx,可能是node以及npm的版本太舊(5.2以下),可以利用npm的指令看版本

npm --version

npm的最新版本(2021/07/03)是6.14.13 (詳參: NPM 5, NPM 6, Yarn: real world installation times comparisons & Announcing npm@6)

create-react-app會產生並下載相關檔案。利用create-react-app有個好處,會幫我們安裝很多好用的套件,例如: babel、ESLint、Prettier、webpack。(詳參: Create React App - Getting Started)

可以看一下package.json (based on react-scripts 2.0.5)

{

"name": "react-sample",

"version": "0.1.0",

"private": true,

"dependencies": {

"react": "^16.6.0",

"react-dom": "^16.6.0",

"react-scripts": "2.0.5"

},

"scripts": {

"start": "react-scripts start",

"build": "react-scripts build",

"test": "react-scripts test",

"eject": "react-scripts eject"

},

"eslintConfig": {

"extends": "react-app"

},

"browserslist": [

">0.2%",

"not dead",

"not ie <= 11",

"not op_mini all"

]

}


執行create-react-app時會自動下載最新的版本,例如,2020/06/24執行時,可以看到react-scripts已經是3.4.1了

{

"name": "test-first-cra",

"version": "0.1.0",

"private": true,

"dependencies": {

"@testing-library/jest-dom": "^4.2.4",

"@testing-library/react": "^9.5.0",

"@testing-library/user-event": "^7.2.1",

"react": "^16.13.1",

"react-dom": "^16.13.1",

"react-scripts": "3.4.1"

},

"scripts": {

"start": "react-scripts start",

"build": "react-scripts build",

"test": "react-scripts test",

"eject": "react-scripts eject"

},

"eslintConfig": {

"extends": "react-app"

},

"browserslist": {

"production": [

">0.2%",

"not dead",

"not op_mini all"

],

"development": [

"last 1 chrome version",

"last 1 firefox version",

"last 1 safari version"

]

}

}

2021/07/03執行時,可以看到react-scripts已經是4.0.3了

{

"name": "react-pwa",

"version": "0.1.0",

"private": true,

"dependencies": {

"@testing-library/jest-dom": "^5.14.1",

"@testing-library/react": "^11.2.7",

"@testing-library/user-event": "^12.8.3",

"react": "^17.0.2",

"react-dom": "^17.0.2",

"react-scripts": "4.0.3",

"web-vitals": "^0.2.4",

"workbox-background-sync": "^5.1.4",

"workbox-broadcast-update": "^5.1.4",

"workbox-cacheable-response": "^5.1.4",

"workbox-core": "^5.1.4",

"workbox-expiration": "^5.1.4",

"workbox-google-analytics": "^5.1.4",

"workbox-navigation-preload": "^5.1.4",

"workbox-precaching": "^5.1.4",

"workbox-range-requests": "^5.1.4",

"workbox-routing": "^5.1.4",

"workbox-strategies": "^5.1.4",

"workbox-streams": "^5.1.4"

},

"scripts": {

"start": "react-scripts start",

"build": "react-scripts build",

"test": "react-scripts test",

"eject": "react-scripts eject"

},

"eslintConfig": {

"extends": [

"react-app",

"react-app/jest"

]

},

"browserslist": {

"production": [

">0.2%",

"not dead",

"not op_mini all"

],

"development": [

"last 1 chrome version",

"last 1 firefox version",

"last 1 safari version"

]

}

}


要執行這個專案,在終端機下執行

npm start

就是去執行scripts裡面的start script,也就是

"scripts": {

"start": "react-scripts start",

"build": "react-scripts build",

"test": "react-scripts test",

"eject": "react-scripts eject"

},

執行之後,vs code會啟動node.js,也會自動開啟網頁 (http://localhost:3000/)。只要更動內容,node.js會自動重啟,新的內容立刻就會出現在網頁上。

專案裡面,可以看到三個資料夾:node_modules, public, src,在src中有很多檔案 ,看一下 src/index.js。

src/index.js

import React from 'react';

import ReactDOM from 'react-dom';

import './index.css';

import App from './App';

import * as serviceWorker from './serviceWorker';


ReactDOM.render(

<React.StrictMode>

<App />

</React.StrictMode>,

document.getElementById('root')

);


// If you want your app to work offline and load faster, you can change

// unregister() to register() below. Note this comes with some pitfalls.

// Learn more about service workers: https://bit.ly/CRA-PWA

serviceWorker.unregister();

先說明一些Javascript語法

  • import (詳參: 模組系統)

    • react跟javascript最大不同的地方就是將系統模組(元件)化,這裡的import,跟java很像,就是指定元件的來源。

import React from 'react';

import ReactDOM from 'react-dom';

import './index.css';

import App from './App';

import * as serviceWorker from './serviceWorker';

利用import就可以使用react定義的元件,這跟java的import類似。

import App from './App';

相對應的要讓別的檔案可以使用我們寫好的類別要使用export (詳參: 淺談JavaScript ES6的import與import{}及export及export default使用方式 )

所以,在App.js裡,會看到:

export default App;

  • ReactDOM (詳參: React篇: HelloWorld解說與JSX語法)

    • ReactDOM.render接受兩個參數,第一個參數是元件,第二個參數是元件要放置的位置。基本上React是利用ReactDOM.render將所有元件轉換為實際的DOM,將內容放到index.html裡的root。

    • 第一個參數的內容是JSX,雖然長得很像是HTML,事實上是javascript。細節後面再說明。

ReactDOM.render(

<React.StrictMode>

<App />

</React.StrictMode>,

document.getElementById('root')

);

  • document.getElementById (詳參: JavaScript HTML DOM Document)

    • document.getElementById是javascript的DOM語法,在react裡是使用所謂的ReactDOM (是個Virtual DOM),在react裡都不會(也不要)使用DOM語法,這裡應該是唯一DOM語法。

ReactDOM.render(

<React.StrictMode>

<App />

</React.StrictMode>,

document.getElementById('root')

);

  • 其他的javascript語法請參閱javascript

  • Strict Mode是react 16.3新增的功能,會幫忙檢查一些應用程式中的可能問題,建議要把這個部分留著。

  • service worker是為了可以達到PWA的功能,細節後面再說明。

我們來研究一下create-react-app產生的檔案內容,看一下./src/index.js。import就跟Java的import一樣,是定義等一下會用到的檔案 (如:類別、CSS) (詳參: 模組系統)

import React from 'react';

import ReactDOM from 'react-dom';

import './index.css';

import App from './App';

import * as serviceWorker from './serviceWorker';

下面這一句的意思是將App元件 (定義在App.js裡)的產出寫到HTML(index.html)的root裡面,並利用StrictMode檢查App以及所包含的所有元件。

ReactDOM.render(

<React.StrictMode>

<App />

</React.StrictMode>,

document.getElementById('root')

);

下面這一句是與serviceWorker有關,簡單的說,serviceWorker會預存網頁,讓執行速度變快,但是,對於開發中的專案,就先不要啟動。

serviceWorker.unregister();

完整檔案

src/index.js

import React from 'react';

import ReactDOM from 'react-dom';

import './index.css';

import App from './App';

import * as serviceWorker from './serviceWorker';


ReactDOM.render(

<React.StrictMode>

<App />

</React.StrictMode>,

document.getElementById('root')

);


// If you want your app to work offline and load faster, you can change

// unregister() to register() below. Note this comes with some pitfalls.

// Learn more about service workers: https://bit.ly/CRA-PWA

serviceWorker.unregister();

新的index.js不再預設serviceWorker,如果要啟動PWA,就要採用PWA的teamplate。

另外,也會啟動效能監控 (reportWebVitals)。

src/index.js (2021/06/29)

import React from 'react';

import ReactDOM from 'react-dom';

import './index.css';

import App from './App';

import * as serviceWorkerRegistration from './serviceWorkerRegistration';

import reportWebVitals from './reportWebVitals';


ReactDOM.render(

<React.StrictMode>

<App />

</React.StrictMode>,

document.getElementById('root')

);


// If you want your app to work offline and load faster, you can change

// unregister() to register() below. Note this comes with some pitfalls.

// Learn more about service workers: https://cra.link/PWA

serviceWorkerRegistration.unregister();


// If you want to start measuring performance in your app, pass a function

// to log results (for example: reportWebVitals(console.log))

// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals

reportWebVitals();

Hello World

如果只是要顯示Hello, world!!!,可以把index.js的內容暫時改為

import React from 'react';

import ReactDOM from 'react-dom';

ReactDOM.render(<h1>Hello, world!!!</h1>,document.getElementById('root'));

你也可以看到,create-react-app其實產生了一些檔案,裡面也包括了./public/index.html,如前面所介紹,裡面雖然有很多內容,但最重要的還是這一段,也就是把Hello, world!!!放到root裡。

<div id="root"></div>

public/index.html

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="utf-8" />

<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />

<meta name="viewport" content="width=device-width, initial-scale=1" />

<meta name="theme-color" content="#000000" />

<meta

name="description"

content="Web site created using create-react-app"

/>

<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />

<!--

manifest.json provides metadata used when your web app is installed on a

user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/

-->

<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />

<!--

Notice the use of %PUBLIC_URL% in the tags above.

It will be replaced with the URL of the `public` folder during the build.

Only files inside the `public` folder can be referenced from the HTML.


Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will

work correctly both with client-side routing and a non-root public URL.

Learn how to configure a non-root public URL by running `npm run build`.

-->

<title>React App</title>

</head>

<body>

<noscript>You need to enable JavaScript to run this app.</noscript>

<div id="root"></div>

<!--

This HTML file is a template.

If you open it directly in the browser, you will see an empty page.


You can add webfonts, meta tags, or analytics to this file.

The build step will place the bundled scripts into the <body> tag.


To begin the development, run `npm start` or `yarn start`.

To create a production bundle, use `npm run build` or `yarn build`.

-->

</body>

</html>

執行npm start之後,可以看到原始碼最後多了:

<script src="/static/js/bundle.js"></script><script src="/static/js/vendors~main.chunk.js"></script><script src="/static/js/main.chunk.js"></script>

完整內容:

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="utf-8" />

<link rel="icon" href="/favicon.ico" />

<meta name="viewport" content="width=device-width, initial-scale=1" />

<meta name="theme-color" content="#000000" />

<meta

name="description"

content="Web site created using create-react-app"

/>

<link rel="apple-touch-icon" href="/logo192.png" />

<!--

manifest.json provides metadata used when your web app is installed on a

user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/

-->

<link rel="manifest" href="/manifest.json" />

<!--

Notice the use of in the tags above.

It will be replaced with the URL of the `public` folder during the build.

Only files inside the `public` folder can be referenced from the HTML.


Unlike "/favicon.ico" or "favicon.ico", "/favicon.ico" will

work correctly both with client-side routing and a non-root public URL.

Learn how to configure a non-root public URL by running `npm run build`.

-->

<title>React App</title>

</head>

<body>

<noscript>You need to enable JavaScript to run this app.</noscript>

<div id="root"></div>

<!--

This HTML file is a template.

If you open it directly in the browser, you will see an empty page.


You can add webfonts, meta tags, or analytics to this file.

The build step will place the bundled scripts into the <body> tag.


To begin the development, run `npm start` or `yarn start`.

To create a production bundle, use `npm run build` or `yarn build`.

-->

<script src="/static/js/bundle.js"></script><script src="/static/js/vendors~main.chunk.js"></script><script src="/static/js/main.chunk.js"></script></body>

</html>

create-react-app其他檔案

來看一下src/App.js,App是個函數,並且回傳要產生的內容。最後的export是讓這個元件可以被使用。(詳參:淺談JavaScript ES6的import與import{}及export及export default使用方式 )

import React from 'react';


function App() {

return (

);

}


export default App;

以上的寫法是所謂的functional component,語法較精簡,但是,把一些運作原理藏起來了,看一下對應的class component的寫法。App這個類別繼承了react裡的Component類別,這個類別裡面有很多方法可以被覆蓋,這裡用到的是render方法。

現在大部分的範例都已經改用functional component,如果看到class component的寫法,就要知道兩者語法不同,不能將class component的寫法與functional component在同一個元件中混用。

import React, { Component } from 'react';


class App extends Component {

render() {

}

}


export default App;

在return/render裡的內容所使用的是JSX,其實樣子就像是html (詳參: React篇: HelloWorld解說與JSX語法Introducing JSXJSX In Depth)。

src/App.js 完整的內容:

import React from 'react';

import logo from './logo.svg';

import './App.css';


function App() {

return (

<div className="App">

<header className="App-header">

<img src={logo} className="App-logo" alt="logo" />

<p>

Edit <code>src/App.js</code> and save to reload.

</p>

<a

className="App-link"

href="https://reactjs.org"

target="_blank"

rel="noopener noreferrer"

>

Learn React

</a>

</header>

</div>

);

}

export default App;

但是不是完全一樣,因為class是javascript的保留字,所以,JSX的class要寫className。也就是說在JSX裡利用className(而不是class)來套用App.css裡的style (詳參: CSS)。

function App() {

return (

<div className="App">

<header className="App-header">

<img src={logo} className="App-logo" alt="logo" />

<p>

Edit <code>src/App.js</code> and save to reload.

</p>

<a

className="App-link"

href="https://reactjs.org"

target="_blank"

rel="noopener noreferrer"

>

Learn React

</a>

</header>

</div>

);

}

export default App;

CSS

在react裡可以使用標準的CSS,這部分應該各位已經在web前端設計中都已經學過了,可以回去複習一下。

  • CSS (吳濟聰老師 計算機概論課程教材)

稍微補充的是:

尺寸

  • 過去元件的高度會直接設定px值,現在會採用vhvw或者vmin。vh就是高度的百分比、vw就是高度的百分比、vmin就是為了我們在使用手機或平板時可能會常常轉來轉去,在vh或vw中取較小的值。如:40vmin就是取vh或vw中取較小的值的40%。

height: 40vmin;

  • 過去字體會直接設定px值,現在會採用rem或em,或者利用螢幕的尺寸做一些調整。

font-size: calc(10px + 2vmin);

排版

在排版的方面會採用Flex,在flex裡,元件安排方向如果是由上而下,就是設定為column。align-items,就是元件中的項目(item),的排列方式。

display: flex;

flex-direction: column;

align-items: center;

動畫

.App-logo {

animation: App-logo-spin infinite 20s linear;

height: 40vmin;

}


@keyframes App-logo-spin {

from {

transform: rotate(0deg);

}

to {

transform: rotate(360deg);

}

}

src/App.css

.App {

text-align: center;

}


.App-logo {

animation: App-logo-spin infinite 20s linear;

height: 40vmin;

}


.App-header {

background-color: #282c34;

min-height: 100vh;

display: flex;

flex-direction: column;

align-items: center;

justify-content: center;

font-size: calc(10px + 2vmin);

color: white;

}


.App-link {

color: #61dafb;

}


@keyframes App-logo-spin {

from {

transform: rotate(0deg);

}

to {

transform: rotate(360deg);

}

}

新的src/App.css

加了pointer-eventsprefers-reduced-motion

.App {

text-align: center;

}


.App-logo {

height: 40vmin;

pointer-events: none;

}


@media (prefers-reduced-motion: no-preference) {

.App-logo {

animation: App-logo-spin infinite 20s linear;

}

}


.App-header {

background-color: #282c34;

min-height: 100vh;

display: flex;

flex-direction: column;

align-items: center;

justify-content: center;

font-size: calc(10px + 2vmin);

color: white;

}


.App-link {

color: #61dafb;

}


@keyframes App-logo-spin {

from {

transform: rotate(0deg);

}

to {

transform: rotate(360deg);

}

}

常見問題

  • 如果無法執行npm或npx,請重新開機,看問題是否能解決。

  • 執行npm start時:

npm ERR! code ENOENT

npm ERR! syscall open

npm ERR! path C:\Users\USER\package.json

npm ERR! errno -4058

npm ERR! enoent ENOENT: no such file or directory, open 'C:\Users\USER\package.json'

npm ERR! enoent This is related to npm not being able to find a file.

表示在目前的資料夾中找不到package.json,應該是在錯誤的資料夾中執行npm start。