refactor(chart): lazy load Chart.js with suspense

- chore: add ico favicon
- chore: lint
- chore: add react-hooks lint rules
This commit is contained in:
Haishan 2018-10-30 23:37:42 +08:00
parent d247e37890
commit 7f75345c03
12 changed files with 81 additions and 67 deletions

View file

@ -10,9 +10,9 @@ parser: babel-eslint
plugins:
- import
- react
- react-hooks
- jest
extends:
- eslint:recommended
- plugin:import/errors
@ -31,3 +31,4 @@ rules:
react/jsx-uses-react: "error"
react/jsx-uses-vars: "error"
react/react-in-jsx-scope: "error"
react-hooks/rules-of-hooks: error

BIN
assets/yacd.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

View file

@ -40,6 +40,7 @@
"modern-normalize": "^0.5.0",
"prop-types": "^15.5.10",
"react": "^16.7.0-alpha.0",
"react-cache": "^2.0.0-alpha.0",
"react-dom": "^16.7.0-alpha.0",
"react-modal": "^3.6.1",
"react-redux": "^6.0.0-alpha.2a2f108",
@ -71,6 +72,7 @@
"eslint-plugin-import": "2.14.0",
"eslint-plugin-jest": "^21.26.1",
"eslint-plugin-react": "7.11.1",
"eslint-plugin-react-hooks": "^0.0.0",
"file-loader": "^2.0.0",
"html-webpack-plugin": "^3.2.0",
"husky": "^1.0.1",

View file

@ -26,6 +26,7 @@ let even = false;
const store = {
logs: [],
size: Size,
fetched: false,
subscribers: [],
appendData(o) {
const now = new Date();
@ -68,15 +69,19 @@ function pump(reader) {
});
}
let fetched = false;
function fetchLogs() {
if (fetched) return store;
if (store.fetched) return store;
store.fetched = true;
const { url, init } = getURLAndInit();
fetch(url, init).then(response => {
fetched = true;
const reader = response.body.getReader();
pump(reader);
});
fetch(url, init)
.then(response => {
const reader = response.body.getReader();
pump(reader);
})
.catch(err => {
store.fetched = false;
console.log('Error', err);
});
return store;
}

View file

@ -1,8 +1,9 @@
import React from 'react';
import React, { Suspense } from 'react';
import ContentHeader from 'c/ContentHeader';
import TrafficChart from 'c/TrafficChart';
import TrafficNow from 'c/TrafficNow';
import Loading from 'c/Loading';
import s0 from 'c/Home.module.scss';
export default function Home() {
@ -14,7 +15,9 @@ export default function Home() {
<TrafficNow />
</div>
<div className={s0.chart}>
<TrafficChart />
<Suspense fallback={<Loading height="200px" />} maxDuration={10}>
<TrafficChart />
</Suspense>
</div>
</div>
</div>

View file

@ -1,14 +1,19 @@
import React from 'react';
import PropTypes from 'prop-types';
import style from './Loading.module.scss';
import s0 from './Loading.module.scss';
const Loading = () => {
const Loading = ({ height }) => {
const style = height ? { height } : {};
return (
<div className={style.loading}>
<div className={style.left + ' ' + style.circle} />
<div className={style.right + ' ' + style.circle} />
<div className={s0.loading} style={style}>
<div className={s0.pulse} />
</div>
);
};
Loading.propTypes = {
height: PropTypes.string
};
export default Loading;

View file

@ -1,50 +1,31 @@
$color1: #2a477a;
$color2: #dddddd;
// $color1: #2a477a;
$color1: #dddddd;
@keyframes moveRight {
0% {
transform: translate(-50px);
}
100% {
transform: translate(10px);
}
}
@keyframes moveLeft {
0% {
transform: translate(50px);
}
100% {
transform: translate(-10px);
}
}
$size: 40px;
.loading {
position: relative;
width: 100%;
height: 300px;
margin: 0 auto;
height: 30vh;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.circle {
width: 40px;
height: 40px;
border-radius: 50%;
position: absolute;
top: 50%;
}
.left {
.pulse {
width: $size;
height: $size;
margin: 10px;
background-color: $color1;
left: 50%;
animation: moveRight 1s ease-in-out 0s infinite alternate;
border-radius: 100%;
animation: pulseScaleOut 1s infinite ease-in-out;
}
.right {
background-color: $color2;
right: 50%;
animation: moveLeft 1s ease-in-out 0s infinite alternate;
@keyframes pulseScaleOut {
0% {
transform: scale(0);
}
100% {
transform: scale(1);
opacity: 0;
}
}

View file

@ -47,7 +47,7 @@ export default function Logs() {
const x = fetchLogs();
setLogs(x.logs);
return x.subscribe(() => setLogs(x.logs));
});
}, []);
return (
<div>

View file

@ -1,8 +1,12 @@
import React, { useEffect } from 'react';
import Chart from 'chart.js/dist/Chart.min.js';
import prettyBytes from 'm/pretty-bytes';
import { fetchData } from '../api/traffic';
import { unstable_createResource as createResource } from 'react-cache';
// const delay = ms => new Promise(r => setTimeout(r, ms));
const chartJSResource = createResource(() => {
return import('chart.js/dist/Chart.min.js').then(c => c.default);
});
const colorCombo = {
0: {
@ -108,13 +112,15 @@ const options = {
};
const chartWrapperStyle = {
// make chartjs chart responsive
position: 'relative',
width: '90%'
};
export default function TrafficChart() {
const Chart = chartJSResource.read();
useEffect(() => {
const ctx = document.getElementById('myChart').getContext('2d');
const ctx = document.getElementById('trafficChart').getContext('2d');
const traffic = fetchData();
const data = {
labels: traffic.labels,
@ -139,7 +145,7 @@ export default function TrafficChart() {
return (
<div style={chartWrapperStyle}>
<canvas id="myChart" />
<canvas id="trafficChart" />
</div>
);
}

View file

@ -2,13 +2,14 @@
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="shortcut icon" href="yacd.ico">
<link rel="icon" type="image/png" sizes="64x64" href="yacd-64.png">
<link rel="icon" type="image/png" sizes="128x128" href="yacd-128.png">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name=viewport content="width=device-width, initial-scale=1">
<meta name="application-name" content="yacd">
<meta name="description" content="Yet Another Clash Dashboard">
<meta name="theme-color" content="#202020">
<link id="favicon" rel="icon" type="image/png" sizes="64x64" href="yacd-64.png">
<link id="favicon" rel="icon" type="image/png" sizes="128x128" href="yacd-128.png">
<title><%= htmlWebpackPlugin.options.title %></title>
<link rel="prefetch" href="https://cdn.jsdelivr.net/npm/@hsjs/fonts@0.0.1/robotomono/v5/L0x5DF4xlVMF-BfR8bXMIjhLq3-cXbKD.woff2">
<meta property="og:image" content="https://user-images.githubusercontent.com/1166872/47304841-536f3d80-d65a-11e8-8908-1917127dafc5.png">

View file

@ -106,11 +106,11 @@ module.exports = {
// test: /[\\/]node_modules[\\/](core-js)[\\/]/,
// chunks: 'all'
// },
chartjs: {
test: /[\\/]node_modules[\\/]chart\.js[\\/]/,
// name: 'chartjs',
chunks: 'all'
},
// chartjs: {
// test: /[\\/]node_modules[\\/]chart\.js[\\/]/,
// // name: 'chartjs',
// chunks: 'all'
// },
react: {
test: /[\\/]node_modules[\\/](react-dom|react|redux|react-router|react-router-dom|schedule|react-redux|react-modal)[\\/]/,
chunks: 'all'

View file

@ -2919,6 +2919,11 @@ eslint-plugin-jest@^21.26.1:
resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-21.26.2.tgz#5b24413970e83e2c5b87c5c047a08a4881783605"
integrity sha512-SCTBC6q182D4qQlQAN81D351jdte/YwTMo4f+l19Gvh1VemaNZP7ak3MLLvw6xkL9dO2FxVjCLk5DCdl1KfdLw==
eslint-plugin-react-hooks@^0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-0.0.0.tgz#9988f14082a159931c3dfa9ba699130457da927a"
integrity sha512-SXyU7C3E8AJbXKMdb10P/zHazcxzfuWR5OFwAVZKXVU7P/H56NLszVG6WdQBo9Pt80FfnPXtUGGbWhs3/98N4w==
eslint-plugin-react@7.11.1:
version "7.11.1"
resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.11.1.tgz#c01a7af6f17519457d6116aa94fc6d2ccad5443c"
@ -6603,6 +6608,11 @@ rc@^1.2.7:
minimist "^1.2.0"
strip-json-comments "~2.0.1"
react-cache@^2.0.0-alpha.0:
version "2.0.0-alpha.0"
resolved "https://registry.yarnpkg.com/react-cache/-/react-cache-2.0.0-alpha.0.tgz#d02d16a4565fd9f12478a2a41e980ba4fcbab4d5"
integrity sha512-o7nA1dIbi6wOoIoQPQBL58CgIQ4tCA3itIR3WlE2VHMIFkIXbvsMyolg+Q9wDVUlCvU6b2n40RTTv8vOmjXCOQ==
react-dom@^16.7.0-alpha.0:
version "16.7.0-alpha.0"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.7.0-alpha.0.tgz#8379158d4c76d63c989f325f45dfa5762582584f"