React.js 一般晚上的教程都是React + webpack + Router 来搭建的项目。都是单页项目,我们将他称为SPA项目。 最近有一个项目一开始是使用React.js来的SPA项目,可是后面发现我们这种类型的家居生活馆的项目来说还是要做SEO,这操作的。
由于项目是自己可以把控,了解一下后发现React.js官方是提供了“同架”这个概念,也就是服务端渲染。可是需要自己搭建和了解它的特性,还要利用node写服务端,还有客户端。在时间不允许的情况下,我最后选了了社区版的Next.js。这可以说是很容易移植的一个框架。
笔者在使用的过程中,遇到了几处坑,这里需要提醒一下,看完后你们不用谷歌,也不用百度,看那些人copy的XXX乐事笔记,看那些你不如直接看官方文档。
1.不要使用npm run dev 来运行 next.js
很多文章都是 叫你安装 next.js 后,就叫你在package.json
添加
"scripts": {
"dev": "next",
"build": "next build",
"start": "next start"
}
然后运行Next.js
项目
笔者这里并不推荐这种方法,由于Next.js访问static
静态文件必须放到跟目录的static
夹下面,你如今做到很多 开发校验 ,服务商都必须你放到跟目录下,比如 www.xxx.com/robots.txt 这样访问路径,如果在next.js
中 是 www.xxx.com/static/robo… 才能访问到。这时候服务商就并不能通过你的校验
在项目中任意创建个文件比如server.js
内容如下
const { createServer } = require('http');
const { parse } = require('url');
const next = require('next');
const { join } = require('path');
const port = parseInt(process.env.PORT, 10) || 3000;
const app = next({ dev: false }); //注意这里!如果你是false的时候需要npm run build 如果是true就是开发模式
const handle = app.getRequestHandler();
app.prepare().then(() => {
createServer((req, res) => {
const parsedUrl = parse(req.url, true)
const rootStaticFiles = ['/robots.txt', '/sitemap.xml', '/favicon.ico']
if (rootStaticFiles.indexOf(parsedUrl.pathname) > -1) {
const path = join(__dirname, 'static', parsedUrl.pathname)
app.serveStatic(req, res, path)
} else {
handle(req, res, parsedUrl)
}
}).listen(port, err => {
if (err) throw err;
console.log(`> Ready on http://localhost:${port}`)
})
})
rootStaticFiles
定义成文件名称数组,其实是使用了next.js的中间件处理。
2.windows location localStorage 对象经常undefined 导致的 异常错误
由于windows location localStorage 这一类都属于客户端js,如果在node【服务端】都没有这一类的全局对象。
我发现只要你在 render() , getInitialProps(),componentWillMount()这三个方法中写客户端全局代码就完全没有问题。
如果非要在render()写的话本人推荐是以下写法
{
this.state.payTypeValue == 'zhifubao' &&
<div>
<form action={`${ApiUri}/v1/alipay/page`} method="post">
<input type="hidden" value={this.state.order_sn} name="order_sn" />
<input type="hidden" value={localStorage.getItem('u_token')} name="token" />
<Button htmlType="submit">
确认支付
</Button>
</form>
</div>
}
使用React.Fragment类的方案让next.js 认为你不是在使用服务端渲染。
3.componentWillMount 和 componentDidMount
在componentWillMount
是不可以添加jqeruy 第三方插件,
在componentDidMount
中是可以的
添加 第三方插件库 只要不是React组件编写形式 或者 服务端代码都会编译错误 所以不要使用
import xxx from 'xxxx'';
我是建议所有的 客户端代码都要写在componentDidMount
方法里面,如果你使用getInitialProps()方法渲染的东西在componentDidMount
并没有任何作用。必须在componentWillMount
中赋值才生效。
比如:
static async getInitialProps({ query }) {
const result = await http.get('v1/good/search', {
params: query
});
let data = result.data.data;
let good_list = data.data;
const res = await http.get('v1/category/tree');
let menuList = res.data.data;
return { good_list, menuList, query };
}
componentWillMount() {
const { good_list, menuList } = this.props;
if (good_list) {
this.setState({
good_list
});
}
if (menuList) {
this.setState({
menuList
})
}
}
引用第三方插件如下
componentDidMount() {
const node = ReactDOM.findDOMNode(this);
window.$ = window.jQuery = require('jquery');
var imagesloaded = require('../../static/vendors/pofo/js/imagesloaded.pkgd.min.js');
var isotope = require('../../static/vendors/pofo/js/isotope.pkgd.min.js');
$(node).imagesLoaded(() => {
$(node).isotope({
// layoutMode: 'masonry',
itemSelector: '.grid-item',
percentPosition: true,
masonry: {
columnWidth: '.grid-sizer'
}
});
$(node).isotope('layout');
})
window.addEventListener('resize', () => {
setTimeout(() => {
$(node).isotope('layout');
}, 500)
}, false);
}
4.getInitialProps 产生的 props 只作用page的当前操作 js 文件
如上 提到 如果你使用getInitialProps()方法渲染的东西在componentDidMount
并没有任何作用。必须在componentWillMount
中赋值才生效。
如果你 写了一个 React.js 组件 。在这个组件里 getInitialProps 你是没有办法 调用到 服务端里面的。 同时你在这个组件里面写任何 http 请求 ,他都只会当作 ajax (客户端)操作处理,解决方法可以在这个组件中使用React.children,如果回到父容器中进行渲染,这样也可以进行服务端渲染。
SPA我用了一个月多月做的商城应用,然后移植到SEO中也要话个8天,然后解决以上的遇到问题。感觉成本还是减少了不少,反正现在百度已经在捉去这个网站了。
如果大家遇到问题可以邮件 rainbowmorel@163.com