逐步建置React + Webpack開發環境

Anderson
20 min readJun 16, 2017

--

webpack近幾年已經是前端的標準配備之一了,強大的功能將gulp, grunt, browserify的優點通通整合起來,以前需要學好幾種包裹,自動化流程,現在只需學一種,就是webpack。配上react和複雜的系統,以前只有後端所使用的編譯再執行,現在也發生在複雜的前端生態身上了。具備以下優點:

  • 可引入特定模組解析js,jsx,css,scss,或是圖片,字型等等
  • 可輕易載入更多客製化或彈性的loader
  • 可載入模組解析ES6,stage0~3的js語法
  • 可執行許多自動化的動作,包含最小化,加入hash等等
  • 可拆分程式碼

單元 1. 利用webpack建制簡單的react開發環境

首先,先建置如下專案結構

|--build
|----Index.html
|--containers
|----App
|------App.jsx
|----Main.jsx
|--package.json
|--webpack.config.js

執行yarn init來初始化 package.json (你也可以用npm init來處理,這邊的例子都會以yarn來做套件管理)

{
"name": "reactStarter",
"version": "1.0.0",
"description": "build the initial environment for react",
"author": "Anderson",
"license": "MIT"
}

繼續安裝以下套件(例子中的webpack套件目前會先以v2.x為主)

yarn add babel-core
yarn add babel-loader
yarn add babel-preset-es2015
yarn add babel-preset-react
yarn add react
yarn add react-dom
yarn add webpack@2.4.1
  • Index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
</head>
<body>
<div id="app"></div>
<script type="text/javascript" src="bundle.js"></script>
</body>
</html>
  • Main.jsx
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App/App.jsx';
ReactDOM.render(<App />, document.getElementById('app'))
  • App.jsx
import React from 'react';class App extends React.Component {
render() {
return <h1>My First React App</h1>;
}
}
export default App;
  • webpack.config.js
var path = require('path');module.exports = {  entry: './containers/Main.jsx',  output: {    path:  path.resolve(__dirname, 'Build/'),    filename: 'bundle.js'  }  module: {    loaders: [{      test: /\.jsx?$/,      exclude: /node_modules/,      loader: 'babel-loader',      query: {        presets: ['es2015', 'react']      }    }]  }};

entry : 需打包檔案執行的進入點,以React為例,通常是最一開始的進入點。

output: 編譯完的檔案需放置的位置和檔名。

module: 官方提供豐富的API,能幫助你編譯模組。這邊使用到的是能幫我們解析jsx的模組。

test: 利用正規表示式的寫法來讓系統了解該解讀那些格式的檔名。

exclude: 在打包時,要例外的資料夾。才不會將node_modules裡的檔案也一併打包。

loader: 因為專案會使用到es6,所以需要用Babel來解析。

query: 載入react和es6來解析React JSX和ES6語法。

程式的部分都完成了,於是我們在命令列中打上 webpack,包裹完的bundle.js就會出現在Build的資料夾下了。

最後打開就資料夾下的Index.html就大功告成了。

單元 2. 加入熱加載,達到live reload功能

上面的例子中,當我們有需要修改元件時,如果每次都要執行webpack指令,使用上就顯得相當不方便,無法立即看到修改後的成果。所以我們這單元介紹的是webpack-dev-server這個套件的使用方式。

webpack-dev-server內建小型的express server,使用webpack-dev-middleware來達到webpack的動態包裹。

首先,先來安裝webpack-dev-server

yarn add webpack-dev-server@1.16.5

在webpack.config.js中module.exports底下多加入這一段來設定預設localhost下的port,webpack有會分成以下兩種模式來自動刷新頁面,

1. iframe mode: 網址要在中間多加上webpack-dev-server的字眼,如原本localhost:7777要改為localhost:7777/webpack-dev-server/index.html。,可以注意到上面會多一條狀態列有點不美觀,所以inline比較受歡迎。

2. inline mode: 使用上有兩種分式,一種是如下在webpack.config.js多加

inline:true 這段。另一種是在指令後方直接多加參數,如webpack-dev-server --inline

devServer: {  inline: true,  port: 7777},

最後就是在package.json的script中多加入這段,然後執行 yarn run dev,連上 localhost:7777 就可以成功執行網站了。可以試著修改一下元件的內容,就會發現畫面可以自動刷新了。

"scripts": {  "dev": "webpack-dev-server --devtool eval --progress --colors --hot --content-base build"}

附帶一提,介紹一下webpack-dev-server後面參數的使用方式:

  • devtool eval: 顯示發生錯誤的行數與檔案名稱
  • progress: 顯示包裹的完成進度
  • colors: 訊息加入顏色
  • content-based build 指向專案最終輸出的資料夾,若沒有該指令,預設會指到根目錄下

單元 3. 加入css樣式

在App底下多加入App.css的檔案

|--build
|----Index.html
|--containers
|----App
|------App.js
|------App.css
|----Main.jsx
|--package.json
|--webpack.config.js

先安裝可以載入css的style-loader和css-loader

yarn add css-loader
yarn add style-loader

在webpack.config.js加入這些loader

var path = require('path');module.exports = {  entry: './containers/Main.jsx',  output: {    path:  path.resolve(__dirname, 'build/'),    filename: 'bundle.js'  },  devServer: {    inline: true,    port: 7777  },  module: {    loaders: [      {        test: /\.jsx?$/,        exclude: /node_modules/,        loader: 'babel',        query: {          presets: ['es2015', 'react']        }      },     {       test: /\.css$/,       exclude: /node_modules/,       loader: 'style-loader!css-loader',     }]   }};

最後就是在App.css中加入一段css來看結果

h1 {  color: blue;}

結果如下

單元 4. 改為載入scss

將單元3加入App.css改為App.scss,並安裝以下sass模組

yarn add sass-loaderyarn add sass-node

把webpack.config.js中,我們在單元3設定的css設定拿掉改為scss的設定

{  test: /\.scss$/,  exclude: /node_modules/,  loader: 'style-loader!css-loader!sass-loader'}

我們把原本在App.js中的html修改一下如下

...
import './App.scss';
class App extends React.Component { render() { return ( <div> <h1>My First React App!</h1> <div className="title">scss works</div> </div>); }}...

再回頭來看我們剛才改的App.scss,

$font-size: 18px;$blue: #3498DB;h1 {  color: lighten($blue, 10%);}.title {  font-size: $font-size;}

結果如下

單元 5. 處理圖片

先安裝可以處理圖片的模組url-loaderfile-loader

yarn add url-loaderyarn add file-loader

webpack.config.js多加入以下設定,其中限制10k以下會被壓縮成base64的字串來減低圖片傳輸的流量

{  test: /\.(png|jpg)$/,  loader: 'url?limit=10240'}

在資料夾中插入一張圖案,並建立對應的資料夾

|--build
|----Index.html
|--containers
|----App
|------App.js
|------App.scss
|----Main.jsx
|--static
|----images
|------baby.jpg

|--package.json
|--webpack.config.js

在App.js中插入程式碼

...class App extends React.Component {  render() {    return (    <div>      <h1>My First React App!</h1>      <div className="title">scss works</div>      <img src={require('../../static/images/baby.jpg')} alt="Smiley face" height="400" width="300" />    </div>);  }}...

單元 6. 使用bootstrap 4

專案目錄改為如下,會新增以下黑體部分的檔案,

|--build
|----Index.html
|--containers
|----App
|------App.js
|------App.scss
|----Main.jsx
|--static
|----images
|------baby.jpg
|--styles
|----bootstrap
|------pre-customizations.scss
|------customizations.scss
|----fonts
|------OpenSans-Light.ttf
|----global.scss

|--.bootstraprc
|--postcss.config.js
|--package.json
|--webpack.config.js

在開始之前,先載入bootstrap對應的套件,

yarn add bootstrap@4.0.0-alpha.6 bootstrap-sass bootstrap-loader postcss-loader resolve-url-loader

新增完套件後,再來就是修改對應的設定讓bootstrap生效。

  • .bootstraprc
# Output debugging info
loglevel: debug
# Major version of Bootstrap: 3 or 4
bootstrapVersion: 4
# If Bootstrap version 4 is used - turn on/off flexbox model
useFlexbox: true
# Webpack loaders, order matters
styleLoaders:
- style-loader
- css-loader
- postcss-loader
- sass-loader
# Extract styles to stand-alone css file
# Different settings for different environments can be used,
# It depends on value of NODE_ENV environment variable
# This param can also be set in webpack config:
# entry: 'bootstrap-loader/extractStyles'
extractStyles: false
# env:
# development:
# extractStyles: false
# production:
# extractStyles: true
# Customize Bootstrap variables that get imported before the
# original Bootstrap variables. Thus original Bootstrap variables
# can depend on values from here. All the bootstrap variables are
# configured with !default, and thus, if you define the variable
# here, then that value is used, rather than the default. However,
# many bootstrap variables are derived from other bootstrap
# variables, and thus, you want to set this up before we load the
# official bootstrap versions. For example, _variables.scss
# contains: $input-color: $gray !default; This means you can define # $input-color before we load _variables.scss
preBootstrapCustomizations: ./styles/bootstrap/pre-customizations.scss
# This gets loaded after bootstrap/variables is loaded and before
# bootstrap is loaded. A good example of this is when you want to
# override a bootstrap variable to be based on the default value of # bootstrap. This is pretty specialized case. Thus, you normally
# just override bootrap variables in preBootstrapCustomizations so
# that derived variables will use your definition.
# For example, in _variables.scss:
# $input-height: (($font-size-base * $line-height) + ($input-padding-y * 2) + ($border-width * 2)) !default;
# This means that you could define this yourself in
# preBootstrapCustomizations. Or you can do this in
# bootstrapCustomizations to make the input height 10% bigger than the default calculation.
# Thus you can leverage the default calculations.
# $input-height: $input-height * 1.10;
bootstrapCustomizations: ./styles/bootstrap/customizations.scss
# Import your custom styles here. You have access to all the
# bootstrap variables. If you require your sass files separately,
# you will not have access to the bootstrap variables, mixins,
# clases, etc. Usually this endpoint-file contains list of @imports # of your application styles.
appStyles: ./styles/global.scss
### Bootstrap styles
styles:
# Mixins
mixins: true
# Reset and dependencies
normalize: true
print: true
# Core CSS
buttons: true
code: true
forms: true
grid: true
images: true
reboot: true
tables: true
type: true
# Components
alert: true
badge: true
breadcrumb: true
button-group: true
card: true
close: true
custom-forms: true
dropdown: true
input-group: true
jumbotron: true
list-group: true
media: true
nav: true
navbar: true
pagination: true
progress: true
responsive-embed: true
transitions: true
# Components JavaScript
carousel: false
modal: false
popover: false
tooltip: false
# Utility classes
utilities: true
### Bootstrap scripts
scripts: false

這邊使用YAML的語法,因為使用的是bootstrap 4版的,所以bootstrapVersion 設為4。另外要載入我們在先前章節提到的style-loader, css-loader, sass-loader, 另外多載入一個postcss-loader。preBootstrapCustomizations 會在載入bootstrap前載入,而bootstrapCustomizations 則會在載入bootstrap後載入,所以可以去複寫掉bootstrap內建的一些設定,改成客製化的基本設定,記得路徑要設為專案底下對應的檔案路徑。中間的屬性則是否需要載入的bootstrap元件,可看需要設定。因為使用react,所以不需要另外載入jquery,除了把components 中javascript區塊中的都設為false,以及最後一段的script: false。如果需要在CLI中看載入元件的過程,可以在第一行的loglevel: debug,如果不需要則設為false。

  • pre-customizations.scss
$font-family-sans-serif: 'OpenSans-Light', Tahoma, "Helvetica Neue", Helvetica, Arial, sans-serif;// This path is relative to this file!
$fonts-url-path: '../fonts';
@font-face {
font-family: 'OpenSans-Light';
src: url('#{$fonts-url-path}/OpenSans-Light.ttf') format('truetype');
}

在載入bootstrap之前,先載入字型檔,而其中的$fonts-url-path為該檔案的相對路徑,去參考專案中所引入的字型檔。

  • customizations.scss
// Customize variables
$btn-secondary-bg: #6e88b7;
$btn-secondary-color: #fff;

在載入bootstrap之後,我們可以複寫掉bootstrap的設定。

  • global.scss
html, body {
height: 100%;
background-color: #333;
}
body {
color: #fff;
text-align: center;
text-shadow: 0 .05rem .1rem rgba(0,0,0,.5);
background: url('../static/images/sample.jpg') no-repeat;
background-size: 100% 100%;
}
#app {
display: initial;
}
  • webpack.config.js
...
module.exports = {
entry: [ 'bootstrap-loader', './containers/Main.jsx' ],
...
module: {
loaders: [
...
{
test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/,
loader: 'url-loader',
options: {
limit: 10000,
mimetype: 'application/octet-stream'
}
},
},

--

--

Anderson

A front-end developer, like the pursuit of beautiful things in life. Hope you like my blog.