1. 程式人生 > 實用技巧 >使用create-react-app 構建react應用(react-scripts)

使用create-react-app 構建react應用(react-scripts)

前言:

create-react-app 是一個全域性的命令列工具用來建立一個新的專案

react-scripts 是一個生成的專案所需要的開發依賴


一般我們開始建立react web應用程式的時候,要自己通過 npm 或者 yarn 安裝專案的全部依賴,再寫webpack.config.js,一系列複雜的配置,搭建好開發環境後寫src原始碼。
現在 如果你正在搭建react執行環境,使用 create-react-app 去自動構建你的app程式。你的專案所在的資料夾下是沒有配置檔案。react-scripts 是唯一的 額外的 構建依賴在你的package.json中,你的執行環境將有每一個你需要用來構建一個現代React app應用程式。你需要的依賴,和在配置檔案中編寫的配置程式碼,react-scripts 都幫你寫了,比如:react-scripts幫你自動下載需要的 webpack-dev-server 依賴,然後react-scripts自己寫了一個nodejs服務端的指令碼程式碼 start.js來 例項化 WebpackDevServer ,並且執行啟動了一個使用 express 的Http伺服器,現在你只需要專心寫src原始碼就可以了。省去了很多精力,最適合快速上手一個demo了。


react-scripts有以下支援,都幫你配置好了:
React, JSX, ES6, and Flow syntax support.
Language extras beyond ES6 like the object spread operator.
Import CSS and image files directly from JavaScript.
Autoprefixed CSS, so you don’t need -webkit or other prefixes.
A build script to bundle JS, CSS, and images for production, with sourcemaps.
A dev server that lints for common errors.

Getting Started

安裝

npm install -g create-react-app


建立一個應用程式

create-react-app my-app
cd my-app


生成的目錄結構

my-app/
  README.md
  node_modules/
  package.json
  .gitignore
  public/
    favicon.ico
    index.html
  src/
    App.css
    App.js
    App.test.js
    index.css
    index.js
    logo.svg

沒有配置檔案(webpack.config.js)

執行應用程式

npm run start 

or

yarn start

在瀏覽器中開啟

http://localhost:3000 

現在我們看 my-app資料夾下的public/index.html 和src/index.js的原始碼,我們可以在這裡編寫專案程式碼,但是注意 public/index.html 是啟動http伺服器的首頁,src/index.js是編譯的入口檔案,只能叫index這個名字,改別的名字不行。

開啟 http://localhost:3000/index.html 首頁,f12檢視 網頁原始碼,你會看到

<script type="text/javascript" src="/static/js/bundle.js"></script>

/static/js/bundle.js
在你的專案my-app你是看不到這個檔案路徑的,你也沒有寫配置檔案webpack.config.js,
http伺服器配置,自動代開瀏覽器視窗,react,es6語法編譯,babel-core,webpack,等等這些 你都沒下載,配置。
這些活,react-scripts 都幫你做了。

剛才的過程分析

回顧
npm run start
我們 一開始這麼啟動服務 執行專案
開啟你的my-app\package.json

"scripts": {
    "start": "react-scripts start",
      ...
  }

所以執行的是 react-scripts start
開啟你的my-app\node_modules\react-scripts這個資料夾下的bin資料夾下的react-scripts.js檔案

#!/usr/bin/env node
var spawn = require('cross-spawn');
var script = process.argv[2];
var args = process.argv.slice(3);

switch (script) {
case 'build':
case 'eject':
case 'start':
case 'test':
  var result = spawn.sync(
    'node',
    [require.resolve('../scripts/' + script)].concat(args),
  .......

上面程式碼中 script 的變數值是 start
所以執行 my-app\node_modules\react-scripts\scripts 資料夾下的 start.js 檔案程式碼節選重點如下

var webpack = require('webpack');
var WebpackDevServer = require('webpack-dev-server');  // 啟動http伺服器
var paths = require('../config/paths');  //要編譯的檔案路徑與生成路徑等
var config = require('../config/webpack.config.dev');
var DEFAULT_PORT = parseInt(process.env.PORT, 10) || 3000; //這就是為什麼 埠號 不是8080 而是 3000 的原因,在這裡可以改成8080,重新npm run start 生效 
detect(DEFAULT_PORT).then(port => {
  if (port === DEFAULT_PORT) {
    run(port); //這裡開始執行
    return;
  }
......

function run(port) { 
// 這裡可以設定 http協議, 或者可以在 npm run start 之前 cmd命令視窗中執行 set HTTPS=true&&npm start 改成https 安全協議
  var protocol = process.env.HTTPS === 'true' ? "https" : "http"; 
  var host = process.env.HOST || 'localhost';   
  setupCompiler(host, port, protocol);  // 編譯原始碼 ,生成路徑
  runDevServer(host, port, protocol);  //啟動 http伺服器
}


//配置http伺服器
function runDevServer(host, port, protocol) {
  var devServer = new WebpackDevServer(compiler, {
   compress: true,   
    clientLogLevel: 'none',
    contentBase: paths.appPublic,   //根據匯入的paths 指定應用根目錄(即index.html所在目錄)
    hot: true,

    publicPath: config.output.publicPath, //根據匯入的 config 變數,指定 虛擬目錄,自動指向path編譯目錄(/assets/ => /build/js/)。html中引用js檔案時,
                                             //必須引用此虛擬路徑(但實際上引用的是記憶體中的檔案,既不是/build/js/也不是/assets/)。
 
    quiet: true,


    watchOptions: {
      ignored: /node_modules/
    },
    // Enable HTTPS if the HTTPS environment variable is set to 'true'
    https: protocol === "https",
    host: host
  });
  /**
   * 省略其他程式碼
   */
    openBrowser(protocol + '://' + host + ':' + port + '/');    // 開啟瀏覽器 向伺服器傳送請求
  });
}


function setupCompiler(host, port, protocol) {

  compiler = webpack(config, handleCompile);  //  根據匯入的 config 變數  指向的 webpack.config.dev 配置檔案  執行
     /**
   * 省略其他程式碼
   */
}

start.js 檔案程式碼 中 匯入了 my-app\node_modules\react-scripts\config資料夾下的 webpack.config.dev.js 與 paths.js
paths.js 程式碼節選如下:

var appDirectory = fs.realpathSync(process.cwd());   // 獲取npm run start 執行所在的路徑
function resolveApp(relativePath) {
  return path.resolve(appDirectory, relativePath);
}

module.exports = {
  appPath: resolveApp('.'),
  ownPath: resolveApp('node_modules/react-scripts'),
  appBuild: resolveApp('build'),
  appPublic: resolveApp('public'),
  appHtml: resolveApp('public/index.html'),   //  這就是  一開始  我們的專案 要使用public/index.html作為 預設首頁 
                //  這裡寫什麼檔名,專案中就要使用什麼檔名  包括 也要有public資料夾
  appIndexJs: resolveApp('src/index.js'),   //編譯的入口檔案的檔名  專案中要包括src資料夾
  appPackageJson: resolveApp('package.json'),
  appSrc: resolveApp('src'),
  yarnLockFile: resolveApp('yarn.lock'),
  testsSetup: resolveApp('src/setupTests.js'),
  appNodeModules: resolveApp('node_modules'),
  // this is empty with npm3 but node resolution searches higher anyway:
  ownNodeModules: resolveOwn('node_modules'),
  nodePaths: nodePaths,
  publicUrl: getPublicUrl(resolveApp('package.json')),
  servedPath: getServedPath(resolveApp('package.json'))
};
 /**
   * 省略其他程式碼
   */
webpack.config.dev.js  程式碼節選如下:

var paths = require('./paths');  //也匯入了 同文件夾下的 paths.js
module.exports = {  entry: [    require.resolve('react-dev-utils/webpackHotDevClient'),    require.resolve('./polyfills'),    paths.appIndexJs     // 編譯的入口檔案  ],  output: {    path: paths.appBuild,    pathinfo: true,       filename: 'static/js/bundle.js', // 只是編譯後生成的目標檔案 ,這就是一開始我們 開啟瀏覽器按f12看到的index.html匯入的           //  <script type="text/javascript" src="/static/js/bundle.js"></script>    publicPath: publicPath  },
 /**
   * 省略其他程式碼
   */

}


已經搭建好執行環境了,接下來 如何開發app

匯入元件

由於babel依賴,這個專案支援es6模組
當你仍然使用require() and module.exports ,我們鼓勵你去使用import and export 替代.

例如:

Button.js

import React, { Component } from 'react';

class Button extends Component {
  render() {
    // ...
  }
}
export default Button; // 不要忘記去使用 export default!

DangerButton.js

import React, { Component } from 'react';
import Button from './Button'; //從另一個檔案匯入一個元件


class DangerButton extends Component {
  render() {
    return <Button color="red" />;
  }
}
export default DangerButton;

增加樣式

Button.css

.Button {
  padding: 20px;
}
Button.js

import React, { Component } from 'react';
import './Button.css'; // 告訴webpack Button.js 使用這些樣式


class Button extends Component {
  render() {
    // You can use them as regular CSS styles
    return <div className="Button" />;
  }
}

Autoprefixer

react-scripts 通過Autoprefixer 幫你的css檔案自動新增瀏覽器相容字首
例如:

.App {
  display: flex;
  flex-direction: row;
  align-items: center;
}
變成

.App {
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
  -webkit-box-orient: horizontal;
  -webkit-box-direction: normal;
      -ms-flex-direction: row;
          flex-direction: row;
  -webkit-box-align: center;
      -ms-flex-align: center;
          align-items: center;
}

增加CSS前處理器

首先在 my-app/ 目錄下 安裝node-sass用來將scss編譯成css

npm install node-sass --save-dev

開啟my-app/package.json,增加以下程式碼到scripts中

   "scripts": {
+    "build-css": "node-sass src/ -o src/",
+    "watch-css": "npm run build-css && node-sass src/ -o src/ --watch",
     "start": "react-scripts start",
     "build": "react-scripts build",
     ......
    }


現在你可以重新命名my-app/src/App.csstomy-app/src/App.scssand 執行 npm run watch-css
或者你可以改成

"scripts": {
    "build-css": "node-sass src/ -o src/",
    "start": "npm run build-css && react-scripts start",  //先執行 build-css 再執行 react-scripts start
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
  }

直接 npm run start


增加圖片

import React from 'react';
import logo from './logo.png'; // 告訴webpack 這個js檔案使用這張圖片

console.log(logo); // /logo.84287d09.png  會改變圖片的名字

function Header() {
  //匯入結果是這個圖片的url地址
  return <img src={logo} alt="Logo" />;
}

export default Header;

當專案構建的時候,Webpack將正確的移動圖片到構建的資料夾下,提供我們正確的路徑

在css工作中的方式也一樣

.Logo {
  background-image: url(./logo.png);
}

webpack發現所有的相對模組, 以 ./ 開始

增加 bootstrap
在react+es6 moudle+bootstrap
你不是一定要React Bootstrap和React 一起使用,但是他是流行的庫去整合 bootstrap 和react應用程式,如果你需要他,你可以通過Create React App整合他,通過以下幾個步驟

首先安裝React Bootstrap and Bootstrap從npm,React Bootstrap 不包括Bootstrap CSS ,所以你需要去安裝
在my-app/ 目錄下安裝

npm install react-bootstrap --save
npm install bootstrap@3 --save

修改my-app/src/index.js
在你的src/index.js 檔案內容的頂部,匯入 Bootstrap CSS 和可選的 Bootstrap theme CSS

import React from 'react';
import ReactDOM from 'react-dom';
import 'bootstrap/dist/css/bootstrap.css';  // 必須的
import 'bootstrap/dist/css/bootstrap-theme.css';  //  可選的
import App from './App';
import './index.css';

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

修改 my-app/src/App.js

import React, { Component } from 'react';
import { Grid, Navbar, Jumbotron, Button } from 'react-bootstrap';

class App extends Component {
  render() {
    return (
      <div>
        <Navbar inverse fixedTop>
          <Grid>
            <Navbar.Header>
              <Navbar.Brand>
                <a href="/">React App</a>
              </Navbar.Brand>
              <Navbar.Toggle />
            </Navbar.Header>
          </Grid>
        </Navbar>
        <Jumbotron>
          <Grid>
            <h1>Welcome to React</h1>
            <p>
              <Button
                bsStyle="success"
                bsSize="large"
                href="http://react-bootstrap.github.io/components.html"
                target="_blank">
                View React Bootstrap Docs
              </Button>
            </p>
          </Grid>
        </Jumbotron>
      </div>
    );
  }
}

export default App;

最後 執行

npm run start