iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 4
5
Modern Web

在 React 生態圈內打滾的一年 feat. TypeScript系列 第 4

Day03 | JSX 瀏覽器看不懂?要翻譯就靠 Babel

前言

基本上瀏覽器只看得懂三種語言,分別是 HTM、CSS 和 JavaScript,因此接下來學習 React 使用的 JSX 語法,瀏覽器無法讀懂,也沒辦法執行正確的結果給我們,因此我們需要 Babel 幫忙,它會依照語法規則將程式碼翻譯給瀏覽器看。

Babel 可以將程式碼編譯成網頁看得懂的樣子。

但 Babel 處理的也不全然是 JavaScript 外的語法,有些新版本的 JavaScript 瀏覽器開放支援前也會看不懂,這時候 Babel 也能將這些新版本的語法翻譯成舊的版本,讓大部分的網頁能夠正確執行。

Babel 也能夠處理不同瀏覽器對 JavaScript 的支援程度。


前置準備

  1. 文中的專案會以前一天的專案架構繼續講解,如果未跟到前一天的進度,可以從 GitHub 上 Clone 下來。
  2. 一顆擁有學習熱忱的心。

注意!
如果 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 到目前為止都只是將所有用到的檔案或套件給打包成同一份執行,

這也是 Webpack 的基本工作,但並不是所有的瀏覽器版本都能夠正確支援 let,於是為了能使用這些好用的新版本 JavaScript 語法,就得搭配使用 Babel 替程式做翻譯。

因為目前還沒在專案裡裝上 React,因此本階段的首要目標,先讓 ES6 的語法能正確被轉換。

安裝 Babel

開啟專案後,輸入以下指令安裝 Babel 的核心套件:

npm install @babel/core @babel/cli --save-dev

安裝 Preset 語法規則

不同的編譯對象會有不同的 Preset,它會觀察程式裡有哪些地方需要做處理語法轉換,因此下載對 ES6 轉換語法的 Preset:

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

在 Webpack 中設置 Babel

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'],
          },
        },
      },
    ],
  },
};

關於上方的設置內容,有幾個比較需要了解:

  1. rules:之後如果有新要轉換語法的地方,都會在這邊設置,包括 Sass 轉換成 css 也是。
  2. test:這裡是目標檔案的副檔名,上方用正規表達式處理,讓所有 .js 結尾的都經由這個 rules 的 loader 做轉換。
  3. presets: 針對不同的副檔名(例如:JSX)會有不同的 Preset,而同一種副檔名也能夠使用多個 Preset。

重新編譯

接著使用 npm run build 再做一次編譯,就會看到他已經成為大部分瀏覽器都支援的可愛模樣:

下載 React

今天再請大家陪我久一點,接下來要下載 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!」,就能看見他在一片程式海中:

https://ithelp.ithome.com.tw/upload/images/20191017/20106935luMEDcFn34.png

雖然被編譯後的程式碼很亂,但仍然可以在裡面找出一些端倪,就是 createElement,而 createElement 不是別的,它正是 React 內的頂層 API,這裡可以查看 官方文件 內的說明:

https://ithelp.ithome.com.tw/upload/images/20191017/20106935ISo3ePW3Hk.png

其實我們在撰寫的 JSX 只是這些頂層 API 的語法糖而已

這邊就如同上方說的,瀏覽器只看得懂三種語言,分別是 HTML、CSS 與 JavaScript,因此 Babel 也只是將讀到的 JSX 再轉為真正的 JavaScript 寫法,如果沒有 Babel,等於就得親自使用 React 的 createElement 去寫,就會變得更複雜且沒那麼直觀。

今天的完整內容會放到 GitHub 上,歡迎各位參考!


結尾

大概是這三天來最長的一篇,也在前置準備的時候遇到一些小問題,但我還是會好好控制不讓每天的曲線跳得太多,如果對文中 Babel 的使用或操作有任何問題,再麻煩留言告訴我,謝謝大家!


上一篇
Day02 | 整齊的程式,讓看的人長命百歲,給我用 ESLint
下一篇
Day04 | SCSS 加上 Webpack 混搭款,讓你寫 CSS 上天堂
系列文
在 React 生態圈內打滾的一年 feat. TypeScript31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中
1
ninten
iT邦新手 5 級 ‧ 2019-09-24 14:51:30

ReactDom.render(document.getElementById('root'), );

這行參數好像相反了,會跳出Error: Minified React error #200

不過Github上的程式碼是正確的

神Q超人 iT邦研究生 5 級 ‧ 2019-09-24 19:47:31 檢舉

萬分感謝!立馬修正過來!

2
alantsui
iT邦新手 5 級 ‧ 2019-10-13 15:16:23

這個系列很好,對C++人來說比較容易入門,不過JS webpack看上去比C++ xmake複雜的說.....

神Q超人 iT邦研究生 5 級 ‧ 2019-10-13 18:58:01 檢舉

其實我沒有用過 C++ 的 xmake,所以無法體會到
至於 Webpack 最麻煩的應該就是版本的更新速度和豐富的生態圈可以用吧,導致每篇教學感覺都有一點不一樣 /images/emoticon/emoticon01.gif

0
matuyou0301
iT邦新手 5 級 ‧ 2021-04-12 09:14:22

Hi, 神Q大大,我想請問為什麼我的bundle.js的大小比你的大上10倍,裡頭也多出很大量的程式碼,設置都是照著神Q走,但不了解為什麼會變這樣。

https://ithelp.ithome.com.tw/upload/images/20210412/20136208q70EMZwnlx.jpg

在https://webpack.js.org/configuration/ 查了一下,試著改變devtool,或者exclude: [path.resolve(dirname, "./nodemodules"]都沒辦法降低bundle的大小,是不是哪裡出錯了QQ。

0
ken12462
iT邦新手 5 級 ‧ 2022-06-01 16:33:47

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/>)

我要留言

立即登入留言