基本上瀏覽器只看得懂三種語言,分別是 HTM、CSS 和 JavaScript,因此接下來學習 React 使用的 JSX 語法,瀏覽器無法讀懂,也沒辦法執行正確的結果給我們,因此我們需要 Babel 幫忙,它會依照語法規則將程式碼翻譯給瀏覽器看。
但 Babel 處理的也不全然是 JavaScript 外的語法,有些新版本的 JavaScript 瀏覽器開放支援前也會看不懂,這時候 Babel 也能將這些新版本的語法翻譯成舊的版本,讓大部分的網頁能夠正確執行。
注意!
如果 ESLint 沒有反應,要再用以下指令另外下載 @babel/runtime:
npm install @babel/runtime --save-dev
他是 Day02 的 eslint-config-airbnb 依賴套件,雖然 Day02 很正常,但複製到 Day03 就出現找不到這個套件的錯誤,我不確定是不是大家都會有這個狀況,如果是的話麻煩留言告訴我,我會直接將該套件補到 Day02 的 Package.json 裡。
在開始前,先將 src/index.js 打開,並將內容改為下方:
for (let i = 0; i <= 10; i += 1) {
console.log(i);
}
完成後透過 npm run build
讓 Webpack 將它打包,並打開打包後的 dist/bundle.js:
會發現在宣告時,依舊是用 let
,也就是說
這也是 Webpack 的基本工作,但並不是所有的瀏覽器版本都能夠正確支援 let
,於是為了能使用這些好用的新版本 JavaScript 語法,就得搭配使用 Babel 替程式做翻譯。
因為目前還沒在專案裡裝上 React,因此本階段的首要目標,先讓 ES6
的語法能正確被轉換。
開啟專案後,輸入以下指令安裝 Babel 的核心套件:
npm install @babel/core @babel/cli --save-dev
不同的編譯對象會有不同的 Preset,它會觀察程式裡有哪些地方需要做處理語法轉換,因此下載對 ES6
轉換語法的 Preset:
npm install @babel/preset-env --save-dev
Babel 會有個設定檔 babel.config.js,但目前暫時不需要也沒關係,因為 Webpack 在編譯時能夠設定 loader 使用 Babel,因此先下載 Webpack 需要的 loader 套件:
npm install babel-loader --save-dev
下載完後,到 webpack.config.js 中的 module
中設置轉換規則:
const path = require('path');
module.exports = {
/* 其餘省略 */
module: {
rules: [
{
test: /.js$/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'],
},
},
},
],
},
};
關於上方的設置內容,有幾個比較需要了解:
rules
:之後如果有新要轉換語法的地方,都會在這邊設置,包括 Sass 轉換成 css 也是。test
:這裡是目標檔案的副檔名,上方用正規表達式處理,讓所有 .js 結尾的都經由這個 rules
的 loader 做轉換。presets
: 針對不同的副檔名(例如:JSX)會有不同的 Preset,而同一種副檔名也能夠使用多個 Preset。接著使用 npm run build
再做一次編譯,就會看到他已經成為大部分瀏覽器都支援的可愛模樣:
今天再請大家陪我久一點,接下來要下載 React,透過 Babel 從 JSX 轉換成一般的 JS:
npm install react --save
npm install react-dom --save
下載好後,打開 src/index.js,將檔名改成 index.jsx,並在裡面輸入:
import React from 'react';
import ReactDom from 'react-dom';
const Main = () => <h1>Hi JSX!</h1>;
ReactDom.render(<Main />, document.getElementById('root'));
上方這裡先複製貼上,React 的使用方式會在後天開始一一為大家說明,今天的目的要能正確編譯 JSX 到 JavaScript。
接下來要下載負責編譯 JSX 語法的 Preset:
npm install @babel/preset-react --save-dev
載好後,再回到 webpack.config.js 身上,加入對 jsx
的 loader,也不要忘了將 entry
的檔案從原本的 index.js 改成 index.jsx:
entry: './src/index.jsx',
module: {
rules: [
/* 其餘省略 */
{
test: /.jsx$/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-react', '@babel/preset-env'],
},
},
},
],
}
這裡的 Preset 配置了兩個,因為 @babel/preset-react
只負責轉換 JSX 的語法,並不會幫忙處理 ES6 的部分,所以另外加上 @babel/preset-env
將 JavaScript 轉換成通用的版本,沒問題的話就能編譯看看:
最後,既然已經打包完 JavaScript 了,何不看看呈現的結果呢?
到 dist 的目錄下新增一個 index.html,並在裡面貼上下方的內容:
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<div id="root"></div>
<script src="./bundle.js"></script>
</body>
</html>
再來將 dist/index.html 用瀏覽器打開,就能看到精美的「Hi JSX!」:
最後來看看 Babel 到底對 JSX 做了什麼,打開 dist/bundle.js 並搜尋 「Hi JSX!」,就能看見他在一片程式海中:
雖然被編譯後的程式碼很亂,但仍然可以在裡面找出一些端倪,就是 createElement
,而 createElement
不是別的,它正是 React 內的頂層 API,這裡可以查看 官方文件 內的說明:
這邊就如同上方說的,瀏覽器只看得懂三種語言,分別是 HTML、CSS 與 JavaScript,因此 Babel 也只是將讀到的 JSX 再轉為真正的 JavaScript 寫法,如果沒有 Babel,等於就得親自使用 React 的 createElement
去寫,就會變得更複雜且沒那麼直觀。
今天的完整內容會放到 GitHub 上,歡迎各位參考!
大概是這三天來最長的一篇,也在前置準備的時候遇到一些小問題,但我還是會好好控制不讓每天的曲線跳得太多,如果對文中 Babel 的使用或操作有任何問題,再麻煩留言告訴我,謝謝大家!
ReactDom.render(document.getElementById('root'), );
這行參數好像相反了,會跳出Error: Minified React error #200
不過Github上的程式碼是正確的
萬分感謝!立馬修正過來!
這個系列很好,對C++人來說比較容易入門,不過JS webpack看上去比C++ xmake複雜的說.....
其實我沒有用過 C++ 的 xmake,所以無法體會到
至於 Webpack 最麻煩的應該就是版本的更新速度和豐富的生態圈可以用吧,導致每篇教學感覺都有一點不一樣
Hi, 神Q大大,我想請問為什麼我的bundle.js的大小比你的大上10倍,裡頭也多出很大量的程式碼,設置都是照著神Q走,但不了解為什麼會變這樣。
在https://webpack.js.org/configuration/ 查了一下,試著改變devtool,或者exclude: [path.resolve(dirname, "./nodemodules"]都沒辦法降低bundle的大小,是不是哪裡出錯了QQ。
React 16 是這樣進行DOM畫面渲染
ReactDom.render(<Main />, document.getElementById('root'));
React 18 的時候出現了警告ReactDOM.render is no longer supported in React 18. Use createRoot instead. Until you switch to the new API, your app will behave as if it’s running React 17.Learn more: https://reactjs.org/link/switch-to-createroot
目前改以下的方式
ReactDom.createRoot(document.getElementById('root')).render(<Main/>)