iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 3
8
Modern Web

一步一腳印的React旅程系列 第 3

[筆記][React]從零到一的webpack開發環境(2)-React開發篇

Hello!大家好啊!記得我們昨天裝了node.jsnpmwebpack還用npm init建立一個專案,然後用webpack -p打包了一個JavaScript檔案讓網站使用,好的!前情堤要結束,如果大家還記得上述的這些,那就可以繼續下去,忘記的話可以到上一篇複習一下XD,不過說了那麼多,本次的主題還是得回到React,所以請跟著這一個下篇一起把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

稍待片刻,下載好後會如下圖:
https://ithelp.ithome.com.tw/upload/images/20180912/20106935xcvN0Cawn4.jpg

也可以觀察package.json中的變化,reactreact-dom已經被加到dependencies中了:
https://ithelp.ithome.com.tw/upload/images/20180912/20106935oaGiVQABMy.jpg

用JSX寫React

如果我們要寫CSS那就建立一個副檔名為.css的檔案;寫JavaScript的話副檔名是.js,而JSX檔案的副檔名當然只能是.jsx啦!所以第一步就先建立一個app.jsx吧!在文件裡我們可以先import剛剛下載的reactreact-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

使用loader

記得一開始說過,瀏覽器只讀得懂HTMLCSSJavaScript嗎?突然發現這一篇都在回憶XD,好那不是重點,重點是,既然瀏覽器看不懂JSX那我們上方建立的app.jsx就一點意義也沒有。那時候我們有個法寶Babel可以使用,而現在...也有!老樣子,透過npm下載吧!

不過我們可不是直接下載Babel,當使用webpack打包檔案的時候,可以使用loader系列的套件,來讓webpack知道說,什麼樣子的檔案要用什麼loader來編譯,所以下方要下載的是babel-loader,這裡使用--save-dev是因為我只有在開發完成後透過webpack打包才需要,真正上線後已經是打包後的JavaScript了,所以加入devDependencies中:

npm install babel-loader --save-dev

https://ithelp.ithome.com.tw/upload/images/20180912/20106935g04zyNKbIm.jpg
裝好後如上圖,可以在藍線處看到目前最新的版本是@8.0.2,根據官方文件,版本8以上的Babel核心套件@babel/core和版本7的babel-core不同,所以務必要確認好自己的版本後在下載對應的核心套件,上方我下載的版本是@8.0.2,對應到的核心套件是@babel/core,所以也要把他下載下來:

npm install @babel/core --save-dev

https://ithelp.ithome.com.tw/upload/images/20180913/20106935dKJPcY6QNK.jpg

有了Babel和他的核心@babel/core外,還要下載針對JSX處理的@babel/preset-react(關於他的文件在這裡),webpack會在打包的時候依照選擇的preset把檔案編譯成JacaSc6ript,一起來下載這個preset吧!

npm install @babel/preset-react --save-dev

https://ithelp.ithome.com.tw/upload/images/20180913/20106935ytnj7lAifg.jpg

到這裡基本上就告一段落了,另外PS一下,既然有處理JSXpreset套件,那也會有翻譯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

https://ithelp.ithome.com.tw/upload/images/20180913/20106935wtZoR3zmTt.jpg

最後的最後,因為webpack並不曉得我們下載了上面那些東西,那要怎麼讓他知道呢?欸嘿嘿!想到webpack的設定,就只能是webpack.config.js了!

上一篇的時候,我們只在webpack.config.js上設定了需要打包哪些檔案,但卻沒有告訴他,這些類型的檔案該以什麼形式去編譯打包,例如需要在JavaScript中將ES6的語法用上面下載的preset-env轉換成ES5,或是要將JSX使用preset-react編譯成一般的JavaScript,這些都是要另外設定的,這些loader的設定會寫在modulerules中,讓我們來看看怎麼做吧!

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物件他會有幾個屬性,讓我們在下方一一說明:

  1. test
    指定編譯檔案的副檔名為何,用正規表達式來尋找結尾處為.jsx的檔案。
  2. exclude
    指定不編譯的路徑,因為我們把網站上傳到server的時候,其實不會連node_nodules資料夾也一起放上去,所以就不需要特別編譯他了。
  3. use
    指定用來編譯符合副檔名條件的loader,這個物件裡面還有兩個屬性:
    1.loader:指定進行編譯的套件,這裡指定剛剛下載的babel-loader
    2.option:指定loader套件中的presets是哪一個,因為我們要編譯的是JSX,所以這裡輸入@babel/preset-react

經過上方的說明,我們已經知道如何設定loader了,但是上方的webpack.config.js中我只設定了JSXloader而已,大家可以試著添加編譯ES6語法的loader試試看,加完可以往下看設定的正不正確!

加上編譯ES6loader

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給一起打包了:
https://ithelp.ithome.com.tw/upload/images/20180915/20106935hAJvcLSmHS.jpg

但是打包過後的檔案那麼難以閱讀,要怎麼確認他有沒有正確的將JavaScript的語法轉換成ES6呢?可以來做個小實驗,我們打開index.js並將ES6的語法加進內容後在進行打包:

let strA = 'Hello, '
strA += 'GQSM!'

console.log(strA)

接著再看看bundle.js的內容,會發現下方紅線處原本用let宣告的地方被編譯成var了:
https://ithelp.ithome.com.tw/upload/images/20180915/201069354jm57OehZr.jpg

但是眼尖的大大應該也有發現,綠線的部分還是有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的語法了:
https://ithelp.ithome.com.tw/upload/images/20180916/20106935kA97MHglgM.jpg

總算編譯完了對吧?那就打開index.html看最後的成果,這可是第一個用webpack環境使用React的網站呢!
https://ithelp.ithome.com.tw/upload/images/20180916/20106935e7SyE9tnuu.jpg

開啟本機伺服器

雖然可以像上方一樣直接打開index.html看畫面,但是開發階段的時候,常常會需要測試目前網站運行的狀況是不是正確的,每改一個地方,就必須要重新刷新一次網頁,真的超麻煩的對吧?像是做一個會員註冊系統,明明只有一個欄位判斷錯誤,改完那一行程式後卻還因為要測試有沒有問題而再填寫一次所有欄位,也太麻煩了!

現在要介紹的套件webpack-dev-server絕對可以縮短花在測試的時間,光看名字就知道,他也是webpack的官方套件,所以我們輸入以下指令安裝:

npm i webpack-dev-server --save-dev

https://ithelp.ithome.com.tw/upload/images/20180916/201069350vVBF3qrx2.jpg
安裝後我們要在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(下方藍字):
https://ithelp.ithome.com.tw/upload/images/20180916/20106935dxvYJ2OSzA.jpg

接著用本機IP或localhost來打開9000port吧!
https://ithelp.ithome.com.tw/upload/images/20180916/20106935QlIVAaaMp4.jpg

另外感覺指令越來越長了,記得我們前一篇在建立npm專案時有個這個選項嗎?
https://ithelp.ithome.com.tw/upload/images/20180916/20106935GT8heD8G4p.jpg

如果是直接使用npm init -y的話也可以在package.json中看到他,如下紅框處(有沒有覺得突然變豐富很多XD):
https://ithelp.ithome.com.tw/upload/images/20180916/201069350SuNnuEWOx.jpg

我們可以在script屬性中自訂指令名稱,以及該指令名稱對應到何種指令,像是我把指令webpack-dev-server的名稱設定成open

"scripts": {
    "open": "webpack-dev-server"
},

設定完後試著在命令提示字元中輸入npm run加上指令名稱,例如我的是:

npm run open

執行時就會自動去設定檔把該名稱的指令取出來,是個很方便的功能,尤其是之後指令可能會有一大串的時候XD
https://ithelp.ithome.com.tw/upload/images/20180916/20106935GiyxYhGw91.jpg

現在把話說回來webpack-dev-server能怎麼縮減開發的時間呢?經過上面的教學都知道,我們現在的基本流程是「改檔案儲存」>「打包」>「執行」>「重新整理看結果」,超麻煩的對吧?光是經過編譯就讓我覺得累了,還得在編譯完後重新整理才能測試,但現在!webpacl-dev-server能讓剛剛那段流程簡化為「改檔案儲存」結束,是不是超簡化XD,我可沒騙你,讓我們來改一下app.jsx中的Hello, world文字:
https://ithelp.ithome.com.tw/upload/images/20180916/20106935k7BsAmsopu.jpg

神奇的一刻在儲存後發生:
https://ithelp.ithome.com.tw/upload/images/20180916/20106935QRwoiLFQkk.jpg

完全不需要重新打包,也不用F5刷新,webpack就幫你做好所有的事情,是不是太棒了/images/emoticon/emoticon42.gif

那最後如果要關閉webpack-dev-server開啟的port號,在命令提示字元的畫面上輸入Ctrl+C就可以了,小弟我以下是直接按兩次Ctrl+c
https://ithelp.ithome.com.tw/upload/images/20180916/20106935ilZuA8UXlV.jpg


以上和前一篇就是從零到有的webpack體驗,是真的摸了很久XD,希望各位看了文章都可以快速上手,不會像我一樣這邊卡一下那邊又卡一下,下一篇就會正式進入React篇了,終於的感覺,哈哈XD,之後的一段路還請大家多多指教!

如果文章中有任何問題或是解釋不清楚的地方,還麻煩各位大大留言告訴我,我會盡快回答及修正文章內容,感謝大家的觀看/images/emoticon/emoticon41.gif


上一篇
[筆記][React]從零到一的webpack開發環境(1)-安裝執行篇
下一篇
[筆記][React]關於Components的那件小事
系列文
一步一腳印的React旅程30

1 則留言

1
Peter
iT邦新手 5 級 ‧ 2019-02-19 15:02:41

你好,寫得真的很詳細,
不過我有遇到一個問題
我直接在命令提示字元執行 webpack-dev-server
他會顯示錯誤
https://ithelp.ithome.com.tw/upload/images/20190219/20112368ArIcZvKNip.png
倒是在 npm.script 把設定寫好後就可以了。

後來我是用npx webpack-dev-server
不曉得你是如何直接輸入就可以使用的

感謝

Hello!你可以嘗試看看將 webpack-dev-server 裝在全域中:
npm i -g webpack-dev-server 再執行確認看看!

Peter iT邦新手 5 級‧ 2019-02-20 11:14:30 檢舉

好的,感謝 /images/emoticon/emoticon34.gif

我要留言

立即登入留言