1. 程式人生 > >React之表格操作

React之表格操作

               

對於官網上的那個表格demo又進行了改造,記錄一下其中的困難和解決思路,當然還有功能沒有完善,會繼續利用空餘時間來實現一下。

——————————————————————————————————————————————————————

1.利用webpack進行了打包壓縮處理,並提取出了的第三方庫檔案;

2.增加了按照價格排序的功能;新加商品後也能按照排序的方式來顯示;

3.增加了分頁的功能;

4.減少了元件不必要的render;

——————————————————————————————————————————————————————

感悟:其實自己實現上面功能的過程也是比較“蛋疼的”,因為表格之間的狀態互動比較多,經常要各個子元件之間通訊,除了一些邏輯需要自己思考處理外,還有就是自己想實現一些不必要的render,這就需要用狀態來判斷,是否需要重新渲染元件,存在狀態難找,和流向不是那麼容易就容易理清楚的困難;隨著表格的功能越來越複雜,後面覺得有必要學習一下Redux了~

webpack

然後我再稍微說幾個需要注意的地方:

1.babel的loader中的名字要寫成'babel-loader'

2.生成  .babelrc 檔案需要寫成  .babelrc.  這樣的形式(前後各一個點)

3.如何設定NODE_ENV=production(React切換成產品模式 )

在cmd中直接敲set NODE_ENV=production

然後在webpack.config.js中

plugins: [             new webpack.DefinePlugin({                'process.env': {                        NODE_ENV
: JSON.stringify('production')                           }                     }),          ]

4.將第三方庫另外打包一份(比如我們的react,react-dom這樣的第三方js,最好和我們自己定義的js分開,這樣webpack利用diff演算法打包的時候也會快一點,畢竟這些第三方庫我們基本不會去修改)

entry: { bundle:__dirname + "/app/main.js"vendor:["react","react-dom"] },
我們需要再entry中定義好,然後再plugins中利用webpakc自帶的外掛進行打包
plugins: [    new
webpack.optimize.UglifyJsPlugin({  output: {      comments: false// remove all comments   },  compress: {      warnings: false   }      }),    new webpack.optimize.CommonsChunkPlugin({ name: 'vendor' }),    new webpack.DefinePlugin({   'process.env': {     NODE_ENV: JSON.stringify('production')    }  }),    ]
5.利用webpack-dev-server進行修改後實時重新整理

自從每次打包後我這邊每改一次都要所有的重新打包,需耗費我10多秒的時間,真的是太....那個啥了;

不過在使用過程中我遇到了一個困難,就是本地執行成功了,但是無法實時重新整理;

我的解決方法如下:(都是針對我自己的demo進行修改,實際情況實際討論)

首先在package.json這個檔案中進行修改

"scripts": {    "start": "webpack",    "dev":"webpack-dev-server --inline --content-base build/"  },
我們執行npm install start的時候是常規的打包,執行npm run dev是啟動webpack-dev-server,--content-base 後面接的是index.html檔案的路徑,我是放在build/下面的

然後修改一下webpack.config.js

output: {       path: __dirname + "/build/js",       filename: "[name].js",       publicPath: 'js/'    },
最後修改一下index.html
<script src="js/vendor.js"></script><script src="js/bundle.js"></script>
這樣就可以實現實時修改檔案,然後瀏覽器會自己重新整理了。

最後我還是想說一下為什麼會出現這個原因,我個人的理解是,開啟webpack-dev-server後進行修改打包後生成的檔案沒有放在我的本地資料夾裡面(Path指定的路徑),而是在PublicPath指定的路徑;

path:用來存放打包後文件的輸出目錄 ;publicPath:指定資原始檔引用的目錄 ;

而我原來的index.html引用的js是用的Path的路徑,我也沒有指定PublicPath的路徑,所以它預設放在根路徑,這個根路徑就是http://localhost:8080/;

如果我不指定路徑,那麼index.html中可以就直接這樣xxx.js引用js;我這裡指定了publicPath : js/" 其實是http://localhost:8080/js,然後引用的時候寫成js/xx.js即可,這樣做是為了和我本地編譯打包的指令碼路徑一致,方便我本地的編譯,不用去再更改指令碼的引用路徑了;

6.如何配置多入口檔案

如果你是多頁面應用,那樣肯定就會有多個入口檔案,在我們的webpack.config.js中的entry需要一個個列出來

譬如:

entry:{a:__dirname + "/app/a.js",b:__dirname + "/app/b.js",c:__dirname + "/app/b.js",......}

這樣需要我們手動一個個去敲,肯定不方便,所以我們這邊要“智慧一點”

我的解決方法如下:

定義一個函式:

function getEntry(){  var entry = { bundle:__dirname + "/app/main.js"vendor:["react","react-dom"],//將第三方的依賴放入一個模組包中,打包成一個模組,如果裡面的檔案還有依賴則依賴性最高的放最後 };  glob.sync(__dirname +'/app/settings/*.js') //讀取開發目錄,並進行路徑裁剪      .forEach(function(name)   {     var start = name.lastIndexOf('/')+1,     end = name.length-3; //儲存各個元件的入口     var n = name.slice(start, end);    entry[n] = name;   });  return entry; };
除了main.js和第三方庫外,還有一些其他的js我寫在了settings目錄下,所以傳的是__dirname +'/app/settings/*.js'    這裡面的具體路徑自己更換,我的app目錄是和webapck.config.js在同一目錄下

然後:

entry:getEntry(), 
就可以把我們需要打包壓縮的js全部包含進去,當前我的例子只是拋磚引玉。

分頁

對於分頁,這裡我花了比較多的時間:

以前做過jq的分頁,知道了大體的思路,首先是需要知道一共有多少個數據,然後是每頁顯示多少行,然後就是求出一個頁數,再對錶格中的資料進行選擇性的顯示。

但是剛接觸到React分頁的時候還是一頭霧水,有點不知道從哪下手,畢竟React不能那麼容易就能操作到每一個節點,輕鬆獲取我們想要的資料。

我這邊的思路首先是從顯示和隱藏每一行開始,因為每一行我這邊封裝成了一個小元件,我這邊完全可以控制其display屬性來顯示,不過這裡需要注意的就是不能用jq的那種方法,addClass,removeClass或者是.css來控制,我們需要用變數來控制,比如:

var display="none" <tr style={{display:display}} />

這裡通過改變display變數來控制顯示。

當我們能夠去顯示和隱藏每一行後,就需要來進行選擇性顯示了和隱藏了;

if(this.props.index<this.props.currentPage*this.props.eachPage||this.props.index>(this.props.currentPage+1)*this.props.eachPage-1){   display="none";  }

this.props.index是當前這一行的索引,即資料當前行數;

this.props.currentPage是當前頁數;

this.props.eachPage是每一頁顯示行數;

比如,我們要顯示第一頁的資料,每一頁顯示5個數據,那麼顯示的具體行數就是0--4,那麼小於0的和大於4的我們就會去隱藏。

這裡的this.props.currentPage其實是個變數,是和另一個頁面跳轉的元件進行通訊的,中間就需要用父元件當作一個媒介來傳遞。當我點選下一頁,或者選擇具體的頁數進行跳轉的時候,需要改變這個資料。

當然我們還需要進行一些邊界問題的處理:

A.比如我們目前是第一頁,那麼我們的首頁和上一頁就要被灰掉,不能進行點選觸發,我們處於最後一頁的時候需要把下一頁和尾頁同樣灰掉;

我的做法如下:

類似於前面的隱藏和顯示,我這裡給首頁,前一頁,下一頁,尾頁都添加了一個變數"ban"為它的className(ban設定的CSS是灰掉這個按鈕);

然後我們就進行判斷,當前是首頁還是尾頁,相應的對這個ban變數進行賦值,如果不滿足條件就賦值一個空值,滿足就賦值我們定義好的css變數;

到這裡我們只是對css進行了灰掉,但是你點選的時候還是會觸發函式,所以我這裡又在點選的時候又進行了一次判斷;由於我這裡是利用的事件代理,所以對於這4個按鈕我只是定義了一個click函式,然後判斷他們的id,來進行判斷到底是哪個按鈕,在這個基礎上我再判斷是否className為ban ,如果是,則不會去觸發。

B.如果我現在處於最後一頁,我需要去刪除掉最後一個數據,那麼表格應該會自動跳轉到前一頁。

(這個問題對我來說也是具有挑戰性的)

我的思路如下:

首先判斷當前是不是最後一頁(如果不是,你刪除資料,表格會自動更新,後面的資料會補上)

       如果是最後一頁,再判斷是不是最後一個數據(這裡的最後一個數據是所有資料的最後一個,那樣這樣刪除跳轉就沒有意義,而且還會產生bug)

             如果不是最後一個數據,再判斷是不是最後一頁的最後一個數據(如果不是,後面會資料會補齊)

                  如果是,就更新當前的頁數為上一頁

ps:這裡判斷總頁數的方法可以參考一下,我這裡是用總商品除以每頁顯示的行數,pasreInt是去除小數部分,如果正好可以整除,那麼除出來的資料正好可以當作頁數,如果不能整除的話,那麼就向上取捨。(具體情況還是具體分析吧,我這裡的總頁數是1,2,3但是我當前頁是0,1,2)

if(this.state.products.length/this.state.eachPage==parseInt(this.state.products.length/this.state.eachPage)) {  var page=parseInt(this.state.products.length/this.state.eachPage); } else {  var page=Math.ceil(this.state.products.length/this.state.eachPage); }  if(this.state.currentPage==page&&this.state.products.length!=0&&this.state.products.length%this.state.eachPage==0) {     this.setState({  products:this.state.products,  currentPage:this.state.currentPage-1      }) } else{     this.setState({  products:this.state.products,      }) }

最後還需要注意的就是,我這裡跳轉具體頁數是select選項和前一頁它們按鈕觸發方式不一樣,前者是onChange後者是onClick

減少render

其實元件更新的時候,很多相關元件也會同時更新渲染,有些元件是死的,有些是沒必要更新的,所以這裡我們可以選擇性進行更新。

這裡就是當它的isAdd屬性改變了我就進行更新,這個就是我們的新增商品的視窗,只有我點選添加了再進行元件渲染,其他時候沒必要進行元件渲染。

shouldComponentUpdate(nextProps, nextState) { if (this.props.isAdd !== nextProps.isAdd) { return true; } return false;}