React + React Router + GitHub Pages + Google Analytics踩坑之路
作者:LiuST
我用React制作了一个单页应用(SPA),在后来的发布在GitHub Pages(或Coding Pages、Gitee Pages)上、引入React Router (v4)、使用Google Analytics分析用户浏览量,乃至用Google AdSense发布广告的过程遇到了许多问题(主要与React Router有关),并且很少看到有人讨论这些问题(可能是这个做法很奇葩吧),于是有了这篇文章记录踩坑之路。
这篇文章并不是教程,许多中间的步骤被省略。如果你略懂这些技术,想采用同样的方法建网站,也许会对你有所帮助。
P.S. 我的网站地址为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 react-router-dom@5.0.1 --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。
问题来了:无法通过路径直接访问页面
通过mydomain.com进入页面,点击主页中的链接后可以进入mydomain.com/app1。
但是如果此时刷新,结果会是404;或者直接访问mydomain.com/app1,结果同样是404。因为React Router是前端的路由,由JavaScript处理的,GitHub Pages并不知道mydomain.com/app1有一个页面。
解决方法1:使用HashRouter而不是BrowserRouter
将index.js中的router改为:
import { HashRouter as Router } from 'react-router-dom';
这样链接就变为mydomain.com/#/app1,可以直接访问。原因是#号后面的内容会被忽略。
优点
- 简单,原生。
缺点
- URL不好看
- 搜索引擎和Google Analytics等不会记录,他们会认为 mydomain.com/#/app1 和 mydomain.com/ 是同一个页面
解决方法2:使用404.html
当GitHub Pages无法找到页面的时候,如果你的repo里有一个404.html,GitHub Pages会返回你的404.html。
你可以直接将index.html复制一份, 改名为404.html,问题就解决了。
还有一个同样原理但是复杂一些的做法:spa-github-pages
优点
- URL好看
缺点
- 当你直接访问mydomain.com/app1时,返回的状态码是404。不利于SEO。Google Analytics和Google AdSense也会误解。
解决方法3:为每个路径创建一个文件夹
这个方法是从LoeiFy的一个issue看到的。
例如我有/app1,/app2两个路径,我就在build之后在build文件夹里新建app1,app2两个文件夹,并将index.html拷贝进去。
这样一来当我访问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按提示操作即可。
但是这样有一个问题,就是在从主页点击链接进入子页面(如mydomain.com/app1)的时候,analytics并不知道,因为这只是用JavaScript控制的页面变化。
关于这个问题,react-ga的一个issue里有很多讨论,其中一个解决方式起作用了,我写在了这篇博客里。
使用Google AdSense
AdSense的审核不容易通过,我在我的网站只有一页的时候(尽管浏览量很高)申请了几次都没有通过,后来有了多个页面的内容时才通过。然后两天后就因为非法流量被限制了广告投放,两个星期后还没有恢复。
在index.html中添加(如果有的话不需要重复):
<script async src="//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>
);
}
}