site: SSR for official website (#3903)

* site: ssr for Layout

* site: SSR for site

* fix: ssr for Anchor

* chore: add ssr test

* chore: update deploy script

* site: udpate detail

* site: revert animation for site

* site: fix loading animation
This commit is contained in:
Benjy Cui 2016-11-21 14:39:15 +08:00 committed by GitHub
parent 34f353deb0
commit 4ee2b9d930
15 changed files with 100 additions and 70 deletions

View File

@ -87,6 +87,10 @@ class AnchorHelper {
getCurrentAnchor(bounds = 5) {
let activeAnchor = '';
if (typeof document === 'undefined') {
return activeAnchor;
}
this.links.forEach(section => {
const target = document.getElementById(section.substring(1));
if (target) {

View File

@ -79,6 +79,10 @@
"@types/react": "~0.14.41",
"@types/react-dom": "~0.14.18",
"antd-tools": "0.14.2",
"babel-cli": "^6.18.0",
"babel-preset-es2015": "^6.18.0",
"babel-preset-react": "^6.16.0",
"babel-preset-stage-0": "^6.16.0",
"babel-eslint": "^7.1.0",
"babel-jest": "^17.0.0",
"babel-plugin-import": "^1.0.0",
@ -131,8 +135,15 @@
"typescript-babel-jest": "^0.1.5",
"values.js": "^1.0.3"
},
"babel": {
"presets": [
"es2015",
"react",
"stage-0"
]
},
"scripts": {
"test": "npm run lint && npm run dist && npm run jest",
"test": "npm run lint && npm run dist && npm run jest && npm run site",
"lint": "npm run tslint && npm run eslint && npm run demolint && npm run lesshint",
"tslint": "antd-tools run ts-lint && npm run compile && rm -rf lib",
"eslint": "eslint test site scripts ./.eslintrc.js ./webpack.config.js --ext '.js,.jsx,.tsx' --ignore-pattern '!.eslintrc.js'",
@ -147,8 +158,10 @@
"compile": "antd-tools run compile && node ./tests/dekko/lib.test.js",
"start": "bisheng start -c ./site/bisheng.config.js --no-livereload",
"site": "bisheng build -c ./site/bisheng.config.js",
"deploy": "npm run clean && bisheng gh-pages -c ./site/bisheng.config.js",
"babel-site": "babel ./site/theme/template --out-dir ./site/theme/template",
"clean-site": "rm site/theme/template/**/*.js site/theme/template/*.js",
"site": "npm run babel-site && bisheng build --ssr -c ./site/bisheng.config.js && npm run clean-site",
"deploy": "npm run clean && npm run site && bisheng gh-pages --push-only",
"pub": "antd-tools run update-self && antd-tools run pub",
"prepublish": "antd-tools run guard",

View File

@ -1,4 +1,4 @@
import appLocaleData from 'react-intl/locale-data/en';
const appLocaleData = require('react-intl/locale-data/en');
module.exports = {
locale: 'en-US',

View File

@ -101,8 +101,11 @@ div.main-container {
animation: loadTween 2s cubic-bezier(0.785, 0.135, 0.15, 0.86) infinite;
}
}
#react-content:empty + .ant-site-loading {
opacity: 1;
#react-content[hidden] {
display: none;
+ .ant-site-loading {
opacity: 1;
}
}
#loading-text {

View File

@ -1 +1,2 @@
import 'react-github-button/assets/style.css';
import './index.less';

View File

@ -24,24 +24,28 @@
</script>
</head>
<body>
<div id="react-content"></div>
<div class="ant-site-loading" id="ant-site-loading">
<img src='https://t.alipayobjects.com/images/rmsweb/T1B9hfXcdvXXXXXXXX.svg'/>
<div id="loading-text">One Design Language</div>
</div>
<script>
<div id="react-content" hidden>
{{ content | safe }}
</div>
<div class="ant-site-loading" id="ant-site-loading">
<img src='https://t.alipayobjects.com/images/rmsweb/T1B9hfXcdvXXXXXXXX.svg'/>
<div id="loading-text">One Design Language</div>
</div>
<script>
(function () {
var content = document.getElementById('loading-text');
content.innerHTML = content.innerHTML.split('').map(function (letter, i) {
var className;
if (i > 0 && i < content.innerText.length - 1) {
className = 'yoyo-x-' + (i % 6);
}
if (letter.trim() === '') {
className += ' blank';
}
return '<span class="' + className + '">' + letter + '</span>';
}).join('');
if (content) {
content.innerHTML = content.innerHTML.split('').map(function (letter, i) {
var className;
if (i > 0 && i < content.innerText.length - 1) {
className = 'yoyo-x-' + (i % 6);
}
if (letter.trim() === '') {
className += ' blank';
}
return '<span class="' + className + '">' + letter + '</span>';
}).join('');
}
})();
// Enable Google Analytics
@ -62,8 +66,8 @@
ga('send', 'pageview');
/* eslint-enable */
}
</script>
<script src="{{ root }}common.js"></script>
<script src="{{ root }}index.js"></script>
</script>
<script src="{{ root }}common.js"></script>
<script src="{{ root }}index.js"></script>
</body>
</html>

View File

@ -9,7 +9,7 @@ import config from '../../';
const SubMenu = Menu.SubMenu;
function getActiveMenuItem(props) {
return props.params.children || props.location.pathname;
return props.params.children || props.location.pathname.replace(/^\//, '');
}
function fileNameToPath(filename) {
@ -120,8 +120,8 @@ export default class MainContent extends React.Component {
getModuleData(props) {
const pathname = props.location.pathname;
const moduleName = /^components/.test(pathname) ?
'components' : pathname.split('/').slice(0, 2).join('/');
const moduleName = /^\/?components/.test(pathname) ?
'components' : pathname.split('/').filter(item => item).slice(0, 2).join('/');
const moduleData = moduleName === 'components' || moduleName === 'changelog' || moduleName === 'docs/react' ?
[...props.picked.components, ...props.picked['docs/react'], ...props.picked.changelog] :
props.picked[moduleName];

View File

@ -5,7 +5,7 @@ import * as utils from '../utils';
const locale = utils.isZhCN() ? 'zh-CN' : 'en-US';
export function collect(nextProps, callback) {
const pageData = nextProps.location.pathname === 'changelog' ?
const pageData = nextProps.location.pathname.endsWith('changelog') ?
nextProps.data.CHANGELOG : nextProps.pageData;
if (!pageData) {
callback(404, nextProps);

View File

@ -3,7 +3,6 @@ import { Link } from 'react-router';
import { FormattedMessage } from 'react-intl';
import ScrollElement from 'rc-scroll-anim/lib/ScrollElement';
import GitHubButton from 'react-github-button';
import 'react-github-button/assets/style.css';
import { Icon } from 'antd';
import QueueAnim from 'rc-queue-anim';

View File

@ -6,8 +6,8 @@ import ScrollOverPack from 'rc-scroll-anim/lib/ScrollOverPack';
import { Icon, Button } from 'antd';
import QueueAnim from 'rc-queue-anim';
const clientHeight = document.documentElement.clientHeight;
function onScrollEvent(e) {
const clientHeight = document.documentElement.clientHeight;
const header = document.getElementById('header');
if (e.pageY >= clientHeight) {
if (header.className.indexOf('home-nav-bottom') < 0) {

View File

@ -30,8 +30,10 @@ class Footer extends React.Component {
//
// 1.
// 2.
if (localStorage.getItem('antd@2.0.0-notification-sent') !== 'true' &&
Date.now() < new Date('2016/10/14').getTime()) {
if (
localStorage.getItem('antd@2.0.0-notification-sent') !== 'true' &&
Date.now() < new Date('2016/10/14').getTime()
) {
this.infoNewVersion();
}
}
@ -65,9 +67,8 @@ class Footer extends React.Component {
}
render() {
const options = Object.keys(docVersions).map(version => (
<Option value={docVersions[version]} key={version}>{version}</Option>
));
const options = Object.keys(docVersions)
.map(version => <Option value={docVersions[version]} key={version}>{version}</Option>);
return (
<footer id="footer">
<ul>

View File

@ -1,7 +1,6 @@
import React from 'react';
import React, { PropTypes } from 'react';
import { Link } from 'react-router';
import { FormattedMessage } from 'react-intl';
import enquire from 'enquire.js';
import classNames from 'classnames';
import { Select, Menu, Row, Col, Icon, Button, Popover } from 'antd';
@ -9,27 +8,26 @@ const Option = Select.Option;
export default class Header extends React.Component {
static contextTypes = {
router: React.PropTypes.object.isRequired,
intl: React.PropTypes.object.isRequired,
router: PropTypes.object.isRequired,
intl: PropTypes.object.isRequired,
}
constructor(props) {
super(props);
this.state = {
menuMode: 'horizontal',
};
}
state = {
menuMode: 'horizontal',
};
componentDidMount() {
enquire.register('only screen and (min-width: 320px) and (max-width: 940px)', {
match: () => {
this.setState({ menuMode: 'inline' });
},
unmatch: () => {
this.setState({ menuMode: 'horizontal' });
},
});
/* eslint-disable global-require */
require('enquire.js')
.register('only screen and (min-width: 320px) and (max-width: 940px)', {
match: () => {
this.setState({ menuMode: 'inline' });
},
unmatch: () => {
this.setState({ menuMode: 'horizontal' });
},
});
/* eslint-enable global-require */
}
handleSearch = (value) => {
@ -51,7 +49,7 @@ export default class Header extends React.Component {
render() {
const { location, picked, isFirstScreen } = this.props;
const components = picked.components;
const module = location.pathname.replace(/\/$/, '').split('/').slice(0, -1).join('/');
const module = location.pathname.replace(/(^\/|\/$)/g, '').split('/').slice(0, -1).join('/');
let activeMenuItem = module || 'home';
if (activeMenuItem === 'components' || location.pathname === 'changelog') {
activeMenuItem = 'docs/react';
@ -67,8 +65,8 @@ export default class Header extends React.Component {
const subtitle = meta.subtitle;
return (
<Option value={url} key={url} data-label={`${meta.title.toLowerCase()} ${subtitle || ''}`}>
<strong>{meta.title}</strong>
{subtitle && <span className="ant-component-decs">{subtitle}</span>}
<strong>{meta.title}</strong>
{subtitle && <span className="ant-component-decs">{subtitle}</span>}
</Option>
);
});

View File

@ -8,12 +8,17 @@ import Footer from './Footer';
import enLocale from '../../en-US';
import cnLocale from '../../zh-CN';
import * as utils from '../utils';
import '../../static/style';
// Expose to iframe
window.react = React;
window['react-dom'] = ReactDOM;
window.antd = require('antd');
if (typeof window !== 'undefined') {
/* eslint-disable global-require */
require('../../static/style');
// Expose to iframe
window.react = React;
window['react-dom'] = ReactDOM;
window.antd = require('antd');
/* eslint-enable global-require */
}
const appLocale = utils.isZhCN() ? cnLocale : enLocale;
addLocaleData(appLocale.data);
@ -23,12 +28,9 @@ export default class Layout extends React.Component {
router: React.PropTypes.object.isRequired,
}
constructor(props) {
super(props);
this.state = {
isFirstScreen: true,
};
}
state = {
isFirstScreen: true,
};
componentDidMount() {
if (typeof ga !== 'undefined') {
@ -43,6 +45,7 @@ export default class Layout extends React.Component {
loadingNode.parentNode.removeChild(loadingNode);
}, 450);
}
document.getElementById('react-content').removeAttribute('hidden');
}
componentWillUnmount() {

View File

@ -20,6 +20,10 @@ export function getMenuItems(moduleData, locale) {
}
export function isZhCN() {
if (typeof location === 'undefined') {
// Use English in SSR.
return false;
}
if (location.search.indexOf('locale=zh-CN') > -1) {
return true;
}

View File

@ -1,4 +1,4 @@
import appLocaleData from 'react-intl/locale-data/zh';
const appLocaleData = require('react-intl/locale-data/zh');
module.exports = {
locale: 'zh-CN',