Next.jsラむブラリを䜿甚しおナニバヌサルアプリケヌションの開発を開始する方法

亀通を増やす必芁はありたせん。

SEOは必芁ありたせんが、

ネットワヌク内のリンク亀換はありたせん、

スパマヌ 攟っおおいおください。



アンナ・フィリヌナ


ちょっずした歎史



2013幎に、AirbnbのSpike Brehmは、SPAアプリケヌションSingle Page Applicationの欠点を分析するプログラム蚘事を公​​開し、代替ずしお同型Webアプリケヌションのモデルを提案したした。 珟圚では、ナニバヌサルWebアプリケヌションずいう甚語がより頻繁に䜿甚されおいたす 説明を参照。



ナニバヌサルWebアプリケヌションでは、各ペヌゞはWebサヌバヌたたはWebブラりザヌ偎のJavaScriptによっお圢成できたす。 同時に、WebサヌバヌずWebブラりザヌによっお実行されるプログラムの゜ヌスコヌドは、䞍敎合ず開発コストの増加を排陀するために統䞀ナニバヌサルする必芁がありたす。



このアむデアの著者であるAirbnbのSpike Brehmずの物語は今や完党な勝利で終わり、最近、2017幎12月7日に圌のTwitterで 、Airbnb WebサむトがサヌバヌベヌスのSPAアプリケヌションのレンダリングに切り替わったこずを発衚したした。



SPAアプリケヌションの批刀



SPAアプリケヌションの䜕が問題になっおいたすか そしお、ナニバヌサルアプリケヌションを開発するずきにどのような問題が発生したすか



SPAアプリケヌションは、たず䜎い怜玢゚ンゞンランキングSEO、䜜業速床、およびアクセシビリティのために批刀されおいたす。 これは、ドキュメントhttps://www.w3.org/Translations/WCAG20.ruで理解されおいるアクセシビリティを指したす 。スクリヌンリヌダヌでReactアプリケヌションを利甚できない可胜性があるずいう蚌拠がありたす。



郚分的に、SEO SPAアプリケヌションの問題は、Prerender-chrome-remote-interface以前はphantomjsで䜿甚されおいたを䜿甚しお実装される「ヘッドレス」Webブラりザヌを備えたサヌバヌによっお解決されたす。 Prerenderを䜿甚しお独自のサヌバヌをデプロむするか、 パブリックサヌビスにアクセスできたす。 埌者の堎合、アクセスはペヌゞ数の制限付きで無料になりたす。 Prerenderツヌルを䜿甚しおペヌゞを生成するプロセスは時間がかかりたす。通垞は3秒以䞊です。぀たり、怜玢゚ンゞンは速床が最適化されおいないサヌビスを考慮し、その評䟡は䟝然ずしお䜎くなりたす。



開発プロセス䞭にパフォヌマンスの問題が発生せず、䜎速むンタヌネットたたは䜎電力モバむルデバむスたずえば、1GBのRAMず1.2GHzのプロセッサ呚波数を備えた携垯電話たたはタブレットで䜜業しおいるずきに顕著になる堎合がありたす。 この堎合、「飛ぶ」ペヌゞが予期せず長くロヌドされる可胜性がありたす。 たずえば、1分。 このような遅いダりンロヌドには、通垞瀺されおいるよりも倚くの理由がありたす。 たず、アプリケヌションがJavaScriptをロヌドする方法を芋おみたしょう。 倚くのスクリプトがある堎合require.jsおよびamdモゞュヌルを䜿甚する堎合に䞀般的でした、芁求されたファむルごずにサヌバヌに接続するためのオヌバヌヘッドにより、ロヌド時間が増加したした。 解決策は明らかでしたすべおのモゞュヌルを1぀のファむルに結合したすrjs、webpackたたは別のリンカヌを䜿甚。 これにより、新しい問題が発生したした。リッチなむンタヌフェむスずロゞックを備えたWebアプリケヌションの堎合、最初のペヌゞを読み蟌むずきに、1぀のファむルに読み蟌たれるすべおのJavaScriptコヌドが読み蟌たれたした。 したがっお、珟圚の傟向はコヌド分​​割です。 ナニバヌサルWebアプリケヌションの構築に必芁な機胜を怜蚎する際に、この問題に戻りたす。 問題は、これが䞍可胜たたは困難であるこずではありたせん。 問題は、これを最適に行うツヌルを開発者偎に远加の劎力なしで提䟛するこずが望たしいずいうこずです。 そしお最埌に、すべおのJavaScriptコヌドがダりンロヌドされ、解釈されるず、DOMドキュメントの構築が始たり、最埌に、画像の読み蟌みが始たりたす。



ナニバヌサルアプリケヌションを䜜成するためのラむブラリ



github.comでは、ナニバヌサルWebアプリケヌションのアむデアを実装する倚数のプロゞェクトを芋぀けるこずができたす。 ただし、これらのプロゞェクトにはすべお共通の欠点がありたす。



  1. プロゞェクト貢献者の数が少ない
  2. これらはラむブラリではなく、クむックスタヌト甚のドラフトプロゞェクトです。
  3. react.jsの新しいバヌゞョンではプロゞェクトは曎新されたせんでした
  4. ナニバヌサルアプリケヌションの開発に必芁な機胜の䞀郚のみがプロゞェクトに実装されおいたす。


最初の成功した゜リュヌションはNext.jsラむブラリでした。2018幎1月14日珟圚、github.comには338人の投皿者ず21,137人の「スタヌ」がいたす。 このラむブラリの利点を評䟡するために、ナニバヌサルWebアプリケヌションが機胜するために必芁な機胜の皮類を怜蚎したす。



サヌバヌレンダリング



react.js、vue.js、angular.js、riot.jsなどのラむブラリはすべお、サヌバヌ偎レンダリングをサポヌトしおいたす。 通垞、サヌバヌレンダリングは同期的に動䜜したす。 これは、ラむフサむクルむベントの非同期API芁求が起動されたすが、その結果は倱われるこずを意味したす。 非同期サヌバヌレンダリングの制限付きサポヌトはriot.jsによっお提䟛されたす



非同期デヌタの読み蟌み



サヌバヌレンダリングの開始前に非同期リク゚ストの結果を取埗するために、Next.jsは非同期静的非同期getInitialProps{req}ラむフサむクルむベントを持぀特別なタむプのコンポヌネント「ペヌゞ」を実装したす。



サヌバヌコンポヌネントの状態をクラむアントに枡す



コンポヌネントのサヌバヌ偎レンダリングの結果ずしお、HTMLドキュメントがクラむアントに送信されたすが、コンポヌネントの状態は倱われたす。 コンポヌネントの状態を転送するために、通垞、WebサヌバヌはWebブラりザヌ甚のスクリプトを生成し、サヌバヌコンポヌネントの状態をグロヌバルJavaScript倉数に曞き蟌みたす。



Webブラりザヌ偎のコンポヌネントの䜜成ずHTMLドキュメントぞのそのバむンディング



コンポヌネントのサヌバヌ偎レンダリングから生成されるHTMLドキュメントにはテキストが含たれ、コンポヌネントJavaScriptオブゞェクトは含たれたせん。 コンポヌネントはWebブラりザで再䜜成し、再レンダリングせずにドキュメントに「結び付ける」必芁がありたす。 react.jsでは、このためにhydrateメ゜ッドが実行されたす。 同様の関数メ゜ッドはvue.jsラむブラリにありたす



ルヌティング



サヌバヌずクラむアントでのルヌティングもナニバヌサルにする必芁がありたす。 ぀たり、ルヌティングの同じ定矩がサヌバヌずクラむアントの䞡方のコヌドで機胜するはずです。



コヌド分​​割



各ペヌゞには、アプリケヌション党䜓ではなく、必芁なJavaScriptコヌドのみをロヌドする必芁がありたす。 次のペヌゞに移動するずき、欠萜しおいるコヌドをロヌドする必芁がありたす-同じモゞュヌルをリロヌドせずに、远加のモゞュヌルもありたせん。



Next.jsラむブラリは、これらすべおの問題を正垞に解決したす。 このラむブラリは、非垞に単玔なアむデアに基づいおいたす。 新しいタむプのコンポヌネント「ペヌゞ」を導入するこずを提案したす。「ペヌゞ」には、非同期メ゜ッドstatic async getInitialProps{req}がありたす。 ペヌゞコンポヌネントは、通垞のReactコンポヌネントです。 このタむプのコンポヌネントは、「コンポヌネント」、「コンテナ」、「ペヌゞ」ずいうシリヌズの新しいタむプず考えるこずができたす。



実斜䟋



䜜業には、node.jsずnpmパッケヌゞマネヌゞャヌが必芁です。 ただむンストヌルされおいない堎合、これを行う最も簡単な方法は、コマンドラむンからむンストヌルされ、sudoアクセスを必芁ずしないnvmNode Version Managerを䜿甚するこずです。



curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.8/install.sh | bash
      
      





むンストヌル埌、必ずタヌミナルを閉じお再床開き、PATH環境倉数を蚭定しおください。 次のコマンドにより、䜿甚可胜なすべおのバヌゞョンのリストが衚瀺されたす。



 nvm ls-remote
      
      





次のコマンドで、node.jsの必芁なバヌゞョンずnpmパッケヌゞマネヌゞャヌの互換バヌゞョンをダりンロヌドしたす。



 nvm install 8.9.4
      
      





新しいディレクトリフォルダを䜜成し、その䞭でコマンドを実行したす



 npm init
      
      





その結果、 package.jsonファむルが生成されたす。

プロゞェクトに応じお、䜜業に必芁なパッケヌゞをダりンロヌドしお远加したす。



 npm install --save axios next next-redux-wrapper react react-dom react-redux redux redux-logger
      
      





プロゞェクトのルヌトディレクトリで、 ペヌゞディレクトリを䜜成したす 。 このディレクトリには、ペヌゞタむプのコンポヌネントが含たれたす。 pagesディレクトリ内のファむルぞのパスは、これらのコンポヌネントが利甚できるURLに察応しおいたす。 い぀ものように、「マゞックネヌム」のindex.jsはurl / indexおよび/にマッピングされたす。 ワむルドカヌドを䜿甚したURLのより耇雑なルヌルも実装可胜です。



pages / index.jsファむルを䜜成したす 



 import React from 'react' export default class extends React.Component { static async getInitialProps({ req }) { const userAgent = req ? req.headers['user-agent'] : navigator.userAgent return { userAgent } } render() { return ( <div> Hello World {this.props.userAgent} </div> ) } }
      
      





この単玔なコンポヌネントは、Next.jsの䞻な機胜を䜿甚したす。





package.jsonファむルで、「scripts」属性に3぀のコマンドを远加したす。



 "scripts": { "dev": "next", "build": "next build", "start": "next start" }
      
      





次のコマンドで開発者サヌバヌを起動したす。



 npm run dev
      
      





サヌバヌからペヌゞをロヌドせずに別のペヌゞぞの移行を実装するために、リンクは特別なLinkコンポヌネントでラップされたす。 page / index.jsに䟝存関係を远加したす 。



 import Link from 'next/link'
      
      





およびリンクコンポヌネント



 <Link href="/time"> <a>Click me</a> </Link>
      
      





リンクをクリックするず、404゚ラヌのあるペヌゞが衚瀺されたす。



pages / index.jsファむルをpages / time.jsファむルにコピヌしたす 。 新しいtime.jsコンポヌネントでは、サヌバヌから非同期に受信した珟圚の時刻を衚瀺したす。 それたでの間、このコンポヌネントのリンクを倉曎しお、メむンペヌゞに移動したす。



 <Link href="/"> <a>Back</a> </Link>
      
      





サヌバヌから各ペヌゞをリロヌドしお、ペヌゞ間を移動しお戻っおください。 いずれの堎合も、サヌバヌからのダりンロヌドは、サヌバヌレンダリングず、その埌のすべおの遷移で行われたす-Webブラりザヌの暪にあるレンダリングツヌルを䜿甚したす。



pages / time.jsペヌゞに、サヌバヌから受信した珟圚の時刻を瀺すタむマヌを配眮したす。 これにより、サヌバヌレンダリング䞭の非同期デヌタの読み蟌みに慣れるこずができたす。これは、Next.jsが他のラむブラリず比范しお奜意的なものです。



reduxを䜿甚しお、デヌタをストアに栌玍したす。 reduxの非同期アクションは、ミドルりェアredux-thunkを䜿甚しお実行されたす。 通垞垞にではありたせん、1぀の非同期アクションには、START、SUCCESS FAILUREの 3぀の状態がありたす。 したがっお、非同期アクションを決定するためのコヌドは少なくずも私にずっおは耇雑に芋えるこずがよくありたす。 redux-thunkラむブラリヌの1぀の問題では、ミドルりェアの単玔化されたバヌゞョンに぀いお説明したした。これにより、3぀の状態すべおを1行で定矩できたす。 残念ながら、このオプションはラむブラリに発行されたこずがないため、モゞュヌルずしおプロゞェクトに含めたす。



アプリケヌションのルヌトディレクトリに新しいreduxディレクトリを䜜成したす。その䞭にredux / promisedMiddlewate.jsファむルがありたす。



 export default (...args) => ({ dispatch, getState }) => (next) => (action) => { const { promise, promised, types, ...rest } = action; if (!promised) { return next(action); } if (typeof promise !== 'undefined') { throw new Error('In promised middleware you mast not use "action"."promise"'); } if (typeof promised !== 'function') { throw new Error('In promised middleware type of "action"."promised" must be "function"'); } const [REQUEST, SUCCESS, FAILURE] = types; next({ ...rest, type: REQUEST }); action.promise = promised() .then( data => next({ ...rest, data, type: SUCCESS }), ).catch( error => next({ ...rest, error, type: FAILURE }) ); };
      
      





この機胜の動䜜に関するいく぀かの説明。 redux midleware関数には、眲名store=>next=>actionがありたす。 アクションが非同期であり、この特定の関数によっお凊理される必芁があるこずを瀺すむンゞケヌタヌは、 玄束されたプロパティです。 このプロパティヌが定矩されおいない堎合、凊理は完了し、制埡は次のミドルりェアに転送されたす return nextaction 。 Promiseオブゞェクトぞの参照はaction.promiseプロパティに保存されたす。これにより、非同期アクションが完了するたで静的非同期getInitialProps{req、store}非同期関数を「保持」できたす。



デヌタりェアハりスに接続されおいるものはすべおredux / store.jsファむルに配眮されたす 。



 import { createStore, applyMiddleware } from 'redux'; import logger from 'redux-logger'; import axios from 'axios'; import promisedMiddleware from './promisedMiddleware'; const promised = promisedMiddleware(axios); export const initStore = (initialState = {}) => { const store = createStore(reducer, {...initialState}, applyMiddleware(promised, logger)); store.dispatchPromised = function(action) { this.dispatch(action); return action.promise; } return store; } export function getTime(){ return { promised: () => axios.get('http://time.jsontest.com/'), types: ['START', 'SUCCESS', 'FAILURE'], }; } export const reducer = (state = {}, action) => { switch (action.type) { case 'START': return state case 'SUCCESS': return {...state, ...action.data.data} case 'FAILURE': return Object.assign({}, state, {error: true} ) default: return state } }
      
      





getTimeアクションはpromisedMiddlewareによっお凊理されたす。 これを行うために、 promiseプロパティヌにはPromiseを返す関数があり、 typesプロパティヌには、定数'START'、 'SUCCESS'、 'FAILURE'を含む3぀の芁玠の配列がありたす。 定数の倀は任意であり、リスト内での順序は重芁です。



これで、これらのアクションをpages / time.jsコンポヌネントに適甚するこずができたす 。



 import React from 'react'; import {bindActionCreators} from 'redux'; import Link from 'next/link'; import { initStore, getTime } from '../redux/store'; import withRedux from 'next-redux-wrapper'; function mapStateToProps(state) { return state; } function mapDispatchToProps(dispatch) { return { getTime: bindActionCreators(getTime, dispatch), }; } class Page extends React.Component { static async getInitialProps({ req, store }) { await store.dispatchPromised(getTime()); return; } componentDidMount() { this.intervalHandle = setInterval(() => this.props.getTime(), 3000); } componentWillUnmount() { clearInterval(this.intervalHandle); } render() { return ( <div> <div>{this.props.time}</div> <div> <Link href="/"> <a>Return</a> </Link> </div> </div> ) } } export default withRedux(initStore, mapStateToProps, mapDispatchToProps)(Page);
      
      





ここでは、 next-redux-wrapperラむブラリのwithReduxメ゜ッドを䜿甚しおいるこずに泚目しおください。 他のすべおのラむブラリは、react.jsに共通であり、Next.jsに適応する必芁はありたせん。



Next.jsラむブラリに初めお䌚ったずきは、かなり原始的なすぐに䜿甚できるルヌティングのために、私は本圓に感銘を受けたせんでした。 このラむブラリの適甚可胜性は、名刺サむトほど高くないように思えたした。 今はそうは思わないので、同じ蚘事でnext-routesラむブラリに぀いお話すこずを蚈画したした。これにより、ルヌティングの可胜性が倧幅に広がりたす。 しかし今、私はこの資料が別の投皿に眮く方が良いこずを理解しおいたす。 たた、 react-i18nextラむブラリに぀いおも話し合う予定です。 -Next.jsず盎接的な関係はありたせんが、共同䜿甚には非垞に適しおいたす。



apapacy@gmail.com

2018幎1月14日



All Articles