Hello!大家好啊!記得我們昨天裝了node.js
、npm
、webpack
還用npm init
建立一個專案,然後用webpack -p
打包了一個JavaScript
檔案讓網站使用,好的!前情堤要結束,如果大家還記得上述的這些,那就可以繼續下去,忘記的話可以到上一篇複習一下XD,不過說了那麼多,本次的主題還是得回到React
,所以請跟著這一個下篇一起把React
給引用到專案中吧!
沒錯,瞬間切入主題就是我的風格(才怪),趁著手感還熱著,來用npm
下載React
吧!這裡我們使用--save
讓他下載完套件直接幫我們記錄到package.json
裡面,至於為什麼不用--save-dev
昨天有說過了,因為React
不只開發的時候會用到,發佈後依然也需要使用他。
貼心小提醒,如果命令提示字元的當前路徑沒有在專案的資料夾,記得要先cd
到專案路徑哦!確定沒問題後就輸入以下指令吧!
npm install react --save
npm install react-dom --save
PS一下,要同時下載多個套件也可以寫成下方這樣:
npm install react react-dom --save
稍待片刻,下載好後會如下圖:
也可以觀察package.json
中的變化,react
和react-dom
已經被加到dependencies
中了:
如果我們要寫CSS
那就建立一個副檔名為.css
的檔案;寫JavaScript
的話副檔名是.js
,而JSX
檔案的副檔名當然只能是.jsx
啦!所以第一步就先建立一個app.jsx
吧!在文件裡我們可以先import
剛剛下載的react
和react-dom
:
import React from 'react';
import ReactDOM from 'react-dom';
接著記得我們第一天的時候,有簡單的使用React
建立一個簡單的物件嗎?沒關係我們來複習一下!把那一段程式放到app.jsx
中:
import React from 'react';
import ReactDOM from 'react-dom';
//建立一個DOM物件
let element = <h1>Hello, world!</h1>
//使用ReactDOM.render把剛建立的物件element插入目標DOM中
ReactDOM.render(
element,
document.getElementById('root')
);
改完JavaScript
後,大家應該知道再來要改什麼吧!沒錯就是HTML
,因為要把用React
建立的物件插入目標DOM
中,所以我們在昨天的HTML
中加個id=root
的標籤吧!
<html>
<head>
</head>
<body>
第一個webpack網站
<div id="root"></div>
<!--這裡只需要嵌入webpack幫我們打包後的js檔就好,這裡指定剛剛output的檔案名稱bundle.js-->
<script src="bundle.js"></script>
</body>
</html>
到這裡,今天的進度已經到達百分之四十了!有沒有很感動,其實我比各位感動XD
記得一開始說過,瀏覽器只讀得懂HTML
、CSS
和JavaScript
嗎?突然發現這一篇都在回憶XD,好那不是重點,重點是,既然瀏覽器看不懂JSX
那我們上方建立的app.jsx
就一點意義也沒有。那時候我們有個法寶Babel
可以使用,而現在...也有!老樣子,透過npm
下載吧!
不過我們可不是直接下載Babel
,當使用webpack
打包檔案的時候,可以使用loader
系列的套件,來讓webpack
知道說,什麼樣子的檔案要用什麼loader
來編譯,所以下方要下載的是babel-loader
,這裡使用--save-dev
是因為我只有在開發完成後透過webpack
打包才需要,真正上線後已經是打包後的JavaScript
了,所以加入devDependencies
中:
npm install babel-loader --save-dev
裝好後如上圖,可以在藍線處看到目前最新的版本是@8.0.2
,根據官方文件,版本8以上的Babel
核心套件@babel/core
和版本7的babel-core
不同,所以務必要確認好自己的版本後在下載對應的核心套件,上方我下載的版本是@8.0.2
,對應到的核心套件是@babel/core
,所以也要把他下載下來:
npm install @babel/core --save-dev
有了Babel
和他的核心@babel/core
外,還要下載針對JSX
處理的@babel/preset-react
(關於他的文件在這裡),webpack
會在打包的時候依照選擇的preset
把檔案編譯成JacaSc6ript
,一起來下載這個preset
吧!
npm install @babel/preset-react --save-dev
到這裡基本上就告一段落了,另外PS一下,既然有處理JSX
的preset
套件,那也會有翻譯ES6
語法的preset
,所以當我去翻了又翻官方的文件後找到這一篇關於@babel/preset-es2015的官方文件,他在名稱下面就註明了一行:
As of Babel v6, all the yearly presets have been deprecated We recommend using @babel/preset-env instead.
意思是「Babel
的版本號在6以後,會建議直接使用@babel/preset-env
。」
@babel/preset-env
的官方文件在這裡,裡面也有一段是這麼說的:
Sidenote, if no targets are specified, @babel/preset-env behaves exactly the same as @babel/preset-es2015
也就是說在一般情況下,@babel/preset-env
的用途是和@babel/preset-es2015
相同的。考慮到我們在未來剩下的27或更多篇中應該會使用到ES6
的語法,所以趁現在一起裝上吧!
npm install @babel/preset-env --save-dev
最後的最後,因為webpack
並不曉得我們下載了上面那些東西,那要怎麼讓他知道呢?欸嘿嘿!想到webpack
的設定,就只能是webpack.config.js
了!
上一篇的時候,我們只在webpack.config.js
上設定了需要打包哪些檔案,但卻沒有告訴他,這些類型的檔案該以什麼形式去編譯打包,例如需要在JavaScript
中將ES6
的語法用上面下載的preset-env
轉換成ES5
,或是要將JSX
使用preset-react
編譯成一般的JavaScript
,這些都是要另外設定的,這些loader
的設定會寫在module
的rules
中,讓我們來看看怎麼做吧!
const path = require('path');
module.exports = {
//如果有一個以上的檔案需要打包,可以傳陣列給entry
entry: ['./index.js', './app.jsx'],
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, './'),
},
//將loader的設定寫在module的rules屬性中
module: {
//rules的值是一個陣列可以存放多個loader物件
rules: [
{ test: /.jsx$/, exclude: /node_modules/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-react'] } } }
]
}
};
設定一個loader
物件他會有幾個屬性,讓我們在下方一一說明:
test
:.jsx
的檔案。exclude
:server
的時候,其實不會連node_nodules
資料夾也一起放上去,所以就不需要特別編譯他了。use
:loader
,這個物件裡面還有兩個屬性:loader
:指定進行編譯的套件,這裡指定剛剛下載的babel-loader
。option
:指定loader
套件中的presets
是哪一個,因為我們要編譯的是JSX
,所以這裡輸入@babel/preset-react
。經過上方的說明,我們已經知道如何設定loader
了,但是上方的webpack.config.js
中我只設定了JSX
的loader
而已,大家可以試著添加編譯ES6
語法的loader
試試看,加完可以往下看設定的正不正確!
加上編譯ES6
的loader
:
const path = require('path');
module.exports = {
entry: ['./index.js', './app.jsx'],
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, './'),
},
module: {
rules: [
//第一個loader編譯JSX
{ test: /.jsx$/, exclude: /node_modules/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-react'] } } },
//第二個loader編譯ES6
{ test: /.js$/, exclude: /node_modules/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env'] } } }
]
}
};
下載完也設定完,現在總可以來打包了吧?沒錯!如你所願,接著就來打包吧!還記得是什麼指令嗎?往下看:
webpack -p
打包完後依然會產生出一個bundle.js
,發現打包過後的檔案內容爆炸性的增加了很多,那是因為webpack
也把React
給一起打包了:
但是打包過後的檔案那麼難以閱讀,要怎麼確認他有沒有正確的將JavaScript
的語法轉換成ES6
呢?可以來做個小實驗,我們打開index.js
並將ES6
的語法加進內容後在進行打包:
let strA = 'Hello, '
strA += 'GQSM!'
console.log(strA)
接著再看看bundle.js
的內容,會發現下方紅線處原本用let
宣告的地方被編譯成var
了:
但是眼尖的大大應該也有發現,綠線的部分還是有ES6
的語法啊!這樣還是沒有編譯完全吧?對的!因為在處理.jsx
的時候,我們只是將JSX
的語法轉換成JavaScript
,所以他並不會特別在去翻成ES5
,那該怎麼辦呢?非常簡單!我們只需要在處理.jsx
時將負責編譯ES6
語法的@babel/preset-env
加進preset
的陣列內就可以了,如下:
//編譯JSX的loader,將@babel/preset-env加進preset中
{ test: /.jsx$/, exclude: /node_modules/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-react','@babel/preset-env'] } } }
加上去後再打包,重新看一下bundle.js
,內容已經全被編譯成ES5
的語法了:
總算編譯完了對吧?那就打開index.html
看最後的成果,這可是第一個用webpack
環境使用React
的網站呢!
雖然可以像上方一樣直接打開index.html
看畫面,但是開發階段的時候,常常會需要測試目前網站運行的狀況是不是正確的,每改一個地方,就必須要重新刷新一次網頁,真的超麻煩的對吧?像是做一個會員註冊系統,明明只有一個欄位判斷錯誤,改完那一行程式後卻還因為要測試有沒有問題而再填寫一次所有欄位,也太麻煩了!
現在要介紹的套件webpack-dev-server
絕對可以縮短花在測試的時間,光看名字就知道,他也是webpack
的官方套件,所以我們輸入以下指令安裝:
npm i webpack-dev-server --save-dev
安裝後我們要在webpack.config.js
中增加devserver
的一些設定,例如要開啟的port
,當然如果沒有特別設定的話他port
的預設值為8080,以下為了區隔所以設定9000:
const path = require('path');
module.exports = {
entry: ['./index.js', './app.jsx'],
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, './'),
},
//將loader的設定寫在module的rules屬性中
module: {
//rules的值是一個陣列可以存放多個loader物件
rules: [
{ test: /.jsx$/, exclude: /node_modules/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-react', '@babel/preset-env'] } } },
{ test: /.js$/, exclude: /node_modules/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env'] } } }
]
},
//增加一個給devserver的設定
devServer: {
//指定開啟port為9000
port: 9000
}
};
完成後在命令提示字元中輸入:
webpack-dev-server
沒問題的話webpack-dev-server
會開始處理檔案,處理完後會顯示開啟的port
(下方藍字):
接著用本機IP或localhost來打開9000port吧!
另外感覺指令越來越長了,記得我們前一篇在建立npm
專案時有個這個選項嗎?
如果是直接使用npm init -y
的話也可以在package.json
中看到他,如下紅框處(有沒有覺得突然變豐富很多XD):
我們可以在script
屬性中自訂指令名稱,以及該指令名稱對應到何種指令,像是我把指令webpack-dev-server
的名稱設定成open
:
"scripts": {
"open": "webpack-dev-server"
},
設定完後試著在命令提示字元中輸入npm run
加上指令名稱,例如我的是:
npm run open
執行時就會自動去設定檔把該名稱的指令取出來,是個很方便的功能,尤其是之後指令可能會有一大串的時候XD
現在把話說回來webpack-dev-server
能怎麼縮減開發的時間呢?經過上面的教學都知道,我們現在的基本流程是「改檔案儲存」>「打包」>「執行」>「重新整理看結果」,超麻煩的對吧?光是經過編譯就讓我覺得累了,還得在編譯完後重新整理才能測試,但現在!webpacl-dev-server
能讓剛剛那段流程簡化為「改檔案儲存」結束,是不是超簡化XD,我可沒騙你,讓我們來改一下app.jsx
中的Hello, world
文字:
神奇的一刻在儲存後發生:
完全不需要重新打包,也不用F5刷新,webpack
就幫你做好所有的事情,是不是太棒了
那最後如果要關閉webpack-dev-server
開啟的port號,在命令提示字元的畫面上輸入Ctrl+C
就可以了,小弟我以下是直接按兩次Ctrl+c
:
以上和前一篇就是從零到有的webpack
體驗,是真的摸了很久XD,希望各位看了文章都可以快速上手,不會像我一樣這邊卡一下那邊又卡一下,下一篇就會正式進入React
篇了,終於的感覺,哈哈XD,之後的一段路還請大家多多指教!
如果文章中有任何問題或是解釋不清楚的地方,還麻煩各位大大留言告訴我,我會盡快回答及修正文章內容,感謝大家的觀看
你好,寫得真的很詳細,
不過我有遇到一個問題
我直接在命令提示字元執行 webpack-dev-server
,
他會顯示錯誤
倒是在 npm.script 把設定寫好後就可以了。
後來我是用npx webpack-dev-server
,
不曉得你是如何直接輸入就可以使用的
感謝
Hello!你可以嘗試看看將 webpack-dev-server
裝在全域中:npm i -g webpack-dev-server
再執行確認看看!
好的,感謝
也是遇到'webpack-dev-server' 不是內部或外部命令、可執行的程式或批次檔。
重裝在全域就OK了!
要在專案中 devDependencies
安裝 webpack, webpack-cli
, 如果在全域裝 webpack, webpack-cli
但 webpack-dev-server
不是全域安裝,就會報錯