React+Router+GitHub Pages+Google Analytics踩坑之路
我用React製作了一個單頁應用(SPA),在後來的發佈在GitHub Pages(或Coding Pages、Gitee Pages)上、引入React Router (v4)、使用Google Analytics分析用戶瀏覽量,乃至用Google AdSense發布廣告的過程遇到了許多問題(主要與React Router有關),並且很少看到有人討論這些問題(可能是這個做法很奇葩吧),於是有了這篇文章記錄踩坑之路。
這篇文章並不是教程,許多中間的步驟被省略。如果你略懂這些技術,想採用同樣的方法建網站,也許會對你有所幫助。
PS我的網站地址為https://lemonjing.com ,歡迎訪問。
創建React應用
(這部分借鑒了教程serverless stack )
初始化React應用
使用create-react-app
腳手架初始化react應用。
npx create-react-app my-app --use-npm
運行react應用。
cd my-app npm start
在git託管平台(如GitHub)上,新建一個repo,得到repo url,如https:// github.com/username/my- app.git 。
將本地的react應用與遠程倉庫連接。
git init git add . git commit -m "First commit" git remote add origin https://github.com/username/my-app.git git remote -v git push -u origin master
處理路由
我的網站裡有不同的頁面,如App1、App2。進入網站後是有一個主頁,提供到App1、App2的鏈接。所以需要用到路由React Router。
安裝React Router v4。
npm install [email protected] --save
修改src/index.js
:
import { BrowserRouter as Router } from 'react-router-dom'; // ... ReactDOM.render( <Router> <App /> </Router>, document.getElementById('root') );
新建src/Routes.js
:
import React from "react"; import { Route, Switch } from "react-router-dom"; // Home.js的内容在后文import Home from "./containers/Home"; // 假设我有App1、App2在src/containers/ 文件夹中import App1 from "./containers/App1"; import App2 from "./containers/App2"; export default function Routes() { return ( <Switch> <Route path="/" exact component={Home} /> <Route path="/app1" exact component={App1} /> <Route path="/app2" exact component={App2} />f { /* 路径不存在,则回到主页。 */ } <Route component={Home} /> </Switch> ); }
在src/App.js
中使用Routes
:
// ... function App(props) { return ( <div> <h1>我的应用</h1> <Routes /> </div> ); }
主頁有鏈接指向App1和App2。 src/containers/Home.js
可以是下面這個樣子:
import React from "react"; import { Link } from "react-router-dom"; export default function Home() { return ( <div className="Home"> <h1>主页</h1> <Link to="/app1">应用1</Link> <Link to="/app2">应用1</Link> </div> ); }
發佈在GitHub Pages
安裝gh-pages
npm install gh-pages --save-dev
修改package.json
:
//..."homepage":"https://username.github.io/reponame",//..."scripts":{//..."predeploy":"npm run build","deploy":"gh-pages -d build"}
部署到github pages。
npm run deploy
實際上運行這行命令發生的就是:
- 運行
predeploy
,也就是npm run build
-
gh-pages
(命令)將指定路徑(在這裡也就是build
文件夾)下的內容複製到git分支gh-pages
(分支的名字),並push。gh-pages
分支是GitHub Pages的默認發布源。
使用自己的域名
將package.json
裡的homepage改為自己的域名。
新建一個文件public/CNAME
,在裡面輸入自己的域名,如mydomain.com
使用其他Pages
根據需要更改package.json的scripts
- deploy
對應的命令。
例如,我要部署在Coding Pages上,使用的命令如下:
gh-pages -d build -b master -o coding -r https://e.coding.net/xxx.git
-d
表示路徑,默認為'.'
-b
表示分支名,默認為gh-pages
-o
表示remote名,默認為origin
-r
表示repo url,默認為url for the origin remote of the current dir。
問題來了:無法通過路徑直接訪問頁面
通過http:// mydomain.com進入頁面,點擊主頁中的鏈接後可以進入http:// mydomain.com/app1 。
但是如果此時刷新,結果會是404;或者直接訪問http:// mydomain.com/app1 ,結果同樣是404。因為React Router是前端的路由,由JavaScript處理的,GitHub Pages並不知道http:// mydomain.com/app1有一個頁面。
解決方法1:使用HashRouter而不是BrowserRouter
將index.js
中的router改為:
import { HashRouter as Router } from 'react-router-dom';
這樣鏈接就變為http:// mydomain.com/# /app1 ,可以直接訪問。原因是#號後面的內容會被忽略。
優點
- 簡單,原生。
缺點
- URL不好看
- 搜索引擎和Google Analytics等不會記錄,他們會認為http:// mydomain.com/# /app1和http:// mydomain.com/是同一個頁面
解決方法2:使用404.html
當GitHub Pages無法找到頁面的時候,如果你的repo裡有一個404.html,GitHub Pages會返回你的404.html。
你可以直接將index.html複製一份, 改名為404.html,問題就解決了。
還有一個同樣原理但是複雜一些的做法: spa-github-pages
優點
- URL好看
缺點
- 當你直接訪問http:// mydomain.com/app1時,返回的狀態碼是404。不利於SEO。 Google Analytics和Google AdSense也會誤解。
解決方法3:為每個路徑創建一個文件夾
這個方法是從LoeiFy的一個issue看到的。
例如我有/app1,/app2兩個路徑,我就在build之後在build文件夾里新建app1,app2兩個文件夾,並將index.html拷貝進去。
這樣一來當我訪問http:// mydomain.com/app1的時候,GitHub Pages就會去/app1文件夾下找index.html,返回回來。
每次都複製一遍未免有些複雜,我們可以自動化這個過程。
在項目根目錄新建deploy.js
:
// deploy.js // 根据你的路径修改routes const routes = [ 'app1' , 'app2' , ] // 给每个route都新建一个文件夹,把index.html拷贝进去const fs = require ( 'fs-extra' ) const path = require ( 'path' ) routes . forEach (( route ) => { fs . copySync ( path . join ( 'build' , 'index.html' ), path . join ( 'build' , route , 'index.html' )) })
然後修改package.json
:
//..."scripts":{//每次build的时候自动执行deploy.js"build":"react-scripts build && node deploy.js",//...}
這樣每次build的時候就會自動執行deploy.js
。
使用Google Analytics
註冊谷歌賬號,到Google Analytics按提示操作即可。
但是這樣有一個問題,就是在從主頁點擊鏈接進入子頁面(如http:// mydomain.com/app1 )的時候,analytics並不知道,因為這只是用JavaScript控制的頁面變化。
關於這個問題, react-ga的一個issue裡有很多討論,其中一個解決方式起作用了,我寫在了這篇部落格里。
使用Google AdSense
AdSense的審核不容易通過,我在我的網站只有一頁的時候(儘管瀏覽量很高)申請了幾次都沒有通過,後來有了多個頁面的內容時才通過。然後兩天后就因為非法流量被限制了廣告投放,兩個星期後還沒有恢復。
在index.html
中添加(如果有的話不需要重複):
<scriptasyncsrc="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
在你需要放廣告的組件中,根據你的client和slot:
export default class AdComponent extends React.Component { componentDidMount () { (window.adsbygoogle = window.adsbygoogle || []).push({}); } render () { return ( <div> { /* ... */ } <ins className='adsbygoogle' style={{ display: 'block' }} data-ad-client='ca-pub-12121212' data-ad-slot='12121212' data-ad-format='auto' /> { /* ... */ } </div> ); } }