├── LICENSE
├── README.md
├── config
├── env.js
├── jest
│ ├── cssTransform.js
│ └── fileTransform.js
├── paths.js
├── webpack.config.js
└── webpackDevServer.config.js
├── package.json
├── public
├── favicon.ico
├── index.html
└── manifest.json
├── scripts
├── build.js
├── start.js
└── test.js
└── src
├── App.css
├── App.js
├── App.test.js
├── component
├── PageA.js
├── PageB.js
├── PageC.js
└── lib
│ └── PageHeader.js
├── css
└── lib
│ ├── common.css
│ ├── pageheader.css
│ └── reset.css
├── images
├── WX20190325-003909@2x.png
├── WX20190325-005512@2x.png
├── WX20190325-005603@2x.png
├── home.png
└── larrow.png
├── index.css
├── index.js
├── logo.svg
└── serviceWorker.js
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 曹小科
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 最近在react 上配置使用vw 适配方案,总体表现优秀。所以很多同学们都在观望vw 适配到底行不行,靠不靠谱。
2 |
3 | 在这里,从我在项目中实战应用下来但经验来说,可行,靠谱!
4 |
5 | 简单配置,一劳永逸,高可靠的还原设计师的稿子,给你底气和设计师对标UI。
6 |
7 | 欢迎挑战还原度,从此做一个自信的前端工程师。哈哈~
8 |
9 | 一定要看到底,可能有你会遇到坑的解药!拿走不谢++++ 给个小星星吧
10 |
11 |
12 | 该demo 为vw 适配方案 react 16 && react-router-dom 4+ 搭配使用,一个多页面路由项目.
13 |
14 | 步骤:
15 |
16 | 1、创建项目 && 初始化
17 | ```html
18 | create-react-app react-vw-layout
19 |
20 | cd react-vw-layout
21 | ```
22 |
23 | 2、开启我们的配置之路&&简单配置
24 |
25 | react 把配置都隐藏了,需要我们手动来显示配置
26 | ```
27 | npm run eject
28 |
29 | //Are you sure you want to eject? This action is permanent. (y/N) y
30 | ```
31 |
32 | 之后可以看到 config文件 && scripts 文件夹
33 |
34 | 
35 |
36 |
37 | 3、安装我们需要配置用到的插件(建议使用cnpm)
38 | ```html
39 | npm install --save postcss-aspect-ratio-mini postcss-px-to-viewport postcss-write-svg postcss-cssnext postcss-viewport-units cssnano
40 | ```
41 |
42 |
43 | 4、开始配置
44 |
45 | 在config/webpack.config.js 中修改代码
46 |
47 | 先引入postcss cssnano插件
48 |
49 | ```html
50 | const postcssAspectRatioMini = require('postcss-aspect-ratio-mini');
51 | const postcssPxToViewport = require('postcss-px-to-viewport');
52 | const postcssWriteSvg = require('postcss-write-svg');
53 | const postcssCssnext = require('postcss-cssnext');
54 | const postcssViewportUnits = require('postcss-viewport-units');
55 | const cssnano = require('cssnano');
56 | ```
57 |
58 | 增加postcss 配置
59 |
60 | {
61 | // Options for PostCSS as we reference these options twice
62 | // Adds vendor prefixing based on your specified browser support in
63 | // package.json
64 | loader: require.resolve('postcss-loader'),
65 | options: {
66 | // Necessary for external CSS imports to work
67 | // https://github.com/facebook/create-react-app/issues/2677
68 | ident: 'postcss',
69 | plugins: () => [
70 | require('postcss-flexbugs-fixes'),
71 | require('postcss-preset-env')({
72 | autoprefixer: {
73 | flexbox: 'no-2009',
74 | },
75 | stage: 3,
76 | }),
77 | //在这个位置加入我们需要配置的代码
78 | //在这个位置加入我们需要配置的代码
79 | //在这个位置加入我们需要配置的代码
80 | postcssAspectRatioMini({}),
81 | postcssPxToViewport({
82 | viewportWidth: 750, // (Number) The width of the viewport.
83 | viewportHeight: 1334, // (Number) The height of the viewport.
84 | unitPrecision: 3, // (Number) The decimal numbers to allow the REM units to grow to.
85 | viewportUnit: 'vw', // (String) Expected units.
86 | selectorBlackList: ['.ignore', '.hairlines', '.list-row-bottom-line', '.list-row-top-line'], // (Array) The selectors to ignore and leave as px.
87 | minPixelValue: 1, // (Number) Set the minimum pixel value to replace.
88 | mediaQuery: false // (Boolean) Allow px to be converted in media queries.
89 | }),
90 | postcssWriteSvg({
91 | utf8: false
92 | }),
93 | postcssPresetEnv({}),
94 | // postcssViewportUnits({
95 | // filterRule: rule => rule.selector.indexOf('::after') === -1 && rule.selector.indexOf('::before') === -1 && rule.selector.indexOf(':after') === -1 && rule.selector.indexOf(':before') === -1
96 | // }),
97 | postcssViewportUnits({}),
98 | cssnano({
99 | "cssnano-preset-advanced": {
100 | zindex: false,
101 | autoprefixer: false
102 | },
103 | })
104 | //在这个位置加入我们需要配置的代码
105 | //在这个位置加入我们需要配置的代码
106 | //在这个位置加入我们需要配置的代码
107 | ],
108 | sourceMap: isEnvProduction && shouldUseSourceMap,
109 | },
110 | }
111 |
112 | 5、配置完成以后,我们可以看到vw 神奇的效果了
113 |
114 | ```html
115 | npm run start
116 | ```
117 |
118 | 浏览器会打开 http://localhost:8080/
119 | ```html
120 | .App {
121 | background-color: bisque;
122 | display: flex;
123 | color: white;
124 | justify-content: center;
125 | align-items: center;
126 | font-size: 30px;
127 | height: 200px;
128 | width: 750px;
129 | }
130 | ```
131 |
132 | 
133 |
134 |
135 | 6、我们加入兼容我们android 低版本的机型hack 加入viewport-units-buggyfill配置
136 |
137 | 同学们,经过实战配置viewport-units-buggyfill之后,4.2 4.3 4.4+ 系统版本上表现都 ok,项目中主要使用flexbox 布局
138 |
139 | demo 版本中直接引入阿里的cdn 文件,在自己项目中可以放自己公司的服务器或者项目中。
140 |
141 | 打开public/index.html,在head 中插入 viewport-units-buggyfill 和body 中使用
142 |
143 |
144 | ```html
145 |
146 |
147 |
148 |
149 |
150 |
157 |
158 | ```
159 |
160 | 最后完整的index.html
161 |
162 | ```html
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
176 |
177 |
186 | react-vw-layout
187 |
188 |
189 |
190 |
191 |
192 |
193 |
203 |
210 |
211 |
212 | ```
213 |
214 |
215 | 项目中有遇到的坑:
216 | ```html
217 | 1、cssnano 如果你的版本为:4+ 以上,请在配置中如下方案配置:
218 |
219 | cssnano({
220 | "cssnano-preset-advanced": {
221 | zindex: false,
222 | autoprefixer: false
223 | },
224 | })
225 |
226 | 我遇到了始终把你定位的z-index值重新计算为:1,巨坑,不然你会一口老血喷出来的。
227 |
228 | 和cssnano 3+版本配置不一样。
229 |
230 |
231 | 2、ios 系统下img不显示问题,解决方案如下:
232 |
233 | /*兼容ios不显示图片问题*/
234 | img {
235 | content: normal !important
236 | }
237 |
238 |
239 | 3、1px 问题,解决方案
240 |
241 | /*1px 解决方案*/
242 |
243 | @svg square {
244 | @rect {
245 | fill: var(--color, white);
246 | width: 100%;
247 | height: 50%;
248 | }
249 | }
250 |
251 | .example-line {
252 | width: 100%;
253 | background: white svg(square param(--color #E6E6E6));
254 | background-size: 100% 1px;
255 | background-repeat: no-repeat;
256 | background-position: bottom left;
257 | }
258 |
259 | /*1px 解决方案*/
260 |
261 | /*伪元素1px*/
262 |
263 | .row-cell:before {
264 | content: " ";
265 | position: absolute;
266 | left: 0;
267 | top: 0;
268 | right: 0;
269 | height: 1px;
270 | border-top: 1px solid #e5e5e5;
271 | color: #e5e5e5;
272 | transform-origin: 0 0;
273 | transform: scaleY(0.5);
274 | z-index: 2;
275 | }
276 | ```
277 |
278 | 如果你不使用react 也不使用vue ,在项目中只使用html页面 vw实现移动端适配,请点这 《如何在html项目中使用vw实现移动端适配》
279 |
280 | 
281 |
282 |
283 |
284 | 
285 |
286 |
--------------------------------------------------------------------------------
/config/env.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const fs = require('fs');
4 | const path = require('path');
5 | const paths = require('./paths');
6 |
7 | // Make sure that including paths.js after env.js will read .env variables.
8 | delete require.cache[require.resolve('./paths')];
9 |
10 | const NODE_ENV = process.env.NODE_ENV;
11 | if (!NODE_ENV) {
12 | throw new Error(
13 | 'The NODE_ENV environment variable is required but was not specified.'
14 | );
15 | }
16 |
17 | // https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use
18 | var dotenvFiles = [
19 | `${paths.dotenv}.${NODE_ENV}.local`,
20 | `${paths.dotenv}.${NODE_ENV}`,
21 | // Don't include `.env.local` for `test` environment
22 | // since normally you expect tests to produce the same
23 | // results for everyone
24 | NODE_ENV !== 'test' && `${paths.dotenv}.local`,
25 | paths.dotenv,
26 | ].filter(Boolean);
27 |
28 | // Load environment variables from .env* files. Suppress warnings using silent
29 | // if this file is missing. dotenv will never modify any environment variables
30 | // that have already been set. Variable expansion is supported in .env files.
31 | // https://github.com/motdotla/dotenv
32 | // https://github.com/motdotla/dotenv-expand
33 | dotenvFiles.forEach(dotenvFile => {
34 | if (fs.existsSync(dotenvFile)) {
35 | require('dotenv-expand')(
36 | require('dotenv').config({
37 | path: dotenvFile,
38 | })
39 | );
40 | }
41 | });
42 |
43 | // We support resolving modules according to `NODE_PATH`.
44 | // This lets you use absolute paths in imports inside large monorepos:
45 | // https://github.com/facebook/create-react-app/issues/253.
46 | // It works similar to `NODE_PATH` in Node itself:
47 | // https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders
48 | // Note that unlike in Node, only *relative* paths from `NODE_PATH` are honored.
49 | // Otherwise, we risk importing Node.js core modules into an app instead of Webpack shims.
50 | // https://github.com/facebook/create-react-app/issues/1023#issuecomment-265344421
51 | // We also resolve them to make sure all tools using them work consistently.
52 | const appDirectory = fs.realpathSync(process.cwd());
53 | process.env.NODE_PATH = (process.env.NODE_PATH || '')
54 | .split(path.delimiter)
55 | .filter(folder => folder && !path.isAbsolute(folder))
56 | .map(folder => path.resolve(appDirectory, folder))
57 | .join(path.delimiter);
58 |
59 | // Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be
60 | // injected into the application via DefinePlugin in Webpack configuration.
61 | const REACT_APP = /^REACT_APP_/i;
62 |
63 | function getClientEnvironment(publicUrl) {
64 | const raw = Object.keys(process.env)
65 | .filter(key => REACT_APP.test(key))
66 | .reduce(
67 | (env, key) => {
68 | env[key] = process.env[key];
69 | return env;
70 | },
71 | {
72 | // Useful for determining whether we’re running in production mode.
73 | // Most importantly, it switches React into the correct mode.
74 | NODE_ENV: process.env.NODE_ENV || 'development',
75 | // Useful for resolving the correct path to static assets in `public`.
76 | // For example,
.
77 | // This should only be used as an escape hatch. Normally you would put
78 | // images into the `src` and `import` them in code to get their paths.
79 | PUBLIC_URL: publicUrl,
80 | }
81 | );
82 | // Stringify all values so we can feed into Webpack DefinePlugin
83 | const stringified = {
84 | 'process.env': Object.keys(raw).reduce((env, key) => {
85 | env[key] = JSON.stringify(raw[key]);
86 | return env;
87 | }, {}),
88 | };
89 |
90 | return { raw, stringified };
91 | }
92 |
93 | module.exports = getClientEnvironment;
94 |
--------------------------------------------------------------------------------
/config/jest/cssTransform.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // This is a custom Jest transformer turning style imports into empty objects.
4 | // http://facebook.github.io/jest/docs/en/webpack.html
5 |
6 | module.exports = {
7 | process() {
8 | return 'module.exports = {};';
9 | },
10 | getCacheKey() {
11 | // The output is always the same.
12 | return 'cssTransform';
13 | },
14 | };
15 |
--------------------------------------------------------------------------------
/config/jest/fileTransform.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const path = require('path');
4 |
5 | // This is a custom Jest transformer turning file imports into filenames.
6 | // http://facebook.github.io/jest/docs/en/webpack.html
7 |
8 | module.exports = {
9 | process(src, filename) {
10 | const assetFilename = JSON.stringify(path.basename(filename));
11 |
12 | if (filename.match(/\.svg$/)) {
13 | return `const React = require('react');
14 | module.exports = {
15 | __esModule: true,
16 | default: ${assetFilename},
17 | ReactComponent: React.forwardRef((props, ref) => ({
18 | $$typeof: Symbol.for('react.element'),
19 | type: 'svg',
20 | ref: ref,
21 | key: null,
22 | props: Object.assign({}, props, {
23 | children: ${assetFilename}
24 | })
25 | })),
26 | };`;
27 | }
28 |
29 | return `module.exports = ${assetFilename};`;
30 | },
31 | };
32 |
--------------------------------------------------------------------------------
/config/paths.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const path = require('path');
4 | const fs = require('fs');
5 | const url = require('url');
6 | const pkg = require('../package.json');
7 | // Make sure any symlinks in the project folder are resolved:
8 | // https://github.com/facebook/create-react-app/issues/637
9 | const appDirectory = fs.realpathSync(process.cwd());
10 | const resolveApp = relativePath => path.resolve(appDirectory, relativePath);
11 |
12 | const envPublicUrl = process.env.PUBLIC_URL;
13 |
14 | function ensureSlash(inputPath, needsSlash) {
15 | const hasSlash = inputPath.endsWith('/');
16 | if (hasSlash && !needsSlash) {
17 | return inputPath.substr(0, inputPath.length - 1);
18 | } else if (!hasSlash && needsSlash) {
19 | return `${inputPath}/`;
20 | } else {
21 | return inputPath;
22 | }
23 | }
24 |
25 | const getPublicUrl = appPackageJson =>
26 | envPublicUrl || require(appPackageJson).homepage;
27 |
28 | // We use `PUBLIC_URL` environment variable or "homepage" field to infer
29 | // "public path" at which the app is served.
30 | // Webpack needs to know it to put the right
26 |
27 |
28 |
29 |
30 |
40 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": ".",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/scripts/build.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Do this as the first thing so that any code reading it knows the right env.
4 | process.env.BABEL_ENV = 'production';
5 | process.env.NODE_ENV = 'production';
6 |
7 | // Makes the script crash on unhandled rejections instead of silently
8 | // ignoring them. In the future, promise rejections that are not handled will
9 | // terminate the Node.js process with a non-zero exit code.
10 | process.on('unhandledRejection', err => {
11 | throw err;
12 | });
13 |
14 | // Ensure environment variables are read.
15 | require('../config/env');
16 |
17 |
18 | const path = require('path');
19 | const chalk = require('react-dev-utils/chalk');
20 | const fs = require('fs-extra');
21 | const webpack = require('webpack');
22 | const bfj = require('bfj');
23 | const configFactory = require('../config/webpack.config');
24 | const paths = require('../config/paths');
25 | const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
26 | const formatWebpackMessages = require('react-dev-utils/formatWebpackMessages');
27 | const printHostingInstructions = require('react-dev-utils/printHostingInstructions');
28 | const FileSizeReporter = require('react-dev-utils/FileSizeReporter');
29 | const printBuildError = require('react-dev-utils/printBuildError');
30 |
31 | const measureFileSizesBeforeBuild =
32 | FileSizeReporter.measureFileSizesBeforeBuild;
33 | const printFileSizesAfterBuild = FileSizeReporter.printFileSizesAfterBuild;
34 | const useYarn = fs.existsSync(paths.yarnLockFile);
35 |
36 | // These sizes are pretty large. We'll warn for bundles exceeding them.
37 | const WARN_AFTER_BUNDLE_GZIP_SIZE = 512 * 1024;
38 | const WARN_AFTER_CHUNK_GZIP_SIZE = 1024 * 1024;
39 |
40 | const isInteractive = process.stdout.isTTY;
41 |
42 | // Warn and crash if required files are missing
43 | if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
44 | process.exit(1);
45 | }
46 |
47 | // Process CLI arguments
48 | const argv = process.argv.slice(2);
49 | const writeStatsJson = argv.indexOf('--stats') !== -1;
50 |
51 | // Generate configuration
52 | const config = configFactory('production');
53 |
54 | // We require that you explicitly set browsers and do not fall back to
55 | // browserslist defaults.
56 | const { checkBrowsers } = require('react-dev-utils/browsersHelper');
57 | checkBrowsers(paths.appPath, isInteractive)
58 | .then(() => {
59 | // First, read the current file sizes in build directory.
60 | // This lets us display how much they changed later.
61 | return measureFileSizesBeforeBuild(paths.appBuild);
62 | })
63 | .then(previousFileSizes => {
64 | // Remove all content but keep the directory so that
65 | // if you're in it, you don't end up in Trash
66 | fs.emptyDirSync(paths.appBuild);
67 | // Merge with the public folder
68 | copyPublicFolder();
69 | // Start the webpack build
70 | return build(previousFileSizes);
71 | })
72 | .then(
73 | ({ stats, previousFileSizes, warnings }) => {
74 | if (warnings.length) {
75 | console.log(chalk.yellow('Compiled with warnings.\n'));
76 | console.log(warnings.join('\n\n'));
77 | console.log(
78 | '\nSearch for the ' +
79 | chalk.underline(chalk.yellow('keywords')) +
80 | ' to learn more about each warning.'
81 | );
82 | console.log(
83 | 'To ignore, add ' +
84 | chalk.cyan('// eslint-disable-next-line') +
85 | ' to the line before.\n'
86 | );
87 | } else {
88 | console.log(chalk.green('Compiled successfully.\n'));
89 | }
90 |
91 | console.log('File sizes after gzip:\n');
92 | printFileSizesAfterBuild(
93 | stats,
94 | previousFileSizes,
95 | paths.appBuild,
96 | WARN_AFTER_BUNDLE_GZIP_SIZE,
97 | WARN_AFTER_CHUNK_GZIP_SIZE
98 | );
99 | console.log();
100 |
101 | const appPackage = require(paths.appPackageJson);
102 | const publicUrl = paths.publicUrl;
103 | const publicPath = config.output.publicPath;
104 | const buildFolder = path.relative(process.cwd(), paths.appBuild);
105 | printHostingInstructions(
106 | appPackage,
107 | publicUrl,
108 | publicPath,
109 | buildFolder,
110 | useYarn
111 | );
112 | },
113 | err => {
114 | console.log(chalk.red('Failed to compile.\n'));
115 | printBuildError(err);
116 | process.exit(1);
117 | }
118 | )
119 | .catch(err => {
120 | if (err && err.message) {
121 | console.log(err.message);
122 | }
123 | process.exit(1);
124 | });
125 |
126 | // Create the production build and print the deployment instructions.
127 | function build(previousFileSizes) {
128 | console.log('Creating an optimized production build...');
129 |
130 | let compiler = webpack(config);
131 | return new Promise((resolve, reject) => {
132 | compiler.run((err, stats) => {
133 | let messages;
134 | if (err) {
135 | if (!err.message) {
136 | return reject(err);
137 | }
138 | messages = formatWebpackMessages({
139 | errors: [err.message],
140 | warnings: [],
141 | });
142 | } else {
143 | messages = formatWebpackMessages(
144 | stats.toJson({ all: false, warnings: true, errors: true })
145 | );
146 | }
147 | if (messages.errors.length) {
148 | // Only keep the first error. Others are often indicative
149 | // of the same problem, but confuse the reader with noise.
150 | if (messages.errors.length > 1) {
151 | messages.errors.length = 1;
152 | }
153 | return reject(new Error(messages.errors.join('\n\n')));
154 | }
155 | if (
156 | process.env.CI &&
157 | (typeof process.env.CI !== 'string' ||
158 | process.env.CI.toLowerCase() !== 'false') &&
159 | messages.warnings.length
160 | ) {
161 | console.log(
162 | chalk.yellow(
163 | '\nTreating warnings as errors because process.env.CI = true.\n' +
164 | 'Most CI servers set it automatically.\n'
165 | )
166 | );
167 | return reject(new Error(messages.warnings.join('\n\n')));
168 | }
169 |
170 | const resolveArgs = {
171 | stats,
172 | previousFileSizes,
173 | warnings: messages.warnings,
174 | };
175 | if (writeStatsJson) {
176 | return bfj
177 | .write(paths.appBuild + '/bundle-stats.json', stats.toJson())
178 | .then(() => resolve(resolveArgs))
179 | .catch(error => reject(new Error(error)));
180 | }
181 |
182 | return resolve(resolveArgs);
183 | });
184 | });
185 | }
186 |
187 | function copyPublicFolder() {
188 | fs.copySync(paths.appPublic, paths.appBuild, {
189 | dereference: true,
190 | filter: file => file !== paths.appHtml,
191 | });
192 | }
193 |
--------------------------------------------------------------------------------
/scripts/start.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Do this as the first thing so that any code reading it knows the right env.
4 | process.env.BABEL_ENV = 'development';
5 | process.env.NODE_ENV = 'development';
6 |
7 | // Makes the script crash on unhandled rejections instead of silently
8 | // ignoring them. In the future, promise rejections that are not handled will
9 | // terminate the Node.js process with a non-zero exit code.
10 | process.on('unhandledRejection', err => {
11 | throw err;
12 | });
13 |
14 | // Ensure environment variables are read.
15 | require('../config/env');
16 |
17 |
18 | const fs = require('fs');
19 | const chalk = require('react-dev-utils/chalk');
20 | const webpack = require('webpack');
21 | const WebpackDevServer = require('webpack-dev-server');
22 | const clearConsole = require('react-dev-utils/clearConsole');
23 | const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
24 | const {
25 | choosePort,
26 | createCompiler,
27 | prepareProxy,
28 | prepareUrls,
29 | } = require('react-dev-utils/WebpackDevServerUtils');
30 | const openBrowser = require('react-dev-utils/openBrowser');
31 | const paths = require('../config/paths');
32 | const configFactory = require('../config/webpack.config');
33 | const createDevServerConfig = require('../config/webpackDevServer.config');
34 |
35 | const useYarn = fs.existsSync(paths.yarnLockFile);
36 | const isInteractive = process.stdout.isTTY;
37 |
38 | // Warn and crash if required files are missing
39 | if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
40 | process.exit(1);
41 | }
42 |
43 | // Tools like Cloud9 rely on this.
44 | const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 8080;
45 | const HOST = process.env.HOST || '0.0.0.0';
46 |
47 | if (process.env.HOST) {
48 | console.log(
49 | chalk.cyan(
50 | `Attempting to bind to HOST environment variable: ${chalk.yellow(
51 | chalk.bold(process.env.HOST)
52 | )}`
53 | )
54 | );
55 | console.log(
56 | `If this was unintentional, check that you haven't mistakenly set it in your shell.`
57 | );
58 | console.log(
59 | `Learn more here: ${chalk.yellow('https://bit.ly/CRA-advanced-config')}`
60 | );
61 | console.log();
62 | }
63 |
64 | // We require that you explictly set browsers and do not fall back to
65 | // browserslist defaults.
66 | const { checkBrowsers } = require('react-dev-utils/browsersHelper');
67 | checkBrowsers(paths.appPath, isInteractive)
68 | .then(() => {
69 | // We attempt to use the default port but if it is busy, we offer the user to
70 | // run on a different port. `choosePort()` Promise resolves to the next free port.
71 | return choosePort(HOST, DEFAULT_PORT);
72 | })
73 | .then(port => {
74 | if (port == null) {
75 | // We have not found a port.
76 | return;
77 | }
78 | const config = configFactory('development');
79 | const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
80 | const appName = require(paths.appPackageJson).name;
81 | const useTypeScript = fs.existsSync(paths.appTsConfig);
82 | const urls = prepareUrls(protocol, HOST, port);
83 | const devSocket = {
84 | warnings: warnings =>
85 | devServer.sockWrite(devServer.sockets, 'warnings', warnings),
86 | errors: errors =>
87 | devServer.sockWrite(devServer.sockets, 'errors', errors),
88 | };
89 | // Create a webpack compiler that is configured with custom messages.
90 | const compiler = createCompiler({
91 | appName,
92 | config,
93 | devSocket,
94 | urls,
95 | useYarn,
96 | useTypeScript,
97 | webpack,
98 | });
99 | // Load proxy config
100 | const proxySetting = require(paths.appPackageJson).proxy;
101 | const proxyConfig = prepareProxy(proxySetting, paths.appPublic);
102 | // Serve webpack assets generated by the compiler over a web server.
103 | const serverConfig = createDevServerConfig(
104 | proxyConfig,
105 | urls.lanUrlForConfig
106 | );
107 | const devServer = new WebpackDevServer(compiler, serverConfig);
108 | // Launch WebpackDevServer.
109 | devServer.listen(port, HOST, err => {
110 | if (err) {
111 | return console.log(err);
112 | }
113 | if (isInteractive) {
114 | clearConsole();
115 | }
116 | console.log(chalk.cyan('Starting the development server...\n'));
117 | openBrowser(urls.localUrlForBrowser);
118 | });
119 |
120 | ['SIGINT', 'SIGTERM'].forEach(function(sig) {
121 | process.on(sig, function() {
122 | devServer.close();
123 | process.exit();
124 | });
125 | });
126 | })
127 | .catch(err => {
128 | if (err && err.message) {
129 | console.log(err.message);
130 | }
131 | process.exit(1);
132 | });
133 |
--------------------------------------------------------------------------------
/scripts/test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Do this as the first thing so that any code reading it knows the right env.
4 | process.env.BABEL_ENV = 'test';
5 | process.env.NODE_ENV = 'test';
6 | process.env.PUBLIC_URL = '';
7 |
8 | // Makes the script crash on unhandled rejections instead of silently
9 | // ignoring them. In the future, promise rejections that are not handled will
10 | // terminate the Node.js process with a non-zero exit code.
11 | process.on('unhandledRejection', err => {
12 | throw err;
13 | });
14 |
15 | // Ensure environment variables are read.
16 | require('../config/env');
17 |
18 |
19 | const jest = require('jest');
20 | const execSync = require('child_process').execSync;
21 | let argv = process.argv.slice(2);
22 |
23 | function isInGitRepository() {
24 | try {
25 | execSync('git rev-parse --is-inside-work-tree', { stdio: 'ignore' });
26 | return true;
27 | } catch (e) {
28 | return false;
29 | }
30 | }
31 |
32 | function isInMercurialRepository() {
33 | try {
34 | execSync('hg --cwd . root', { stdio: 'ignore' });
35 | return true;
36 | } catch (e) {
37 | return false;
38 | }
39 | }
40 |
41 | // Watch unless on CI, in coverage mode, explicitly adding `--no-watch`,
42 | // or explicitly running all tests
43 | if (
44 | !process.env.CI &&
45 | argv.indexOf('--coverage') === -1 &&
46 | argv.indexOf('--no-watch') === -1 &&
47 | argv.indexOf('--watchAll') === -1
48 | ) {
49 | // https://github.com/facebook/create-react-app/issues/5210
50 | const hasSourceControl = isInGitRepository() || isInMercurialRepository();
51 | argv.push(hasSourceControl ? '--watch' : '--watchAll');
52 | }
53 |
54 | // Jest doesn't have this option so we'll remove it
55 | if (argv.indexOf('--no-watch') !== -1) {
56 | argv = argv.filter(arg => arg !== '--no-watch');
57 | }
58 |
59 |
60 | jest.run(argv);
61 |
--------------------------------------------------------------------------------
/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | background-color: bisque;
3 | display: flex;
4 | color: white;
5 | justify-content: center;
6 | align-items: center;
7 | font-size: 30px;
8 | height: 200px;
9 | width: 750px;
10 | }
11 |
12 | .App-logo {
13 | animation: App-logo-spin infinite 20s linear;
14 | height: 80px;
15 | pointer-events: none;
16 | }
17 |
18 | .App-header {
19 | background-color: #282c34;
20 | min-height: 100vh;
21 | display: flex;
22 | flex-direction: column;
23 | align-items: center;
24 | justify-content: center;
25 | font-size: calc(10px + 2vmin);
26 | color: white;
27 | }
28 |
29 | .App-link {
30 | color: #61dafb;
31 | }
32 |
33 | .list {
34 | margin-top: 150px;
35 | }
36 |
37 | .list-row {
38 | margin: 50px 0;
39 | font-size: 28px;
40 | }
41 |
42 | @keyframes App-logo-spin {
43 | from {
44 | transform: rotate(0deg);
45 | }
46 | to {
47 | transform: rotate(360deg);
48 | }
49 | }
50 |
51 | .test {
52 | background-color: bisque;
53 | display: flex;
54 | color: white;
55 | justify-content: center;
56 | align-items: center;
57 | font-size: 30px;
58 | height: 200px;
59 | width: 750px;
60 | }
61 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react';
2 | import {Route, Switch, NavLink, Redirect, withRouter, Link} from 'react-router-dom'
3 | import Loadable from 'react-loadable';
4 | import PageA from './component/PageA';
5 | import PageB from './component/PageB';
6 | import PageC from './component/PageC';
7 | import PageHeader from './component/lib/PageHeader';
8 |
9 | import './css/lib/reset.css';
10 | import './css/lib/common.css';
11 | import './css/lib/pageheader.css';
12 | import './App.css';
13 |
14 | const PageAComponent = Loadable({
15 | loader: () => import('./component/PageA'),
16 | loading: PageA,
17 | });
18 | const PageBComponent = Loadable({
19 | loader: () => import('./component/PageB'),
20 | loading: PageB,
21 | });
22 | const PageCComponent = Loadable({
23 | loader: () => import('./component/PageC'),
24 | loading: PageC,
25 | });
26 | const HomeComponent = () => {
27 | return (
28 |
29 |
30 |
31 | Link-To-PageA
32 |
33 |
34 | Link-To-PageB
35 |
36 |
37 | Link-To-PageC
38 |
39 |
40 |
41 |
42 | Hi React
43 |
44 |
);
45 | }
46 |
47 | class App extends Component {
48 | render() {
49 | return (
50 |
51 |
52 |
53 |
54 |
55 |
56 |
60 |
61 |
62 |
63 | );
64 | }
65 | }
66 |
67 | export default withRouter(App);
68 |
--------------------------------------------------------------------------------
/src/App.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | it('renders without crashing', () => {
6 | const div = document.createElement('div');
7 | ReactDOM.render(, div);
8 | ReactDOM.unmountComponentAtNode(div);
9 | });
10 |
--------------------------------------------------------------------------------
/src/component/PageA.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react';
2 | import PageHeader from './lib/PageHeader';
3 | import {NavLink} from 'react-router-dom'
4 | import '../App.css';
5 |
6 | class PageA extends Component {
7 | constructor(props) {
8 | super(props)
9 | }
10 |
11 | // 组件装载之后调用
12 | componentDidMount() {
13 |
14 | }
15 | //渲染
16 | render() {
17 | return (
18 |
19 |
20 |
21 |
22 | Link-To-PageB
23 |
24 |
25 | Link-To-PageC
26 |
27 |
28 |
29 | React Page A
30 |
31 |
);
32 | }
33 |
34 | // 组件被卸载
35 | componentWillUnmount() {
36 |
37 | }
38 | }
39 |
40 | export default PageA;
41 |
--------------------------------------------------------------------------------
/src/component/PageB.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react';
2 | import PageHeader from './lib/PageHeader';
3 | import '../App.css';
4 |
5 |
6 | class PageB extends Component {
7 | constructor(props) {
8 | super(props)
9 | }
10 |
11 | // 组件装载之后调用
12 | componentDidMount() {
13 |
14 | }
15 |
16 | //渲染
17 | render() {
18 | return (
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | React Page B
28 |
29 |
);
30 | }
31 |
32 | // 组件被卸载
33 | componentWillUnmount() {
34 |
35 | }
36 | }
37 |
38 | export default PageB;
39 |
--------------------------------------------------------------------------------
/src/component/PageC.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react';
2 | import PageHeader from './lib/PageHeader';
3 | import '../App.css';
4 |
5 | class PageC extends Component {
6 | constructor(props) {
7 | super(props)
8 | }
9 |
10 | // 组件装载之后调用
11 | componentDidMount() {
12 |
13 | }
14 |
15 | //渲染
16 | render() {
17 | return (
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | React Page C
27 |
28 |
);
29 | }
30 |
31 | // 组件被卸载
32 | componentWillUnmount() {
33 |
34 | }
35 | }
36 |
37 | export default PageC;
38 |
--------------------------------------------------------------------------------
/src/component/lib/PageHeader.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react';
2 | import {withRouter} from 'react-router-dom'
3 |
4 | import leftArrow from '../../images/larrow.png';
5 |
6 | class PageHeader extends Component {
7 | constructor(props) {
8 | super(props)
9 | this.handlePageBack = this.handlePageBack.bind(this);
10 | }
11 |
12 | // 组件装载之后调用
13 | componentDidMount() {
14 |
15 | }
16 |
17 | handlePageBack() {
18 | this.props.history.goBack();
19 | }
20 |
21 | //渲染
22 | render() {
23 | return (
24 |
25 |
26 |
27 |

28 |
29 |
{this.props.title || ''}
30 |
31 |
32 |
);
33 | }
34 |
35 | // 组件被卸载
36 | componentWillUnmount() {
37 |
38 | }
39 | }
40 |
41 | export default withRouter(PageHeader);
42 |
--------------------------------------------------------------------------------
/src/css/lib/common.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by caofk on 2019/3/14.
3 | */
4 |
5 | /*兼容ios不显示图片问题*/
6 | img {
7 | content: normal !important
8 | }
9 |
10 | /*兼容ios不显示图片问题*/
11 |
12 | /*flexbox 常用布局方式*/
13 |
14 | /*伸缩盒子模型*/
15 |
16 | .box {
17 | display: -webkit-box; /* 老版本语法: Safari, iOS, Android browser, older WebKit browsers. */
18 | display: -moz-box; /* 老版本语法: Firefox (buggy) */
19 | display: -ms-flexbox; /* 混合版本语法: IE 10 */
20 | display: -webkit-flex; /* 新版本语法: Chrome 21+ */
21 | display: flex; /* 新版本语法: Opera 12.1, Firefox 22+ */
22 | -webkit-flex-wrap: nowrap;
23 | -ms-flex-wrap: nowrap;
24 | flex-wrap: nowrap;
25 | }
26 |
27 | /*从左至右*/
28 |
29 | .box-lr {
30 | -webkit-box-direction: normal;
31 | -webkit-box-orient: horizontal;
32 | -moz-flex-direction: row;
33 | -webkit-flex-direction: row;
34 | flex-direction: row;
35 | }
36 |
37 | /*从右至左*/
38 |
39 | .box-rl {
40 | -webkit-box-pack: end;
41 | -webkit-box-direction: reverse;
42 | -webkit-box-orient: horizontal;
43 | -moz-flex-direction: row-reverse;
44 | -webkit-flex-direction: row-reverse;
45 | flex-direction: row-reverse;
46 | }
47 |
48 | /*从上至下*/
49 |
50 | .box-tb {
51 | -webkit-box-direction: normal;
52 | -webkit-box-orient: vertical;
53 | -moz-flex-direction: column;
54 | -webkit-flex-direction: column;
55 | flex-direction: column;
56 | }
57 |
58 | /*从下至上*/
59 |
60 | .box-bt {
61 | -webkit-box-pack: end;
62 | -webkit-box-direction: reverse;
63 | -webkit-box-orient: vertical;
64 | -moz-flex-direction: column-reverse;
65 | -webkit-flex-direction: column-reverse;
66 | flex-direction: column-reverse;
67 | }
68 |
69 | /*主轴居中*/
70 |
71 | .box-pack-center {
72 | -webkit-box-pack: center;
73 | -moz-justify-content: center;
74 | -webkit-justify-content: center;
75 | justify-content: center;
76 | }
77 |
78 | /*主轴居左*/
79 |
80 | .box-pack-start {
81 | -webkit-box-pack: start;
82 | -moz-justify-content: flex-start;
83 | -webkit-justify-content: flex-start;
84 | justify-content: flex-start;
85 | }
86 |
87 | /*主轴居右*/
88 |
89 | .box-pack-end {
90 | -webkit-box-pack: end;
91 | -moz-justify-content: flex-end;
92 | -webkit-justify-content: flex-end;
93 | justify-content: flex-end;
94 | }
95 |
96 | /*主轴左右不留白*/
97 |
98 | .box-pack-between {
99 | -webkit-box-pack: justify;
100 | -moz-justify-content: space-between;
101 | -webkit-justify-content: space-between;
102 | justify-content: space-between;
103 | }
104 |
105 | /*主轴左右留白*/
106 |
107 | .box-pack-between {
108 | -moz-justify-content: space-around;
109 | -webkit-justify-content: space-around;
110 | justify-content: space-around;
111 | }
112 |
113 | /*交叉轴居中对齐*/
114 |
115 | .box-align-center {
116 | -webkit-box-align: center;
117 | -moz-align-items: center;
118 | -webkit-align-items: center;
119 | align-items: center;
120 | }
121 |
122 | /*交叉轴居左对齐*/
123 |
124 | .box-align-start {
125 | -webkit-box-align: start;
126 | -moz-align-items: start;
127 | -webkit-align-items: flex-start;
128 | align-items: flex-start;
129 | }
130 |
131 | /*交叉轴居右对齐*/
132 |
133 | .box-align-end {
134 | -webkit-box-align: end;
135 | -moz-align-items: end;
136 | -webkit-align-items: flex-end;
137 | align-items: flex-end;
138 | }
139 |
140 | /**单个指定的子元素自定义对齐方式,可以不同于其他子元素对齐方式**/
141 |
142 | /**指定子元素居中对齐**/
143 |
144 | .self-align-center {
145 | align-self: center;
146 | -webkit-align-self: center;
147 | margin: 0 auto;
148 | }
149 |
150 | /**指定子元素顶部对齐**/
151 |
152 | .self-align-start {
153 | align-self: flex-start;
154 | -webkit-align-self: flex-start;
155 | }
156 |
157 | /**指定子元素底部对齐**/
158 |
159 | .self-align-end {
160 | align-self: flex-end;
161 | -webkit-align-self: flex-end;
162 | }
163 |
164 | /**指定子元素拉伸**/
165 |
166 | .self-align-stretch {
167 | align-self: stretch;
168 | -webkit-align-self: stretch;
169 | }
170 |
171 | /**子元素换行**/
172 |
173 | .box-wrap {
174 | -webkit-flex-wrap: wrap;
175 | -ms-flex-wrap: wrap;
176 | flex-wrap: wrap;
177 | }
178 |
179 | /**子元素不换行**/
180 |
181 | .box-nowrap {
182 | -webkit-flex-wrap: nowrap;
183 | -ms-flex-wrap: nowrap;
184 | flex-wrap: nowrap;
185 | }
186 |
187 | /*允许子元素伸展(1倍)*/
188 |
189 | .flex {
190 | -moz-flex-grow: 1;
191 | -webkit-flex-grow: 1;
192 | flex-grow: 1;
193 | }
194 |
195 | /*允许子元素收缩(1倍)*/
196 |
197 | .shrink {
198 | -moz-flex-shrink: 1;
199 | -webkit-flex-shrink: 1;
200 | flex-shrink: 1;
201 | }
202 |
203 | /**水平居中*/
204 |
205 | .box-center-center {
206 | display: -webkit-box;
207 | -webkit-box-align: center;
208 | -webkit-box-pack: center;
209 | display: -moz-box;
210 | -moz-box-align: center;
211 | -moz-box-pack: center;
212 | text-align: center;
213 | }
214 |
215 | /**垂直居中*/
216 |
217 | .box-center-center-v {
218 | display: -webkit-box;
219 | -webkit-box-align: center;
220 | -webkit-box-pack: center;
221 | display: -moz-box;
222 | -moz-box-align: center;
223 | -moz-box-pack: center;
224 | text-align: center;
225 | -webkit-box-orient: vertical;
226 | -moz-box-orient: vertical;
227 | }
228 |
229 | /*flexbox 常用布局方式*/
230 |
231 | /*1px 解决方案*/
232 |
233 | @svg square {
234 | @rect {
235 | fill: var(--color, white);
236 | width: 100%;
237 | height: 50%;
238 | }
239 | }
240 |
241 | .example-line {
242 | width: 100%;
243 | background: white svg(square param(--color #E6E6E6));
244 | background-size: 100% 1px;
245 | background-repeat: no-repeat;
246 | background-position: bottom left;
247 | }
248 |
249 | /*1px 解决方案*/
250 |
251 | /*伪元素1px*/
252 |
253 | .row-cell:before {
254 | content: " ";
255 | position: absolute;
256 | left: 0;
257 | top: 0;
258 | right: 0;
259 | height: 1px;
260 | border-top: 1px solid #e5e5e5;
261 | color: #e5e5e5;
262 | -webkit-transform-origin: 0 0;
263 | transform-origin: 0 0;
264 | -webkit-transform: scaleY(0.5);
265 | transform: scaleY(0.5);
266 | left: 15px;
267 | z-index: 2;
268 | }
269 |
270 | .row-cell-64D26D:before {
271 | content: " ";
272 | position: absolute;
273 | left: 0;
274 | bottom: -1px;
275 | right: 0;
276 | height: 1px;
277 | border-top: 1px solid #64D26D;;
278 | color: #64D26D;
279 | -webkit-transform-origin: 0 0;
280 | transform-origin: 0 0;
281 | -webkit-transform: scaleY(0.5);
282 | transform: scaleY(0.5);
283 | z-index: 2;
284 | }
285 | /*伪元素1px*/
286 | .margin-page-header {
287 | margin-top: 96px;
288 | }
289 |
--------------------------------------------------------------------------------
/src/css/lib/pageheader.css:
--------------------------------------------------------------------------------
1 |
2 | /*nav 导航栏*/
3 |
4 | .nav {
5 | position: fixed;
6 | z-index: 100;
7 | top: 0;
8 | left: 0;
9 | background: #1890ff;
10 | height: 96px;
11 | width: 100%;
12 | }
13 | .nav-line {
14 | border: 1px solid white;
15 | height: 44px;
16 | width: 2px;
17 | margin-right: 22px;
18 | margin-left: 34px;
19 | border-right: 0;
20 | border-bottom: 0;
21 | border-top: 0;
22 | }
23 |
24 | .left-arrow {
25 | width: 30px;
26 | height: 30px;
27 | margin-left: 33px;
28 | }
29 |
30 | .nav-text-min-width {
31 | min-width: 360px;
32 | }
33 |
34 | .nav-left-text {
35 | font-family: DroidSansFallback;
36 | font-size: 32px;
37 | color: #FFFFFF;
38 | line-height: 32px;
39 | }
40 |
41 | .right-text {
42 | font-family: PingFangSC-Regular;
43 | font-size: 26px;
44 | color: #FFFFFF;
45 | letter-spacing: 1px;
46 | text-align: right;
47 | line-height: 34px;
48 | position: absolute;
49 | height: 34px;
50 | right: 24px;
51 | top: 50%;
52 | margin-top: -17px;
53 | }
54 |
55 | /*nav 导航栏*/
56 |
--------------------------------------------------------------------------------
/src/css/lib/reset.css:
--------------------------------------------------------------------------------
1 | /* http://meyerweb.com/eric/tools/css/reset/
2 | v2.0 | 20110126
3 | License: none (public domain)
4 | */
5 |
6 | html, body, div, span, applet, object, iframe,
7 | h1, h2, h3, h4, h5, h6, p, blockquote, pre,
8 | a, abbr, acronym, address, big, cite, code,
9 | del, dfn, em, img, ins, kbd, q, s, samp,
10 | small, strike, strong, sub, sup, tt, var,
11 | b, u, i, center,
12 | dl, dt, dd, ol, ul, li,
13 | fieldset, form, label, legend,
14 | table, caption, tbody, tfoot, thead, tr, th, td,
15 | article, aside, canvas, details, embed,
16 | figure, figcaption, footer, header, hgroup,
17 | menu, nav, output, ruby, section, summary,
18 | time, mark, audio, video {
19 | margin: 0;
20 | padding: 0;
21 | border: 0;
22 | font-size: 100%;
23 | font: inherit;
24 | vertical-align: baseline;
25 | }
26 | /* HTML5 display-role reset for older browsers */
27 | article, aside, details, figcaption, figure,
28 | footer, header, hgroup, menu, nav, section {
29 | display: block;
30 | }
31 | body {
32 | line-height: 1;
33 | }
34 | ol, ul {
35 | list-style: none;
36 | }
37 | blockquote, q {
38 | quotes: none;
39 | }
40 | blockquote:before, blockquote:after,
41 | q:before, q:after {
42 | content: '';
43 | content: none;
44 | }
45 | table {
46 | border-collapse: collapse;
47 | border-spacing: 0;
48 | }
49 |
--------------------------------------------------------------------------------
/src/images/WX20190325-003909@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/caoxiaoke/react-vw-layout/0e0080358a006dcba7283c89c4fa9045110423b5/src/images/WX20190325-003909@2x.png
--------------------------------------------------------------------------------
/src/images/WX20190325-005512@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/caoxiaoke/react-vw-layout/0e0080358a006dcba7283c89c4fa9045110423b5/src/images/WX20190325-005512@2x.png
--------------------------------------------------------------------------------
/src/images/WX20190325-005603@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/caoxiaoke/react-vw-layout/0e0080358a006dcba7283c89c4fa9045110423b5/src/images/WX20190325-005603@2x.png
--------------------------------------------------------------------------------
/src/images/home.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/caoxiaoke/react-vw-layout/0e0080358a006dcba7283c89c4fa9045110423b5/src/images/home.png
--------------------------------------------------------------------------------
/src/images/larrow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/caoxiaoke/react-vw-layout/0e0080358a006dcba7283c89c4fa9045110423b5/src/images/larrow.png
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
5 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
6 | sans-serif;
7 | -webkit-font-smoothing: antialiased;
8 | -moz-osx-font-smoothing: grayscale;
9 | }
10 |
11 | code {
12 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
13 | monospace;
14 | }
15 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 | import {BrowserRouter} from 'react-router-dom'
6 |
7 | import * as serviceWorker from './serviceWorker';
8 |
9 | ReactDOM.render(
10 |
11 |
12 | ,
13 | document.getElementById('root')
14 | );
15 |
16 | // If you want your app to work offline and load faster, you can change
17 | // unregister() to register() below. Note this comes with some pitfalls.
18 | // Learn more about service workers: https://bit.ly/CRA-PWA
19 | serviceWorker.unregister();
20 |
--------------------------------------------------------------------------------
/src/logo.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/src/serviceWorker.js:
--------------------------------------------------------------------------------
1 | // This optional code is used to register a service worker.
2 | // register() is not called by default.
3 |
4 | // This lets the app load faster on subsequent visits in production, and gives
5 | // it offline capabilities. However, it also means that developers (and users)
6 | // will only see deployed updates on subsequent visits to a page, after all the
7 | // existing tabs open on the page have been closed, since previously cached
8 | // resources are updated in the background.
9 |
10 | // To learn more about the benefits of this model and instructions on how to
11 | // opt-in, read https://bit.ly/CRA-PWA
12 |
13 | const isLocalhost = Boolean(
14 | window.location.hostname === 'localhost' ||
15 | // [::1] is the IPv6 localhost address.
16 | window.location.hostname === '[::1]' ||
17 | // 127.0.0.1/8 is considered localhost for IPv4.
18 | window.location.hostname.match(
19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
20 | )
21 | );
22 |
23 | export function register(config) {
24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
25 | // The URL constructor is available in all browsers that support SW.
26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
27 | if (publicUrl.origin !== window.location.origin) {
28 | // Our service worker won't work if PUBLIC_URL is on a different origin
29 | // from what our page is served on. This might happen if a CDN is used to
30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374
31 | return;
32 | }
33 |
34 | window.addEventListener('load', () => {
35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
36 |
37 | if (isLocalhost) {
38 | // This is running on localhost. Let's check if a service worker still exists or not.
39 | checkValidServiceWorker(swUrl, config);
40 |
41 | // Add some additional logging to localhost, pointing developers to the
42 | // service worker/PWA documentation.
43 | navigator.serviceWorker.ready.then(() => {
44 | console.log(
45 | 'This web app is being served cache-first by a service ' +
46 | 'worker. To learn more, visit https://bit.ly/CRA-PWA'
47 | );
48 | });
49 | } else {
50 | // Is not localhost. Just register service worker
51 | registerValidSW(swUrl, config);
52 | }
53 | });
54 | }
55 | }
56 |
57 | function registerValidSW(swUrl, config) {
58 | navigator.serviceWorker
59 | .register(swUrl)
60 | .then(registration => {
61 | registration.onupdatefound = () => {
62 | const installingWorker = registration.installing;
63 | if (installingWorker == null) {
64 | return;
65 | }
66 | installingWorker.onstatechange = () => {
67 | if (installingWorker.state === 'installed') {
68 | if (navigator.serviceWorker.controller) {
69 | // At this point, the updated precached content has been fetched,
70 | // but the previous service worker will still serve the older
71 | // content until all client tabs are closed.
72 | console.log(
73 | 'New content is available and will be used when all ' +
74 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
75 | );
76 |
77 | // Execute callback
78 | if (config && config.onUpdate) {
79 | config.onUpdate(registration);
80 | }
81 | } else {
82 | // At this point, everything has been precached.
83 | // It's the perfect time to display a
84 | // "Content is cached for offline use." message.
85 | console.log('Content is cached for offline use.');
86 |
87 | // Execute callback
88 | if (config && config.onSuccess) {
89 | config.onSuccess(registration);
90 | }
91 | }
92 | }
93 | };
94 | };
95 | })
96 | .catch(error => {
97 | console.error('Error during service worker registration:', error);
98 | });
99 | }
100 |
101 | function checkValidServiceWorker(swUrl, config) {
102 | // Check if the service worker can be found. If it can't reload the page.
103 | fetch(swUrl)
104 | .then(response => {
105 | // Ensure service worker exists, and that we really are getting a JS file.
106 | const contentType = response.headers.get('content-type');
107 | if (
108 | response.status === 404 ||
109 | (contentType != null && contentType.indexOf('javascript') === -1)
110 | ) {
111 | // No service worker found. Probably a different app. Reload the page.
112 | navigator.serviceWorker.ready.then(registration => {
113 | registration.unregister().then(() => {
114 | window.location.reload();
115 | });
116 | });
117 | } else {
118 | // Service worker found. Proceed as normal.
119 | registerValidSW(swUrl, config);
120 | }
121 | })
122 | .catch(() => {
123 | console.log(
124 | 'No internet connection found. App is running in offline mode.'
125 | );
126 | });
127 | }
128 |
129 | export function unregister() {
130 | if ('serviceWorker' in navigator) {
131 | navigator.serviceWorker.ready.then(registration => {
132 | registration.unregister();
133 | });
134 | }
135 | }
136 |
--------------------------------------------------------------------------------