元件化是近年來前端最重要的一個概念,其實他的本質是聚焦專案中複雜問題的分解。用比較抽象的話來說,就是用樂高積木的思想理解專案,搭建專案。即用無數個不同的小積木(功能簡單,結構單一),構建出來一個龐大的精巧的玩具(功能結構都很複雜)。要做到這一點需要將元件理解到最為透徹。
元件劃分的藝術
- 元件的劃分,從最簡單出入手,可以按照層級劃分,即每一個專案都可以分為Header,Footer,Content這三大部分,然後Header可以分為Logo,HeaderText,以及HeaderMore,HeaderSearch等幾個部分,這是從最簡單視覺角度分拆,我們可以把它叫做橫向拆分吧。
- 另一種拆分是更加對資料理解透徹之後產生的,就像一些後臺專案一樣,將要連結資料庫的操作分為Model層,在前端即可以理解為將ajax封裝起來,封為Model層,將業務邏輯封裝為Controller層,在前端就是將對ajax資料的操作分為Controller層,將最終資料的組裝,返回分為View層,在前端就是將展現分為View層,這種分層就是典型的MVC架構,只有將這種資料架構理解到深處,才可以真正的上手大型專案。
- 我們這次就聚焦於React下的MVC架構,記錄這條路上應該如何走。
最簡單的元件組合
- 容器元件和展示元件
- 不知道大家有沒有注意觀察過淘寶網,它裡面有一部分資料是相同的,但是展現形式不同,那這個時候,我們就可以把元件拆開,分為容器元件,和展示元件,容器元件負責拿回來資料,而展示元件就負責展示,在這種情況下,我們可以有不同的展示元件,也可以有不用的容器元件,而我們開發的過程就更像組合積木的過程了
- 容器元件的特點:
- 更關心行為部分,即ajax,以及資料操作
- 負責渲染對應的展示元件
- 定義事件處理
- 用class宣告
- 表現元件的特點:
- 更關心視覺展現
- 負責渲染HTML
- 用props從父元件接受資料
- 通常是無狀態函式元件/pureComponent
// 關注點分離
// 表現元件
const ShowList = ({data})=>(<div>{data.map(item=>(<h3 key={item.id}>{item.text}</h3>))}</div>)
// 容器元件
class Game extends React.Component{
constructor(){
super();
this.state = {
data: []
}
}
componentDidMount = () => {
axios.get("http://localhost:8081")
.then(res=>res.data)
.then(data=>this.setState(data));
}
render(){
//組裝
return (<div className="container">
<ShowList {...this.state}/>
{/* {this.state.data.map(item=><h3 key={item.id}>{item.text}</h3>)} */}
</div>)
}
}
複製程式碼
- React中的HOC
- 當我們的一部分業務程式碼需要複用時,React官方在最開始推薦我們的方法是mixins,但這種方法很容易就導致程式碼mixins之間相互依賴,變數函式衝突等問題
- 後來就有了高階元件,react官方也就推薦這種方式作為複用元件邏輯的官方方法,他並不是React官方API。而是由於元件組合特性衍生出來的一種設計模式,這種方法他可以很好的將功能整合,不影響外部環境,並且可以通過多層包裝完成minxin的效果,具體效果如下
// 高階元件,用元件生成元件
const BasicComponent = ({name, width})=>{
return (<h1>我是{name},當前視窗寬度為{width}px</h1>)
};
const withWindowResize = RawComponent=>{
return class NewComponent extends Component {
constructor(props){
super(props);
this.state = {
innerWidth:0
}
}
componentDidMount = () => {
this.setState({
innerWidth: window.innerWidth
});
}
componentWillMount = () => {
window.addEventListener("resize", this.handelResize, false);
}
componentWillUnmount = () => {
window.removeEventListener("resize", this.handelResize, false);
}
handelResize = ()=>{
this.setState({
innerWidth: window.innerWidth
});
}
render(){
return (<RawComponent name="xiaoming" width={this.state.innerWidth}></RawComponent>)
}
}
};
const Game = withWindowResize(BasicComponent);
export default Game;
複製程式碼
函式子元件
函式子元件可以理解為通過從props傳遞引數,然後在props.children定義函式,將引數處理後放入props.children執行這個函式。
class FetchWrapper extends Component {
static propTypes = {
children: PropTypes.func.isRequired,
url: PropTypes.string.isRequired
};
constructor(props) {
super(props);
this.state = {
data: []
}
}
componentDidMount() {
fetch(
this.props.url
)
.then(response => response.json())
.then((data) => this.setState(
{data: data}
))
.catch(console.error);
}
render() {
return this.props.children(this.state.data)
}
}
class App extends Component {
constructor() {
super();
}
render() {
return (
<div>
<FetchWrapper url="https://api.github.com/users/gaearon/gists">
{data => {
return data.map(item => {
return (
<li style={{textAlign: 'left'}} onClick={this.clickList} key={item.id}>
<h5>
{item.id}
</h5>
<p>
{item.description || "no desc"}
</p>
</li>
)
})
}}
</FetchWrapper>
</div>
)
}
}
複製程式碼
CONTEXT 跨元件傳參
context主要用於元件中的跨層級傳參,他的底層就是使用了函式子元件,使用如下
const FirstContext = React.createContext("#");
class Game extends Component {
constructor(){
super();
this.state = {
}
}
render(){
return (
<div>
// 供應商
<FirstContext.Provider value="$">
// 消費者
<FirstContext.Consumer>
{value=>[1,2,3,4].map(i=><h3 key={i}>{i*100}_{value}</h3>)}
</FirstContext.Consumer>
</FirstContext.Provider>
<FirstContext.Consumer>
{value=>[1,2,3,4].map(i=><h3 key={i}>{i*100}_{value}</h3>)}
</FirstContext.Consumer>
</div>
)
}
}
複製程式碼