React入門 (2)—實現微博展現列表

前言

  若是歷來不瞭解React先看前篇React入門 (1)—使用指南(包括ES5和ES6對比)
  本文爲了能將前篇學到的react知識學以至用,作了一個相似微博展現列表的demo。使用的是ES6+React+JSX+Webpack+Babel+NPMcss

設計思路

  圖爲截取的微博展現列表,我這麼來劃分組件:
weibo_html

  花括號括起來的是我要寫的幾個組件:react

  • ContentImg組件:帶圖片的微博裏的圖片部分
  • CommentForm組件:點擊評論後,彈出來的評論下拉框
  • OneWB組件:一條微博,這條微博多是純文字的,帶一張圖片的,帶多張圖片的,甚至是轉發的。
  • ListWB組件:OneWB的集合,是一個頁面要展現的全部微博的集合。

  最外層節點放在最上面,每一個控件的數量經過後臺給的數據控制,組件之間的關係用樹狀圖表示:
_webpack

  本文章只實現純文字,帶圖片的微博。帶視屏,轉發的能夠用相似的方法實現,就不贅述了。後端數據的結構設計爲:es6

var dataList=[  
   {
        headUrl:'http://img1.gtimg.com/tech/pics/hv1/238/85/1736/112905313.jpg',
        nickName:'Robin',
        content:'拿快遞拿快遞3號小郵局爆倉啦',
        NoCollect:132,
        NoForward:202,
        NoComment:142,
        NoPointGreat:423,
        contentImgUrls:[
           "http://img1.gtimg.com/tech/pics/hv1/238/85/1736/112905313.jpg",
           "http://img1.gtimg.com/tech/pics/hv1/238/85/1736/112905313.jpg"
        ]
    },
    {
        //內容同上
    }
    ];
  • 必填
      headUrl:別人的頭像連接,nickName:別人的暱稱,content:微博文字內容,NoCollect:收藏數,NoForward:轉發數,NoComment:評論數,NoPointGreat:點贊數。
  • 非必填
      contentImgUrls:微博圖片內容,能夠爲空或不存在。

  圖中組件的數量是根據後端給的數據來決定的,dataList的元素個數表明OneWB的個數,contentImgUrls的元素個數決定了微博帶不帶圖片,以及展現幾個。web

搭建環境

  使用webpack+npm。
  個人工程目錄結構如圖:
  screenshot
  webpack以commonjs的形式來書寫腳本,是如今很火的模塊加載器+打包工具。使用方法:npm

1.建立package.json文件

  npm init
  參數能夠不填,回車代替。
  執行完會生成package.json文件json

2.在終端安裝你須要的插件

  npm install 插件名 --save-dev.
  ('--save-dev' 通常在開發者開發項目的時候用,這個指令會把安裝的包依賴寫入package.json的devDependencies字段中。若是命令去掉'-dev',則會記錄到dependencies字段中)。
  須要安裝這麼些個:
  npm install -save-dev webpack
  npm install -save-dev react
  npm install -save-dev react-dom
  npm install --save-dev babel-core
  npm install --save-dev babel-loader
  npm install --save-dev babel-preset-es2015 ,babel6才須要裝
  npm install -save-dev babel-preset-react,babel6才須要裝
  若是用的別人的工程,已存在package.json文件且內容完整,那麼直接npm install不須要手動安裝了。後端

3.建立webpack.config.js,並配置

  根據webpack.config.js文件來決定webpack要作哪些動做。數組

//webpack.config.js 文件內容
var path = require('path');

module.exports = {
  entry: {
    'index': './index.js' //key只是個名字,能夠自由改
  },
  output: {
    path: './build',
    filename: 'entry.js',//也能夠動態生成文件名 filename:'[name].js',將根據entry中的key生成名字
  },
  module: {
    loaders: [{
      test: /\.jsx?$/,
      loader: 'babel',
      /* babel6 才須要配置這個,presets裏面兩個預編譯插件,前一個用於編譯es6,後一個用於編譯react。按需配置。這個工程都須要。
      query:{
        presets: ['es2015','react']
      }*/
    }]
  }
};

  這段代碼主要告訴了webpack:

  • 哪一個文件須要打包(entry字段),打包以後生成的新文件存到哪一個路徑(output中的path)、新文件叫什麼名字(output中的filename);
  • 要使用哪些加載器(module.loaders)。這裏要使用babel來編譯jsx和es6的代碼。

  總得來講,webpack從entry拿到目標文件,經過loaders進行編譯,從output輸出,其餘功能由plugins引入。
  注:index.js:負責渲染組件到頁面上。至關於一個總的出口。由於會自動加載依賴關係,因此webpack.config.js文件只須要配置這一文件便可。
  另外,這個工程比較簡單,只需配置一個js文件。

  • 若是要打包多個js文件,這麼配置:
entry: {
    'file1': './index.js', //key只是個名字,能夠自由改
    'file2': './index2.js'
  }
  • 若是須要打包css文件,或解析less文件,須要配置ExtractTextPlugin。詳情看官方文檔

4. 在終端執行命令:webpack

  會在build文件夾下生成編譯後(未壓縮)的js文件,entry.js。若是編譯錯誤會在命令行提示錯誤緣由,entry.js爲上一版本的內容。
  你也可使用其餘命令,方便開發:
    webpack -w 監聽你的代碼修改,實施打包生成entry.js, 通常都直接使用這個命令。
    webpack -p 打包js文件,並壓縮。

5. 開始編碼

  完成以上四步,你就能夠一邊編寫代碼,用require(es6用import)來加載依賴的模塊,一邊在瀏覽器查看效果啦。而且若是之後有新的項目,直接拷貝package.json和webpack.config.js ,秒搭建環境。

編碼階段

  須要用的react特性有:事件,state,props。依然能夠參見上一篇的詳細介紹。

  • 每一個組件文件都須要引入react import React from 'react';,以及依賴的其餘組件。
    index.js還須要引入reactdom import ReactDOM from 'react-dom';,由於渲染頁面的方法render()是reactdom的。
  • 用class定義組件,不要忘記使用module.exports = ListWB; 開放組件給其餘文件引用。

index.html

  只需引入一個文件,編譯後的文件entry.js。div#place爲react組件的父容器。是否是看起來好像什麼都沒有呢?react中,經過js來插入組件,咱們看index.js文件

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>weibo-react</title>
    <link rel="stylesheet" href="css/style.css">
  </head>
  <body>
    <div id="place">
    <!-- 此處插入react插件 -->
    </div>
    <!-- dom定義 -->
    <script src="build/entry.js"></script>
  </body>
</html>

index.js - js入口文件

  使用自定義的react組件,渲染頁面。

import React from 'react';
import ReactDOM from 'react-dom';
import ListWB from  './components/list-wb.jsx';//微博列表組件

var dataList=[{},{}];//這裏存儲的是後端傳來的數據。包括頭像,內容,點贊數等等
ReactDOM.render(<ListWB data={dataList} />,
  document.getElementById('place')
);

  ListWB是微博列表組件,經過自定義屬性data傳遞後端數據,並set到子組件中,展現到頁面上。
注意import ListWB from './components/list-wb.jsx';必定要記得引入自定義組件模塊。

list-wb.jsx - 微博列表組件

import React from 'react';
import OneWB from  './one-wb.jsx';//標籤的名字根據這個變量名來決定

class ListWB extends React.Component{
  render() {
    // 遍歷後端給的數據,而且插入
    var oneWBNodes = this.props.data.map(function(aWB,index){
      return <OneWB oneData={aWB} key={index}></OneWB>;
    });
    return <div>
        {oneWBNodes}
        </div>;
  }
}
module.exports = ListWB;

筆記:

  • map方法會遍歷數組中的全部元素,並執行匿名函數,當前元素aWB做爲匿名函數第一個實參,當前元素索引index做爲第二個實參。this.props.data存儲的後端數據list,每一個元素執行完匿名函數return的值會鏈接(相加)起來。因此oneWBNodes獲得是OneWB的list。
  • 注意有一個自定義屬性key。對於循環出的每一個子組件,若是不定義key會報警。
    screenshot
      react中data-reactid的取名方式:
    react_dom_id_1_
      左圖爲沒有數組的「dom樹」取名,右圖爲有數組的「dom樹」取名。當圖的.0.1是數組時,id會按照右圖的虛線框那麼取。
      react組件中,每一個標籤都會有一個惟一的編號data-reactid,如:data-reactid=".0.$0a.0.1.0.1.1"。這裏的key是data-reactid的最後一個小數點後面的值。因此key只須要在本身兄弟節點內惟一便可。
      若是不按照要求,讓多個子元素的key相同,那麼只會識別展現第一個子元素。

one-wb.jsx - 單個微博組件

  該組件表明單條微博。要處理的有:

  • 屬性
    存儲了後端傳過來的數據。正確分配給子組件或者set到本身的原生屬性中。
  • 事件
    觸發後使用react的state來自動渲染頁面。點擊「評論」展開評論塊,點擊其餘(如「轉發」,「贊」)收起評論。這裏經過給react的state增長一個字段isComment來控制評論區塊的展示。點擊「評論」,isComment設爲true,展現評論區塊;不然isComment設爲false。
  • JSX中的if-else
    JSX中是沒有if-else的(由於jsx只是個語法糖,最終仍是要翻譯成原生js。好比:
    <div id={if (condition) { 'msg' }}>Hello World!</div>
    編譯後是:
React.createElement("div", {id: if (condition) { 'msg' }}, "Hello World!");

  然而咱們仍是有不少場景須要根據條件判斷某些組件要不要展現。
  咱們用這幾種方式來實現:

  • 三元操做符
React.render(<div id={condition ? 'msg' : ''}>Hello World!</div>, mountNode);
  • 在jsx之外賦值給變量
var vid='';
  if(condition){
    vid='msg';
  }
  React.render(<div id={vid}>Hello World!</div>, mountNode);
  • 當即執行的匿名函數
<div id={(()=>{
     if(this.state.isComment){
          return 4;
     }
 })()}>
</div>

  匿名函數這種方式也一樣能夠用在標籤中間 <div>{(()=>{//函數內容})()}</div>

  我使用的是第二種方式,代碼仍是更易維護一些。
  這個文件的代碼:

import React from 'react';
import CommentForm from './comment-form.jsx';
import ContentImg from './content-img.jsx';

class OneWB extends React.Component{
  constructor(props){
    super(props);
    this.state={
      isComment:false,
    };
  }

  render() {
   var oneData=this.props.oneData;
   var commentForm;
   var contentImgs;
   if(this.state.isComment) {
     //控制評論框是否展示,由於是動態的,因此放在state而不是props
     commentForm =<CommentForm data-my-head-img={oneData.headUrl}/>;
   }

   if(oneData.contentImgUrls){
     //若後端給的數據中有圖片url,則展現
     contentImgs = <ContentImg content-img-urls={oneData.contentImgUrls} />
   }
   return <div className="big-center" >
     <div className="one-wb ">
       <div className="clearfix">
         <div className="ow-left">
            <img className="nick-img" src={oneData.headUrl}/>
         </div>
         <div className="ow-right">
           <div className="ow-nick row">
             <span>{oneData.nickName}</span>
           </div>
           <div className="ow-content row">{oneData.content}</div>
           {contentImgs}
         </div>
       </div>
       <div className="ow-footer row ">
         <ul className="clearfix" onClick={this.handlerForwardClick.bind(this)}>
           <li className="li-side-border"><span>收藏</span> {oneData.NoCollect}</li>
           <li className="li-side-border"><span>轉發</span> {oneData.NoForward}</li>
           <li className="li-side-border"><span>評論</span> {oneData.NoComment}</li>
           <li><span>贊</span> {oneData.NoPointGreat}</li>
         </ul>
       </div>
     </div>
     {commentForm}
   </div>;
  }
  handlerForwardClick(event) {
    if(event.target.innerText == '評論'){
      this.setState({isComment:true});
    }else{
      this.setState({isComment:false});

    }
  }
}
module.exports = OneWB;

content-img.jsx - 微博圖片組件

  由於子組件放在一個數組裏,一樣須要注意配置key

import React from 'react';

class ContentImg extends React.Component{

  render() {
    var imgNodes=this.props['content-img-urls'].map(function(oneImg,index){
      return <li  key={index} ><img src={oneImg} alt="微博配圖" /></li>;
    });
    return <div  className="row extra-content clearfix">
      <ul>
        {imgNodes}
      </ul>
    </div>
  }
}
module.exports = ContentImg;

comment-form.jsx - 評論框組件

import React from 'react';

class CommentForm extends React.Component{
  render() {
    var imgUrl=this.props['data-my-head-img'];
    return <div className="row comment-form clearfix" >
          <div className="ow-left">
              <img src={imgUrl} alt="頭像" className="little-head" />
          </div>
          <div className="ow-right" >
            <textarea name="name" rows="8" cols="40" className="comment-box"></textarea>
            <input className="comment-btn" type="submit" value="評論"/>
          </div>
    </div>;
  }
}
module.exports = CommentForm;

效果展現

screenshot

總結

  看完這篇文章,相信可以快速在工做當中實踐了吧 ~(≧▽≦)/~ 。但願之後能出一些深入的文章。

相關文章
相關標籤/搜索