此賬號為華為云開發者社區官方運營賬號,提供全面深入的云計算前景分析、豐富的技術干貨、程序樣例,分享華為云前沿資訊動態
本文分享自華為云社區《在 Vue 中如何使用 JSX,就這么簡單!【建議收藏】》,作者:紙飛機 。
JSX 是一種Javascript 的語法擴展,JSX=Javascript + XML,即在 Javascript 里面寫 XML,因為 JSX 的這個特性,所以他即具備了 Javascript 的靈活性,同時又兼具 html 的語義化和直觀性。(個人建議靈活度強的部分組件可以用JSX來代替,整個項目JSX屬實沒必要)
XML學習地址(學與不學可隨意,了解就ok):https://www.w3school.com.cn/xml/index.asp用template的弊端:https://www.mk2048.com/blog/blog_h1c2c22ihihaa.html
有時候,我們使用渲染函數(render function)來抽象組件,渲染函數不是很清楚的參見官方文檔, 而渲染函數有時候寫起來是非常痛苦的,所以只需要有個了解。
渲染函數:https://cn.vuejs.org/v2/guide/render-function.html#%E5%9F%BA%E7%A1%80
createElement(
'anchored-heading', {
props: {
level: 1
}
}, [
createElement('span', 'Hello'),
' world!'
]
)
其對應的模板是下面:
<anchored-heading :level="1">
<span>Hello</span> world!
</anchored-heading>
你看這寫起來多費勁,這個時候就派上 JSX 上場了。在 Vue 中使用 JSX,需要使用Babel 插件,它可以讓我們回到更接近于模板的語法上,接下來就讓我們一起開始在 Vue 中寫 JSX 吧。
vue create vue-jsx
# 選擇vue2的
安裝依賴:
npm install @vue/babel-preset-jsx @vue/babel-helper-vue-jsx-merge-props
# or
yarn add @vue/babel-preset-jsx @vue/babel-helper-vue-jsx-merge-props
配置.babelrc(babel.config.js) :
module.exports={
presets: [
'@vue/cli-plugin-babel/preset',
['@vue/babel-preset-jsx',
{
'injectH': false
}]
]
}
配置后我們啟動項目:
yarn serve
demo結構圖:
配置了babel.config.js后,我們把App.vue引入的HelloWorld.vue改為HelloWorld.js,并且刪除HelloWorld.js中關于template和style,以及script標簽。
export default {
name: 'HelloWorld',
props: {
msg: String
}
}
這里展示在 Vue 中書寫一些基礎內容。
純文本、動態內容、標簽使用、自定義組件、樣式和class
import myComponent from './myComponent'
import './HelloWorld.css'
// 創建一個組件button
const ButtonCounter={
name: "button-counter",
props: ["count"],
methods: {
onClick() {
this.$emit("change", this.count + 1);
}
},
render() {
return (
<button onClick={this.onClick}>數量 {this.count}+</button>
);
}
};
export default {
name: 'HelloWorld',
components: {
myComponent
},
data () {
return {
text:'hello 紙沒了飛機',
inputText:'我吃了',
count: 0
}
},
props: {
msg: String
},
watch: {},
methods: {
onChange(val) {
this.count=val;
alert(this.count)
}
},
render() {
// const {text,inputText,count}=this //通過解構,下方return片段中就不需要this
return (
<div>
<h3>內容</h3>
{/* 純文本 */}
<p>hello, I am Gopal</p>
{/* 動態內容 */}
<p>{ this.text }</p>
<p>hello { this.msg }</p>
{/* 輸入框 */}
<input/>
{/* 自定義組件 */}
<myComponent/>
<ButtonCounter
style={{ marginTop: "10px" }}
count={this.count}
type="button"
onChange={this.onChange}
/>
</div>
);
}
}
題外話:創建組件那里大家可以多學學const 創建的ButtonCounter組件的那種方式。在React中也是經常會這么創建的。
這么看的話和在template里寫沒有多大區別,標簽該是啥還是啥沒有變化。那么這么一想的話,style呢,class呢?接下來就是style和class樣式的寫法(包括動態樣式)
我們先給h3綁定一個class為colorRed:
<h3 class="colorRed">內容</h3>
審查元素發現直接寫class綁定是可以的:
那么class的樣式怎么寫呢?畢竟js文件里寫<style></style>貌似是不行的!
1、全局樣式
App.vue
<style>
.colorRed{
color: red;
}
</style>
2、引入一個css文件或者配合style-loader引入一個less、sass、stylus文件
注意:都需要安裝配置對應的loader,既然都是JSX了,那我們用stylus來講解下,相信less、sass大家都會了。stylus是一個省略了{},靠縮緊來識別的css編譯器。(不想用stylus可跳過,樣式這塊可隨意)
yarn add global stylus
yarn add --dev stylus stylus-loader
各種style安裝見:https://www.cnblogs.com/jimc/p/10265198.html
安裝完成后新建HelloWorld.styl,然后引入。
stylus的使用:https://www.jianshu.com/p/5fb15984f22d
stylus官網:https://stylus.zcopy.site/
控制臺stylus報錯見:https://blog.csdn.net/csdn_zhoushengnan/article/details/109448369
vscode編輯期報錯:安裝編輯器stylus語法插件,并重啟
效果:
行內樣式style:
<p style="color:blue">hello, I am Gopal</p>
動態綁定class和style
<p style={this.isGreen?'color:green':''}>{ this.text }</p>
<p class={this.isYellow?'colorYellow':''}>hello { this.msg }</p>
<p style={this.isRed?colorRed:''}>紅色的文字</p>
屬性綁定和普通HTML一樣的
畢竟class和style可都是html的屬性,這點相信大家都知道的。
<input placeholder="我是placeholder" />
<input placeholder={placeholderText} />
{/* 解構N個屬性,要啥放啥 */}
<div {...attrObj} />
效果:
常用指令
template常用指令:v-html| v-text、v-if、v-for、v-modal等。template的指令在JSX是無法使用的,故需要一些寫法,請看下面。
我新建個instructions.js用來示范指令這塊。在App.vue中引入。
v-html| v-text
在 JSX 里面,如果要設置 dom 元素的 innerHTML,就用到 domProps。
render() {
const { htmlCode }=this
return (
<div>
<div domPropsInnerHTML={htmlCode}></div>
</div>
);
}
雖然v-text有domPropsInnerText,但沒有用的必要。
v-if
分簡單的和復雜的。
簡單:
render() {
let bool=false
return (
<div>
{ bool ? <button>按鈕1</button> : <button>按鈕2</button>}
</div>
);
}
復雜:
render() {
let num=3
if(num===1){ return( <button>按鈕1</button> ) }
if(num===2){ return( <button>按鈕2</button> ) }
if(num===3){ return( <button>按鈕3</button> ) }
}
v-for
就使用 map 方法來實現,在react中也是如此。
render(h) {
var list=[1,2,3]
return(
<div>
{ list.map(item=> <button>按鈕{item}</button>) }
</div>
)
}
v-modal
注意:新版 vue-cli4 中,已經默認集成了 JSX 語法對 v-model 的支持,可以直接使用
<inputv-model={this.value}>
如果你的項目比較老,也可以安裝插件babel-plugin-jsx-v-model 來進行支持
我可是cli4,我來驗證下:
驗證結果:(通過)
當然以上兩種方式你都不想搞,你也可以手動支持,這就涉及到監聽事件了,請向下看。
監聽事件及事件修飾符
監聽事件想到用 onChange, onClick等。需要注意的是,傳參數不能使用 onClick={this.removePhone(params)},這樣子會每次 render 的時候都會自動執行一次方法應該使用 bind,或者箭頭函數來傳參
methods: {
handleClick(val){
alert(val)
}
},
<button type="button" onClick={this.handleClick.bind(this, 11)}>點擊bind</button>
<button type="button" onClick={()=> this.handleClick(11)}>點擊箭頭函數</button>
上面提到的用過監聽事件來實現v-modal
<input type="text" value={this.text} onInput={this.input}/>
methods: {
input(e){
this.text=e.target.value
}
}
除此之外,還可以使用對象的方式去監聽事件:
render() {
return (
<input
value={this.content}
on={{
focus: this.$_handleFocus,
input: this.$_handleInput
}}
nativeOn={{
click: this.$_handleClick
}}
></input>
)
}
其他事件的使用同理都是加on。
事件修飾符
和指令一樣,除了個別的之外,大部分的事件修飾符都無法在JSX中使用,這時候你肯定已經習慣了,肯定有替代方案的。
.stop : 阻止事件冒泡,在JSX中使用event.stopPropagation()來代替
.prevent:阻止默認行為,在JSX中使用event.preventDefault()來代替
.self:只當事件是從偵聽器綁定的元素本身觸發時才觸發回調,使用下面的條件判斷進行代替
if (event.target !==event.currentTarget){
return
}
.enter與keyCode: 在特定鍵觸發時才觸發回調
if(event.keyCode===13) {
// 執行邏輯
}
除了上面這些修飾符之外,尤大大為了照顧我們這群CV仔,還是做了一點優化的,對于.once,.capture,.passive,.capture.once,尤大大提供了前綴語法幫助我們簡化代碼
render() {
return (
<div
on={{
// 相當于 :click.capture
'!click': this.$_handleClick,
// 相當于 :input.once
'~input': this.$_handleInput,
// 相當于 :mousedown.passive
'&mousedown': this.$_handleMouseDown,
// 相當于 :mouseup.capture.once
'~!mouseup': this.$_handleMouseUp
}}
></div>
)
}
如果有參數傳遞給方法,不能直接(參數),會在頁面中立即觸發,需要我在下面這種寫法:
clickOnce(val) {
alert(val);
},
<button
type="button"
on={{
'~click': ()=>this.clickOnce('只能點一次'),
}}
>
事件修飾符點擊一次
</button>
使用范圍(結合第三方ui組件)
不僅僅在 render 函數里面使用 JSX,而且還可以在 methods 里面返回 JSX,然后在 render 函數里面調用這個方法。并且也可以直接使用例如elementui等ui組件。
JSX 還可以直接賦值給變量、例如使用elementui的el-dialog。(您在測試該案例時記得安裝elemnt)
methods: {
$_renderFooter() {
return (
<div>
<el-button>確定</el-button>
<el-button onClick={ ()=>this.closeDialog() }>取消</el-button>
</div>
);
},
openDialog(){
this.visible=true
},
closeDialog(){
this.visible=false
}
},
render() {
const buttons=this.$_renderFooter();
return (
<div>
<Button onClick={ ()=>this.openDialog() }>打開Dialog</Button>
<el-dialog visible={this.visible}>
<div>彈窗內容</div>
<template slot="footer">{buttons}</template>
</el-dialog>
</div>
);
}
插槽就是子組件中提供給父組件使用的一個占位符,插槽分為默認插槽,具名插槽和作用域插槽,下面我依次為您帶來每種在JSX中的用法與如何去定義插槽。
默認插槽
使用默認插槽
使用element-ui的Dialog時,彈框內容就使用了默認插槽,在JSX中使用默認插槽的用法與普通插槽的用法基本是一致的,如下代碼所示:
render() {
return (
<ElDialog title="彈框標題" visible={true}>
{/*這里就是默認插槽*/}
<div>這里是彈框內容</div>
</ElDialog>
)
}
自定義默認插槽
在Vue的實例this上面有一個屬性slots,這個上面就掛載了一個這個組件內部的所有插槽,使用this.slots,這個上面就掛載了一個這個組件內部的所有插槽,使用this.slots.default就可以將默認插槽加入到組件內部。
export default {
props: {
visible: {
type: Boolean,
default: false
}
},
render() {
return (
<div class="custom-dialog" vShow={this.visible}>
{/**通過this.$slots.default定義默認插槽*/}
{this.$slots.default}
</div>
)
}
}
使用:
<myComponent visible={true} slot>我是自定義默認插槽</myComponent>
另vShow相當于 v-show,不代表別的也可以這樣!
具名插槽
使用具名插槽有時候我們一個組件需要多個插槽,這時候就需要為每一個插槽起一個名字,比如element-ui的彈框可以定義底部按鈕區的內容,就是用了名字為footer的插槽。
render() {
return (
<ElDialog title="彈框標題" visible={true}>
<div>這里是彈框內容</div>
{/** 具名插槽 */}
<template slot="footer">
<ElButton>確定</ElButton>
<ElButton>取消</ElButton>
</template>
</ElDialog>
)
}
自定義具名插槽
在上節自定義默認插槽時提到了slots,對于默認插槽使用this.slots,對于默認插槽使用this.slots.default,而對于具名插槽,可以使用this.$slots.footer進行自定義。
render() {
return (
<div class="custom-dialog" vShow={this.visible}>
{this.$slots.default}
{/**自定義具名插槽*/}
<div class="custom-dialog__foolter">{this.$slots.footer}</div>
</div>
)
}
使用:
<myComponent visible={true}>
<template slot="footer">
<ElButton>確定</ElButton>
<ElButton>取消</ElButton>
</template>
</myComponent>
作用域插槽
使用作用域插槽
有時讓插槽內容能夠訪問子組件中才有的數據是很有用的,這時候就需要用到作用域插槽,在JSX中,因為沒有v-slot指令,所以作用域插槽的使用方式就與模板代碼里面的方式有所不同了。比如在element-ui中,我們使用el-table的時候可以自定義表格單元格的內容,這時候就需要用到作用域插槽。
<myComponent1
visible={this.visible}
{...{
scopedSlots: {
test: ({ user })=> {
// 這個user就是子組件傳遞來的數據,同理可這樣拿到el-table的row,不過test得是default,不過案例還是我這樣
<div style="color:blue;">快來啊,{user.name},看看這個作用域插槽</div>
},
},
}}
></myComponent1>
自定義作用域插槽
子組件中通過{this.$scopedSlots.test({ user: {name:‘紙飛機’}})} 指定插槽的名稱是 test,并將 user 傳遞給父組件。父組件在書寫子組件標簽的時候,通過 scopedSlots 值指定插入的位置是 test,并在回調函數獲取到子組件傳入的 user 值
注意:作用域插槽是寫在子組件標簽中的,類似屬性。而不是像具名插槽放在標簽內部
新建個作用域插槽.js
// 一個為jsx的子組件(玩玩插槽)
export default {
name: "myComponent",
data() {
return {
};
},
props: {
visible: {
type: Boolean,
default: false,
},
listData: {
type: Array,
default: function() {
return [];
},
},
},
render() {
return (
<div vShow={this.visible}>
{/**自定義作用域插槽*/}
<div class="item">
{this.$scopedSlots.test({
user: {name:'紙飛機'}
})}
</div>
</div>
);
},
};
效果:
函數式組件是一個無狀態、無實例的組件,詳見官網說明,新建一個 FunctionalComponent.js 文件,內容如下:
// export default ({ props })=> <p>hello {props.message}</p>;
// 或者推薦下方寫法
export default {
functional: true,
name: "item",
render(h, context) {
console.log(context.props)
return <div style="color:red;font-size:18px;font-weight:bold">{context.props.message}</div>;
},
};
HelloWorld.js中使用:
<funComponent message="展示下函數式組件"></funComponent>
效果:
https://codechina.csdn.net/qq_32442973/vue2-jsx-demo.git
無論你是要用vue2的jsx還是vue3的jsx都沒有本質區別,畢竟vue3是向下兼容vue2的;倘若你真的要學vue3的JSX,我建議你學完vue2的再去學;另我不推薦在vue中所有的組件和頁面都用JSX,兩者需要權衡利弊;同時也不必擔心JSX和template的相互嵌套,兩者是可以互相嵌套的。
參考:
https://www.cnblogs.com/ainyi/p/13324222.html
https://www.jb51.net/article/205764.htm
https://cn.vuejs.org/v2/guide/render-function.html#事件-amp-按鍵修飾符
https://www.cnblogs.com/htoooth/p/6973238.html
https://www.jianshu.com/p/84b708c80598
https://cloud.tencent.com/developer/article/1704608
點擊關注,第一時間了解華為云新鮮技術~華為云博客_大數據博客_AI博客_云計算博客_開發者中心-華為云
avaScript 是語言,而 React 是工具。
力爭用最簡潔的方式讓你入門 React,前提是你已了解 JavaScript 和 HTML。
如果你在學習 React 的同時,也在學習 JavaScript,那么這里羅列了一些必須要懂的 JavaScript 概念,來幫助你更好地學習 React。
本文將不會深入講解關于 JavaScript 方面的知識,你無需非常精通 JavaScript 才能學習 React,但以上的一些概念是最適合初學者掌握的 JavaScript 的重要知識。
當然,你也可以跳過這些基本概念直接進入下面章節的學習,當遇到不理解的問題再回過頭來翻閱這里的概念。
要理解 React 如何工作,首先要搞清楚瀏覽器是如何解釋你的代碼并轉化成 UI 的。
當用戶訪問一個網頁時,服務器將返回一段 html 代碼到瀏覽器,它可能看起來是這樣的:
瀏覽器閱讀了 html 代碼,并將它結構化為 DOM。
DOM 是一個 html 元素的對象化表達,它是銜接你的代碼和 UI 之間的橋梁,它表現為一個父子關系的樹形結構。
你可以使用 JavaScript 或 DOM 的內置方法來監聽用戶事件,操作 DOM 包括,查詢、插入、更新、刪除界面上特定的元素,DOM 操作不僅允許你定位到特定的元素上,并且允許你修改它的內容及樣式。
小問答:你可以通過操作 DOM 來修改頁面內容嗎?
讓我們一起來嘗試如何使用 JavaScript 及 DOM 方法來添加一個 h1 標簽到你的項目中去。
打開我們的代碼編輯軟件,然后創建一個新的 index.html 的文件,在文件中加入以下代碼:
<!-- index.html -->
<html>
<body>
<div></div>
</body>
</html>
然后給定 div 標簽一個特定的 id ,便于后續我們可以定位它。
<!-- index.html -->
<html>
<body>
<div id="app"></div>
</body>
</html>
要在 html 文件中編寫 JavaScript 代碼,我們需要添加 script 標簽
<!-- index.html -->
<html>
<body>
<div id="app"></div>
<script type="text/javascript"></script>
</body>
</html>
現在,我們可以使用 DOM 提供的 getElementById 方法來通過標簽的 ID 定位到指定的元素。
<!-- index.html -->
<html>
<body>
<div id="app"></div>
<script type="text/javascript">
const app=document.getElementById('app');
</script>
</body>
</html>
你可以繼續使用 DOM 的一系列方法來創建一個 h1 標簽元素,h1 元素中可以包含任何你希望展示的文本。
<!-- index.html -->
<html>
<body>
<div id="app"></div>
<script type="text/javascript">
// 定位到 id 為 app 的元素
const app=document.getElementById('app');
// 創建一個 h1 元素
const header=document.createElement('h1');
// 創建一個文本節點
const headerContent=document.createTextNode(
'Develop. Preview. Ship. ',
);
// 將文本節點添加到 h1 元素中去
header.appendChild(headerContent);
// 將 h1 元素添加到 id 為 app 的元素中去
app.appendChild(header);
</script>
</body>
</html>
至此,你可以打開瀏覽器來預覽一下目前的成果,不出意外的話,你應該可以看到一行使用 h1 標簽的大字,寫道:Develop. Preview. Ship.
此時,如果你打開瀏覽器的代碼審查功能,你會注意到在 DOM 中已經包含了剛才創建的 h1 標簽,但源代碼的 html 中卻并沒有。換言之,你所創建的 html 代碼中與實際展示的內容是不同的。
這是因為 HTML 代碼中展示的是初始化的頁面內容,而 DOM 展示的是更新后的頁面內容,這里尤指你通過 JavaScript 代碼對 HTML 所改變后的內容。
使用 JavaScript 來更新 DOM,是非常有用的,但也往往比較繁瑣。你寫了如下那么多內容,僅僅用來添加一行 h1 標簽。如果要編寫一個大一些的項目,或者團隊開發,就感覺有些杯水車薪了。
<!-- index.html -->
<script type="text/javascript">
const app=document.getElementById('app');
const header=document.createElement('h1');
const headerContent=document.createTextNode('Develop. Preview. Ship. ');
header.appendChild(headerContent);
app.appendChild(header);
</script>
以上這個例子中,開發者花了大力氣來“指導”計算機該如何做事,但這似乎并不太友好,或者有沒有更友好的方式讓計算機迅速理解我們希望達到的樣子呢?
以上就是一個很典型的命令式編程,你一步一步的告訴計算機該如何更新用戶界面。但對于創建用戶界面,更好的方式是使用聲明式,因為那樣可以大大加快開發效率。相較于編寫 DOM 方法,最好有種方法能聲明開發者想要展示的內容(本例中就是那個 h1 標簽以及它包含的文本內容)。
換句話說就是,命令式編程就像你要吃一個披薩,但你得告訴廚師該如何一步一步做出那個披薩;而聲明式編程就是你告訴廚師你要吃什么樣的披薩,而無需考慮怎么做。
而 React 正是那個“懂你”的廚師!
作為一個 React 開發者,你只需告訴 React 你希望展示什么樣的頁面,而它會自己找到方法來處理 DOM 并指導它正確地展示出你所要的效果。
小問答:你覺得以下哪句話更像聲明式?
A:我要吃一盤菜,它要先放花生,然后放點雞肉丁,接著炒一下...
B:來份宮保雞丁
要想在項目中使用 React,最簡單的方法就是從外部 CDN(如:http://unpkg.com)引入兩個 React 的包:
<!-- index.html -->
<html>
<body>
<div id="app"></div>
<script src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<script type="text/javascript">
const app=document.getElementById('app');
</script>
</body>
</html>
這樣就無需使用純 JavaScript 來直接操作 DOM 了,而是使用來自 react-dom 中的 ReactDOM.render() 方法來告訴 React 在 app 標簽中直接渲染 h1 標簽及其文本內容。
<!-- index.html -->
<html>
<body>
<div id="app"></div>
<script src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<script type="text/javascript">
const app=document.getElementById('app');
ReactDOM.render(<h1>Develop. Preview. Ship. </h1>, app);
</script>
</body>
</html>
但當你在瀏覽器中運行的時候,它會報一個語法錯誤:
因為代碼中的 <h1>Develop. Preview. Ship. </h1> 并不是 JavaScript 代碼,而是 JSX。
JSX 是一種 JS 的語法擴展,它使得你可以用類 HTML 的方式來描述界面。你無需學習 HTML 和 JavaScript 之外的新的符號和語法等,只需要遵守以下三條規則即可:
<!-- 你可以使用 div 標簽 -->
<div>
<h1>Hedy Lamarr's Todos</h1>
<img
src="https://i.imgur.com/yXOvdOSs.jpg"
alt="Hedy Lamarr"
className="photo"
/>
<ul>
...
</ul>
</div>
<!-- 你也可以使用空標簽 -->
<>
<h1>Hedy Lamarr's Todos</h1>
<img
src="https://i.imgur.com/yXOvdOSs.jpg"
alt="Hedy Lamarr"
className="photo"
/>
<ul>
...
</ul>
</>
<!-- 諸如 img 必須自關閉 <img />,而包圍類標簽必須成對出現 <li></li> -->
<>
<img
src="https://i.imgur.com/yXOvdOSs.jpg"
alt="Hedy Lamarr"
className="photo"
/>
<ul>
<li>Invent new traffic lights</li>
<li>Rehearse a movie scene</li>
<li>Improve the spectrum technology</li>
</ul>
</>
<!-- 如 stroke-width 必須寫成 strokeWidth,而 class 由于是 react 的關鍵字,因此替換為 className
<img
src="https://i.imgur.com/yXOvdOSs.jpg"
alt="Hedy Lamarr"
className="photo"
/>
JSX 并不是開箱即用的,瀏覽器默認情況下是無法解釋 JSX 的,所以你需要一個編譯器(compiler),諸如 Babel,來將 JSX 代碼轉換為普通的瀏覽器能理解的 JavaScript。
復制粘貼以下腳本到 index.html 文件中:
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
另外,你還需要告訴 Babel 需要轉換哪些代碼,為需要轉換的代碼添加類型 type="text/jsx"
<html>
<body>
<div id="app"></div>
<script src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<!-- Babel Script -->
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/jsx">
const app=document.getElementById('app');
ReactDOM.render(<h1>Develop. Preview. Ship. </h1>, app);
</script>
</body>
</html>
現在可以再次回到瀏覽器中刷新頁面來確認是否能成功展示了。
使用聲明式的 React,你只編寫了以下代碼:
<script type="text/jsx">
const app=document.getElementById("app")
ReactDOM.render(<h1>Develop. Preview. Ship. </h1>, app)
</script>
而命令式代碼如此前編寫的:
<script type="text/javascript">
const app=document.getElementById('app');
const header=document.createElement('h1');
const headerContent=document.createTextNode('Develop. Preview. Ship. ');
header.appendChild(headerContent);
app.appendChild(header);
</script>
相比較后不難發現,你節省了很多重復冗余的工作。
這就是 React,一款富含可重用代碼,為你節省時間和提高效率的工具。
目前,你還無需過多關注究竟 React 用了什么神奇的魔法實現這樣的功能。當然如果你感興趣的話,可以參考 React 的官方文檔中的 UI Tree 和 render 兩個章節。
在真正上手 React 項目之前,還有三個最重要的 React 核心概念需要理解
在后續的章節中我們將逐一學習以上三個核心概念。
一個用戶界面可以被分割成更小的部分,我們稱之為“組件”。它是自包含、可重用的代碼塊,你可以把它想象成樂高玩具,獨立的磚塊可以拼裝成更大的組合結構。如果你要更新界面的某一部分,你可以僅更新特定的組件或“磚塊”。
模塊化讓你的代碼更具有可維護性,因為你可以輕易地添加、修改、刪除特定的組件而無需改動程序的其他部分。React 組件其實就是用 JavaScript 編寫的,接下來我們將學習如何編寫一個從 JavaScript 原生到 React 的組件。
在 React 中,組件就是函數,我們在 script 中插入一個 header 方法:
<script type="text/jsx">
const app=document.getElementById("app")
function header() {}
ReactDOM.render(<h1>Develop. Preview. Ship. </h1>, app)
</script>
組件函數返回一個界面元素(即我們前面所提到過的單根節點的元素),可以使用 JSX 語法,如:
<script type="text/jsx">
const app=document.getElementById("app")
function header() {
return (<h1>Develop. Preview. Ship. </h1>)
}
ReactDOM.render(, app)
</script>
然后將 header 傳入 ReactDOM.render 的第一個參數中去:
ReactDOM.render(header, app)
但如果你現在刷新瀏覽器預覽效果的話,將會報錯,因為還需要做兩件事。
首先,React 組件必須以大寫字母開頭:
// 首字母大寫
function Header() {
return <h1>Develop. Preview. Ship. </h1>;
}
ReactDOM.render(Header, app);
其次,你在使用 React 組件時,也需要使用 JSX 的語法格式,將組件名稱用 <> 擴起來:
function Header() {
return <h1>Develop. Preview. Ship. </h1>;
}
<br/>ReactDOM.render(<Header />, app);
一個應用程序通常包含多個組件,有的甚至是組件嵌套的組件。例如我們來創建一個 HomePage 組件:
function Header() {
return <h1>Develop. Preview. Ship. </h1>;
}
function HomePage() {
return <div></div>;
}
ReactDOM.render(<Header />, app);
然后將 Header 組件放入 HomePage 組件中:
function Header() {
return <h1>Develop. Preview. Ship. </h1>;
}
function HomePage() {
return (
<div>
{/* 嵌套的 Header 組件 */}
<Header />
</div>
);
}
ReactDOM.render(<HomePage />, app);
你可以繼續以這種方式嵌套 React 組件,以形成一個更大的組件。
比如上圖中,你的頂層組件是 HomePage,它下面包含了一個 Header,一個 ARTICLE 和一個 FOOTER。然后 HEADER 組件下又包含了它的子組件等等。
這樣的模塊化使得你可以在項目的許多其他地方重用組件。
如果你重用 Header 組件,你將顯示相同的內容兩次。
function Header() {
return <h1>Develop. Preview. Ship. </h1>;
}
function HomePage() {
return (
<div><br/> <Header />
<Header />
</div>
);
}
但如果你希望在標題中傳入不同的文本,或者你需要從外部源獲取數據再進行文本的設置時,該怎么辦呢?
普通的 HTML 元素允許你通過設置對應標簽的某些重要屬性來修改其實際的展示內容,比如修改 <img> 的 src 屬性就能修改圖片展示,修改 <a> 的 href 就能改變超文本鏈接的目標地址。
同樣的,你可以通過傳入某些屬性值來改變 React 的組件,這被稱為參數(Props)。
與 JavaScript 函數類似,你可以設計一個組件接收一些自定義的參數或者屬性來改變組件的行為或展示效果,并且還允許通過父組件傳遞給子組件。
?? 注意:React 中,數據流是順著組件數傳遞的。這被稱為單向數據流。
在 HomePage 組件中,你可以傳入一個自定義的 title 屬性給 Header 組件,就如同你傳入了一個 HTML 屬性一樣。
// function Header() {
// return <h1>Develop. Preview. Ship. </h1>
// }
function HomePage() {
return (
<div>
<Header title="Hello React" />
</div>
);
}
// ReactDOM.render(<HomePage />, app)
然后,Header 作為子組件可以接收這些傳入的參數,可在組件函數的第一個入參中獲得。
function Header(props) {
return <h1>Develop. Preview. Ship. </h1>
}
你可以嘗試打印 props 來查看它具體是什么東西。
function Header(props) {
console.log(props) // { title: "Hello React" }
return <h1>Hello React</h1>
}
由于 props 是一個 JS 對象,因此你可以使用對象解構來展開獲得對象中的具體鍵值。
function Header({ title }) {
console.log(title) // "Hello React"
return <h1>Hello React</h1>
}
現在你就能使用 title 變量來替換 h1 標題中的文本了。
function Header({ title }) {
console.log(title) // "Hello React"
return <h1>title</h1>
}
但當你打開瀏覽器刷新頁面時,你會發現頁面上展示的是標題文本是 title,而不是 title 變量的值。這是因為 React 不能對純文本進行解析,這就需要你額外地對文本展示做一些處理。
要在 JSX 中使用你定義的變量,你需要使用花括號 {} ,它允許你在其中編寫 JavaScript 表達式
function Header({ title }) {
console.log(title) // "Hello React"
return <h1>{ title }</h1>
}
通常,它支持如下幾種方式:
這樣你就能根據參數輸出不同的標題文本了:
function Header({ title }) {
return <h1>{title ? title : 'Hello React!'}</h1>;
}
function Page() {
return (
<div>
<Header title="Hello JavaScript!" />
<Header title="Hello World!" />
</div>
);
}
通常我們會有一組數據需要展示,它以列表形式呈現,你可以使用數組方法來操作數據,并生成在樣式上統一的不同內容。
例如,在 HomePage 中添加一組名字,然后依次展示它們。
function HomePage() {
const names=['Mike', 'Grace', 'Margaret'];
return (
<div>
<Header title="Develop. Preview. Ship. " />
</div>
);
}
然后你可以使用 Array 的 map 方法對數據進行迭代輸出,并使用箭頭函數來將數據映射到每個迭代項目上。
function HomePage() {
const names=['Mike', 'Grace', 'Margaret'];
return (
<div>
<Header title="Develop. Preview. Ship. " />
<ul>
{names.map((name)=> (
<li>{name}</li>
))}
</ul>
</div>
);
}
現在如果你打開瀏覽器查看,會看到一個關于缺少 key 屬性的警告。這是因為 React 需要通過 key 屬性來唯一識別數組上的元素來確定最終需要在 DOM 上更新的項目。通常我們會使用 id,但本例子中你可以直接使用 name,因為它們的值也是唯一不同的。
function HomePage() {
const names=['Mike', 'Grace', 'Margaret'];
return (
<div>
<Header title="Develop. Preview. Ship. " />
<ul>
{names.map((name)=> (
<li key={name}>{name}</li>
))}
</ul>
</div>
);
}
首先,我們看下 React 是如何通過狀態和事件處理來幫助我們增加交互性的。
我們在 HomePage 組件中添加一個“喜歡”按鈕:
function HomePage() {
const names=['Mike', 'Grace', 'Margaret'];
return (
<div>
<Header title="Develop. Preview. Ship. " />
<ul>
{names.map((name)=> (
<li key={name}>{name}</li>
))}
</ul>
<button>Like</button>
</div>
);
}
要讓按鈕在被點擊的時候做些什么時,你可以在按鈕上添加 onClick 事件屬性:
function HomePage() {
// ...
return (
<div>
{/* ... */}
<button onClick={}>Like</button>
</div>
);
}
在 React 中,屬性名稱都是駝峰命名式的,onClick 是許多事件屬性中的一種,還有一些其他的事件屬性,如:輸入框會有 onChange ,表單會有 onSubmit 等。
你可以定義一個函數來處理以上一些事件,當它被觸發的時候。事件處理函數可以在返回語句之前定義,如:
function HomePage() {
// ...
function handleClick() {
console.log("I like it.")
}
return (
<div>
{/* ... */}
<button onClick={}>Like</button>
</div>
)
}
接著你就可以在 onClick 中調用 handleClick 方法了。
function HomePage() {
// ...
function handleClick() {
console.log('I like it.');
}
return (
<div>
{/* ... */}
<button onClick={handleClick}>Like</button>
</div>
);
}
React 里有一系列鉤子函數(Hooks),你可以利用鉤子函數在組件中創建狀態,你可以把狀態理解為在界面上隨時間或者行為變化的一些邏輯信息,通常情況下是由用戶觸發的。
你可以通過狀態來存儲和增加用戶點擊喜歡按鈕的次數,在這里我們可以使用 React 的 useState 鉤子函數。
function HomePage() {
React.useState();
}
useState 返回一個數組,你可以使用數組解構來使用它。
function HomePage() {
const []=React.useState();
// ...
}
該數組的第一個值是狀態值,你可以定義為任何變量名稱:
function HomePage() {
const [likes]=React.useState();
// ...
}
該數組的第二個值是狀態修改函數,你可以定義為以 set 為前綴的函數名,如 setLikes。
function HomePage() {
const [likes, setLikes]=React.useState();
// likes 存儲了喜歡被點擊的次數;setLikes 則是用來修改該次數的函數
// ...
}
同時,你可以在定義的時候給出 likes 的初始值
function HomePage() {
const [likes, setLikes]=React.useState(0);
}
然后你可以嘗試查看你設置的初始值是否生效
function HomePage() {
// ...
const [likes, setLikes]=React.useState(0);
return (
// ...
<button onClick={handleClick}>Like({likes})</button>
);
}
最后,你可以在每次按鈕被點擊后調用 setLikes 方法來更新 likes 變量的值。
function HomePage() {
// ...
const [likes, setLikes]=React.useState(0);
function handleClick() {
setLikes(likes + 1);
}
return (
<div>
{/* ... */}
<button onClick={handleClick}>Likes ({likes})</button>
</div>
);
}
點擊喜歡按鈕將會調用 handleClick 方法, 然后調用 setLikes 方法將更新后的新值傳入該函數的第一個入參中。這樣 likes 變量的值就變成了新值
本章節僅對狀態做了簡單的介紹,舉例了 useState 的用法,你可以在后續的學習中了解到更多的狀態管理和數據流處理的方法,更多的內容可以參考官網的 添加交互性 和 狀態管理 兩個章節進行更深入的學習。
小問答:請說出參數(Props)和狀態(State)的區別?
到此為止,你已了解了 React 的三大核心概念:組件、參數和狀態。對這些概念的理解越深刻,對今后開發 React 應用就越有幫助。學習的旅程還很漫長,途中若有困惑可以隨時回看本文,或閱讀以下主題文章進行更深入的學習:
React 的學習資源層出不窮,你可以在互聯網上搜索 React 來獲取無窮無盡的資源,但在我看來最好的仍是官方提供的《React 文檔》,它涵蓋了所有你需要學習的主題。
最好的學習方法就是實踐。
React框架推薦使用的DOM語法格式為JSX語法,屬于一種JavaScript擴展,React使用JSX來描述用戶界面。我們可以粗略的認為JSX是JavaScript和HTML的結合,但是在實際的使用過程中還有一定的細節區別。本文就帶領大家系統的學習JSX語法的格式。
const element=<div>你好,React!</div>;
這里需要注意,上述代碼不是JavaScript字符串,所以沒有用任何引號引住;也不是HTML代碼。而是JSX語法格式。
React應用的最小單位是“元素”,JSX語法格式就是React推薦用來聲明元素的。
對于相互嵌套的JSX元素,JSX語法推薦使用()擴住。
const ulElement=(
<ul>
<li>第一名</li>
<li>第二名</li>
</ul>
)
使用()擴住嵌套的JSX元素,讓格式更加清晰。從實際的操作來看,不書寫()也是可以的。
在JSX元素中使用變量必須使用{}擴住,變量可以用于JSX元素的內容中,也可以用于JSX元素的HTML屬性取值上。
let name='張三';
const strongElement=<strong>你好,{name}</strong>
let url='https://www.baidu.com';
const link=<a href={url}>百度一下</a>
對于具有return返回值的函數,JSX元素可以像使用變量一樣,利用{}擴住對函數進行調用。
function getSum(a,b){
return a+b;
}
let a=10,b=20;
const sum=<div>{a}+{b}={getSum(a,b)}</div>
上述幾種情況中舉的案例元素(element、ulEelement、strongElement、link、sum)都可以直接用在ReactDOM。render()方法的第一個參數中,充當向第二個參數所指的DOM節點中放入的元素。以sum為例,代碼如下所示。
ReactDOM.render(
sum,
document.querySelector('#app')
);
JSX元素在書寫時還要注意下列語法規定:
請大家仔細閱讀下列代碼:
const element=(
/*一個完整的JSX元素:本注釋沒有在任何標記內部,所以不用{}擴住*/
<div> {/*唯一的根節點:本注釋在標記內部,必須用{}擴住*/}
<div className="top"> {/*className屬性的使用*/}
{/*style屬性的使用必須是雙大括號*/}
<div style={{width:'1200px',height:'40px',backgroundColor:'#3385ff'}}>
歡迎學習React框架。
<img src=”images/01.jpg” /> {/*標記必須有結尾標識*/}
</div>
</div>
</div>
);
ReactDOM.render(
element,
document.querySelector('#app')
);
上述代碼在瀏覽器中運行,可以從開發人員工具的Elements選項卡中看到下列如圖所示的結構。
從圖中可以看得出,id屬性取值為app的div和class屬性取值為top的div之間,有一個空的div。這是由于為了滿足JSX元素必須具備一個唯一的根節點,而設置的最外層的div標記對。為了不讓最外層的根節點顯示在DOM結構中,React建議使用React.Fragment作為所有JSX元素最外層的根節點。代碼改為如下格式。
const element=(
/*一個完整的JSX元素:本注釋沒有在任何標記內部,所以不用{}擴住*/
<React.Fragment> {/*唯一的根節點:本注釋在標記內部,必須用{}擴住*/}
<div className="top"> {/*className屬性的使用*/}
{/*style屬性的使用必須是雙大括號*/}
<div style={{width:'1200px',height:'40px',backgroundColor:'#3385ff'}}>
歡迎學習React框架。
<img src=”images/01.jpg” /> {/*標記必須有結尾標識*/}
</div>
</div>
</ React.Fragment >
);
這時再看開發人員工具的Elements選項卡,React.Fragment標記位置是沒有任何標記對的。
在JSX格式中遍歷數組不能使用for循環,只能使用ES5為數組提供的各種方法。下面的例子展示了用數組的map()方法來遍歷數組的功能實現。
例1:實現頁面導航條的JSX格式。
let navText=['首頁','產品展示','技術展望','視頻會議','金牌團隊','關于我們'];
let navLink=['index.html','product.html','technology.html','videol.html','team.html','about.html'];
const nav=(
<React.Fragment>
<div className="top">
<div className="topContent">
<ul>
{
navText.map((item,index)=>{
return <li key={index}><a href={navLink[index]}>{item}</a></li>
})
}
</ul>
</div>
</div>
</React.Fragment>
);
從上述代碼中可以看出下列有關JavaScript語言在JSX語法中的規范:
例2:有一個JSON數組,每個元素有兩個屬性:isShow和file。其中isShow取值為邏輯值,file取值為表示圖片路徑的字符串。當isShow取值為true時,file指定的圖片要顯示在頁面中;當isShow取值為false時,file指定的圖片不顯示在頁面中。
let picture=[
{isShow:true,file:'images/01.jpg'},
{isShow:true,file:'images/02.jpg'},
{isShow:false,file:'images/03.jpg'},
{isShow:true,file:'images/04.jpg'},
{isShow:true,file:'images/05.jpg'}
];
const img=(
<React.Fragment>
<h2>圖片欣賞</h2>
<div className="picture">
{
picture.filter((item,index)=>{
return item.isShow
}).map((item,index)=>{
return <img src={item.file} key={index} />
})
}
</div>
</React.Fragment>
);
上述代碼中,使用數組的filter()方法對picture數組進行篩選,篩選條件是isShow取值為true。然后再對篩選出來的數組元素使用map()方法進行遍歷,以用來在頁面中顯示圖片。
在JSX格式中是不能直接使用JavaScript的if語句的,我們通過下列五種方式來為大家講解可行的方法。
例:設置一個變量flag。規定當flag=true時,頁面中顯示一個類名為box的div標記;當flag=false時,頁面中顯示一個類名為fox的div標記。
JavaScript提供的三元運算符(? :)也被稱為“三目運算符”。該運算符適合分為兩種情況的分支判定。
let flag=true;
const element=(
<React.Fragment>
{
flag?
<div className="box">
我是box元素......
</div>
:
<div className="fox">
我是fox元素
</div>
}
</React.Fragment>
);
JavaScript提供的邏輯與運算符(&&)的短路原理規定:當&&運算的左側為false時,右側不予計算。該運算符適合多分支的判定。
let flag=true;
const element=(
<React.Fragment>
{flag && <div className="box">
我是box元素......
</div>}
{!flag && <div className="fox">
我是fox元素
</div>}
</React.Fragment>
);
上述代碼中,因為flag變量的取值為true,所以!flag的取值為false,則!flag &&右側的元素不再計算,也就不會被渲染出來。
既然JSX格式不允許直接使用if語句,那我們就不在JSX格式中使用。我們可以在JSX格式以外的區域使用if語句。
let flag=false;
let target;
if(flag){
target=(
<div className="box">
我是box元素......
</div>
)
}else{
target=(
<div className="fox">
我是fox元素
</div>
)
}
const element=(
<React.Fragment>
{target}
</React.Fragment>
);
上述代碼中定義了一個名為target的變量,通過在JSX格式以外進行if語句的判定,讓target變量獲得不同的JSX元素,最終在React.Fragment標記對中使用{target}引用了判定后的結果。
我們也可以在JSX格式以外的區域聲明一個函數,在函數體總使用if語句進行判定,并最終將需要渲染的JSX格式利用return語句返回。
let flag=true;
function getTarget(){
if(flag){
return (
<div className="box">
我是box元素......
</div>
)
}else{
return (
<div className="fox">
我是fox元素
</div>
)
}
}
const element=(
<React.Fragment>
{getTarget()}
</React.Fragment>
);
上述代碼中定義了一個名為getTarget的函數,該函數內部使用if判定flag變量的值,然后利用return語句將需要的JSX元素返回出去。在React.Fragment標記對中只需要使用{getTarget()}調用該函數即可。
本文是React系列教程的第二篇文章,主要為大家講解了React框架中JSX語法的書寫格式。系統的學會了JSX語法的書寫格式,對于編寫復雜的React項目有很大的幫助。明天會為大家系統的講解React組件的使用方法。
小海前端,具有18年Web項目開發和前后臺培訓經驗,在前端領域著有較為系統的培訓教材,對Vue.js、微信小程序開發、uniApp、React等全棧開發領域都有較為深的造詣。入住?,希望能夠更多的結識Web開發領域的同仁,將Web開發大力的進行普及。同時也愿意與大家進行深入的技術研討和商業合作。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。