在使用webpack建立ReactJS專案之前,要先安裝nodeJS,安裝完後
輸入指令
node -v
以及
npm -v
可查看現在使用的版本
我使用的版本是
node v6.2.1
npm v3.10.3
確定npm安裝完成後即可開始接下來的步驟
以下步驟參考 build-a-hn-front-page
使用指令
npm install webpack@1.13.1 --save-dev
將webpack安裝進專案資料夾中
--save-dev
代表將webpack安裝儲存於專案目錄下
這句指令可簡寫成
npm i webpack@1.13.1 -D
npm install
可簡寫成npm i
--save-dev
可以簡寫成-D
npm install
相關指令簡寫可參考 install | npm Document
--save-dev
指令代表安裝在哪參考 WEBPACK入門教學筆記
按照以下結構建立檔案與資料夾來測試環境設定:
- app/
- main.js
- build/
- index.html
- (bundle.js) (此檔案不用建立,使用
webpack
指令後會自動生成)
- package.json
- webpack.config.js
/* webpack.config.js */
var path = require('path');
module.exports = {
entry: [path.resolve(__dirname, 'app/main.js')],
output: {
path: path.resolve(__dirname, 'build'),
filename: 'bundle.js'
}
};
我們先測試一下
document.write('It works');
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
</head>
<body>
<script src="bundle.js"></script>
</body>
</html>
html中script tag引入了 bundle.js, 這是執行webpack
指令後,將js打包之後輸出的檔案
輸入指令
webpack
用瀏覽器開啟build資料夾中的index.html,如果成功的話,會看到瀏覽器顯示 It works
在專案目錄下用指令
npm init
建立 package.json ,建立的過程中全按enter就好
{
...
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "./node_modules/.bin/webpack"
}
}
現在輸入指令
npm run build
就等同於輸入指令./node_modules/.bin/webpack
為什麼不直接用webpack
而是前面要用./node_modules/.bin/webpack
呢?
原因可參考 cleaner installation for developers (Evernote) 或 cleaner installation for developers (影片 REACT JS TUTORIAL #1) 8:51 ~ 10:08
安裝 React
npm i react@15.2.1 react-dom@15.2.1 -D
安装 Babel 的 loader 以支持 ES6 語法
npm i babel-core@6.10.4 babel-loader@6.2.4 babel-preset-es2015@6.9.0 babel-preset-react@6.11.1 -D
/* webpack.config.js */
var path = require('path');
module.exports = {
entry: [path.resolve(__dirname, 'app/main.js')],
output: {
path: path.resolve(__dirname, 'build'),
filename: 'bundle.js'
},
module: {
loaders: [
{
test: /\.js$/,
loaders: ['babel?presets[]=es2015,presets[]=react']
}
]
}
};
此處test的語法為正規表示法(Regular Expression)
loaders相關的語法參考
stackoverflow: Webpack - Error: Cannot define 'query' and multiple loaders in loaders list
測試一下開發環境是否建立完成
/* main.js */
import React from 'react';
import { render } from 'react-dom';
export default class Main extends React.Component {
render() {
return (
<div>Hello World</div>
);
}
};
var myElement = document.getElementById('content');
render(<Main />, myElement);
Hello World被放進id為content的DOM元素中
所以需要增加元件到index.html中
...
<body>
<div id="content"></div>
<script src="./bundle.js"></script>
</body>
...
經過以上步驟,我們已將所有的環境設定建立完成
此專案程式碼參考 Rhadow's Tech Note
按照以下結構建立檔案與資料夾來建立專案:
- app/
- main.js
- TestOne.js
- TestTwo.js
- build/
- index.html
- (bundle.js) (此檔案不用建立,使用
webpack
指令後會自動生成)
- package.json
- webpack.config.js
/* TestOne.js */
import React from 'react';
export default class TestOne extends React.Component {
render() {
return (
<div>Hello I am TestOne Component</div>
);
}
};
/* TestTwo.js */
import React from 'react';
export default class TestTwo extends React.Component {
render() {
return (
<h1>Hello I am TestTwo Component</h1>
);
}
};
import和export為es6的語法
用法可參考 module (Evernote) 或者是 module (影片 Javascript ES6 Cheatsheet #2) 13:33 ~ 17:37
class和extend也是es6的語法
用法可參考 class (Evernote) 或者是 class (影片 Javascript ES6 Cheatsheet #2) 4:32 ~ 7:11
export default用法參考 ES6 Modules (Evernote)
React es6的寫法和之前版本寫法的差異
可參考 React.createClass versus extends React.Component
/* main.js */
import React from 'react';
import { render } from 'react-dom';
import TestOne from './TestOne.js';
import TestTwo from './TestTwo.js';
export default class Main extends React.Component {
constructor() {
super();
this.state = {
isActive: false
};
this.handleClick = this.handleClick.bind(this);
}
handleClick () {
var active = !this.state.isActive;
this.setState({ isActive: active });
}
render() {
return (
<div>
<input type="button" onClick={this.handleClick} value="Press Me!"/>
{this.state.isActive ? <TestTwo /> : <TestOne />}
</div>
);
}
};
var myElement = document.getElementById('content');
render(<Main />, myElement);
{ render } 為es6 destructuring assignment的寫法
詳細用法可參考 desturcturing assignment (Evernote) 或 destructuring assignment (影片 Javascript ES6 Cheatsheet) 1:35 ~ 10:51
關於component state的寫法
可參考 Best Practices for Component State in React.js
這邊要注意一點
React的createClass方法(舊寫法)的this的context一律對應到class
但React class extends React.Component(es6,新寫法)中method裡的this的context會改變
所以要記得在後面加bind(this)
最後用瀏覽器開啟 build/index.html
無打字錯誤的話,應該能看到功能執行
以下內容參考 Rhadow's Tech Note
每次我們修改程式碼,想要看到執行結果時,都必須要輸入指令 npm run build
還要到瀏覽器重新整理,非常麻煩
webpack-dev-server 讓我們可以省去這個麻煩
首先,先安裝webpack-dev-server,執行指令
npm i webpack-dev-server@1.14.1 -D
{
...
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "./node_modules/.bin/webpack",
"dev": "./node_modules/.bin/webpack-dev-server --devtool eval --progress --colors --inline --hot --content-base build"
},
...
}
dev內的指令解釋如下:
webpack-dev-server 會在 localhost:8080 建立起專案的 server
--devtool eval
會顯示出發生錯誤的行數與檔案名稱
--progress
會顯示出打包的過程
--colors
會幫 webpack 顯示的訊息加入顏色
--inline --hot
參考 inline (Evernote) 或 inline (影片REACT JS TUTORIAL #1) 8:01 ~ 8:50
--content-base build
指向專案最終輸出的資料夾build
var path = require('path');
module.exports = {
entry: ['webpack/hot/dev-server', path.resolve(__dirname, 'app/main.js')],
output: {
path: path.resolve(__dirname, 'build'),
filename: 'bundle.js'
},
module: {
loaders: [
{
test: /\.js$/,
loaders: ['babel?presets[]=es2015,presets[]=react']
}
]
}
};
這樣就完成 LiveReload 的功能囉
執行 npm run dev
後開啟瀏覽器到 http://localhost:8080/
檢查程式是否正確執行
也可試著改變程式碼,觀察瀏覽器畫面是否有跟著變動。
以下內容參考 Rhadow's Tech Note
如果有跟著以上每一步做的話
程式跑起來後應該會有一個按鈕和 “Hello, I’m TestOne Component” 的字出現在畫面上。
重複點擊按鈕,”Hello, I’m TestOne Component” 和 “Hello, I’m TestTwo Component” 會輪流出現在畫面上。
現在請將畫面切到顯示 “Hello, I’m TestTwo Component” ,並到 TestTwo.js 將 “Hello, I’m TestTwo Component” 改為 “Hello” 後儲存。
完成後,會發現瀏覽器畫面會回到最初的 “Hello, I’m TestOne Component”,這是因為 LiveReload 會重新整理畫面並將 React 元件的 state 重設回到初始值。
react-hot-loader 的功用就是在不清除 state 的狀態下更新畫面
讓我們趕快來看看該如何達成這個效果吧。
npm i react-hot-loader@1.3.0 -D
...
module.exports = {
entry: [
'webpack-dev-server/client?http://localhost:8080',
'webpack/hot/only-dev-server',
path.resolve(__dirname, 'app/main.js')
],
...
}
再來修改 module 屬性如下:
...
module.exports = {
...
module: {
loaders: [
{
test: /\.js$/,
loaders: ['react-hot', 'babel?presets[]=es2015,presets[]=react'],
include: path.join(__dirname, 'app')
}
]
},
要特別注意的地方是一定要加入 include: path.join(__dirname, 'app')
,不然 webpack
會把 node_modules 內的 js 檔都透過 react-hot-loader
跑一遍,會因此導致 Cannot read property ‘NODE_ENV’ of undefined
錯誤造成程式無法正常運作。
...
var webpack = require('webpack');
module.exports = {
...
plugins: [
//new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin()
]
}
這邊的 webpack.NoErrorsPlugin()
是選擇性的,主要的功能是當更改完的程式碼有語法錯誤時不要重新整理。 當錯誤修好後,畫面會自動重新整理。
new webpack.HotModuleReplacementPlugin()
則是當不使用 webpack-dev-server
的 inline-mode 時才需要加入。本例是使用 inline-mode,你可以試著把註解掉的部分加回程式裡,當元件在做更新時,會出現 Uncaught RangeError: Maximum call stack size exceeded
的錯誤訊息。inline-mode 詳情請參考 webpack-dev-server
說明。
現在執行 npm run dev
後再一次做本段落最開始的小實驗,就會發現更改程式碼後,畫面不會再回到首頁了。
顯示Hello I am TestOne Component
按下Click me按鈕後
變成顯示粗體的Hello I am TestOne Component