国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

node.js|vue.js|jquery|angularjs|React|json|js教程|

服務(wù)器之家 - 編程語(yǔ)言 - JavaScript - js教程 - 使用JSX 建立組件 Parser(解析器)開(kāi)發(fā)的示例

使用JSX 建立組件 Parser(解析器)開(kāi)發(fā)的示例

2022-02-24 16:50三鉆 js教程

這篇文章主要介紹了使用JSX 建立組件 Parser(解析器)開(kāi)發(fā)的示例(前端組件化)本文重點(diǎn)講述我們?nèi)绾螐?開(kāi)始搭建一個(gè)組件系統(tǒng),基于標(biāo)記語(yǔ)言的Parser的一種風(fēng)格

目錄
  • JSX 環(huán)境搭建
  • 建立項(xiàng)目
  • 初始化 NPM
  • 安裝 webpack
  • 安裝 Babel
  • 配置 webpack
  • 安裝 Babel-loader
  • 模式配置
  • 引入 JSX
  • JSX 基本用法
  • JSX 基礎(chǔ)原理
  • 實(shí)現(xiàn) createElement 函數(shù)
  • 實(shí)現(xiàn)自定義標(biāo)簽

這里我們一起從 0 開(kāi)始搭建一個(gè)組件系統(tǒng)。首先通過(guò)上一篇《前端組件化基礎(chǔ)知識(shí)》中知道,一個(gè)組件可以通過(guò) Markup 和 JavaScript 訪問(wèn)的一個(gè)環(huán)境。

所以我們的第一步就是建立一個(gè)可以使用 markup 的環(huán)境。這里我們會(huì)學(xué)習(xí)使用兩種建立 markup 的風(fēng)格。

第一種是基于與 React 一樣的 JSX 去建立我們組件的風(fēng)格。第二種則是我們?nèi)ソ⒒陬愃?Vue 的這種,基于標(biāo)記語(yǔ)言的 Parser 的一種風(fēng)格。

JSX 環(huán)境搭建

JSX 在大家一般認(rèn)知里面,它是屬于 React 的一部分。其實(shí) Facebook 公司會(huì)把 JSX 定義為一種純粹的語(yǔ)言擴(kuò)展。而這個(gè) JSX 也是可以被其他組件體系去使用的。

甚至我們可以把它單獨(dú)作為一種,快捷創(chuàng)建 HTML 標(biāo)簽的方式去使用。

建立項(xiàng)目

那么我們就從最基礎(chǔ)的開(kāi)始,首先我們需要?jiǎng)?chuàng)建一個(gè)新的項(xiàng)目目錄:

  1. mkdir jsx-component

初始化 NPM

在你們喜歡的目錄下創(chuàng)建這個(gè)項(xiàng)目文件夾。建立好文件夾之后,我們就可以進(jìn)入到這個(gè)目錄里面并且初始化 npm

  1. npm init

執(zhí)行以上命令之后,會(huì)出現(xiàn)一些項(xiàng)目配置的選項(xiàng)問(wèn)題,如果有需要可以自行填寫。不過(guò)我們也可以直接一直按回車,然后有需要的同學(xué)可以后面自己打開(kāi) package.json 自行修改。

安裝 webpack

Wepack 很多同學(xué)應(yīng)該都了解過(guò),它可以幫助我們把一個(gè)普通的 JavaScript 文件變成一個(gè)能把不同的 import 和 require 的文件給打包到一起。

所以我們需要安裝 webpack ,當(dāng)然我們也可以直接使用 npx 直接使用 webpack,也可以全局安裝 webpack-cli。

那么這里我們就使用全局安裝 webpack-cli:

  1. npm install -g webpack webpack-cli

安裝完畢之后,我們可以通過(guò)輸入下面的一條命令來(lái)檢測(cè)一下安裝好的 webpack 版本。如果執(zhí)行后沒(méi)有報(bào)錯(cuò),并且出來(lái)了一個(gè)版本號(hào),證明我們已經(jīng)安裝成功了。

  1. webpack --version

安裝 Babel

因?yàn)?JSX 它是一個(gè) babel 的插件,所以我們需要依次安裝 webpack,babel-loader, babel 和 babel 的 plugin。

這里使用 Babel 還有一個(gè)用處,它可以把一個(gè)新版本的 JavaScript 編譯成一個(gè)老版本的 JavaScript,這樣我們的代碼就可以在更多老版本的瀏覽器中運(yùn)行。

安裝 Babel 我們只需要執(zhí)行以下的命令即可。

  1. npm install --save-dev webpack babel-loader

這里我們需要注意的是,我們需要加上 --save-dev,這樣我們就會(huì)把 babel 加入到我們的開(kāi)發(fā)依賴中。

使用JSX 建立組件 Parser(解析器)開(kāi)發(fā)的示例

執(zhí)行完畢后,我們應(yīng)該會(huì)看到上面圖中的消息。

為了驗(yàn)證我們是正確安裝好了,我們可以打開(kāi)我們項(xiàng)目目錄下的 package.json

  1. {
  2. "name": "jsx-component",
  3. "version": "1.0.0",
  4. "description": "",
  5. "main": "index.js",
  6. "scripts": {
  7. "test": "echo \"Error: no test specified\" && exit 1"
  8. },
  9. "author": "",
  10. "license": "ISC",
  11. "devDependencies": {
  12. "babel-loader": "^8.1.0",
  13. "webpack": "^5.4.0"
  14. }
  15. }

好,我們可以看到在 devDependencies 下方,確實(shí)是有我們剛剛安裝的兩個(gè)包。還是擔(dān)心的同學(xué),可以再和 package.json 確認(rèn)一下眼神哈。

使用JSX 建立組件 Parser(解析器)開(kāi)發(fā)的示例

配置 webpack

到這里我們就需要配置一下 webpack。配置 webpack 我們需要?jiǎng)?chuàng)建一個(gè) webpack.config.js 配置文件。

在我們項(xiàng)目的根目錄創(chuàng)建一個(gè) webpack.config.js 文件。

首先 webpack config 它是一個(gè) nodejs 的模塊,所以我們需要用 module.exports 來(lái)寫它的設(shè)置。而這個(gè)是早期 nodejs 工具常見(jiàn)的一種配置方法,它用一個(gè) JavaScript 文件去做它的配置,這樣它在這個(gè)配置里面就可以加入一些邏輯。

  1. module.exports = {}

Webpack 最基本的一個(gè)東西,就是需要設(shè)置一個(gè) entry (設(shè)置它的入口文件)。這里我們就設(shè)置一個(gè) main.js 即可。

  1. module.exports = {
  2. entry: "./main.js"
  3. }

這個(gè)時(shí)候,我們就可以先在我們的根目錄下創(chuàng)建一個(gè) main.js 的文件了。在里面我們先加入一個(gè)簡(jiǎn)單的 for 循環(huán)。

  1. // main.js 文件內(nèi)容
  2. for (let i of [1, 2, 3]) {
  3. console.log(i);
  4. }

這樣 webpack 的基本配置就配置好了,我們?cè)诟夸浵聢?zhí)行一下 webpack 來(lái)打包一下 main.js 的文件來(lái)看看。需要執(zhí)行下面的這行命令進(jìn)行打包:

  1. webpack

使用JSX 建立組件 Parser(解析器)開(kāi)發(fā)的示例

執(zhí)行完畢之后,我們就可以在命令行界面中看到上面這樣的一段提示。

注意細(xì)節(jié)的同學(xué),肯定要舉手問(wèn)到,同學(xué)同學(xué)!你的命令行中報(bào)錯(cuò)啦!黃色部分確實(shí)有給我們一個(gè)警告,但是不要緊,這個(gè)我們接下的配置會(huì)修復(fù)它的。

這個(gè)時(shí)候我們會(huì)發(fā)現(xiàn),在我們的根目錄中生成了一個(gè)新的文件夾 dist。這個(gè)就是 webpack 打包默認(rèn)生成的文件夾,我們所有打包好的 JavaScript 和資源都會(huì)被默認(rèn)放入這個(gè)文件夾當(dāng)中。

這里我們就會(huì)發(fā)現(xiàn),這個(gè) dist 文件夾里面有一個(gè)打包好的 main.js 的文件,這個(gè)就是我們寫的 main.js,通過(guò) webpack 被打包好的版本。

然后我們打開(kāi)它,就會(huì)看到它被 babel 編譯過(guò)后的 JavaScript 代碼。我們會(huì)發(fā)現(xiàn)我們短短的幾行代碼被加入了很多的東西,這些其實(shí)我們都不用管,那都是 Webpack 的 “喵喵力量”。

使用JSX 建立組件 Parser(解析器)開(kāi)發(fā)的示例

在代碼的最后面,還是能看到我們編寫的 for 循環(huán)的,只是被改造了一下,但是它的作用是一致的。

安裝 Babel-loader

接下來(lái)我們來(lái)安裝 babel-loader,其實(shí) babel-loader 并沒(méi)有直接依賴 babel 的,所以我們才需要另外安裝 @babel/core@babel/preset-env。我們只需要執(zhí)行下面的命令行來(lái)安裝:

  1. npm install --save-dev @babel/core @babel/preset-env

使用JSX 建立組件 Parser(解析器)開(kāi)發(fā)的示例

最終的結(jié)果就如上圖一樣,證明安裝成功了。這個(gè)時(shí)候我們就需要在 webpack.config.js 中配置上,讓我們打包的時(shí)候用上 babel-loader。

在我們上面配置好的 webpack.config.jsentry 后面添加一個(gè)選項(xiàng)叫做 module

然后模塊中我們還可以加入一個(gè) rules,這個(gè)就是我們構(gòu)建的時(shí)候所使用的規(guī)則。而 rules 是一個(gè)數(shù)組類型的配置,這里面的每一個(gè)規(guī)則是由一個(gè) test 和一個(gè) use 組成的。

test:

  • test 的值是一個(gè)正則表達(dá)式,用于匹配我們需要使用這個(gè)規(guī)則的文件。這里我們需要把所有的 JavaScript 文件給匹配上,所以我們使用 /\.js/ 即可。

use: loader:

  • 只需要加入我們的 babel-loader 的名字即可

options:

presets:

  • 這里是 loader 的選項(xiàng),這里我們需要加入 @babel/preset-env

最后我們的配置文件就會(huì)是這個(gè)樣子:

  1. module.exports = {
  2. entry: './main.js',
  3. module: {
  4. rules: [
  5. {
  6. test: /\.js$/,
  7. use: {
  8. loader: 'babel-loader',
  9. options: {
  10. presets: ['@babel/preset-env'],
  11. },
  12. },
  13. },
  14. ],
  15. },
  16. };

這樣配置好之后,我們就可以來(lái)跑一下 babel 來(lái)試一試會(huì)是怎么樣的。與剛才一樣,我們只需要在命令行執(zhí)行 webpack 即可。

使用JSX 建立組件 Parser(解析器)開(kāi)發(fā)的示例

如果我們的配置文件沒(méi)有寫錯(cuò),我們就應(yīng)該會(huì)看到上面圖中的結(jié)果。

然后我們進(jìn)入 dist 文件夾,打開(kāi)我們編譯后的 main.js,看一下我們這次使用了 babel-loader 之后的編譯結(jié)果。

使用JSX 建立組件 Parser(解析器)開(kāi)發(fā)的示例

編譯后的結(jié)果,我們會(huì)發(fā)現(xiàn) for of 的循環(huán)被編譯成了一個(gè)普通的 for 循環(huán)。這個(gè)也可以證明我們的 babel-loader 起效了,正確把我們新版本的 JavaScript 語(yǔ)法轉(zhuǎn)成能兼容舊版瀏覽器的 JavaScript 語(yǔ)法。

到了這里我們已經(jīng)把 JSX 所需的環(huán)境給安裝和搭建完畢了。

模式配置

最后我們還需要在 webpack.config.js 里面添加一個(gè)環(huán)境配置,不過(guò)這個(gè)是可加也可不加的,但是我們?yōu)榱似綍r(shí)開(kāi)發(fā)中的方便。

所以我們需要在 webpack.config.js 中添加一個(gè) mode,這我們使用 development。這個(gè)配置表示我們是開(kāi)發(fā)者模式。

一般來(lái)說(shuō)我們?cè)诖a倉(cāng)庫(kù)里面寫的 webpack 配置都會(huì)默認(rèn)加上這個(gè) mode: 'development' 的配置。當(dāng)我們真正發(fā)布的時(shí)候,我們就會(huì)把它改成 mode: 'production'

  1. module.exports = {
  2. entry: './main.js',
  3. mode: 'development',
  4. module: {
  5. rules: [
  6. {
  7. test: /\.js$/,
  8. use: {
  9. loader: 'babel-loader',
  10. options: {
  11. presets: ['@babel/preset-env'],
  12. },
  13. },
  14. },
  15. ],
  16. },
  17. };

改好之后,我們?cè)谑褂?webpack 編譯一下,看看我們的 main.js 有什么區(qū)別。

使用JSX 建立組件 Parser(解析器)開(kāi)發(fā)的示例

顯然我們發(fā)現(xiàn),編譯后的代碼沒(méi)有被壓縮成一行了。這樣我們就可以調(diào)試 webpack 生成的代碼了。這里我們可以注意到,我們?cè)?main.js 中的代碼被轉(zhuǎn)成字符串,并且被放入一個(gè) eval() 的函數(shù)里面。那么我們就可以在調(diào)試的時(shí)候把它作為一個(gè)單獨(dú)的文件去使用了,并且可以進(jìn)行斷點(diǎn)調(diào)試。

引入 JSX

萬(wàn)事俱備,只欠東風(fēng)了,最后我們需要如何引入 JSX呢?在引入之前,我們來(lái)看看,如果就使用現(xiàn)在的配置在我們的 main.js 里面使用 JSX 語(yǔ)法會(huì)怎么樣。作為程序員的我們,總得有點(diǎn)冒險(xiǎn)精神!

使用JSX 建立組件 Parser(解析器)開(kāi)發(fā)的示例

所以我們?cè)?main.js 里面加入這段代碼:

  1. var a = <div/>

然后大膽地執(zhí)行 webpack 看看!

使用JSX 建立組件 Parser(解析器)開(kāi)發(fā)的示例

好家伙!果然報(bào)錯(cuò)了。這里的報(bào)錯(cuò)告訴我們,在 = 后面不能使用 “小于號(hào)”,但是在正常的 JSX 語(yǔ)法中,這個(gè)其實(shí)是 HTML 標(biāo)簽的 “尖括號(hào)”,因?yàn)闆](méi)有 JSX 語(yǔ)法的編譯過(guò)程,所以 JavaScript 默認(rèn)就會(huì)認(rèn)為這個(gè)就是 “小于號(hào)”。

所以我們要怎么做讓我們的 webpack 編譯過(guò)程支持 JSX 語(yǔ)法呢?這里其實(shí)就是還需要我們加入一個(gè)最關(guān)鍵的一個(gè)包,而這個(gè)包名非常的長(zhǎng),叫做 @babel/plugin-transform-react-jsx。執(zhí)行以下命令來(lái)安裝它:

  1. npm install --save-dev @babel/plugin-transform-react-jsx

安裝好之后,我們還需要在 webpack 配置中給他加入進(jìn)去。我們需要在 module 里面的 rules 里面的 use 里面加入一個(gè) plugins 的配置,然后在其中加入 ['@babel/plugin-transform-react-jsx']

然后最終我們的 webpack 配置文件就是這樣的:

  1. module.exports = {
  2. entry: './main.js',
  3. mode: 'development',
  4. module: {
  5. rules: [
  6. {
  7. test: /\.js$/,
  8. use: {
  9. loader: 'babel-loader',
  10. options: {
  11. presets: ['@babel/preset-env'],
  12. plugins: ['@babel/plugin-transform-react-jsx'],
  13. },
  14. },
  15. },
  16. ],
  17. },
  18. };

配置好之后,我們?cè)偃?zhí)行一下 webpack。這時(shí)候我們發(fā)現(xiàn)沒(méi)有再報(bào)錯(cuò)了。這樣也就證明我們的代碼現(xiàn)在是支持使用 JSX 語(yǔ)法了。

最后我們來(lái)圍觀一下,最后編程的效果是怎么樣的。

使用JSX 建立組件 Parser(解析器)開(kāi)發(fā)的示例

我們會(huì)發(fā)現(xiàn),在 eval 里面我們加入的 <div/> 被翻譯成一個(gè) React.createElement("div", null) 的函數(shù)調(diào)用了。

所以接下來(lái)我們就一起來(lái)看一下,我們應(yīng)該怎么實(shí)現(xiàn)這個(gè) React.createElement,以及我們能否把這個(gè)換成我們自己的函數(shù)名字。

JSX 基本用法

首先我們來(lái)嘗試?yán)斫?JSX,JSX 其實(shí)它相當(dāng)于一個(gè)純粹在代碼語(yǔ)法上的一種快捷方式。在上一部分的結(jié)尾我們看到,JSX語(yǔ)法在被編譯后會(huì)出現(xiàn)一個(gè) React.createElement 的調(diào)用。

JSX 基礎(chǔ)原理

那么這里我們就先修改在 webpack 中的 JSX 插件,給它一個(gè)自定義的創(chuàng)建元素函數(shù)名。我們打開(kāi) webpack.config.js,在 plugins 的位置,我們把它修改一下。

  1. module.exports = {
  2. entry: './main.js',
  3. mode: 'development',
  4. module: {
  5. rules: [
  6. {
  7. test: /\.js$/,
  8. use: {
  9. loader: 'babel-loader',
  10. options: {
  11. presets: ['@babel/preset-env'],
  12. plugins: [
  13. [
  14. '@babel/plugin-transform-react-jsx',
  15. { pragma: 'createElement' }
  16. ]
  17. ],
  18. },
  19. },
  20. },
  21. ],
  22. },
  23. };

上面我們只是把原來(lái)的 ['@babel/plugin-transform-react-jsx'] 參數(shù)改為了 [['@babel/plugin-transform-react-jsx', {pragma: 'createElement'}]]。加入了這個(gè) pragma 參數(shù),我們就可以自定義我們創(chuàng)建元素的函數(shù)名。

這么一改,我們的 JSX 就與 React 的框架沒(méi)有任何聯(lián)系了。我們執(zhí)行一下 webpack 看一下最終生成的效果,就會(huì)發(fā)現(xiàn)里面的 React.createElement 就會(huì)變成 createElement

使用JSX 建立組件 Parser(解析器)開(kāi)發(fā)的示例

接下來(lái)我們加入一個(gè) HTML 文件來(lái)執(zhí)行我們的 main.js 試試。首先在根目錄創(chuàng)建一個(gè) main.html,然后輸入一下代碼:

  1. <script src="./main.js"></script>

然后我們執(zhí)行在瀏覽器打開(kāi)這個(gè) HTML 文件。

使用JSX 建立組件 Parser(解析器)開(kāi)發(fā)的示例

這個(gè)時(shí)候我們控制臺(tái)會(huì)給我們拋出一個(gè)錯(cuò)誤,我們的 createElement 未定義。確實(shí)我們?cè)?main.js 里面還沒(méi)有定義這個(gè)函數(shù),所以說(shuō)它找不到。

所以我們就需要自己編寫一個(gè) createElement 這個(gè)函數(shù)。我們直接打開(kāi)根目錄下的 main.js 并且把之前的 for 循環(huán)給刪除了,然后加上這段代碼:

  1. function createElement() {
  2. return;
  3. }
  4.  
  5. let a = <div />;

這里我們就直接返回空,先讓這個(gè)函數(shù)可以被調(diào)用即可。我們用 webpack 重新編譯一次,然后刷新我們的 main.html 頁(yè)面。這個(gè)時(shí)候我們就會(huì)發(fā)現(xiàn)報(bào)錯(cuò)沒(méi)有了,可以正常運(yùn)行。

實(shí)現(xiàn) createElement 函數(shù)

使用JSX 建立組件 Parser(解析器)開(kāi)發(fā)的示例

在我們的編譯后的代碼中,我們可以看到 JSX 的元素在調(diào)用 createElement 的時(shí)候是傳了兩個(gè)參數(shù)的。第一個(gè)參數(shù)是 div, 第二個(gè)是一個(gè) null

這里第二個(gè)參數(shù)為什么是 null 呢?其實(shí)第二個(gè)參數(shù)是用來(lái)傳屬性列表的。如果我們?cè)?main.js 里面的 div 中加入一個(gè) id="a" ,我們來(lái)看看最后編譯出來(lái)會(huì)有什么變化。

使用JSX 建立組件 Parser(解析器)開(kāi)發(fā)的示例

我們就會(huì)發(fā)現(xiàn)第二個(gè)參數(shù)變成了一個(gè)以 Key-Value 的方式存儲(chǔ)的JavaScript 對(duì)象。到這里如果我們想一下,其實(shí) JSX 也沒(méi)有那么神秘,它只是把我們平時(shí)寫的 HTML 通過(guò)編譯改寫成了 JavaScript 對(duì)象,我們可以認(rèn)為它是屬于一種 “[[語(yǔ)法糖]]”。

但是 JSX 影響了代碼的結(jié)構(gòu),所以我們一般也不會(huì)完全把它叫作語(yǔ)法糖。

接下來(lái)我們來(lái)寫一些更復(fù)雜一些的 JSX,我們給原本的 div 加一些 children 元素。

  1. function createElement() {
  2. return;
  3. }
  4.  
  5. let a = (
  6. <div id="a">
  7. <span></span>
  8. <span></span>
  9. <span></span>
  10. </div>
  11. );

最后我們執(zhí)行以下 webpack 打包看看效果。

使用JSX 建立組件 Parser(解析器)開(kāi)發(fā)的示例

在控制臺(tái)中,我們可以看到最后編譯出來(lái)的結(jié)果,是遞歸的調(diào)用了 createElement 這個(gè)函數(shù)。這里其實(shí)已經(jīng)形成了一個(gè)樹(shù)形的結(jié)構(gòu)。

父級(jí)就是第一層的 div 的元素,然后子級(jí)就是在后面當(dāng)參數(shù)傳入了第一個(gè) createElement 函數(shù)之中。然后因?yàn)槲覀兊?span 都是沒(méi)有屬性的,所以所有后面的 createElement 的第二個(gè)參數(shù)都是 null

根據(jù)我們這里看到的一個(gè)編譯結(jié)果,我們就可以分析出我們的 createElement 函數(shù)應(yīng)有的參數(shù)都是什么了。

  • 第一個(gè)參數(shù) type —— 就是這個(gè)標(biāo)簽的類型
  • 第二個(gè)參數(shù) attribute —— 標(biāo)簽內(nèi)的所有屬性與值
  • 剩余的參數(shù)都是子屬性 ...children —— 這里我們使用了 JavaScript 之中比較新的語(yǔ)法 ...children 表示把后面所有的參數(shù) (不定個(gè)數(shù)) 都會(huì)變成一個(gè)數(shù)組賦予給 children 變量

那么我們 createElement 這個(gè)函數(shù)就可以寫成這樣了:

  1. function createElement(type, attributes, ...children) {
  2. return;
  3. }

函數(shù)我們有了,但是這個(gè)函數(shù)可以做什么呢?其實(shí)這個(gè)函數(shù)可以用來(lái)做任何事情,因?yàn)檫@個(gè)看起來(lái)長(zhǎng)的像 DOM API,所以我們完全可以把它做成一個(gè)跟 React 沒(méi)有關(guān)系的實(shí)體 DOM。

比如說(shuō)我們就可以在這個(gè)函數(shù)中返回這個(gè) type 類型的 element 元素。這里我們把所有傳進(jìn)來(lái)的 attributes 給這個(gè)元素加上,并且我們可以給這個(gè)元素掛上它的子元素。

創(chuàng)建元素我們可以用 createElement(type),而加入屬性我們可以使用 setAttribute(),最后掛上子元素就可以使用 appendChild()

  1. function createElement(type, attributes, ...children) {
  2. // 創(chuàng)建元素
  3. let element = document.createElement(type);
  4. // 掛上屬性
  5. for (let attribute in attributes) {
  6. element.setAttribute(attribute);
  7. }
  8. // 掛上所有子元素
  9. for (let child of children) {
  10. element.appendChild(child);
  11. }
  12. // 最后我們的 element 就是一個(gè)節(jié)點(diǎn)
  13. // 所以我們可以直接返回
  14. return element;
  15. }

這里我們就實(shí)現(xiàn)了 createElement 函數(shù)的邏輯。最后我們還需要在頁(yè)面上掛載上我們的 DOM 節(jié)點(diǎn)。所以我們可以直接掛載在 body 上面。

  1. // 在 main.js 最后加上這段代碼
  2. let a = (
  3. <div id="a">
  4. <span></span>
  5. <span></span>
  6. <span></span>
  7. </div>
  8. );
  9.  
  10. document.body.appendChild(a);

這里還需要注意的是,我們的 main.html 中沒(méi)有加入 body 標(biāo)簽,沒(méi)有 body 元素的話我們是無(wú)法掛載到 body 之上的。所以這里我們就需要在 main.html 當(dāng)中加入 body 元素。

  1. <body></body>
  2. <script src="dist/main.js"></script>

好,這個(gè)時(shí)候我們就可以 webpack 打包,看一下效果。

使用JSX 建立組件 Parser(解析器)開(kāi)發(fā)的示例

Wonderful! 我們成功的把節(jié)點(diǎn)生成并且掛載到 body 之上了。但是如果我們的 div 里面加入一段文字,這個(gè)時(shí)候就會(huì)有一個(gè)文本節(jié)點(diǎn)被傳入我們的 createElement 函數(shù)當(dāng)中。毋庸置疑,我們的 createElement 函數(shù)以目前的邏輯是肯定無(wú)法處理文本節(jié)點(diǎn)的。

接下來(lái)我們就把處理文本節(jié)點(diǎn)的邏輯加上,但是在這之前我們先把 div 里面的 span 標(biāo)簽刪除,換成一段文本 “hello world”。

  1. let a = <div id="a">hello world</div>;

在我們還沒(méi)有加入文本節(jié)點(diǎn)的邏輯之前,我們先來(lái) webpack 打包一下,看看具體會(huì)報(bào)什么錯(cuò)誤。

使用JSX 建立組件 Parser(解析器)開(kāi)發(fā)的示例

使用JSX 建立組件 Parser(解析器)開(kāi)發(fā)的示例

首先我們可以看到,在 createElement 函數(shù)調(diào)用的地方,我們的文本被當(dāng)成字符串傳入,然后這個(gè)參數(shù)是接收子節(jié)點(diǎn)的,并且在我們的邏輯之中我們使用了 appendChild,這個(gè)函數(shù)是接收 DOM 節(jié)點(diǎn)的。顯然我們的文本字符串不是一個(gè)節(jié)點(diǎn),自然就會(huì)報(bào)錯(cuò)。

通過(guò)這種調(diào)試方式我們可以馬上定位到,我們需要在哪里添加邏輯去實(shí)現(xiàn)這個(gè)功能。這種方式也可以算是一種捷徑吧。

所以接下來(lái)我們就回到 main.js,在我們掛上子節(jié)點(diǎn)之前,判斷以下 child 的類型,如果它的類型是 “String” 字符串的話,就使用 createTextNode() 來(lái)創(chuàng)建一個(gè)文本節(jié)點(diǎn),然后再掛載到父元素上。這樣我們就完成了字符節(jié)點(diǎn)的處理了。

  1. function createElement(type, attributes, ...children) {
  2. // 創(chuàng)建元素
  3. let element = document.createElement(type);
  4. // 掛上屬性
  5. for (let name in attributes) {
  6. element.setAttribute(name, attributes[name]);
  7. }
  8. // 掛上所有子元素
  9. for (let child of children) {
  10. if (typeof child === 'string')
  11. child = document.createTextNode(child);
  12. element.appendChild(child);
  13. }
  14. // 最后我們的 element 就是一個(gè)節(jié)點(diǎn)
  15. // 所以我們可以直接返回
  16. return element;
  17. }
  18.  
  19. let a = <div id="a">hello world</div>;
  20.  
  21. document.body.appendChild(a);

我們用這個(gè)最新的代碼 webpack 打包之后,就可以在瀏覽器上看到我們的文字被顯示出來(lái)了。

到了這里我們編寫的 createElement 已經(jīng)是一個(gè)比較有用的東西了,我們已經(jīng)可以用它來(lái)做一定的 DOM 操作。甚至它可以完全代替我們自己去寫 document.createElement 的這種反復(fù)繁瑣的操作了。

這里我們可以驗(yàn)證以下,我們?cè)?div 當(dāng)中重新加上我們之前的三個(gè) span, 并且在每個(gè) span 中加入文本。11

  1. let a = (
  2. <div id="a">
  3. hello world
  4. <span>a</span>
  5. <span>b</span>
  6. <span>c</span>
  7. </div>
  8. );

然后我們重新 webpack 打包后,就可以看到確實(shí)是可以完整這種 DOM 的操作的。

使用JSX 建立組件 Parser(解析器)開(kāi)發(fā)的示例

現(xiàn)在的代碼已經(jīng)可以完成一定的組件化的基礎(chǔ)能力。

實(shí)現(xiàn)自定義標(biāo)簽

之前我們都是在用一些,HTML 自帶的標(biāo)簽。如果我們現(xiàn)在把 div 中的 d 改為大寫 D 會(huì)怎么樣呢?

  1. let a = (
  2. <Div id="a">
  3. hello world
  4. <span>a</span>
  5. <span>b</span>
  6. <span>c</span>
  7. </Div>
  8. );

使用JSX 建立組件 Parser(解析器)開(kāi)發(fā)的示例

使用JSX 建立組件 Parser(解析器)開(kāi)發(fā)的示例

果不其然,就是會(huì)報(bào)錯(cuò)的。不過(guò)我們找到了問(wèn)題根源的關(guān)鍵,這里我們發(fā)現(xiàn)當(dāng)我們把 div 改為 Div 的時(shí)候,傳入我們 createElement 的 div 從字符串 ‘div' 變成了一個(gè) Div 類。

當(dāng)然我們的 JavaScript 中并沒(méi)有定義 Div 類,這里自然就會(huì)報(bào) Div 未定義的錯(cuò)誤。知道問(wèn)題的所在,我們就可以去解決它,首先我們需要先解決未定義的問(wèn)題,所以我們先建立一個(gè) Div 的類。

  1. // 在 createElment 函數(shù)之后加入
  2. class Div {}

然后我們就需要在 createElement 里面做類型判斷,如果我們遇到的 type 是字符類型,就按原來(lái)的方式處理。如果我們遇到是其他情況,我們就實(shí)例化傳過(guò)來(lái)的 type

  1. function createElement(type, attributes, ...children) {
  2. // 創(chuàng)建元素
  3. let element;
  4. if (typeof type === 'string') {
  5. element = document.createElement(type);
  6. } else {
  7. element = new type();
  8. }
  9.  
  10. // 掛上屬性
  11. for (let name in attributes) {
  12. element.setAttribute(name, attributes[name]);
  13. }
  14. // 掛上所有子元素
  15. for (let child of children) {
  16. if (typeof child === 'string') child = document.createTextNode(child);
  17. element.appendChild(child);
  18. }
  19. // 最后我們的 element 就是一個(gè)節(jié)點(diǎn)
  20. // 所以我們可以直接返回
  21. return element;
  22. }

這里我們還有一個(gè)問(wèn)題,我們有什么辦法可以讓自定義標(biāo)簽像我們普通 HTML 標(biāo)簽一樣操作呢?在最新版的 DOM 標(biāo)準(zhǔn)里面是有辦法的,我們只需要去注冊(cè)一下我們自定義標(biāo)簽的名稱和類型。

但是我們現(xiàn)行比較安全的瀏覽版本里面,還是不太建議這樣去做的。所以在使用我們的自定義 element 的時(shí)候,還是建議我們自己去寫一個(gè)接口。

首先我們是需要建立標(biāo)簽類,這個(gè)類能讓任何標(biāo)簽像我們之前普通 HTML 標(biāo)簽的元素一樣最后掛載到我們的 DOM 樹(shù)上。

它會(huì)包含以下方法:

  • mountTo() —— 創(chuàng)建一個(gè)元素節(jié)點(diǎn),用于后面掛載到 parent 父級(jí)節(jié)點(diǎn)上
  • setAttribute() —— 給元素掛上所有它的屬性
  • appendChild() —— 給元素掛上所有它的子元素

首先我們來(lái)簡(jiǎn)單實(shí)現(xiàn)以下我們 Div 類中的 mountTo 方法,這里我們還需要給他加入 setAttributeappendChild 方法,因?yàn)樵谖覀兊?createElement 中有掛載屬性子元素的邏輯,如果沒(méi)有這兩個(gè)方法就會(huì)報(bào)錯(cuò)。但是這個(gè)時(shí)候我們先不去實(shí)現(xiàn)這兩個(gè)方法的邏輯,方法內(nèi)容留空即可。

  1. class Div {
  2. setAttribute() {}
  3. appendChild() {}
  4. mountTo(parent) {
  5. this.root = document.createElement('div');
  6. parent.appendChild(this.root);
  7. }
  8. }

這里面其實(shí)很簡(jiǎn)單首先給類中的 root 屬性創(chuàng)建成一個(gè) div 元素節(jié)點(diǎn),然后把這個(gè)節(jié)點(diǎn)掛載到這個(gè)元素的父級(jí)。這個(gè) parent 是以參數(shù)傳入進(jìn)來(lái)的。

然后我們就可以把我們?cè)瓉?lái)的 body.appendChild 的代碼改為使用 mountTo 方法來(lái)掛載我們的自定義元素類。

  1. // document.body.appendChild(a);
  2. a.mountTo(document.body);

用現(xiàn)在的代碼,我們 webpack 打包看一下效果:

使用JSX 建立組件 Parser(解析器)開(kāi)發(fā)的示例

我們可以看到我們的 Div 自定義元素是有正確的被掛載到 body 之上。但是 Div 中的 span 標(biāo)簽都是沒(méi)有被掛載上去的。如果我們想它與普通的 div 一樣去工作的話,我們就需要去實(shí)現(xiàn)我們的 setAttributeappendChild 邏輯。

接下來(lái)我們就一起來(lái)嘗試完成剩余的實(shí)現(xiàn)邏輯。在開(kāi)始寫 setAttribute 和 appendChild 之前,我們需要先給我們的 Div 類加入一個(gè)構(gòu)造函數(shù) constructor。在這里個(gè)里面我們就可以把元素創(chuàng)建好,并且代理到 root 上。

  1. constructor() {
  2. this.root = document.createElement('div');
  3. }

然后的 setAttribute 方法其實(shí)也很簡(jiǎn)單,就是直接使用 this.root 然后調(diào)用 DOM API 中的 setAttribute 就可以了。而 appendChild 也是同理。最后我們的代碼就是如下:

  1. class Div {
  2. // 構(gòu)造函數(shù)
  3. // 創(chuàng)建 DOM 節(jié)點(diǎn)
  4. constructor() {
  5. this.root = document.createElement('div');
  6. }
  7. // 掛載元素的屬性
  8. setAttribute(name, attribute) {
  9. this.root.setAttribute(name, attribute);
  10. }
  11. // 掛載元素子元素
  12. appendChild(child) {
  13. this.root.appendChild(child);
  14. }
  15. // 掛載當(dāng)前元素
  16. mountTo(parent) {
  17. parent.appendChild(this.root);
  18. }
  19. }

我們 webpack 打包一下看看效果:

使用JSX 建立組件 Parser(解析器)開(kāi)發(fā)的示例

我們可以看到,div 和 span 都被成功掛載到 body 上。也證明我們自制的 div 也能正常工作了。

這里還有一個(gè)問(wèn)題,因?yàn)槲覀冏詈笳{(diào)用的是 a.mountTo(),如果我們的變量 a 不是一個(gè)自定義的元素,而是我們普通的 HTML 元素,這個(gè)時(shí)候他們身上是不會(huì)有 mountTo 這個(gè)方法的。

所以這里我們還需要給普通的元素加上一個(gè) Wrapper 類,讓他們可以保持我們?cè)仡惖臉?biāo)準(zhǔn)格式。也是所謂的標(biāo)準(zhǔn)接口。

我們先寫一個(gè) ElementWrapper 類,這個(gè)類的內(nèi)容其實(shí)與我們的 Div 是基本一致的。唯有兩個(gè)區(qū)別

  1. 在創(chuàng)建 DOM 節(jié)點(diǎn)的時(shí)候,可以通過(guò)傳當(dāng)前元素名 type 到我們的構(gòu)造函數(shù),并且用這個(gè) type 去建立我們的 DOM 節(jié)點(diǎn)
  2. appendChild 就不能直接使用 this.root.appendChild,因?yàn)樗衅胀ǖ臉?biāo)簽都被改為我們的自定義類,所以 appendChild 的邏輯需要改為 child.mountTo(this.root)
  1. class ElementWrapper {
  2. // 構(gòu)造函數(shù)
  3. // 創(chuàng)建 DOM 節(jié)點(diǎn)
  4. constructor(type) {
  5. this.root = document.createElement(type);
  6. }
  7. // 掛載元素的屬性
  8. setAttribute(name, attribute) {
  9. this.root.setAttribute(name, attribute);
  10. }
  11. // 掛載元素子元素
  12. appendChild(child) {
  13. child.mountTo(this.root);
  14. }
  15. // 掛載當(dāng)前元素
  16. mountTo(parent) {
  17. parent.appendChild(this.root);
  18. }
  19. }
  20.  
  21. class Div {
  22. // 構(gòu)造函數(shù)
  23. // 創(chuàng)建 DOM 節(jié)點(diǎn)
  24. constructor() {
  25. this.root = document.createElement('div');
  26. }
  27. // 掛載元素的屬性
  28. setAttribute(name, attribute) {
  29. this.root.setAttribute(name, attribute);
  30. }
  31. // 掛載元素子元素
  32. appendChild(child) {
  33. child.mountTo(this.root);
  34. }
  35. // 掛載當(dāng)前元素
  36. mountTo(parent) {
  37. parent.appendChild(this.root);
  38. }
  39. }

這里我們還有一個(gè)問(wèn)題,就是遇到文本節(jié)點(diǎn)的時(shí)候,是沒(méi)有轉(zhuǎn)換成我們的自定義類的。所以我們還需要寫一個(gè)給文本節(jié)點(diǎn),叫做 TextWrapper

  1. class TextWrapper {
  2. // 構(gòu)造函數(shù)
  3. // 創(chuàng)建 DOM 節(jié)點(diǎn)
  4. constructor(content) {
  5. this.root = document.createTextNode(content);
  6. }
  7. // 掛載元素的屬性
  8. setAttribute(name, attribute) {
  9. this.root.setAttribute(name, attribute);
  10. }
  11. // 掛載元素子元素
  12. appendChild(child) {
  13. child.mountTo(this.root);
  14. }
  15. // 掛載當(dāng)前元素
  16. mountTo(parent) {
  17. parent.appendChild(this.root);
  18. }
  19. }

有了這些元素類接口后,我們就可以改寫我們 createElement 里面的邏輯。把我們?cè)镜?document.createElementdocument.createTextNode 都替換成實(shí)例化 new ElementWrapper(type)new TextWrapper(content)即可。

  1. function createElement(type, attributes, ...children) {
  2. // 創(chuàng)建元素
  3. let element;
  4. if (typeof type === 'string') {
  5. element = new ElementWrapper(type);
  6. } else {
  7. element = new type();
  8. }
  9.  
  10. // 掛上屬性
  11. for (let name in attributes) {
  12. element.setAttribute(name, attributes[name]);
  13. }
  14. // 掛上所有子元素
  15. for (let child of children) {
  16. if (typeof child === 'string')
  17. child = new TextWrapper(child);
  18. element.appendChild(child);
  19. }
  20. // 最后我們的 element 就是一個(gè)節(jié)點(diǎn)
  21. // 所以我們可以直接返回
  22. return element;
  23. }

然后我們 webpack 打包一下看看。

使用JSX 建立組件 Parser(解析器)開(kāi)發(fā)的示例

沒(méi)有任何意外,我們整個(gè)元素就正常的被掛載在 body 的上了。同理如果我們把我們的 Div 改回 div 也是一樣可以正常運(yùn)行的。

當(dāng)然我們一般來(lái)說(shuō)也不會(huì)寫一個(gè)毫無(wú)意義的這種 Div 的元素。這里我們就會(huì)寫一個(gè)我們組件的名字,比如說(shuō) Carousel,一個(gè)輪播圖的組件。

完整代碼 —— 對(duì)你有用的話,就給我一個(gè) ?? 吧,謝謝!


使用JSX 建立組件 Parser(解析器)開(kāi)發(fā)的示例

 

我們?cè)谶@里互相監(jiān)督,互相鼓勵(lì),互相努力走上人生學(xué)習(xí)之路,讓學(xué)習(xí)改變我們生活!

學(xué)習(xí)的路上,很枯燥,很寂寞,但是希望這樣可以給我們彼此帶來(lái)多一點(diǎn)陪伴,多一點(diǎn)鼓勵(lì)。我們一起加油吧! (? •??•?)?


到此這篇關(guān)于使用JSX 建立組件 Parser(解析器)開(kāi)發(fā)的示例的文章就介紹到這了,更多相關(guān)JSX建立組件Parser內(nèi)容請(qǐng)搜索服務(wù)器之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持服務(wù)器之家!

原文鏈接:https://tridiamond.blog.csdn.net/article/details/112352745

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 国产精品第一国产精品 | 成人免费在线观看视频 | 国产成人精品一区二区三区视频 | 免费一级电影 | 中文字幕乱码一区二区三区 | 99热热热| 欧美日韩国产在线观看 | 亚洲精品在线视频观看 | 黄色在线免费观看视频网站 | 欧美日韩激情一区二区三区 | 欧美精品久久久久久久久老牛影院 | 日韩中文字幕一区 | 日日麻批免费视频40分钟 | 精品综合久久 | 国产高清亚洲 | 久久久免费视频观看 | 激情六月婷 | 精品一区二区视频 | 骚视频网站 | 91.成人天堂一区 | 中文字幕1区 | 中文字幕一区二区三区四区五区 | 亚洲美女在线视频 | 国产伦理一区 | 天天久久 | 久久久婷| 国产片在线观看 | 日日摸夜夜添夜夜添精品视频 | 亚洲成人午夜电影 | 成人国产精品一区二区免费麻豆 | 国产精品美女一区 | 看黄免费在线 | 激情综合五月天 | 精品www| 久久精品一区二区 | 一区日韩 | 欧美日韩国产免费 | 中文一区| 国产精品久久久久久av下载网址 | 国产精品久久久久久久一区探花 | 亚洲精品在线视频 |