作者:LiuST
我用React制作了一个单页应用(SPA),在后来的发布在GitHub Pages(或Coding Pages、Gitee Pages)上、引入React Router (v4)、使用Google Analytics分析用户浏览量,乃至用Google AdSense发布广告的过程遇到了许多问题(主要与React Router有关),并且很少看到有人讨论这些问题(可能是这个做法很奇葩吧),于是有了这篇文章记录踩坑之路。
这篇文章并不是教程,许多中间的步骤被省略。如果你略懂这些技术,想采用同样的方法建网站,也许会对你有所帮助。
P.S. 我的网站地址为https://lemonjing.com,欢迎访问。
(这部分借鉴了教程serverless stack)
使用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(
,
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 (
f
{ /* 路径不存在,则回到主页。 */ }
);
}
在src/App.js
中使用Routes
:
// ...
function App(props) {
return (
我的应用
);
}
主页有链接指向App1和App2。src/containers/Home.js
可以是下面这个样子:
import React from "react";
import { Link } from "react-router-dom";
export default function Home() {
return (
主页
应用1
应用1
);
}
安装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
根据需要更改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。
详见npm_gh-pages
通过mydomain.com进入页面,点击主页中的链接后可以进入mydomain.com/app1。
但是如果此时刷新,结果会是404;或者直接访问mydomain.com/app1,结果同样是404。因为React Router是前端的路由,由JavaScript处理的,GitHub Pages并不知道mydomain.com/app1有一个页面。
将index.js
中的router改为:
import { HashRouter as Router } from 'react-router-dom';
这样链接就变为mydomain.com/#/app1,可以直接访问。原因是#号后面的内容会被忽略。
当GitHub Pages无法找到页面的时候,如果你的repo里有一个404.html,GitHub Pages会返回你的404.html。
你可以直接将index.html复制一份, 改名为404.html,问题就解决了。
还有一个同样原理但是复杂一些的做法:spa-github-pages
这个方法是从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按提示操作即可。
但是这样有一个问题,就是在从主页点击链接进入子页面(如mydomain.com/app1)的时候,analytics并不知道,因为这只是用JavaScript控制的页面变化。
关于这个问题,react-ga的一个issue里有很多讨论,其中一个解决方式起作用了,我写在了这篇博客里。
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 (
{ /* ... */ }
{ /* ... */ }
);
}
}