摘要:最佳實(shí)踐一個(gè)文件一個(gè)組件。,這是包含的是無(wú)副作用的純函數(shù)式計(jì)算狀態(tài)操作的函數(shù)。,的啟動(dòng)腳本,啟動(dòng)開(kāi)發(fā)模式,項(xiàng)目打包,運(yùn)行單元測(cè)試等等。每次代碼推送到之前也會(huì)執(zhí)行所有單元測(cè)試用例,全部通過(guò)才可以繼續(xù)推送。,首次安裝依賴包之后生成的文件。
前段時(shí)間 React license 的問(wèn)題鬧的沸沸揚(yáng)揚(yáng),搞得 React 社區(qū)人心惶惶,好在最終 React 團(tuán)隊(duì)聽(tīng)取了社區(qū)意見(jiàn)把 license 換成了 MIT。不管 React license 如何,React 都是一個(gè)值得好好學(xué)習(xí)的優(yōu)秀視圖庫(kù)。
本項(xiàng)目算不上什么大型項(xiàng)目,但依然按照大型項(xiàng)目的標(biāo)準(zhǔn)采用前端流行的最佳實(shí)踐來(lái)打造一個(gè)有良好代碼質(zhì)量,高性能,高可維護(hù)性,模塊化的應(yīng)用。本項(xiàng)目是基于 react, redux 構(gòu)建的 2048,此外也使用了近兩年優(yōu)秀的開(kāi)源工具來(lái)提高代碼質(zhì)量,包括 eslint,stylelint,prettier 等等,以及 travis,codecov 等持續(xù)集成,持續(xù)部署等服務(wù)來(lái)保障代碼質(zhì)量和提高開(kāi)發(fā)效率。
項(xiàng)目地址,喜歡的話 github 點(diǎn)個(gè) star 支持下吧?
預(yù)覽 桌面端 移動(dòng)端 特性 響應(yīng)式自適應(yīng)桌面和移動(dòng)平臺(tái)不同分辨率和尺寸,支持移動(dòng)平臺(tái)瀏覽器觸控操作。下面的動(dòng)圖模擬了不同分辨率下的顯示效果。實(shí)現(xiàn)方式主要是把 css 單位從 px 換成了 vw 和 rem ,各元素的尺寸是按照分辨率來(lái)進(jìn)行縮放的。css 媒體查詢到移動(dòng)瀏覽器的話,調(diào)整部分組件的位置,隱藏部分不重要的組件,使頁(yè)面更加緊湊。
數(shù)據(jù)持久化網(wǎng)頁(yè)應(yīng)用最怕斷電和離線,第一個(gè)問(wèn)題通過(guò) store.subscribe 訂閱 redux 狀態(tài)更新,把狀態(tài)序列化到 localStorage 儲(chǔ)存,即使刷新,斷電,程序奔潰再次打開(kāi)仍然是最新的狀態(tài),第二個(gè)問(wèn)題借助 chrome 的 PWA 技術(shù),即使斷開(kāi)網(wǎng)絡(luò)仍然可以訪問(wèn)緩存的資源文件。
Redux 狀態(tài)redux 是一個(gè)可預(yù)測(cè)的 JS 狀態(tài)管理容器,結(jié)合 Redux DevTools extension 擴(kuò)展可以很方便的進(jìn)行應(yīng)用狀態(tài)穿梭,對(duì)輔助開(kāi)發(fā)和debug大有裨益。不僅可以查看 redux 保存的狀態(tài),還可以隨時(shí)回到到過(guò)去某個(gè)時(shí)刻的狀態(tài)就像時(shí)間穿梭機(jī)一樣,也看得到 redux 每次 action 的觸發(fā),以及每次觸發(fā)造成的狀態(tài)改動(dòng)。
評(píng)論系統(tǒng)借助 github issue api,使用 github 賬號(hào)登錄之后以回復(fù) issue 的方式留言。留言支持 markdown 格式,和 github issue 體驗(yàn)類似。
PWA在支持 PWA 技術(shù)的瀏覽器上(比如較新的 chrome)打開(kāi)頁(yè)面會(huì)自動(dòng)詢問(wèn)你添加到屏幕,添加過(guò)程就像原生應(yīng)用的安裝一樣。應(yīng)用添加之后就可以像原生應(yīng)用一樣離線操作,也可以卸載應(yīng)用。下圖演示了 PWA 在 chrome 上面的添加過(guò)程,添加完成之后桌面會(huì)出現(xiàn)添加的應(yīng)用,即便關(guān)閉所有網(wǎng)絡(luò)仍然可以像原生應(yīng)用一樣正常操作。
i18n應(yīng)用支持多語(yǔ)言,且自動(dòng)適配瀏覽器語(yǔ)言設(shè)置。目前檢測(cè)到瀏覽器支持中文優(yōu)先使用中文,否則默認(rèn)使用英文顯示。需要更多語(yǔ)言支持,編輯 src/utils/i18n.js 的 data 對(duì)象,添加對(duì)應(yīng)語(yǔ)言文字即可。
react 最佳實(shí)踐一個(gè)文件一個(gè)組件。
盡量使用無(wú)狀態(tài)(Stateless)組件,也就是如果只是寫一個(gè)單純展示的組件,不需要組件保存自己的狀態(tài),不需要生命周期方法或者 refs 來(lái)操作 DOM 的組件則優(yōu)先使用無(wú)狀態(tài)組件,采用函數(shù)的形式。以項(xiàng)目 Tips 組件示例:
import React from "react"; import PropTypes from "prop-types"; import styles from "./tips.scss"; export default function Tips({ title, content }) { return (); } Tips.propTypes = { title: PropTypes.string.isRequired, content: PropTypes.string.isRequired };{title}
{content}
和上面相反,如果你需要組件生命周期方法優(yōu)化組件性能(典型應(yīng)用,重寫 shouldComponentUpdate 方法),需要組件保存自己的狀態(tài),或者用 refs 操作 DOM,你就需要一個(gè)有狀態(tài)組件,采用 es6 class 繼承 React.Component 的寫法。組件示例:
import React from "react"; import PropTypes from "prop-types"; import classnames from "classnames"; import styles from "./cell.scss"; import { isObjEqual } from "../../utils/helpers"; export default class Cell extends React.Component { static propTypes = { value: PropTypes.number.isRequired }; shouldComponentUpdate(nextProps, nextState) { return ( !isObjEqual(nextProps, this.props) || !isObjEqual(nextState, this.state) ); } render() { const { props: { value } } = this; const color = `color-${value}`; return (); } } {value || null}
事件綁定 this 方法。在構(gòu)造函數(shù)里面綁定一次 this 之后后面就可以正常使用。以 ControlPanel 組件部分代碼示例:
constructor(...args) { super(...args); this.handleMoveUp = this.handleMoveUp.bind(this); this.handleMoveDown = this.handleMoveDown.bind(this); this.handleMoveLeft = this.handleMoveLeft.bind(this); this.handleMoveRight = this.handleMoveRight.bind(this); this.handleKeyUp = this.handleKeyUp.bind(this); this.handleSpeakerClick = this.handleSpeakerClick.bind(this); this.handleUndo = this.handleUndo.bind(this); }
使用 propTypes 屬性進(jìn)行傳入 prop 的校驗(yàn)??梢孕r?yàn) prop 的類型和是否必需,非必需的 prop 還必需填寫 defaultProps 默認(rèn)值。以無(wú)狀態(tài)組件 Button 的部分代碼示例:
Button.propTypes = { children: PropTypes.oneOfType([PropTypes.node]), onClick: PropTypes.func, size: PropTypes.oneOf(["lg", "md", "sm", "xs"]), type: PropTypes.oneOf([ "default", "primary", "warn", "danger", "success", "royal" ]).isRequired }; Button.defaultProps = { children: "", onClick() {}, size: "md", };
使用 HOC(Higher-Order Components) 代替 mixin。mixin 官方已經(jīng)不推薦使用了,redux 的 connect 方法就是 HOC 的應(yīng)用。
為了提高應(yīng)用性能,避免不必要的視圖重繪,在需要的組件使用 shouldComponentUpdate 方法;以組件 Row 示例:
// 如果該行沒(méi)有格子需要刷新也沒(méi)有組件自己的狀態(tài)刷新, // 則該組件不執(zhí)行 render 方法, // 避免每次別的行數(shù)據(jù)刷新也跟著重新渲染。 shouldComponentUpdate(nextProps, nextState) { return ( !isObjEqual(nextProps, this.props) || !isObjEqual(nextState, this.state) ); }項(xiàng)目結(jié)構(gòu)
本項(xiàng)目是基于 Facebook 官方出品的 create-react-app 腳手架搭建的,reject 后做了適當(dāng)修改以適配項(xiàng)目需求。
調(diào)整如下webpack 添加 scss 支持。之所以沒(méi)有用 CssInJS 的方案是因?yàn)檫@些方案普遍不完美,也考慮到要遵循樣式和結(jié)構(gòu)分離的原則,scss 是目前比較成熟的 css 預(yù)處理器,社區(qū)輪子也比較多,開(kāi)發(fā)起來(lái)很方便。推薦學(xué)習(xí) scss/sass 教程。添加 sass-loader 到 scss 規(guī)則下面最下面即可。配置代碼
開(kāi)啟 css module 支持。在大型項(xiàng)目里面組件之間需要盡量解耦,但是 css 類名的全局特性很容易導(dǎo)致意料之外的錯(cuò)誤。開(kāi)啟 css module 之后,所有的類名最終都會(huì)被一小段 hash 值填充,所以類名也就有一定的唯一性,不容易污染全局的代碼。配置代碼
添加 stylelint 支持。js 代碼已經(jīng)有 eslint (但采用了更流行,校驗(yàn)更嚴(yán)格的 airbnb 規(guī)則) 來(lái)檢查代碼,但是樣式代碼也需要保持代碼風(fēng)格統(tǒng)一,同時(shí)校驗(yàn)規(guī)則一般有社區(qū)的最佳實(shí)踐。配置代碼
添加靜態(tài)資源 cdn 支持。由于項(xiàng)目部署在 github page 在國(guó)內(nèi)訪問(wèn)速度不是很理想,所以在可能的情況下盡量減小 js 包的大小對(duì)頁(yè)面加載速度至關(guān)重要。像 ReactDOM 這類較大的 npm 包從打包文件剝離出去采用 CDN 來(lái)加載,可顯著減小打包文件的大小。(PS:之所以 CDN 加載比較快,是因?yàn)?CDN 提供商在全國(guó)各地都建立了緩存服務(wù)器,資源就近獲取比自己從 github 獲取快得多,而且一般 CDN 的帶寬也比較充裕)把 React 和 ReactDOM 剝離出去只需要在 html 文件添加 CDN 的 [script 標(biāo)簽](),同時(shí)在 webpack 添加 externals 屬性,該屬性指定代碼 import 該包時(shí)直接從全局變量獲取。剝離后打包的 js 文件大小從 278kb 減小到 164 kb。
添加 webpack 代碼壓縮插件。默認(rèn)的 webpack 配置直接輸出原始的 js,css 代碼,但添加壓縮過(guò)后,文件顯著減?。╦s 文件從 164kb 到 49kb),對(duì)于移動(dòng)瀏覽器來(lái)說(shuō)打開(kāi)速度得到明顯提升。配置代碼
添加 webpack-bundle-analyzer 插件,通過(guò)各模塊包所占打包文件后的比重來(lái)分析項(xiàng)目代碼,借此優(yōu)化代碼。比如,React 和 ReactDOM 的剝離就是因?yàn)榉治龊蟀l(fā)現(xiàn)這兩個(gè)包所占比重較大。
文件結(jié)構(gòu)
src, 項(xiàng)目源代碼大部分都在這里,主要是 react 組件 js 代碼 和 scss 樣式代碼。次級(jí)目錄包含了 jest 單元測(cè)試代碼,測(cè)試代碼盡量和源代碼挨著,以方便編寫。
assets,主要存放一些全局樣式代碼,icon svg 文件,游戲音效 mp3 文件,圖片等等;
components,存放 react dumb 組件, 每個(gè)組件包含在采用首字母大寫的目錄的 index.js 里面,同時(shí)該目錄包含該組件用到樣式的 scss 文件,盡量一個(gè)目錄包含該組件所需的所有代碼避免污染其他代碼,提高組件復(fù)用性。
containers,存放 react smart 組件,該目錄結(jié)構(gòu)和 components 類似,但因?yàn)槭?smart 組件,所以這里的組件可以操作 redux 的數(shù)據(jù),不用太考慮復(fù)用性。
reducers,這是 redux 包含的是無(wú)副作用的純函數(shù)式計(jì)算狀態(tài)操作的函數(shù)。
utils,包括評(píng)論組件初始化,i18n 多語(yǔ)言文件,移動(dòng)瀏覽器滑動(dòng)檢測(cè)和注冊(cè) ServiceWorker 等等。
index.js,項(xiàng)目入口文件,主要把 react 根組件 渲染到指定 DOM 節(jié)點(diǎn),并且注冊(cè) ServiceWorker。
store.js,redux store 初始化,同時(shí) store.subscribe 訂閱應(yīng)用狀態(tài)更新,序列化狀態(tài)存到 localStorage。
public,包括項(xiàng)目的 html 文件,網(wǎng)站 icon favicon 和 PWA manifest 文件。
config,主要包括 webpack 的各種配置文件。
scripts,npm 的啟動(dòng)腳本,啟動(dòng)開(kāi)發(fā)模式,項(xiàng)目打包,運(yùn)行 jest 單元測(cè)試等等。
build,項(xiàng)目打包后的輸出目錄。
screenshots,README 各種圖片的原圖,為了國(guó)內(nèi)用戶訪問(wèn)方便實(shí)際上 README 的圖片來(lái)自新浪微博的圖床。
.editorconfig,通用的編輯器配置,統(tǒng)一不同編輯器 / IDE 的代碼格式。
.eslintignore,需要 eslint 忽略的文件或者目錄,規(guī)則類似 .gitignore
.travis.yml, 持續(xù)集成腳本,每次提交代碼到 github 之后,測(cè)試服務(wù)器都會(huì)自動(dòng)運(yùn)行該腳本執(zhí)行測(cè)試用例,并輸出代碼覆蓋率,最后自動(dòng)部署到 github page。所有狀態(tài)都在項(xiàng)目中 README 的徽章中可見(jiàn)。
package.json,項(xiàng)目基本信息和部分配置都存在這里。常見(jiàn)的內(nèi)容包括項(xiàng)目的各類依賴包,各種啟動(dòng)腳本,項(xiàng)目 homepage 等等;為了減少根項(xiàng)目的文件數(shù)目,jest,babel,eslint,stylelint 的配置也寫在這里。值得注意的是,項(xiàng)目中引入 husky,在每次代碼 commit 之前都會(huì)執(zhí)行 lint-staged,以自動(dòng)執(zhí)行 prettier 來(lái)美化代碼格式。每次代碼推送 到 github 之前也會(huì)執(zhí)行所有單元測(cè)試用例,全部通過(guò)才可以繼續(xù)推送。
yarn.locl,yarn 首次安裝依賴包之后生成的 lock 文件。通過(guò) yarn 來(lái)安裝依賴包時(shí),yarn 自動(dòng)把項(xiàng)目的依賴包(包括依賴包依賴的父級(jí)包)固定在指定的版本(包括依賴包安裝的 url 和 hash 值),這樣所有開(kāi)發(fā)環(huán)境都使用 yarn 來(lái)管理項(xiàng)目,不同的機(jī)器不同的系統(tǒng)安裝出來(lái)包都是一樣的,這樣就避免了之前 npm 的缺陷(版本要求太松或者父級(jí)包版本更新等等導(dǎo)致每次安裝出來(lái)的依賴版本不一樣)。
技術(shù)棧react,組件式構(gòu)建 UI
redux,管理應(yīng)用狀態(tài)
babel,把 es2017+ 語(yǔ)法轉(zhuǎn)成 es5 兼容語(yǔ)法
webpack,代碼熱加載,scss 樣式文件處理,組件編譯打包等等
scss,成熟的 css 預(yù)處理器(之所以沒(méi)有用 CssInJS 的方案是因?yàn)檫@些方案普遍不完美,也考慮到要遵循樣式和結(jié)構(gòu)分離的原則)
eslint,使用流行的 airbnb 的代碼規(guī)范嚴(yán)格約束代碼風(fēng)格
stylelint,scss 代碼風(fēng)格檢查
jest,fb 出品的代碼測(cè)試框架,snapshot 功能對(duì)測(cè)試 react 組件 UI 十分方便
Prettier,js 和 scss 代碼格式美化工具
PWA(Progressive Web Apps),借助瀏覽器 service worker 能力,使 web 應(yīng)用在移動(dòng)平臺(tái)有接近原生應(yīng)用的能力,可離線使用,接收通知消息等等
運(yùn)行 & 測(cè)試 & 打包因?yàn)榕渲梦募昧?es6+ 語(yǔ)法所以要求 node 的版本大于 6.10,同時(shí)建議使用 yarn 來(lái)管理依賴包。fork 項(xiàng)目之后可以按如下命令操作。
npm i -g yarn # 安裝 yarn git clone [email protected]:<你的名字>/React-2048-game.git cd React-2048-game yarn # 安裝依賴包 yarn start # 開(kāi)啟調(diào)試模式,啟動(dòng)后自動(dòng)打開(kāi)瀏覽器 http://localhost:3000 yarn test # 自動(dòng)測(cè)試 yarn build # 打包代碼踩坑記錄
在調(diào)煙花動(dòng)畫的時(shí)候發(fā)現(xiàn)沒(méi)效果,仔細(xì)對(duì)比了下 webpack 編譯后的 css 文件發(fā)現(xiàn)所有的 @keyframes 的名字都加了 hash 值(也就是當(dāng)成普通的局部 css 類名),解決辦法就是在 @keyframes 的名字前面和整個(gè) scss 文件添加偽類 :global,可以參考煙花的 scss 文件,這不是完美的解決辦法(css 類名不再有局部特性),后續(xù)再深挖一下。
css module 用到的 :global 這個(gè)不是標(biāo)準(zhǔn)的偽類,所以 stylelint 需要添加配置以忽略這個(gè)錯(cuò)誤。參見(jiàn) package.json 的 stylelint.rules。
項(xiàng)目地址,喜歡的話 github 點(diǎn)個(gè) star 支持下吧?文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/89005.html
摘要:前端日?qǐng)?bào)精選帶來(lái)了什么以及對(duì)的解釋專題之亂序第期如何無(wú)痛降低面條代碼復(fù)雜度道阻且長(zhǎng)啊前端面試總結(jié)附答案上前端安全知識(shí)中文開(kāi)源許可證教程阮一峰的網(wǎng)絡(luò)日志裝飾器讓你的代碼更簡(jiǎn)潔掘金什么是函數(shù)眾成翻譯和十分鐘快速入門眾成翻譯設(shè)計(jì)最佳實(shí) 2017-10-12 前端日?qǐng)?bào) 精選 React 16 帶來(lái)了什么以及對(duì) Fiber 的解釋JavaScript專題之亂序【第1076期】 如何無(wú)痛降低 if...
摘要:要求通過(guò)要求數(shù)據(jù)變更函數(shù)使用裝飾或放在函數(shù)中,目的就是讓狀態(tài)的變更根據(jù)可預(yù)測(cè)性單向數(shù)據(jù)流。同一份數(shù)據(jù)需要響應(yīng)到多個(gè)視圖,且被多個(gè)視圖進(jìn)行變更需要維護(hù)全局狀態(tài),并在他們變動(dòng)時(shí)響應(yīng)到視圖數(shù)據(jù)流變得復(fù)雜,組件本身已經(jīng)無(wú)法駕馭。今天是 520,這是本系列最后一篇文章,主要涵蓋 React 狀態(tài)管理的相關(guān)方案。 前幾篇文章在掘金首發(fā)基本石沉大海, 沒(méi)什么閱讀量. 可能是文章篇幅太長(zhǎng)了?掘金值太低了? ...
摘要:多端統(tǒng)一開(kāi)發(fā)框架優(yōu)秀學(xué)習(xí)資源匯總官方資源項(xiàng)目倉(cāng)庫(kù)官方文檔項(xiàng)目倉(cāng)庫(kù)官方文檔微信小程序官方文檔百度智能小程序官方文檔支付寶小程序官方文檔字節(jié)跳動(dòng)小程序官方文檔文章教程不敢閱讀包源碼帶你揭秘背后的哲學(xué)從到構(gòu)建適配不同端微信小程序等的應(yīng)用小程序最 Awesome Taro 多端統(tǒng)一開(kāi)發(fā)框架 Taro 優(yōu)秀學(xué)習(xí)資源匯總 showImg(https://segmentfault.com/img/r...
摘要:不過(guò)今天我希望能夠更進(jìn)一步,不僅僅再抱怨現(xiàn)狀,而是從我個(gè)人的角度來(lái)給出一個(gè)逐步深入學(xué)習(xí)生態(tài)圈的方案。最后,我還是想提到下對(duì)于的好的學(xué)習(xí)方法就是回顧參照各種各樣的代碼庫(kù),學(xué)習(xí)人家的用法與實(shí)踐。 本文翻譯自A-Study-Plan-To-Cure-JavaScript-Fatigue。筆者看到里面的幾張配圖著實(shí)漂亮,順手翻譯了一波。本文從屬于筆者的Web Frontend Introduc...
摘要:在線注冊(cè)賬號(hào),數(shù)據(jù)存儲(chǔ)于。年了,還不使用的異步控制體系。過(guò)度對(duì)數(shù)據(jù)模型進(jìn)行裝飾的結(jié)果便是高耦合,這跟我初衷是基于在線存儲(chǔ)數(shù)據(jù)有關(guān)。 為什么又是Todo,全世界的初學(xué)者都在做todo嗎?可能很多人要問(wèn)這句話,其實(shí)這句話可以等同于: 為什么你做了個(gè)云音樂(lè)播放器? 為什么你做了個(gè)新聞閱讀APP? 為什么你做了個(gè)VUE/REACT版本的CNODE? 究其本質(zhì),這幾個(gè)應(yīng)用都是data-map...
閱讀 1015·2021-11-23 09:51
閱讀 2727·2021-08-23 09:44
閱讀 688·2019-08-30 15:54
閱讀 1464·2019-08-30 13:53
閱讀 3131·2019-08-29 16:54
閱讀 2551·2019-08-29 16:26
閱讀 1220·2019-08-29 13:04
閱讀 2348·2019-08-26 13:50