搭建react框架(会持续更新,加入各个集成模块)

Posted 天叔

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了搭建react框架(会持续更新,加入各个集成模块)相关的知识,希望对你有一定的参考价值。

前提: 需要安装Node.js (>6)版本

 


 

1.cmd进到本地某个目录, 逐行输入以下指令(以下括号为注释)

 

npm install -g create-react-app   (全局安装create-react-app, 默认会安装在C盘个人用户下)

create-react-app my-app (此步安装my-app以及需要的模块到当前文件夹下)

cd my-app (进入到my-app目录)

npm start (启动react项目Demo,可输入localhost:3000进入看demo)

 


 

2.开发模式选择用npm start

 

①首先为什么呢?这涉及到热模块更换(HMR),笼统的说就是更改了JS代码保存后,网页会自动进行刷新,启动新代码。

 

②热模块更换这里也会有bug存在,这和所用的浏览器也会有关,例如有时候刷新页面,会发现代码还是旧的,这点IE11时常会出现该bug。另外关于热模块更换的详细知识会在以后的Webpack编译篇讲解。

 

③官方推荐的npm start指令所对应的server就是以nodejs+express起的,详细可看以下代码:

 

  1 \'use strict\';
  2 
  3 /* eslint func-names: off */
  4 require(\'./polyfills\');
  5 
  6 const fs = require(\'fs\');
  7 const http = require(\'http\');
  8 const path = require(\'path\');
  9 const url = require(\'url\');
 10 const chokidar = require(\'chokidar\');
 11 const compress = require(\'compression\');
 12 const del = require(\'del\');
 13 const express = require(\'express\');
 14 const httpProxyMiddleware = require(\'http-proxy-middleware\');
 15 const ip = require(\'ip\');
 16 const killable = require(\'killable\');
 17 const serveIndex = require(\'serve-index\');
 18 const historyApiFallback = require(\'connect-history-api-fallback\');
 19 const selfsigned = require(\'selfsigned\');
 20 const sockjs = require(\'sockjs\');
 21 const spdy = require(\'spdy\');
 22 const webpack = require(\'webpack\');
 23 const webpackDevMiddleware = require(\'webpack-dev-middleware\');
 24 const OptionsValidationError = require(\'./OptionsValidationError\');
 25 const optionsSchema = require(\'./optionsSchema.json\');
 26 
 27 const clientStats = { errorDetails: false };
 28 const log = console.log; // eslint-disable-line no-console
 29 
 30 function Server(compiler, options) {
 31   // Default options
 32   if (!options) options = {};
 33 
 34   const validationErrors = webpack.validateSchema(optionsSchema, options);
 35   if (validationErrors.length) {
 36     throw new OptionsValidationError(validationErrors);
 37   }
 38 
 39   if (options.lazy && !options.filename) {
 40     throw new Error("\'filename\' option must be set in lazy mode.");
 41   }
 42 
 43   this.hot = options.hot || options.hotOnly;
 44   this.headers = options.headers;
 45   this.clientLogLevel = options.clientLogLevel;
 46   this.clientOverlay = options.overlay;
 47   this.progress = options.progress;
 48   this.disableHostCheck = !!options.disableHostCheck;
 49   this.publicHost = options.public;
 50   this.allowedHosts = options.allowedHosts;
 51   this.sockets = [];
 52   this.contentBaseWatchers = [];
 53 
 54   // Listening for events
 55   const invalidPlugin = () => {
 56     this.sockWrite(this.sockets, \'invalid\');
 57   };
 58   if (this.progress) {
 59     const progressPlugin = new webpack.ProgressPlugin((percent, msg, addInfo) => {
 60       percent = Math.floor(percent * 100);
 61       if (percent === 100) msg = \'Compilation completed\';
 62       if (addInfo) msg = `${msg} (${addInfo})`;
 63       this.sockWrite(this.sockets, \'progress-update\', { percent, msg });
 64     });
 65     compiler.apply(progressPlugin);
 66   }
 67   compiler.plugin(\'compile\', invalidPlugin);
 68   compiler.plugin(\'invalid\', invalidPlugin);
 69   compiler.plugin(\'done\', (stats) => {
 70     this._sendStats(this.sockets, stats.toJson(clientStats));
 71     this._stats = stats;
 72   });
 73 
 74   // Init express server
 75   const app = this.app = new express(); // eslint-disable-line
 76 
 77   app.all(\'*\', (req, res, next) => { // eslint-disable-line
 78     if (this.checkHost(req.headers)) { return next(); }
 79     res.send(\'Invalid Host header\');
 80   });
 81 
 82   // middleware for serving webpack bundle
 83   this.middleware = webpackDevMiddleware(compiler, options);
 84 
 85   app.get(\'/__webpack_dev_server__/live.bundle.js\', (req, res) => {
 86     res.setHeader(\'Content-Type\', \'application/javascript\');
 87     fs.createReadStream(path.join(__dirname, \'..\', \'client\', \'live.bundle.js\')).pipe(res);
 88   });
 89 
 90   app.get(\'/__webpack_dev_server__/sockjs.bundle.js\', (req, res) => {
 91     res.setHeader(\'Content-Type\', \'application/javascript\');
 92     fs.createReadStream(path.join(__dirname, \'..\', \'client\', \'sockjs.bundle.js\')).pipe(res);
 93   });
 94 
 95   app.get(\'/webpack-dev-server.js\', (req, res) => {
 96     res.setHeader(\'Content-Type\', \'application/javascript\');
 97     fs.createReadStream(path.join(__dirname, \'..\', \'client\', \'index.bundle.js\')).pipe(res);
 98   });
 99 
100   app.get(\'/webpack-dev-server/*\', (req, res) => {
101     res.setHeader(\'Content-Type\', \'text/html\');
102     fs.createReadStream(path.join(__dirname, \'..\', \'client\', \'live.html\')).pipe(res);
103   });
104 
105   app.get(\'/webpack-dev-server\', (req, res) => {
106     res.setHeader(\'Content-Type\', \'text/html\');
107     res.write(\'<!DOCTYPE html><html><head><meta charset="utf-8"/></head><body>\');
108     const outputPath = this.middleware.getFilenameFromUrl(options.publicPath || \'/\');
109     const filesystem = this.middleware.fileSystem;
110 
111     function writeDirectory(baseUrl, basePath) {
112       const content = filesystem.readdirSync(basePath);
113       res.write(\'<ul>\');
114       content.forEach((item) => {
115         const p = `${basePath}/${item}`;
116         if (filesystem.statSync(p).isFile()) {
117           res.write(\'<li><a href="\');
118           res.write(baseUrl + item);
119           res.write(\'">\');
120           res.write(item);
121           res.write(\'</a></li>\');
122           if (/\\.js$/.test(item)) {
123             const htmlItem = item.substr(0, item.length - 3);
124             res.write(\'<li><a href="\');
125             res.write(baseUrl + htmlItem);
126             res.write(\'">\');
127             res.write(htmlItem);
128             res.write(\'</a> (magic html for \');
129             res.write(item);
130             res.write(\') (<a href="\');
131             res.write(baseUrl.replace(/(^(https?:\\/\\/[^\\/]+)?\\/)/, "$1webpack-dev-server/") + htmlItem); // eslint-disable-line
132             res.write(\'">webpack-dev-server</a>)</li>\');
133           }
134         } else {
135           res.write(\'<li>\');
136           res.write(item);
137           res.write(\'<br>\');
138           writeDirectory(`${baseUrl + item}/`, p);
139           res.write(\'</li>\');
140         }
141       });
142       res.write(\'</ul>\');
143     }
144     writeDirectory(options.publicPath || \'/\', outputPath);
145     res.end(\'</body></html>\');
146   });
147 
148   let contentBase;
149   if (options.contentBase !== undefined) { // eslint-disable-line
150     contentBase = options.contentBase; // eslint-disable-line
151   } else {
152     contentBase = process.cwd();
153   }
154 
155   // Keep track of websocket proxies for external websocket upgrade.
156   const websocketProxies = [];
157 
158   const features = {
159     compress() {
160       if (options.compress) {
161         // Enable gzip compression.
162         app.use(compress());
163       }
164     },
165 
166     proxy() {
167       if (options.proxy) {
168         /**
169           * Assume a proxy configuration specified as:
170           * proxy: {
171           *   \'context\': { options }
172           * }
173           * OR
174           * proxy: {
175           *   \'context\': \'target\'
176           * }
177           */
178         if (!Array.isArray(options.proxy)) {
179           options.proxy = Object.keys(options.proxy).map((context) => {
180             let proxyOptions;
181             // For backwards compatibility reasons.
182             const correctedContext = context.replace(/^\\*$/, \'**\').replace(/\\/\\*$/, \'\');
183 
184             if (typeof options.proxy[context] === \'string\') {
185               proxyOptions = {
186                 context: correctedContext,
187                 target: options.proxy[context]
188               };
189             } else {
190               proxyOptions = Object.assign({}, options.proxy[context]);
191               proxyOptions.context = correctedContext;
192             }
193             proxyOptions.logLevel = proxyOptions.logLevel || \'warn\';
194 
195             return proxyOptions;
196           });
197         }
198 
199         const getProxyMiddleware = (proxyConfig) => {
200           const context = proxyConfig.context || proxyConfig.path;
201 
202           // It is possible to use the `bypass` method without a `target`.
203           // However, the proxy middleware has no use in this case, and will fail to instantiate.
204           if (proxyConfig.target) {
205             return httpProxyMiddleware(context, proxyConfig);
206           }
207         };
208 
209         /**
210         * Assume a proxy configuration specified as:
211         * proxy: [
212         *   {
213         *     context: ...,
214         *     ...options...
215         *   },
216         *   // or:
217         *   function() {
218         *     return {
219         *       context: ...,
220         *       ...options...
221         *     };
222         *   }
223         * ]
224         */
225         options.proxy.forEach((proxyConfigOrCallback) => {
226           let proxyConfig;
227           let proxyMiddleware;
228 
229           if (typeof proxyConfigOrCallback === \'function\') {
230             proxyConfig = proxyConfigOrCallback();
231           } else {
232             proxyConfig = proxyConfigOrCallback;
233           }
234 
235           proxyMiddleware = getProxyMiddleware(proxyConfig);
236           if (proxyConfig.ws) {
237             websocketProxies.push(proxyMiddleware);
238           }
239 
240           app.use((req, res, next) => {
241             if (typeof proxyConfigOrCallback === \'function\') {
242               const newProxyConfig = proxyConfigOrCallback();
243               if (newProxyConfig !== proxyConfig) {
244                 proxyConfig = newProxyConfig;
245                 proxyMiddleware = getProxyMiddleware(proxyConfig);
246               }
247             }
248             const bypass = typeof proxyConfig.bypass === \'function\';
249             // eslint-disable-next-line
250             const bypassUrl = bypass && proxyConfig.bypass(req, res, proxyConfig) || false;
251 
252             if (bypassUrl) {
253               req.url = bypassUrl;
254               next();
255             } else if (proxyMiddleware) {
256               return proxyMiddleware(req, res, next);
257             } else {
258               next();
259             }
260           });
261         });
262       }
263     },
264 
265     historyApiFallback() {
266       if (options.historyApiFallback) {
267         // Fall back to /index.html if nothing else matches.
268         app.use(historyApiFallback(typeof options.historyApiFallback === \'object\' ? options.historyApiFallback : null));
269       }
270     },
271 
272     contentBaseFiles() {
273       if (Array.isArray(contentBase)) {
274         contentBase.forEach((item) => {
275           app.get(\'*\', express.static(item));
276         });
277       } else if (/^(https?:)?\\/\\//.test(contentBase)) {
278         log(\'Using a URL as contentBase is deprecated and will be removed in the next major version. Please use the proxy option instead.\');
279         log(\'proxy: {\\n\\t"*": "<your current contentBase configuration>"\\n}\'); // eslint-disable-line quotes
280         // Redirect every request to contentBase
281         app.get(\'*\', (req, res) => {
282           res.writeHead(302, {
283             Location: contentBase + req.path + (req._parsedUrl.search || \'\')
284           });
285           res.end();
286         });
287       } else if (typeof contentBase === \'number\') {
288         log(\'Using a number as contentBase is deprecated and will be removed in the next major version. Please use the proxy option instead.\');
289         log(\'proxy: {\\n\\t"*": "//localhost:<your current contentBase configuration>"\\n}\'); // eslint-disable-line quotes
290         // Redirect every request to the port contentBase
291         app.get(\'*\', (req, res) => {
292           res.writeHead(302, {
293             Location: `//localhost:${contentBase}${req.path}${req._parsedUrl.search || \'\'}`
294           });
295           res.end();
296         });
297       } else {
298         // route content request
299         app.get(\'*\', express.static(contentBase, options.staticOptions));
300       }
301     },
302 
303     contentBaseIndex() {
304       if (Array.isArray(contentBase)) {
305         contentBase.forEach((item) => {
306           app.get(\'*\', serveIndex(item));
307         });
308       } else if (!/^(https?:)?\\/\\//.test(contentBase) && typeof contentBase !== \'number\') {
309         app.get(\'*\', serveIndex(contentBase));
310       }
311     },
312 
313     watchContentBase: () => {
314       if (/^(https?:)?\\/\\//.test(contentBase) || typeof contentBase === \'number\') {
315         throw new Error(\'Watching remote files is not supported.\');
316       } else if (Array.isArray(contentBase)) {
317         contentBase.forEach((item) => {
318           this._watch(item);
319         });
320       } else {
321         this._watch(contentBase);
322       }
323     },
324 
325     before: () => {
326       if (typeof options.before === \'function\') { options.before(app, this); }
327     },
328 
329     middleware: () => {
330       // include our middleware to ensure it is able to handle \'/index.html\' request after redirect
331       app.use(this.middleware);
332     },
333 
334     after: () => {
335       if (typeof options.after === \'function\') { options.after(app, this); }
336     },
337 
338     headers: () => {
339       app.all(\'*\', this.setContentHeaders.bind(this));
340     },
341 
342     magicHtml: () => {
343       app.get(\'*\', this.serveMagicHtml.bind(this));
344     },
345 
346     setup: () => {
347       if (typeof options.setup === \'function\') {
348         log(\'The `setup` option is deprecated and will be removed in v3. Please update your config to use `before`\');
349         options.setup(app, this);
350       }
351     }
352   };
353 
354   const defaultFeatures = [\'before\', \'setup\', \'headers\', \'middleware\'];
355   if (options.proxy) { defaultFeatures.push(\'proxy\', \'middleware\'); }
356   if (contentBase !== false) { defaultFeatures.push(\'contentBaseFiles\'); }
357   if (options.watchContentBase) { defaultFeatures.push(\'watchContentBase\'); }
358   if (options.historyApiFallback) {
359     defaultFeatures.push(\'historyApiFallback\', \'middleware\');
360     if (contentBase !== false) { defaultFeatures.push(\'contentBaseFiles\'); }
361   }
362   defaultFeatures.push(\'magicHtml\');
363   if (contentBase !== false) { defaultFeatures.push(\'contentBaseIndex\'); }
364   // compress is placed last and uses unshift so that it will be the first middleware used
365   if (options.compress) { defaultFeatures.unshift(\'compress\'); }
366   if (options.after) { defaultFeatures.push(\'after\'); }
367 
368   (options.features || defaultFeatures).forEach((feature) => {
369     features[feature]();
370   });
371 
372   if (options.https) {
373     // for keep supporting CLI parameters
374     if (typeof options.https === \'boolean\') {
375       options.https = {
376         key: options.key,
377         cert: options.cert,
378         ca: options.ca,
379         pfx: options.pfx,
380         passphrase: options.pfxPassphrase,
381         requestCert: options.requestCert || false
382       };
383     }
384 
385     let fakeCert;
386     if (!options.https.key || !options.https.cert) {
387       // Use a self-signed certificate if no certificate was configured.
388       // Cycle certs every 24 hours
389       const certPath = path.join(__dirname, \'../ssl/server.pem\');
390       let certExists = fs.existsSync(certPath);
391 
392       if (certExists) {
393         const certStat = fs.statSync(certPath);
394         const certTtl = 1000 * 60 * 60 * 24;
395         const now = new Date();
396 
397         // cert is more than 30 days old, kill it with fire
398         if ((now - certStat.ctime) / certTtl > 30) {
399           log(\'SSL Certificate is more than 30 days old. Removing.\');
400           del.sync([certPath], { force: true });
401           certExists = false;
402         }
403       }
404 
405       if (!certExists) {
406         log(\'Generating SSL Certificate\');
407         const attrs = [{ name: \'commonName\', value: \'localhost\' }];
408         const pems = selfsigned.generate(attrs, {
409           algorithm: \'sha256\',
410           days: 30,
411           keySize: 2048,
412           extensions: [{
413             name: \'basicConstraints\',
414             cA: true
415           }, {
416             name: \'keyUsage\',
417             keyCertSign: true,
418             digitalSignature: true,
419             nonRepudiation: true,
420             keyEncipherment: true,
421             dataEncipherment: true
422           }, {
423             name: \'subjectAltName\',
424             altNames: [
425               {
426                 // type 2 is DNS
427                 type: 2,
428                 value: \'localhost\'
429               },
430               {
431                 type: 2,
432                 value: \'localhost.localdomain\'
433               },
434               {
435                 type: 2,
以上是关于搭建react框架(会持续更新,加入各个集成模块)的主要内容,如果未能解决你的问题,请参考以下文章

(头条新闻)Cordova+React+OnsenUI+Redux新闻App开发实战教程

tomcat+jenkins+gitlab自动化构建框架搭建

如何搭建一个REACT全家桶框架

react知识点总结(持续更新。。。)

linux 搭建jenkins

React Native常用三方组件库大全