jsx編譯後生成什麼
㈠ 為什麼我推薦使用JSX開發Vue3
以防萬一有的同學實在不看官方文檔,我先提一嘴,SFC 就是寫 Vue 組件的時候寫的.vue文件,這一個文件就是一個 SFC,全稱 Single File Component,也即單文件組件。
在開始說我個人的觀點之前,我們先來看幾個事實:
一是: Vue3 的定義原生支持 JSX,並且 Vue3 源碼中有jsx.d.ts來便於使用 JSX。 不知道同學們看到這里會想到什麼, 我的第一反應是: 社區對於 JSX 的需求聲音是不小的,所以會反向推動 Vue3 官方對於 JSX 的支持。
二是:AntDesign 的 vue3 版本,基本全部都是用 JSX 開發的,而且 Vue3 現在官方的 babel-jsx 插件就是阿里的人一開始維護的, 雖然我向來不喜歡阿里系的 KPI 推動技術方式,而且現在的 JSX 語法支持也不是很符合我的期望,但至少在使用 JSX 開發是更優秀的選擇這點上,我還是很認可 AntDesign 團隊的。
OK,說這些呢,主要是先擺出一些事實作為依據,讓有些同學可以不需要拿什麼:
這些觀點來批鬥我,首先我都會從客觀的角度來分析為什麼,至少是我是能講出優劣勢的理由的。
OK,前言差不多到這里,接下來咱給您分析分析,為什麼你應該選擇 JSX 來開發 Vue。
其實第一點就已經是殺手了,對於想要使用 TypeScript 來開發 Vue3 應用的同學來說,這簡直就是 SFC 無法克服的世界難題。
一句話概括: TypeScript 原生支持 JSX 語法,而基本無望 TS 官方能支持 SFC 的 template 語法 。
TS 毫無疑問在前端社區的重要性越來越大,但凡未來對於代碼質量有一定要求的前端團隊,都應該會選擇使用 TS 來進行開發。 而且現在基本上在 NPM 上都能看到包你都能找到對應的 TS 定義,現在使用 TS 開發成本已經只剩下 你是不是會 TS 語法了 ,在這種情況下是否支持 TS 則是開發模式在未來走不走的遠的重要原因。
目前 SFC 只能通過shim讓 TS 可以引入.vue文件,但是對於所有 SFC 的組件的定義都是一樣的:
也就是說你引入的 SFC 組件,TS 是不知道這個組件的 Props 應該接收什麼的。所以你無法享受到這些 TS 的優勢:
當然你會說既然 Vue 官方能開發處 SFC 的語法,自然會支持這些特性。我表示這當然有可能,但是這個難度是非常大的,需要很多方面的支持,甚至可能需要 TS 官方團隊願意協助, 但是我想不到 TS 官方有什麼理由來支持 SFC,因為這只是 Vue 自己創建的方言,在其他場景下是沒有使用的,TS 是面向全社區的,我覺得他們不會考慮主動來支持 SFC。
那麼有同學要問了,JSX 不也是非原生的 JS 語法么,他怎麼就能讓 TS 官方支持了呢,是不是 FB 和微硬之間有什麼 PY 交易?
這就涉及第二點了,JSX 和靜態模板的靈活性區別。
很多人弄錯了一個問題,就是覺得 SFC 的模板語法和 JSX 是一樣的,都是一種別人發明的語法,並不是 JS 原生的。這是事實,但又有一些區別,這個區別主要是體現在對於 JSX 的認知上。
一句話概括: JSX 並沒有擴展 JS 的語法,他只是縮略了 JS 的寫法!其本質就是 JS 的語法糖
就像 es6 給增加的語法糖,比如
這種寫法並沒有擴展 JS 的能力,只是簡便了寫法,JSX 也是一樣的。
JSX 其實就是方法調用,他和 JS 是有一對一對應關系的,我們來看一個例子:
這里的 JSX 語法編譯之後其實就是:
而 JSX 就是這些了,沒有什麼更多的內容,所以說 JSX 只是 方便我們寫嵌套的函數調用的語法糖 ,而其本身沒有擴展任何其他的內容。
但是 SFC 就不一樣了。
SFC 定義的不僅是語法,更是文件。
SFC 的具體定義是 單文件組件 ,它本身就是把一個文件看作一個單位,所以他的約束性是要大很多的,你必須具有固定的文件結構才能使用 SFC,這做了很多的限制:
我們一點點來講
這個說實話非常非常不方便,很多時候我們寫一個頁面的時候其實經常會需要把一些小的節點片段拆分到小組件裡面進行復用(如果你現在沒有這個習慣可能就是因為 SFC 的限制讓你習慣了全部寫在一個文件內)。
React 生態中豐富的 css-in-js 方案就是很好的例子,我們可以通過:
如果我們這個頁面需要使用特定樣式的按鈕,通過這種方式在頁面文件裡面封裝一下是非常常見的。因為沒必要把這個組件拆分出去,他也不是一個可復用的組件,拆分出去了還要多一次import。
Vue 生態基本沒有 css-in-js 的成熟方案其實跟這個限制也很有關系。
再來一個例子,比如我們封裝了一個 Input 組件,我們希望同時導出 Password 組件和 Textarea 組件來方便用戶根據實際需求使用,而這兩個組件本身內部就是用的 Input 組件,只是定製了一些 props:
在 JSX 中可以非常簡單地實現,但是如果通過 SFC,你可能就要強行拆成三個文件,另外為了方便,你可能還要增加一個index.js來導出這三個組件,你能想像這多了多少工作量么。
我不知道有多少同學看過 Vue 的 template 編譯出來之後的代碼,以我的經驗來說看過的可能不會超過 50%(樂觀估計),建議同學們如果還不了解的,可以去嘗試看一下。
為什麼要看這個呢?因為你看了之後你會發現,你在 template 裡面寫的類似 HTMl 的內容,其實跟 HTML 根本沒啥關系,他們也會被編譯成類似 JSX 編譯出來的結果。
類似這樣的結果,而這裡面h函數調用的結果就是一個 VNode,是 Vue 中的節點的基礎單元。那麼既然這些單元就是一個對象,其實理所當然的,他們是可以作為參數傳遞的。 也就是說,理論上他們是可以通過props把節點當作參數傳遞給其他組件的。
這個做法在 React 中非常常見,叫做renderProps,並且其非常靈活:
但是因為 SFC 模板的限制,我們很難在 SFC 裡面的 props 上寫節點:
這樣寫是不行的,因為 SFC 定義了:header綁定接受的只能是 js 表達式,而 顯然不是。
因為通過 props 傳遞不行,所以 Vue 才發明了 slot 插槽的概念
雖然我們一直再說 Vue 簡單,但是事實上ScopedSlots一度成為新手理解 Vue 的噩夢,很多同學都被這個繞來繞去的作用域整的死去活來。
我們看一個ScopedSlots的例子:
這里ctx是Comp裡面的屬性,通過這種方式傳遞出來,讓我們在當前組件可以調用父組件裡面的屬性。這簡直就是理解的噩夢,但是如果用 JSX 實現類似功能就非常簡單:
我們只是給一個叫做scope的 props 傳遞來一個函數,這個函數接受一個name屬性,在Comp裡面會調用這個函數並傳入name。 簡單來說我們傳入的就是一個構建節點片段的函數,就是這么簡單。
這就是因為 SFC 的模板的限制,導致靈活性不足,Vue 需要去創造概念,創造關鍵字來抹平這些能力的不足,而創造的概念自然就引入了學習成本。
所以其實我一直不認可 Vue 比 React 好學的說法的,如果你真的認真研究所有用法,並且總是嘗試用最合理的方式實現功能,那麼 Vue 絕對不會比 React 簡單。
這個體現在兩個方面,一個是我們定義在全局的一些固定數據如果要在組件內使用的話,就要通過this掛載到組件上。
比如我們緩存了一份城市數據,這種數據基本上是不會改的,所以也沒必要掛載到組件上讓其能夠響應式。但是在 SFC 裡面這是做不到的, 因為模板的執行上下文是在編譯時綁定。你在模板裡面訪問的變數,都會在編譯時自動綁定到this上,因為模板需要編譯,其本身也是字元串不具有作用域的概念。
而這在 JSX 中則不復存在:
另外一個方面則是在組件使用上,在 SFC 中,組件必須事先注冊,因為我們在模板裡面寫的只能是字元串而不能是具體某個組件變數。 那麼模板中的組件和真實的組件對象只能通過字元串匹配來實現綁定。這帶來了以下問題:
在 JSX 中則沒有這些問題,因為 JSX 裡面直接使用組件引用作為參數:
其實上面能看出來,除了 SFC 本身的問題之外,Vue 使用字元串模板也會帶來很多的靈活性問題。 最直接的證據,就是 Vue 使用了directive來擴展功能(當然這不是 Vue 發明的,老早的模板引擎就有類似問題)。
為什麼說directive是不得已的選擇呢?因為靜態模板缺失邏輯處理的能力。我們拿列表循環舉例,在 JS 中我們可以非常方便地通過map函數來創建列表:
而因為 JSX 本身就是函數調用,所以上面的代碼和 JSX 結合起來也非常自然:
上面的例子對應到 JS 如下:
這仍然是因為 JSX 只是 JS 的語法糖的原因,所有能在 JS 中實現的在 JSX 裡面都能實現。
而 SFC 的模板是基於字元串編譯的,其本身就是一段字元串,我們不能直接在模板裡面寫map來循環節點,(當然我們可以在可以接收表達式的地方寫,比如v-on裡面)。
那麼我們不能循環節點,有需要這樣的功能來渲染列表,怎麼辦呢?就是發明一個標志來告訴編譯器這里需要循環,在 Vue 中的體現就是v-for指令。
同學們可能要問了,既然 Vue 能實現v-for,為什麼不直接實現表達式循環列表呢?他當然也可以實現,但是他肯定不會這么選,因為成本太高了。 他要這么做就相當於他要實現一個 JS 引擎,而其實裡面很多內容又是不必須的,一個v-for其實就能夠適用大部分情況了。
但有了v-for就需要v-if,那麼後面還會需要其他各種能力,這就是一種方言的產生和發展的過程。
當然指令也不僅僅是 JS 表達式的代替品,其本身也是增加了一些其他能力的,比如它能夠讓我們更方便地訪問 DOM 節點, 但是嘛,我們用框架的理由不就是為了能夠盡可能的屏蔽 DOM 操作嘛
以上就是我對應該選擇使用 JSX 還是 SFC 進行開發的分析,其實歸根到底 SFC 的問題在於其沒有擁抱 JS, 他的語法是自己發明的,他需要有一個 JS 實現的 compiler 來讓其最終能在 JS 環境中運行,這本質上就是一種發明, 我們不能否認發明確實有優點,但我們也不能只看有點不看問題,沒能擁抱 JS 自然就很難完全復用 JS 社區的優勢 而 JS 社區一直在蓬勃發展,好用的工具一直在涌現, 而 SFC 想要使用 JS 社區的這些工具還要自己再實現一份 ,我們可以細數以下 SFC 做了哪些兼容
基本上常用的工具我們都需要等待 Vue 社區或者官方開發了插件之後才能運行。而 JSX 因為有 babel 和 typescript 的官方支持, 基本上所有新的 JS 生態工具原生都是支持的。
在這 Vue3 開始預備發力的階段,我們還是希望 Vue 社區能夠使用更優秀更規范的方式來進行開發, 其實如果我們直接使用 JSX 開發 Vue3,我們會發現很多時候我們都不需要用到emit、attrs這些概念, 甚至如果 Vue3 的 JSX 插件支持,我們甚至能夠拋棄slots。
但是因為 Vue3 一定要考慮兼容 Vue2,導致本身潛力很好的 Vue3 總是顯得縮手縮腳,這不得不說是一種遺憾。
㈡ browserify 怎麼把jsx轉化js
Browserify 可以讓你使用類似於 node 的 require() 的方式來組織瀏覽器端的 javascript 代碼,通過預編譯讓前端 Javascript 可以直接使用 Node NPM 安裝的一些庫。
安裝:
npm install -g browserify
示例
這是 main.js 的內容,像普通的 nodejs 程序那樣使用 require() 載入庫和文件:
var foo = require('./foo.js');
var bar = require('../lib/bar.js');
var gamma = require('gamma');
var elem = document.getElementById('result');
var x = foo(100) + bar('baz');
elem.textContent = gamma(x);
導出的方法:
mole.exports = function (n) { return n * 111 }
使用 browserify 編譯:
$ browserify main.js > bundle.js
現在 main.js 需要的所有其它文件都會被編譯進 bundle.js 中,包括很多層 require() 的情況也會一起被遞歸式的編譯過來。
編譯好的 js 可以直接拿到瀏覽器使用
<script src="bundle.js"></script>
㈢ 如何使用 ES6 編寫一個 React 模塊,並且編譯後發布到 NPM
前言
如果你在使用 React, 那麼肯定已經擼了好多自己的組件, 並嘗試著共享出來。在 OneAPM 前端開發過程中, 我們也曾遇到了一些組件共享的問題:
例如:
是通過 git 直接發布還是通過 NPM 發布 ?
發布的是 ES5 的代碼還是 ES6 的代碼 ?
如何解決 Babel5 和 Babel6 的沖突 ?
這篇文章會通過編寫一個叫做 MyComponet 的例子來演示發布一個模塊需要注意的地方, 並不涉及單元測試和代碼規范等。
前端開發果真是發展迅猛,剛享受到由模塊化,組件化和單元測試帶來的種種好處,又得迅速擁抱 Grunt, Gulp, Browserify, Webpack 這類自動化工具的變革。除了工具和生態圈,JavaScript 本身也在飛速發展著。ES2015(ES6) ,ES2016(ES7) ... 照這樣的節奏,幾乎是一年一個標准。標准多了,為解決兼容性的問題,竟也派生出了 源代碼 和 編譯 的概念。前端開發者通過語法糖、轉化器、Polyfill 等,可以享受到標准乃至尚未定稿草案里的規范的便利,大幅提升開發效率。
至於這個模塊本身,它的功能特別簡單, 就是顯示模塊自身的的屬性。
源代碼
我們來編寫組件 MyComponent.jsx ,放到項目的 src 目錄下。
import React from 'react';
const MyComponent = props=> {
return <div>
props:
<pre>{JSON.stringify(props, null, 2)}</pre>
</div>
}
export default MyComponent;
關於各種文件放在哪裡, 這里是我推薦的一些約定:
src 下用於存放源代碼
lib 是編譯後的代碼,這個目錄只讀
所有包含 ES6 語法的文件名統一後綴為 .es6
所有包含 JSX 語法的文件後統一綴名為 .jsx
假設源代碼里還有另外兩個文件 foo.es6 和 bar.js,簡化起見都丟到 src 的根目錄下。
編譯
為了把 ES6 代碼編譯成 ES5,需要安裝 Babel,這個工具可以說野心極大,一次編譯可以讓 JavaScript 運行在所有地方。(聽起來是不是有點 Java 的作風)
目前最常用的是 Babel5 版本,但是 Babel6 版本的設計更為精巧,已經非常推薦更新。也正是由於 Babel 有兩個版本,所以開發過程中很有可能遇到這樣的情況,
模塊 A 的開發依賴於 Babel5 版本,而模塊 B 依賴於 Babel6 版本。
解決這個問題最好的做法就是把 A 和 B 拆開,獨立開發和發布。並且在發布到 NPM 的時候發布是的編譯後的,也就是 ES5 版本的代碼。
所以如果你的機器上的 babel 是全局安裝的,是時候卸載它了,因為它的版本不是 5 就是 6 ,會導致一些不可預見的問題。
npm uninstall babel-cli --global
正確的安裝方式是把 babel-cli 作為 develeopment 的依賴
npm install babel-cli --save-dev
使用的時候並不是直接調用全局的 Babel 而是調用依賴里的 Babel 可執行文件
./node_moles/.bin/babel
如果按照前文的約定來組織代碼,src 目錄結構看起來是這樣的
src
├── bar.js
├── foo.es6
└── MyComponent.jsx
模塊所有的代碼都在一個目錄下,這樣編譯過程就簡單多了,兩條命令就可以完成
./node_moles/.bin/rimraf lib
./node_moles/.bin/babel src ---files --source-maps --extensions .es6,.es,.jsx --out-dir lib
輸出目錄的結構
lib
├── bar.js
├── foo.js
├── foo.js.map
├── MyComponent.js
└── MyComponent.js.map
具體解釋一下各個命令的作用:
第一條命令 ./node_moles/.bin/rimraf lib
作用 編譯前清空之前的 lib 目錄,這是一個好習慣,可以杜絕對 lib 下的文件的任何手動更改。
第二條命令
./node_moles/.bin/babel src --out-dir lib --source-maps --extensions .es6,.es,.jsx ---files
作用 遍歷 src 目錄下的文件,如果後綴名是 .es/.es6/.jsx 中的一種,就編譯成 ES5,否則就直接拷貝到輸出目錄 lib 下
參數詳解:
--out-dir lib 指定輸出目錄為 lib
--extensions .es6,.es,.jsx 指定需要編譯的文件類型
---files 對於不需要編譯的文件直接拷貝
--source-maps 生成 souce-map 文件
編譯過程中還隱含了一個步驟就是載入 .babelrc 文件里的配置,該文件內容如下
{
"presets": [
"es2015",
"stage-0",
"react"
]
}
這是因為 Babel6 採用了插件化的設計,做到了靈活配置:如果要轉換 JSX 語法文件,就加上 react 的 preset,同時項目依賴里要添加
babel-preset-react
npm install babel-preset-react --save-dev
樣例代碼
開發和調試 React 模塊目前最好用的打包工具還是 Webpack,在項目跟目錄下,新建一個 example 目錄:
example/index.html
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<title>Example</title>
</head>
<body></body>
<script src="bundle.js"></script>
</html>
example/src/index.jsx
import React from 'react';
import MyComponent,{foo,bar} from '../../';
import {render} from 'react-dom';
var element = document.createElement("div");
document.body.appendChild(element);
render(<MyComponent name="myComponent"/>, element);
webpack.config.js
var path = require('path');
mole.exports = {
entry: path.join(__dirname, 'example', 'src', 'index.jsx'),
output: {
filename: 'bundle.js'
},
mole: {
loaders: [{
test: /\.jsx$/,
loader: 'babel',
include: [
path.join(__dirname, 'example')
]
}]
},
devServer: {
contentBase: path.join(__dirname, 'example')
}
}
運行樣例代碼
./node_moles/.bin/webpack-dev-server
發布
發布前,還有一件事就是為你的模塊添加一個入口文件 index.js
mole.exports = require('./lib/MyComponent');
exports.default = require('./lib/MyComponent');
exports.bar = require('./lib/bar');
exports.foo = require('./lib/foo');
接下來就是發布到 NPM 了。
npm publish
使用
別的開發者在使用你新發布的模塊的時候可以這樣導入
import MyComponent,{foo,bat} from 'react-component-example'
導入的直接是 ES5 代碼,跳過編譯從而避免了出現 Babel 版本不一致的問題,並且速度更快,是不是很棒!
不過假設你的模塊包含很多組件,開發者可能只想用其中的一個或某幾個,這時可以這樣導入:
import MyComponent from 'react-component-example/src/MyComponent.jsx'
導入的是 ES6 代碼,並且會被加入父級項目的編譯過程。
㈣ 怎麼在html5中直接編譯jsx
/newscodejs.asp?lm2=84&list=5&icon=/img/fk.gif&tj=0&font=9&hot=0&new=0&line=0&lmname=0&open=1&n=36&more=0&t=0&week=0&zzly=0&hit=0&pls=0
這個文件顯示的結果必須是js格式
如輸出<img src="1.jpg" />
需要寫成
document.write("<img src=\"1.jpg\" />")
㈤ PS導入.jsx文件後生成的東西可能為xml嗎(人物的動作動畫)
不能,ps無法生成xml。
㈥ js 和 jsx 有什麼區別
js就是前端開發語言,jsx是React框架下用的,要在React框架中的編譯器編譯成js語言才能使用的
㈦ 如何使用react-tools將jsx編譯成JavaScript
使用react-tools將jsx編譯成JavaScript方法:
1,通過npm安裝react-tools
npm –g react-tools
2,通過cmd進入項目根目錄執行watch命令 jsx --watch src/ build/
src路徑下存放的是jsx文件,編譯後的js存放到build路徑下
3,當目標文件變化以後,自動構建生成新的js文件。
㈧ 如何使用react-tools將jsx編譯成JavaScript
jsx--watchsrc/build/目前官方已經停止維護 jsx 工具,推薦使用 babel 來替代。
㈨ jsx是javascript的一種語法擴展嗎
對的,JSX就是Javascript和XML結合的一種格式。React發明了JSX,利用HTML語法來創建虛擬DOM。當遇到<,JSX就當HTML解析,遇到{就當JavaScript解析。
如下(JS寫法)
varchild1=React.createElement('li',null,'FirstTextContent');
varchild2=React.createElement('li',null,'SecondTextContent');
varroot=React.createElement('ul',{className:'my-list'},child1,child2);
等價於(JSX寫法)
varroot=(
<ulclassName="my-list">
<li>FirstTextContent</li>
<li>SecondTextContent</li>
</ul>
);
後者將XML語法直接加入JS中,通過代碼而非模板來高效的定義界面。之後JSX通過翻譯器轉換為純JS再由瀏覽器執行。在實際開發中,JSX在產品打包階段都已經編譯成純JavaScript,JSX的語法不會帶來任何性能影響。另外,由於JSX只是一種語法,因此JavaScript的關鍵字class, for等也不能出現在XML中,而要如例子中所示,使用className, htmlFor代替,這和原生DOM在JavaScript中的創建也是一致的。JSX只是創建虛擬DOM的一種語法格式而已,除了用JSX,我們也可以用JS代碼來創建虛擬DOM。