React移動專案總結
背景
接觸React半年了,一路走過來,團隊做了幾個專案,不斷的總結經驗,不斷的重構,也看了很多大牛總結的react經驗。
嘗試把自己遇到的問題總結分享出來,希望更多前輩指導指導。
總結基於兩個專案
Mac下開發
js
react
用class的寫法寫元件,和React.createClass不太一樣。具體babel有文字介紹react-on-es6-plus
有自己團隊的一套簡單的規範standard,就是airbnb搬過來的
redux
用redux做資料流,redux-thunk做非同步。
貌似actions reducers沒法非同步按需載入,於是自己倒騰了非同步載入的redux-async-actions-reducers
and 如果你的應用不夠大的話,就沒有必要非同步載入了,比如手機web。 全部打包也不會很大。
比如說下單系統的公共檔案 common.xxxx.js 佔了80%的程式碼量,在開發的時候達到1.4M,webpack -p 後是 500+kb,經過gzip壓縮之後是100kb。所以檔案大小壓根不是什麼事,沒有必要糾結太多。之前是否使用immutable就糾結了很久。
然而,更多應該關注到js的執行時間上,css和html的渲染處理上,檔案大小真的沒有這麼重要。
react-router
browserHistory
需要後臺配合,所以用了hashHistory
。 但肯定前者更友好。
路由過場動畫react-addons-css-transition-group。做到了想微信那樣前進後退的切換,很爽。(後來考慮到移動端效能不足問題,沒有采用動畫)
class App extends React.Component{
render(){
const action = this.props.location.action;
let transitionName = `page`;
// REPLEASE
if (action === `PUSH`) {
transitionName = `page-r2l`;
} else if (action === `POP`) {
transitionName = `page-l2r`;
}
return (
<ReactCSSTransitionGroup
component="div"
transitionName={transitionName}
transitionEnterTimeout={200}
transitionLeaveTimeout={200}
>
{React.cloneElement(this.props.children, {
key: this.props.location.pathname
})}
</ReactCSSTransitionGroup>
);
}
}
es6/7
寫程式碼太爽了,跟著潮流走。需要在.babelrc
檔案上配置好
{
"presets": [
"react",
"es2015",
"stage-0"
]
}
至於es7的stage-x是啥,看http://www.csdn.net/article/2…。也可以無腦的設定stage-0
immutable
確實是會因為一些引用問題導致資料不正確,問題難以發現和排查。和元件的多次渲染。
於是引用immutable,需要克服的是團隊的接受能力,需要點學習成本,但是帶來的效能提升是很高的(做shouleComponentUpdate)。
至於很多人都提到包大小問題,個人認為不用擔心,webpack -p壓縮+gzip,基本壓到很小的體積。我的在120kb,這可是全部程式碼(js+css)啊。
fastclick
在移動端會點選延遲,原因百度吧。用了react-fastclick
來處理,具體看這裡 移動端300ms點選延遲和點選穿透問題
相容
專案用了很多es6特性,瀏覽器不支援。可以引入 babel-polyfill。
當然babel-polyfill比較大,你也可以根據專案的具體情況來引入指定的方法。如core-js/es6/object.js
core-js/es6/promise.js
等等
直接和commons一起打包即可
entry: {
`commons`: [
`core-js/es6/object.js`,
`core-js/es6/promise.js`,
...
]
}
btw,我在移動埠的時候引入core-js的2.x版本,直接就報錯了。 遇到的同學可以降級到1.x版本。(具體原因還沒有排查)
如果需要相容到ie,據說有挺多坑。 推薦看下這篇文章使用ES6的瀏覽器相容性問題
css
產品主要場景在微信端,所以選擇了weui
,使用的感覺是目前weui提供的元件相對少,但是足夠用。weui的剋制也保證了weui的質量。有時候讀程式碼時候發現weui確實沉澱了很多精華在裡面。 配色方面基於weui做改造覆蓋,所以我們選擇了引入weui的less,方便用裡面的已經定義好的變數。
用了大量的Flex佈局,很靈活,降低CSS難度。
字型檔案用了阿里的iconfont。 收集好圖片下載下來,推送到github,然後在釋出到npm。
border
Retina屏的boder和pc的不一樣。 有很多解決方案,可以參考weui對於border的處理。 還可以看這裡 Retina屏的移動裝置如何實現真正1px的線
然後實踐過程中1px遇到的問題遠不止於此,上一個連結提到的只是點也不夠全面,獨立總結了下 移動端1px border
構建
babel
babel做es6/7的轉換,以前一般在loaders上直接寫babel的配置,比如
// 寫在webpack.config.js中
loaders: [{
test: /.js$/,
loader: `babel?presets[]=react,presets[]=es2015,presets[]=stage-0`
}]
現在切換到用.babelrc
配置上.(也是官方推薦的方法)
{
"presets": [
"react",
"es2015",
"stage-0"
]
}
熱載入
可以在webpack.config.dev.js中配置,不過用命令列的形式更簡潔
webpack-dev-server --inline --hot ...
css
用了postcss
來處理css3的相容性。
然而你可能會有機會發現開發的時候會生成 -webkit-flex 這種字首,釋出後卻丟失了。(日了狗)也許是國外的瀏覽器環境及比較好(國內android被微信內建瀏覽器統一了,iOS微信還有大約10%的iOS8的使用者,有些css屬性需要-webkit-字首)
鑑於此,特別注意這個寫法css?-autoprefixer
,具體看-webkit-flex 被移除了
js版本控制
官方介紹的很詳細 long-term-caching
用hash做js的版本號,通過AssetsPlugin
生成記錄版本號的檔案build/webpack-assets.js
,然後頁面引入這個檔案就可以得到js檔案的版本號了。
output: {
path: path.join(__dirname, `build`),
filename: `[name].[hash].js`,
publicPath: `/react-mgm/build/`
},
plugins: [
new webpack.NoErrorsPlugin(),
new AssetsPlugin({
filename: `build/webpack-assets.js`,
processOutput: function (assets) {
return `window.WEBPACK_ASSETS = ` + JSON.stringify(assets);
}
})
],
// index.html
<script>
document.write(`<script src="../build/webpack-assets.js"></script>`);
</script>
<script
document.write(`<script src="` + window.WEBPACK_ASSETS[`index`].js + `"></script>`);
</script>
公共檔案commons處理
見
webpack commons hash
如何確定哪些檔案應該打包在commons
構建加速
1
http://webpack.github.io/docs…
devtool 設定 eval
2
一些檔案直接引用打包好的版本可以加快構建。
module: {
(...省略)
noParse: [
// `react/dist/react.min.js`,
// `react-dom/dist/react-dom.min.js`,
`react-router/umd/ReactRouter.min.js`,
`redux/dist/redux.min.js`,
`react-redux/dist/react-redux.min.js`,
`underscore/underscore-min.js`
]
},
resolve: {
alias: {
// react 沒法加速build,因為react-addons-css-transition-group
// `react`: `react/dist/react.min.js`,
// `react-dom`: `react-dom/dist/react-dom.min.js`,
`react-router`: `react-router/umd/ReactRouter.min.js`,
`redux`: `redux/dist/redux.min.js`,
`react-redux`: `react-redux/dist/react-redux.min.js`,
`underscore`: `underscore/underscore-min.js`
}
},
構建環境
目前知道需要設定兩個地方,命令列中加入 NODE_ENV
NODE_ENV=production webpack xxxxx
and webpack配置裡面加入plugin,這樣程式碼就能通過if(__DEBUG__)
這種程式碼做環境差異。
new webpack.DefinePlugin({
__DEBUG__: env === `development` ? true : false,
"process.env": { // 幹掉 https://fb.me/react-minification 提示
NODE_ENV: env === `development` ? JSON.stringify("development") : JSON.stringify("production")
}
})
打包庫檔案
見webpack.config.js
webpack.config.min.js
有些庫作為依賴項,不應該打包進庫檔案中,用externals
來描述
externals: {
`react`: `react`,
`react-dom`: `react-dom`,
`underscore`: `underscore`,
`classnames`: `classnames`
},
css檔案獨立打包,用ExtractTextPlugin
來描述。
最後做壓縮
new webpack.optimize.UglifyJsPlugin({
compressor: {
screw_ie8: true,
warnings: false
}
})
npm
用了npm script來統一開發規範。npm start
來開啟開發npm run deploy
來發布npm run publishpatch
來發布到npm,並同步到淘寶映象來做加速
"scripts": {
"precommit": "eslint ./src/component/",
"pre": "npm install;",
"clear": "rm -rf build; mkdir build;",
"start": "npm run clear; webpack-dev-server --config webpack.config.dev.js --port 4000 --host 0.0.0.0 --inline --hot --devtool eval --progress --color --profile",
"deploy": "npm install; npm run build && npm run build:min",
"build": "webpack --progress --color --profile",
"build:min": "webpack --config webpack.config.min.js",
"publishpatch": "npm run deploy; git add --all; git commit -m `c`; npm version patch; git push origin master:master; npm publish; npm publish --registry=`https://registry.npmjs.org`; cnpm sync react-mgm; npm version;"
},
另外在專案中遇到版本依賴的問題。開發的時候好好的,釋出後就出問題了。 原因是npm依賴不一致問題。要麼固定版本號,但是隻能固定專案的依賴,依賴的依賴就沒法固定了。 有個方案不錯 npm shrinkwrap
規範
eslint
安裝npm install husky
的時候會自動往你的git hooks上加程式碼,提交程式碼的時候觸發想要的npm scripts。
我們用eslint來做檢測,配置見package.json的npm run precommit
eslint的配置用eslintrc.js
官方推薦的寫法,具體配置弄成一個自己的庫了。
用了eslint推薦的配置再結合eslint-plugin-react的配置
module.exports = {
"plugins": [
"gm"
],
"extends": ["plugin:gm/recommended"]
}
server服務
在開發時間避免等後臺api,找了json-server來做api服務,rest風格,很方便。
等後臺ready了,再通過上面提到的server代理呼叫聯調。
效能優化
其他
鍵盤呼氣
如果你的輸入框比較低的話,鍵盤呼氣就會擋住輸入框。 iphone會自動把input移到可見的位置,而android不會。 可以在android上對輸入框使用 scrollIntoViewIfNeed 使元素可見。
and在react下,會出現本來點輸入框的,結果卻是點了其他東西,觸發其他邏輯了。 所以這裡就搞了500ms的延遲。
自動呼氣鍵盤
在android鍵盤需要使用者觸發才可以呼氣。iOS 加個autoFocus即可。
判斷元素可見
一開始是慢慢的算offsetTop,如果層次很深的話,還要算多個parent的offsetTop,然後才能得出,如此必然很煩。 可以用 getBoundingClientRect 即可。