很久很久以前,就有人用CSS來時給HTML內容添加樣式。CSS可以最大限度的分離樣式和內容,選擇器也可以很方便的給某些元素添加樣式。你根本找不到任何不用CSS的理由。
但是在React這裏就是另外一回事了,雖然React不是不用CSS。但是,它在給元素添加樣式的時候方式不同。React的核心哲學之一就是讓可視化的組件自包含,並且可複用。這就是爲什麼HTML元素和Javascript放在一起組成了Component
(組件)。
React的自包含組件需要在定義的時候就定義好樣式,這樣才能實現自包含。本文即將帶你學習如何給React的組件添加樣式,當然其中包括如何使用CSS。兩個都會講到,雖然React不鼓勵這樣。
準備工作
要在HTML也中使用React有兩種方法,一個是使用Webpack編譯打包,另一個是使用網頁直接添加React相關js文件。
用webpack來編譯、打包React組件。並在一個index.html的頁面中使用該代碼。具體的準備步驟可以看這裏。最後打包到一個叫做bundle.js的文件中。HTML頁面看起來是這樣的:
<html>
<head>
<meta charset="utf-8">
<title>Add style to React</title>
</head>
<body>
<div id="content" />
<script src="public/bundle.js" type="text/javascript"></script>
<span style="float:center">Yo!</span>
</body>
</html>
也可以在網頁中直接使用React的js代碼。
<html>
<head>
<meta charset="utf-8">
<title>Add style to React</title>
<!-- 所需要引入的React及相關js -->
<script src="https://npmcdn.com/[email protected]/dist/react.js"></script>
<script src="https://npmcdn.com/[email protected]/dist/react-dom.js"></script>
<script src="https://npmcdn.com/[email protected]/browser.min.js"></script>
<script src="https://npmcdn.com/[email protected]/dist/jquery.min.js"></script>
<!-- end -->
</head>
<body>
<div id="content" />
<!-- 我們自己的js -->
<script src="public/bundle.js" type="text/javascript"></script>
<span style="float:center">Yo!</span>
</body>
</html>
jquery文件可以不用添加,這裏用jquery是用來請求服務器的,暫時用不到。
無論使用哪一種方式。最後在頁面中使用的js都是bundle.js。如果用了webpack的方式,那麼bundle.js就是由webpack便已打包生成的。如果用的第二種方法,那麼bundle.js就是我們自己手動編寫的js代碼。
<div id="content" />
React生成的HTML元素都會放在這個div
裏面。
項目結構
使用Webpack的話,項目的整體結果是這樣的:
-webapp
|--public // webpack 編譯打包後的js文件所在目錄
|--css // css文件所在的目錄
|--src // 使用React編寫的代碼所在目錄
|--index.html // HTML頁面
如果你使用網頁內部加載React的話,那麼就直接在public目錄下創建一個bundle.js文件,並在index.html引用即可。
我們就以一個用戶登錄的界面喂例子。登錄,用戶需呀輸入用戶名、密碼,然後點擊登錄按鈕。
React代碼
我們來看看要實現這個功能React代碼應該什麼樣的。
import React from 'react';
import {render} from 'react-dom';
import LabeledInputText from './LabeledInputText';
import SubmitButton from './SubmitButton';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {un: '', pwd: ''};
this.handleLogin = this.handleLogin.bind(this);
this.handleUserNameChanged = this.handleUserNameChanged.bind(this);
this.handlePasswordChanged = this.handlePasswordChanged.bind(this);
}
handleUserNameChanged(un) {
this.setState({un: un});
}
handlePasswordChanged(pwd) {
this.setState({pwd: pwd});
}
handleLogin() {
// $.ajax({
// url: this.props.url,
// dataType: 'json',
// method: 'POST',
// data: this.state,
// cache: false,
// success: function(data) {
// this.setState({data: data});
// }.bind(this),
// error: function(xhr, status, err) {
// console.error(this.props.url, status, err.toString());
// }.bind(this)
// });
alert(`${this.state.un}, ${this.state.pwd}`);
}
render() {
var divStyle = {
color: 'blue',
wdith: '150px',
paddingTop: '10px',
display: 'inline-block'
};
return (
<div style={divStyle}>
<p> Yo, React </p>
<LabeledInputText labelText="Username" onUserNameChanged={this.handleUserNameChanged} />
<LabeledInputText labelText="Password" onPasswordChanged={this.handlePasswordChanged} />
<SubmitButton title="Submit" onLogin={this.handleLogin} />
</div>
);
}
}
render(<App />, document.getElementById('content'));
// LabeledInputText
import React from 'react';
export default class LabeledInputText extends React.Component {
constructor(props) {
super(props);
this.handleTextChange = this.handleTextChange.bind(this);
}
handleTextChange(e) {
if (this.props.labelText.toLowerCase() == 'username') {
this.props.onUserNameChanged(e.target.value);
} else {
this.props.onPasswordChanged(e.target.value);
}
}
render() {
return (
<div>
<span>{`${this.props.labelText} :`}</span>
<input type="text" placeholder={this.props.labelText} onChange={this.handleTextChange} />
</div>
);
}
}
// SubmitButton
import React from 'react';
export default class SubmitButton extends React.Component {
constructor(props) {
super(props);
// this.state = {value: ''};
// bind event handler
this.handleLogin = this.handleLogin.bind(this);
}
handleLogin(e) {
// this.setState({value: e.target.value});
// alert('hello react');
this.props.onLogin()
}
render() {
return (
<input onClick={this.handleLogin} type="button" value={this.props.title} />
);
}
}
App
類是這個登錄界面的整體。裏面的HTML元素可以分爲兩類,一個是label和label後面的輸入框,另一類是提交按鈕。
LabeledInputText
是label和輸入框的組合。SubmitButton
是提交按鈕。
生成出來的HTML頁面是這樣的:
<html><head>
<meta charset="utf-8">
<title>Add style to React</title>
</head>
<body>
<div id="content">
<p> Yo, React </p>
<div>
<div>
<span>Username :</span>
<input type="text" placeholder="Username">
</div>
<div>
<span>Password :</span>
<input type="text" placeholder="Password">
</div>
<input type="button" value="Submit">
</div>
</div>
</body>
</html>
在添加樣式之後,效果是這樣的:
使用CSS添加樣式
在React組件上使用CSS樣式比你想的還要簡單。因爲最終React還是把組件都轉化成了HTML元素,而你會的各種CSS技巧一樣都可以作用在這些元素上。但是還是有一些小小的地方需要注意:
理解生成的HTML
在使用CSS之前,你需要知道React生成的HTML元素是什麼樣子的。看起來很容易理解,因爲JSX語法和HTML元素非常接近。
import React from 'react';
import {render} from 'react-dom';
import LabeledInputText from './LabeledInputText';
import SubmitButton from './SubmitButton';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {un: '', pwd: ''};
}
render() {
var divStyle = {
color: 'blue',
wdith: '150px',
paddingTop: '10px',
display: 'inline-block'
};
return (
// <div style={divStyle}>
<div className="box-group">
{/*<p> Yo, React </p>*/}
<LabeledInputText labelText="Username" onUserNameChanged={this.handleUserNameChanged} />
<LabeledInputText labelText="Password" onPasswordChanged={this.handlePasswordChanged} />
<SubmitButton title="Click" onLogin={this.handleLogin} />
</div>
);
}
}
render(<App />, document.getElementById('content'));
在render
方法裏的就是整體的React組件的結構。所有的東西都在一個div裏面。LabeledInputText
就是一個文字Label和一個input的文本輸入框的組合。SubmitButton
是一個可以點擊的按鈕,這裏其實是一個含有click事件的div。
全部組件生成HTML之後:
<div id="content">
<div data-reactroot="" class="box-group">
<div class="form-control">
<span>Username :</span><input type="text" placeholder="Username">
</div>
<div class="form-control form-under">
<span>Password :</span><input type="text" placeholder="Password">
</div>
<div class="form-control form-under form-button">
Click
</div>
</div>
</div>
裏面一些div包含的各種元素,就如前文所說的一樣。另外還有的就是很多的css的class。
React中使用CSS
首先添加css樣式文件:
input:focus{
outline: none !important;
border:1px solid red;
/*box-shadow: 0 0 10px #719ECE;*/
}
.box-group {
width:230px;
border: 1px solid blue;
padding:5px;
margin: 10px;
}
.form-control {
padding:5px;
}
.form-under {
margin-top:10px;
}
.form-button {
display:block;
background-color:red;
text-align: center;
}
上面就是main.css文件包含的全部的樣式。這些樣式主要是給登錄的整個界面元素的邊框設置爲藍色,然後在用戶名、密碼和登錄按鈕之間增加間距,最後給按鈕指定背景色爲紅色。
接下來需要在React的組件中使用這些樣式。但是直接使用class是不行的。畢竟JSX和HTML元素是有區別的。就以登錄按鈕爲例:
<div onClick={this.handleLogin} className="form-control form-under form-button">
{this.props.title}
</div>
在React中指定class名稱使用className
。React的className
最後就會轉化成HTML的class
。
這個登錄按鈕的樣式有一點複雜:className="form-control form-under form-button"
使用了三個不同的css的selector。這些selector的樣式都會應用到這個登錄按鈕上。
用React的方式來添加樣式
React推崇的是內聯的方式定義樣式。這樣做的目的就在於讓你的組件更加的容易複用。和組件相關的全部內容聚合到一起,包括你的組件看起來是什麼樣的,是如何工作的。
下面就把之前添加的全部的樣式className
都去掉,回到最開始的狀態。
/* index.js */
<div>
{/*<p> Yo, React </p>*/}
<LabeledInputText labelText="Username" onUserNameChanged={this.handleUserNameChanged} />
<LabeledInputText labelText="Password" onPasswordChanged={this.handlePasswordChanged} />
<SubmitButton title="Click" onLogin={this.handleLogin} />
</div>
/*LabeledInputText*/
<div>
<span>{`${this.props.labelText} :`}</span>
<input type="text" placeholder={this.props.labelText} onChange={this.handleTextChange} />
</div>
/*SubmitButton*/
<div onClick={this.handleLogin}>
{this.props.title}
</div>
登錄使用的三個組件的render
方法返回的內容的className
已經全部都去掉了。
要往React組件內添加一個自定義的樣式對象,這個對象包含的就是css樣式的名稱和樣式的值。只不過樣式的名稱不是css的background-color
而是JSX的backgroundColor
。例如:
let divStyle = {
width:'230px',
border: '1px solid blue',
padding:'5px',
margin: '10px'
};
return (
<div style={divStyle}>
{/*<p> Yo, React </p>*/}
<LabeledInputText labelText="Username" onUserNameChanged={this.handleUserNameChanged} />
<LabeledInputText labelText="Password" onPasswordChanged={this.handlePasswordChanged} />
<SubmitButton title="Click" onLogin={this.handleLogin} />
</div>
);
小貼士
在React裏註釋不能用HTML的方式,那是木有用的。也不能直接用js的
註釋,那也是不行的。而是用大括號括起來,之後用/**/來註釋。
看起來是這樣的`{/* 這是一個註釋 */}`。
divStyle
就是我們定義的樣式對象。要使用這個樣式,只要在React組件中給style
賦值。如:style={divStyle}
。
讓樣式可以自定義
要讓組件的子組件的某些樣式可以自定義很簡單。只需要使用React組件的props
。比如,我現在想要定製不同的用戶名、密碼輸入框的邊框顏色。
<div style={divStyle}>
{/*<p> Yo, React </p>*/}
<LabeledInputText labelText="Username" bordercolor="green" onUserNameChanged={this.handleUserNameChanged} />
<LabeledInputText labelText="Password" bordercolor="red" onPasswordChanged={this.handlePasswordChanged} />
<SubmitButton title="Click" onLogin={this.handleLogin} />
</div>
之後在LabeledInputText
文件中:
styleObj = Object.assign({}, this.pwdStyle, {border: '1px solid ' + this.props.bordercolor});
每次需要用到邊框值的時候都從props裏面取:this.props.bordercolor
。
根據不同的狀態顯示不同顏色
HTML的文本輸入框有兩種狀態,focused和blured。用戶要輸入內容的時候,文本框就在focus的狀態下。用戶的焦點移開,比如開始輸入密碼的時候,用戶名的文本框就在blur狀態下了。
在focus的狀態下的時候,顯示指定顏色的邊框,否則不顯示邊框。這個時候就要用到React的另一個重要概念:State
。
首先,給input註冊focus和blur的事件處理方法。
<input type="text" ref="theInput" placeholder={this.props.labelText}
style={this.getInputStyles()}
onChange={this.handleTextChange}
onFocus={this.handleFocus}
onBlur={this.handleBlur} />
// hanleFocus & handleBlur
handleFocus() {
this.setState({focused: true});
}
handleBlur() {
this.setState({focused: false});
}
狀態都存在state
裏了。然後在input裏指定style:style={this.getInputStyles()}
。getInputStyles
方法就會根據不同的狀態返回不同的樣式。
getInputStyles() {
let styleObj;
if (this.state.focused == true) {
styleObj = {outlineStyle: 'none'};
}
return styleObj;
}
這個方法在focus的時候去除了input默認的效果,blur的時候保持原樣。
最後
隨着React學習的深入,你會發現React添加樣式的方式和之前的方式大有不同。如果你透過上面的例子收入思考的話你會發現,之所以React使用了和以往不同的添加樣式方法是有原因的。HTML、CSS和Javascript這樣的傳統方法在處理網頁的時候是非常有用的,但是在處理React組件組成的複雜的界面的web app的時候卻顯得力不從心。