徒手擼個react項目框架(上)

最近學習react.js,發現項目框架除了使用的js庫不一樣(vue.js、react.js),配置基本上是大同小異的css

徒手擼個vue項目框架(上)

徒手擼個vue項目框架(下)

徒手擼個react項目框架(上)

徒手擼個react項目框架(下)

1、準備工做

1.新建reactProject文件夾

進入根目錄,初始化項目html

cd reactProject

npm init -y // -y是採用默認配置
複製代碼

此時目錄出現package.json文件前端

2.建立項目結構

在根目錄下新建src文件夾,在src下暫時新建名爲index的js文件做爲入口文件vue

根目錄下建立一個index.html,做爲入口頁面node

3.使用webpack

下載webpack時你可能會出現無限下載webpack-cli的問題,這是由於你沒有先全局安裝webpack和webpack-cli的緣由react

// webpack4.X開始webpack-cli被提出來做爲一個獨立的包了
// 在下載webpack同時也要下載webpack-cli,且必須同時下載不然會報錯,由於版本不匹配
cnpm install webpack-cli webpack --save-dev
複製代碼

webpack默認只能打包js模塊,它能夠將你寫的多個js模塊打包成一個js文件,最後在入口頁面引入它webpack

webpack4開始默認大於配置,換句話說能夠不用再引入一個配置文件來打包項目,所以他有不少默認值web

默認入口文件是src下的index.js,輸出爲dist目錄下的main.js(假如沒有dist目錄會自動建立)npm

可是它仍然是高配置的,假如須要咱們只需在項目根目錄下新建webpack.config.js來進行一切的配置json

相比於webpack4以前的版本,它的配置項多出一個mode選項,可選值爲"development" 或 "production"(默認),它們的區別就是development打包輸出的文件不是壓縮版本的

4.使用react.js

react和vue不一樣的是,react使用兩個包協同工做

  • react包:負責組件或者虛擬dom
  • react-dom包:負責將組件或者虛擬dom插入到根節點
cnpm install react react-dom --save-dev
複製代碼

index.js中

// index.js
import React from 'react'
// 這個包名必須這樣寫
import ReactDOM from 'react-dom'
/* *createElement: 建立虛擬dom元素 * 第一個參數爲標籤類型 * 第二個參數爲標籤屬性對象 * 剩餘參數皆爲參數爲子節點 */
const dom = React.createElement("h1",{id: "test"},"hello react")
// render函數是將虛擬dom插入目標容器Target container 
ReactDOM.render(
    dom,
    document.getElementById("root")
)
複製代碼

index.html中

//  index.html
<div id ="root"></div>
複製代碼

頁面中會看到hello react的字樣,審查元素以下

<div id="root"><h1 id="test">hello react</h1></div>
複製代碼

這說明咱們的準備工做都成功完成了

2、完善框架功能

1.使用webpack-dev-server

每次寫完新的內容要想看到效果,就必須使用webpack進行打包,咱們更但願當代碼改變時自動打包編譯

webpack-dev-server能夠幫咱們作到!

a.下載
cnpm i webpack-dev-server --save-dev
複製代碼
b.使用

咱們不能像使用webpack命令同樣使用webpack-dev-server命令,由於webpack-dev-server是局部安裝的,而令行裏只能使用全局安裝的包,使用局部安裝的包,咱們須要使用在package.json中配置scripts

// package.json
"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "webpack-dev-server --hot"
},
複製代碼

而後再命令行使用npm run dev

npm run dev
複製代碼

注意看下面的節選的代碼

npm run dev

> vueproject@1.0.0 dev C:\myProject\reactProject
> webpack-dev-server

i 「wds」: Project is running at http://localhost:8080/
i 「wds」: webpack output is served from /
i 「wds」: Content not from webpack is served from C:\myProject\reactProject
i 「wdm」: Hash: e70fb3ae9bf074915cad
Version: webpack 4.35.0
複製代碼

從這裏咱們知道兩件事: 首先,咱們的項目運行在本機8080端口,其次webpack的output輸出在根目錄下,因此記得修改index.html中引入main.js的路徑,不然你是看不到新的效果的

可是咱們在根目錄下並無看到這個文件,這是由於它被放在內存中(這樣的讀寫速度快),而不是磁盤中,另外咱們還能夠修改端口,甚至能夠在編譯完成後自動打開瀏覽器

它具體的配置能夠是在webpack的devServer

devServer:{
    host: '127.0.0.1',
    port: 8081,
    open: true
}
複製代碼

也能夠是在cli裏,這是最暴力的方式,可是端口仍是放在devServer裏,方便之後項目的配置

// package.json
"scripts": {
    "dev": "webpack-dev-server --open --port 30000"
},
複製代碼

2.使用html-webpack-plugin

既然將main.js放在內存中能夠加快讀寫速度,那是否是把頁面放在內存中能夠進一步加快讀寫速度了?

答案是確定的!使用html-webpack-plugin就能夠作到

a.下載
cnpm i html-webpack-plugin --save-dev
複製代碼
b.使用
// webpack.config.js
const htmlWebpackPlugin = require('html-webpack-plugin')
...
plugins: [
    new htmlWebpackPlugin({
      template: path.join(__dirname, "./index.html"),
      filename: "index.html"
    })
  ],
複製代碼

上面的代碼是根據磁盤中的index.html在內存中生成一個index.html,咱們在瀏覽器中審查頁面發現會多一個script標籤,這是插件自動將內存中的main.js加入到內存頁面中了,因此咱們這是應該刪除手動添加的script標籤

// index.html
<body>
    <div id="app"></div>
    <!--刪除或者註釋掉 <script src="./main.js"></script> -->
</body>
複製代碼

如今它已經能夠本身跑起來並自動監聽變化作出反應了

3、繼續完善框架(項目)功能

1.使用jsx語法

雖然開起來已經很完美了,可是像前面面那樣寫react虛擬dom老是以爲很複雜,做爲前端工程師,咱們更但願寫的dom就是這樣的,而後把它和以前同樣插入

const dom = <h1 id="test">hello react</h1>
ReactDOM.render(
    dom,
    document.getElementById("root")
)
複製代碼

這徹底是能夠的,上面的寫法就是react的擴展語法jsx,使用jsx語法必須使用babel-loader

a.下載

有了前面文章的經驗,我發現若是如今下載babel-loader最好使用下面的形式,由於babel已經更新到7.x.x了,寫法和以前的版本有很大差別,不然老是會出現一些版本不兼容的錯誤

//下載loader加載器
cnpm i @babel/core babel-loader --save-dev
//下載插件
cnpm i @babel/plugin-transform-runtime --save-dev
//下載預設
cnpm i @babel/preset-env @babel/preset-react --save-dev
複製代碼
b.使用

babel的兩種使用方法前面已經說過了,這裏再簡單的說說官網給出的別的簡單方法

第一種方法,在rules中配置babel-loader同時提供options選項

// 第一種寫法。
//除了基本的配置test外,可使用options,裏面必須有presets和plugins兩個選項
// presets是預設的babel語法轉換規則,這裏除了使用最新的規則外仍是用了針對react的jsx轉換規則
// plugins是指明使用的插件
module: {
    rules: [
      {
        test: /\.js|jsx$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader",
          options: {
            presets: ["@babel/preset-env", "@babel/preset-react"],
            plugins: ["@babel/plugin-transform-runtime"]
          }
        }
      }
    ]
  },
複製代碼

第二種方法根目錄新建.babelrc文件,格式同json

{
    presets: ["@babel/preset-env", "@babel/preset-react"],
    plugins: ["@babel/plugin-transform-runtime"]
}
//它會自動識別到這個文件
//此時能夠刪掉第一種方法中的options選項
複製代碼

第三種方法在package.json中添加babel節點

{
  "name": "my-package",
  "version": "1.0.0",
  "babel": {
    "presets": ["@babel/preset-env", "@babel/preset-react"],
    "plugins": ["@babel/plugin-transform-runtime"]
  }
}
複製代碼

第四種方法是根目錄新建.babelrc.js 配置和前面都同樣,只是可用js編寫

const presets = ["@babel/preset-env", "@babel/preset-react"];
const plugins = ["@babel/plugin-transform-runtime"];
// 這種寫法的好處是你能夠調用node API
if (process.env["ENV"] === "prod") {
  plugins.push(...);
}
module.exports = { presets, plugins };
複製代碼

固然還有cli方法,或者選擇轉換node_modules目錄的babel.confog.js的方法,但都是比較複雜或者冷門的

2.使用樣式表

在src下新建了一個css樣式表文件夾,裏面寫了本身針對本身組件的樣式,而後在須要的jsx文件中引入它,像我這樣

/* css/index.css文件 */
.test{
    color: green   
}
複製代碼
//在index.jsx文件中引入樣式
import React from "react";
import "@/css/index.css"

export default function Index() {
    return <div className="test" id="index">index page</div>;
}

//在login.jsx中不引入樣式,可是添加class
import React from "react";

export default class Login extends React.Component {
  render() {
  // 由於class是js的關鍵字,因此jsx中使用className做爲html的class
    return <div className="test">login page</div>;
  }
}
複製代碼

這時候會報錯,提示使用合適的loader

和vue同樣要使用樣式表必須使用loader,由於它們都是jsx語法生成虛擬dom的,而jsx沒法解析樣式表

a.下載
cnpm i style-loader css-loader --save-dev
複製代碼
b.使用

下載完以後和vue項目同樣配置文件

rules: [
    ...
    {
        test: /\.css$/,
        use: ["style-loader","css-loader"]
    }
]
複製代碼
c.問題

雖然這時候可使用樣式了可是,你會發現你沒有引入樣式的login組件頁面也是綠色字體了

這是由於webpack會將全部的樣式放在一個style標籤而後插入head標籤下,它的做用域是全局了,審查元素你看到的是這樣的

一樣的問題,vue能夠在style標籤添加scoped指令來控制樣式做用域,原理就是在在樣式表中選擇器前加個組件的標識,react中沒有指令的概念,怎麼解決了這個問題了?

好在css-loader能夠提供模塊化功能,咱們只需以下改動

rules: [
    ...
    {
        test: /\.css$/,
        // 這種寫法和url帶參數很相似,這是開啓模塊化的意思
        use: ["style-loader","css-loader?modules"]
    }
]
複製代碼

此時咱們的引入css樣式須要一個參數去接收css暴露出來的模塊(固然這是css-loader的功能)

//在index.jsx文件中引入樣式
import React from "react";
import indxCss from "@/css/index.css"
console.log(indxCss) // => {test: "q7KCiLIWvHKVJp6HMfV2y"}
export default function Index() {
    return <div className={indxCss.test} id="index">index page</div>;
}
複製代碼

同時說個有意思的事,css-loader除了modules參數外,還有不少參數 若是你不喜歡隨機的字符串作樣式的標識,能夠設置localIdentName參數,它是如下方式的組合

  • path: 文件的路徑
  • name: 文件名稱
  • local:樣式名稱
  • hash:32哈西值,後面能夠定義長度
rules: [
    ...
    {
        test: /\.css$/i,
        // 下面這種寫法是老版本的寫法,會一直報錯無效的版本,別問我是怎麼知道的,webpack官網沒有提示
        //use: ["style-loader","css-loader?modules"],
        // 新版本3.0.0是這樣的
        // 去css-loader的npm官網才能看到這種寫法
        use: [
          { loader: "style-loader" },
          {
            loader: "css-loader",
            options: {
              modules: {
                localIdentName: "[path][name]-[local]-[hash:base64:5]"
              }
            }
          }
        ]
    }
]
複製代碼

雖然如今看上去是完美的,可是有個問題是假如如今引入第三方庫,它也是css文件,也會被模塊化的,但咱們但願它是全局有效的,比較好的作法就是第三方庫採用css樣式,而本身的樣式啓用scss或者less,因此你得安裝它們的loader

cnpm i less-loader --save-dev
複製代碼

添加loader

rules: [
    ...
    {
        test: /\.css$/,
        use: ["style-loader", "css-loader"]
     },
    {
        test: /\.less$/i,
        use: [
          { loader: "style-loader" },
          {
            loader: "css-loader",
            options: {
              modules: {
                localIdentName: "[path][name]-[local]-[hash:base64:5]"
              }
            }
          },
          {loader: "less-loader"}
        ]
    }
]
複製代碼

3.使用圖片

若是在樣式中添加以下樣式

.test{
    background-image: url(../imgs/11.jpg)
}
複製代碼

會發現jsx也沒法處理圖片url的,因此得添加loader去處理這些

a.下載

url-loader內部使用了file-loader,因此兩個loader要同時下載

cnpm i url-loader file-loader --save-dev
複製代碼
b.使用

基本的使用以下

module: {
    rules: [
        ...
        {
            test: /\.(png|jpe?g|gif|svg|eot|ttf|woff|woff2)$/,
            loader: "url-loader"
        }
    ]
  },
複製代碼
c.options

這個和css-loader設置同樣

module: {
    rules: [
      {
        test: /\.(jpg|png|jpeg)$/,
        loader: "url-loader",
          options: {
            limit: 8000,    // 當文件字節大小超過限定值時觸發後面的設置
            name: "[hash:8]-[name].[ext]"  // 這是在原先的名稱和後綴名前加了八位的哈希碼
          }
      }
    ]
  },
複製代碼

4、結語

目前基本的功能都有了,可是仍是不夠完美,下期將會引入react-router,對業務進行封裝。從目前來看,無論vue項目框架仍是react框架,有極大的類似之處,學了vue學react就簡單多了,並且這是學習react對vue的理解也上升了一個層次。

若是你也作到這裏相信你也會有這種感覺

努力、奮鬥

相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息