1. 程式人生 > 實用技巧 >搭建簡易React

搭建簡易React

一步一個腳印搭建簡易React

準備環境:

  1. node環境
  2. vscode編輯器

初始化專案

// 建立一個資料夾,當然你也可以手動建立
mkdir my-react
// 進入到專案目錄
cd my-react
// 生成pakeage.json檔案,這個檔案主要是用來記錄這個專案的詳細資訊的,它會將我們在專案開發中所要用到的包,以及專案的詳細資訊等記錄在這個專案中
npm init
複製程式碼

搭建專案環境

由於我們的目的是實現一個簡易的React,為了能把更多的重心花在React上,我們需要藉助一些工具來幫助我們處理一些React之外的問題。

  1. webpack: 我們需要使用webapck將我們的專案打包,最終生成一個js檔案
  2. babel:能幫助我們將高階的ES語法向下轉化成瀏覽器識別的低版本的ES語法

webpack

cnpm install --save-dev webapck webapck-cli
複製程式碼

然後在我們專案的根目錄新建webapck.config.js檔案來告訴webapck如何打包我們的js檔案

module.exports = {
  entry: './src/main.js',
};
複製程式碼

同時建立src目錄,並且新建main.js作為專案的入口檔案

// main.js
const array = [1, 2, 3, 4, 5];
array.find((item) => item === 1);
複製程式碼

babel

babel需要安裝的包比較多

cnpm install --save-dev babel-loader @babel/core @babel/preset-env
複製程式碼
  1. babel-loader: 使用babel-loader處理js檔案,會將es5以上的語法進行轉義
  2. @babel/core: 封裝了babel-loader需要用到的api
  3. @babel/preset-env: babel 內部經歷了「解析 - 轉換 - 生成」三個步驟。而 @babel/core 這個庫則負責「解析」,具體的「轉換」和「生成」步驟則交給各種外掛(plugin)和預設(preset)來完成

tips1:

使用@babel開頭是為了宣告作用域

tips2: @babel/preset-*實際上就是各種外掛的打包組合,也就是說各種轉譯規則的統一設定,目的是告訴loader要以什麼規則來轉化成對應的js版本

好了,回到我們的專案本身,我們需要告訴webapck,用babel-loader去打包我們的js檔案,以及babel-loader對應的配置。

module.exports = {
  entry: {
    main: "./src/main.js",
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ["@babel/preset-env"],
          },
        },
      },
    ],
  },
};
複製程式碼

第一次打包編譯

在終端執行npx webpack,就可以在dist檔案加下檢視打包出來的檔案了

tips: 使用npx可以保證我們執行的是當前專案

my-react
│ 
│
└───dist
│   │   main.js
└───src
│   │   main.js
└───package.json
|
└───webpack.config.js
複製程式碼

引入babel外掛支援jsx語法

現在我們嘗試在main.js中宣告一個不一樣的變數,然後再執行npx webpack

const array = [1, 2, 3, 4, 5];

array.find((item) => item === 1);

const ele = <div id="id" class="mr5" >
  <span> zaoren </span>
</div>;
console.log(ele);
複製程式碼

我們看到報錯了,原因是我們不能解析jsx語法

因此,我們引入另一款babel外掛來幫助我們解析jsx語法 - @babel/plugin-transform-react-jsx

cnpm install --save-dev @babel/plugin-transform-react-jsx
複製程式碼

然後在我們的webapck.config.js中引入外掛

{
    test: /\.js$/,
    use: {
      loader: 'babel-loader',
      options: {
        presets: ["@babel/preset-env"],
        plugins: ['@babel/plugin-transform-react-jsx']
      },
    },
  },
複製程式碼

然後再用webapck打包一下,發現沒有報錯,打包成功了!

然後我們嘗試著用一個main.html來引入我們打包後的main.js

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <script src="./main.js"></script>
</body>
</html>
複製程式碼

但是當我們在瀏覽器中執行的時候發現報了個錯誤

咦?React未定義,想想我們這篇文章的主題是什麼?不就是實現一個簡易的React嗎?所以,到現在為止,我們不進一步使用工具來幫助我們簡化工作量。完全靠自己了!!!

開始動手寫React

從打包後的程式碼可以看出,首先,我們需要一個React變數,React變數的createElement方法接受了三個引數

  1. DOM節點型別
  2. DOM節點上的屬性物件
  3. DOM上的子節點children

我們來簡單動手實現一下React這個物件,並且把建立好的DOM物件掛載到html頁面的body上https://www.douban.com/group/topic/198335398/

// main.js
let React = {
  createElement: (tagName, attributes, ...children) => {
    let ele =  document.createElement(tagName);
      
    Object.keys(attributes || {}).forEach(key => {
      ele.setAttribute(key, attributes[key]);
    });
    
    children.forEach(child => {
      ele.appendChild(child);
    })

    return ele;
  },
};

const ele = <div id="id" style="background: red" >
  <span>zaoren1</span>
  <span>zaoren2</span>
</div>;

document.body.appendChild(ele);

複製程式碼

執行npx webpack之後,我們開啟瀏覽器發現報錯了!

除錯後發現,我們這個t為文字“zaoren”,並且原生的Web API中createElement這個方法是不支援新增文字節點的,需要使用createTextNode方法(詳情見MDN)

因此在建立子節點的時候,需要對是否是文字節點進行判斷。

children.forEach(child => {
  if (typeof child === "string") {
    child = document.createTextNode(child);
  }
  ele.appendChild(child);
})
複製程式碼

重新npx webpack,檢視效果 可以看到我們的<div>子節點<span>以及我們的一些屬性都能成功設定啦!恭喜你,已經完成了第一步!

與屬性相對應對的是函式,但目前我們的React還是沒有處理事件的能力的,那麼只需要在attribute中過濾出函式屬性,然後增加事件監聽器(這裡簡單用正則去匹配on字串)

... 省略部分
Object.keys(attributes || {}).forEach(key => {
  if (key.match(/^on/)) {
    let eventType = key.replace(/^on/, '').toLocaleLowerCase();
    ele.addEventListener(eventType, attributes[key]);
    return
  }
  ele.setAttribute(key, attributes[key]);
});

... 省略部分
const ele = <div id="id" style="background: red" >
  <span onClick={() => {console.log('add event success!')}}>zaoren1</span>
  <span>zaoren2</span>
</div>;
複製程式碼

點選zaoren1,可以看到我們已經成功新增click事件啦! https://www.douban.com/group/topic/198377234/

專案原始碼

本文使用 mdnice 排版


作者:棗仁,
連結:https://juejin.im/post/6886708531903496205
來源:掘金
著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。