React之表格操作

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


github:https://github.com/liuzaijiang/React-text

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

在上一篇的基礎上http://blog.csdn.net/liuzijiang1123/article/details/66974630 又做了一部分修改:

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

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

3.增加了分頁的功能;

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

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

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


webpack

webpack打包壓縮我是參考這篇文章:http://www.jianshu.com/p/4df92c335617

當然現在webpack都已經是2.X了,所以還是會有版本迭代的:https://doc.webpack-china.org/guides/migrating/


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

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;
}









發佈了128 篇原創文章 · 獲贊 54 · 訪問量 21萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章