React开发-公司内部

96
深圳A1照教练
2018.03.14 10:19 字数 3671

React开发

一. Node.js开发环境的搭建及配置

Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。

  1. 下载安装和配置

    1. 下载安装

      • 下载:http://nodejs.cn/download/

      • 安装成功后,打开CMD窗口,键入

        node -v

      • 如果提示Node命令不存在,请手动修改环境变量,将nodejs安装目录添加至path目录

    2. 包管理工具
      常用的有3种

      • npm Node自带,速度较慢
      • yarn 推荐使用,速度快。
      • cnpm 淘宝弄得
    3. 配置

      • 设置代理(NPM默认代理很卡,需设置为淘宝镜像)

        npm config set registry https://registry.npm.taobao.org

      • 安装yarn模块(强烈推荐),-g 表示全局模块

        npm install yarn -g

    4. 常用的命令

      • 安装模块

        npm install <模块名> <-g> <--save或者--save-dev>
        yarn add <模块名> <--save或者--save-dev>

      • 卸载模块

        npm uninstall <模块名> <-g>
        yarn remove <模块名>

  1. 开始使用Node
    1. 常见第一个Node项目

      在D盘创建一个文件夹webapp,通过命令行创建webapp项目

      D:\webapp>npm init -y

      项目创建成功后,目录中多出了一个package.json文件

    2. 配置文件package.json

      {
        "name": "webapp",
        "version": "1.0.0",
        "description": "",
        "main": "index.js",
        "scripts": {
          "test": "echo \"Error: no test specified\" && exit 1"
        },
        "keywords": [],
        "author": "",
        "license": "ISC"
      }
      
      • name:表示项目名称

      • version:版本号

      • description:描述

      • main:加载时的入口文件,当你把项目分享给别人引用时,就是别人导入的模块。

      • scripts:脚本。

        命令行键入 npm test 等同于键入 echo "Error: no test specified" && exit 1

      • dependencies:依赖的模块

      • devDependencies:开发环境依赖的模块

      • license:开源协议

      更多:http://blog.csdn.net/woxueliuyun/article/details/39294375

二、React开发

React是用于构建用户界面的 JavaScript 库。
官网:https://doc.react-china.org/

  1. 构建第一个React项目

    1. 添加全局模块create-react-app(官方项目模板)

      npm install -g create-react-app

    2. 创建React项目,名为webapp

      D:>create-react-app webapp --scripts-version=react-scripts-ts

    3. 运行

      D:\webapp>yarn start

    运行成功后,浏览器会自动打开该项目。

  2. 简单分析官方的项目模板里的内容

    1. 目录结构

      • node_modules目录:存放的是项目依赖的模块(包括dependencies和devDependencies)
        • @type目录:typescript类型文件(有些模块源码不是用typescript写的,因此需要额外的添加类型文件)
        • script目录:带有script字眼的目录,通常是封装好的命令脚本。
        • 其它:引入的模块
      • public目录:静态文件或者图片。
        • html文件,React会通过Js代码将项目添加到id为root的div下
        • 可以新增一个css文件,进行全局样式的配置
      • src目录:我们的项目
        • 项目入口文件是index.tsx
        • registerServiceWorker.ts是端口服务配置文件(涉及到NodeJs的API)
      • package.json,NodeJs依赖关系配置文件
      • tsconfig.json,typescript配置文件
      • tslint.json,代码规范配置文件(暂时不敢用,要求很严格)
    2. package.json文件分析

      {
        "name": "webapp",
        "version": "0.1.0",
        "private": true,
        "dependencies": {
          "react": "^16.2.0",
          "react-dom": "^16.2.0",
          "react-scripts-ts": "2.13.0"
        },
        "scripts": {
          "start": "react-scripts-ts start",
          "build": "react-scripts-ts build",
          "test": "react-scripts-ts test --env=jsdom",
          "eject": "react-scripts-ts eject"
        },
        "devDependencies": {
          "@types/jest": "^22.2.0",
          "@types/node": "^9.4.7",
          "@types/react": "^16.0.40",
          "@types/react-dom": "^16.0.4",
          "typescript": "^2.7.2"
        }
      }
      
      1. dependencies:依赖了三个模块react和react-dom是必须的核心模块,react-scripts-ts是react封装的webpack脚本模块。
      2. devDependencies:主要是依赖了typescript模块,因为React源码是用flow(类似TypeScript)写的,因此需要添加Type类型支持。
      3. scripts:常用的几个脚本。可以通过 npm start执行。
    3. tsconfig.json文件分析

      {
        "compilerOptions": {
          "outDir": "build/dist",
          "module": "esnext",
          "target": "es5",
          "lib": ["es6", "dom"],
          "sourceMap": true,
          "allowJs": true,
          "jsx": "react",
          "moduleResolution": "node",
          "rootDir": "src",
          "forceConsistentCasingInFileNames": true,
          "noImplicitReturns": true,
          "noImplicitThis": true,
          "noImplicitAny": true,
          "strictNullChecks": true,
          "suppressImplicitAnyIndexErrors": true,
          "noUnusedLocals": true
        },
        "exclude": [
          "node_modules",
          "build",
          "scripts",
          "acceptance-tests",
          "webpack",
          "jest",
          "src/setupTests.ts"
        ]
      }
      

      关键的几个配置介绍

      1. outDir编译后的文件路径。
      2. module模块化方式,(commonjs等)
      3. target编译后的js版本
      4. lib开发所有的Js版本
      5. rootDir是ts项目的路径。
      6. exclude和include,包含或不包含的文件夹。
        其它的根据英文字面意思即可。
  3. 开始编写我们自己的代码
    开发一个如下图的网页,点击添加,会将内容追加到下面:
    [图片上传失败...(image-5e9ea2-1520993928646)]

    src目录下添加 APP.tsx ListComponent.tsx AddComponent.tsx

    APP.tsx

    import * as React from 'react';
    import AddComponent from "./AddComponent";
    import ListComponent from "./ListComponent";
    
    export default class App extends React.Component<any, { data: string[] }> {
        private _styles = {
            container: {
                width: "100%",
                height: "100%",
            }
        };
    
        constructor(prop:any,context){
            super(prop, context);
            this.state = {
                data:[]
            }
        }
    
        render() {
            console.log("App render");
            return (
                <div style={this._styles.container}>
                    <AddComponent add={(text) => this.setState({data:[...this.state.data,text]})}/>
                    <ListComponent data={this.state.data}/>
                </div>
            );
        }
    }
    

    AddComponent.tsx

    import * as React from "react";
    
    export default class AddComponent extends React.Component<{ add(text): void; }, any> {
        private _height = "40px";
        private _currValue = "";
    
        private _styles= {
            container: {
                width: "300px",
                height: this._height,
                display: "flex",
                flexDirection: "row",
                margin: "100px",
            },
            input: {
                height: "100%",
                flex: 1,
            },
            add: {
                height: "100%",
                padding: "0 10px",
                margin: "5px 10px"
            }
        };
    
        render() {
            console.log("AddComponent render");
            return (
                <div style={this._styles.container}>
                    <input
                        style={this._styles.input}
                        type="text"
                        placeholder="请输入要添加的内容"
                        onChange={(event) => this._currValue = event.target.value}/>
                    <input
                        style={this._styles.add}
                        type="button"
                        value="添加"
                        onClick={() => {
                            this.props.add(this._currValue);
                        }}/>
                </div>
            );
        }
    }
    

    ListComponent.tsx

    import * as React from "react";
    
    export default class ListComponent extends React.Component<{ data: string[] }, any> {
        private _styles = {
            container: {
                width: "100%",
                flex: 1,
            },
        };
    
        render() {
            console.log("ListComponent render");
            return (
                <ul style={this._styles.container}>
                    {
                        this.props.data.map((value, index, array) => {
                            return (
                                <li>{"第" + index + "条记录," + value}</li>
                            );
                        })
                    }
                </ul>
            );
        }
    }
    
    1. React实现了 JavaScript 中写 HTML , HTML 中写 JavaScript
      • JavaScript 中写 HTML ,是通过括号包裹的,可以写成如下:
      let element = (<div><div>)
      
      • HTML 中写 JavaScript ,是通过大括号包裹的,可以写成如下
      let element = (<div>{"你好"+"!"}<div>)
      
    2. React组件必须继承React.Component,HTML布局通过render方法返回。
    3. React动态的更新组件式通过setState方法执行的。
    4. React可以通过外部传入属性,如:ListComponent组件中的data参数,获取方式是:this.props.data。
    5. 样式文件直接使用JSON来写即可,通过style引入。

    React是单向数据流单页面组件,每次刷新都会调用render方法,手动刷新是通过调用setState方法。
    烦恼:

    1. 由于刷新组件只能通过setState进行,意味着只能在组件内部进行更新页面。
    2. 由于是单向数据流,因此无法修改父组件传过来的属性,但是有时候就是要修改。

    这时候数据绑定模块就出现了,主要有两种mobx和redux,redux虽然是主流,但是使用起来非常繁琐,Mobx是后起之秀,结合了redux的理念,操作简单,容易上手,网上也有相关的对比。

  4. Mobx的使用

    1. 项目引入Mobx模块
      修改package.json文件,在dependencies节点添加

          "mobx": "^3.6.1",
          "mobx-react": "^4.4.2",
      

      执行命令行安装

      yarn install
      2. 修改App.tsx和ListComponent.tsx文件

      import * as React from 'react';
      import AddComponent from "./AddComponent";
      import ListComponent, {appState} from "./ListComponent";
      
      export default class App extends React.Component<any, { data: string[] }> {
          private _styles = {
              container: {
                  width: "100%",
                  height: "100%",
              }
          };
      
          constructor(prop:any,context){
              super(prop, context);
              this.state = {
                  data:[]
              }
          }
      
          render() {
              console.log("App render");
              return (
                  <div style={this._styles.container}>
                      <AddComponent add={(text) => appState.add(text)}/>
                      <ListComponent/>
                  </div>
              );
          }
      }
      
      import * as React from "react";
      import {observer} from "mobx-react";
      import {observable} from "mobx";
      
      class AppState {
          @observable data: string[] = [];
      
          add(text:string){
              this.data = [...this.data, text];
          }
      }
      export const appState = new AppState();
      
      @observer
      export default class ListComponent extends React.Component<any, any> {
          private _styles = {
              container: {
                  width: "100%",
                  flex: 1,
              },
          };
      
          render() {
              console.log("ListComponent render");
              return (
                  <ul style={this._styles.container}>
                      {
                          appState.data.map((value, index, array) => {
                              return (
                                  <li>{"第" + index + "条记录," + value}</li>
                              );
                          })
                      }
                  </ul>
              );
          }
      }
      

      yarn start 运行即可

    2. 原理简单分析

      • observable 被观察者,mobx实时监测被观察者的变化,如果发生变化,就会通知observer更新。
      • observer 观察者,收到observable的更新通知后,observer会检查这个observable自己有没有在使用,如果在使用,就会更新(调用render方法)
  5. 单页面跳转必备-路由器 router
    官网:https://react-guide.github.io/react-router-cn/

    1. 项目引入react-router模块
      修改package.json, 在dependencieshe和节点添加

        "dependencies": {
          "history": "^4.6.2",
          "react-router-dom": "^4.2.2",
        },
        "devDependencies": {
          "@types/history": "^4.6.2",
          "@types/react-router-dom": "^4.2.2",
        }
      

      执行命令行安装

      yarn install

    2. 开始编写路由器
      修改APP.tsx

      import React from "react";
      import {Route, Router} from "react-router";
      import {createBrowserHistory} from "history";
      
      export const browserHistory = createBrowserHistory();
      
      export default class App extends React.Component{
          render(){
              return(
                  <div style={{width:"100%",height:"100%",display:"flex",flexDirection:"column"}}>
                      <ul>
                          {
                              ["nav1","nav2"].map((value, index, array)=>{
                                  return(
                                      <li onClick={()=>{
                                          if(index ===0){
                                              browserHistory.push("/first")
                                          }else if(index ===1){
                                              browserHistory.push("/second")
                                          }
                                      }}>{value}</li>
                                  );
                              })
                          }
                          <Router history={browserHistory}>
                              <div>
                                  <Route exact path="/" component={Home}/>
                                  <Route path="/first" component={First}/>
                                  <Route path="/second" component={Second}/>
                              </div>
                          </Router>
                      </ul>
                  </div>
              );
          }
      }
      class Home extends React.Component{
          render(){
              return(
                  <div>
                      Home界面
                  </div>
              );
          }
      }
      class First extends React.Component{
          render(){
              return(
                  <div>
                      First界面
                  </div>
              );
          }
      }
      class Second extends React.Component{
          render(){
              return(
                  <div>
                      Second界面
                  </div>
              );
          }
      }
      
      

      yarn start 运行即可

    3. 原理简单分析

      • 待定,未知。

三、Webpack打包机

官网:http://www.css88.com/doc/webpack/

他可以轻松的处理 JavaScript 依赖关系和加载顺序,并按规则打包或压缩成指定格式。

  1. 原理介绍

    [图片上传失败...(image-91324f-1520993928646)]

    有了打包机:
    
    • 项目可以使用更高级的JS语法(ES6以上)。
    • 项目可以使用更牛逼的TS语法
    • 项目可以轻松的压缩成单js css文件
    • 项目可以编译成指定ES版本的JS语法(目前浏览器完美支持es5
    • 项目可以把HTML写进JS文件,在根据loader转换成HTML(React)
    • 很多很多。。。。只有你有对应loader。
  2. webpack语法的简单介绍
    可以在根目录新增一个webpack.config.js配置文件,通过webpack --config webpack.config.js 运行。

    const HtmlWebpackPlugin = require('html-webpack-plugin');
    const path = require('path');
    
    module.exports = {
        entry: './src/index.js',
        output: {
            filename: 'bundle.js',
            path: path.resolve(__dirname, 'dist')
        },
        module: {
            rules: [
                {
                    test: /\.css$/,
                    use: [
                        'style-loader',
                        'css-loader'
                    ]
                }
            ]
        },
        plugins: [
            new HtmlWebpackPlugin({
                title: 'Output Management'
            })
        ],
    };
    
    1. entry:入口文件。
    2. output:输出路径
    3. rules:规则配置,上面例子中配置了 当发现css文件时的处理方式。
    4. plugins:插件,使用别人封装好的插件处理,比如发现html文件时使用的HtmlWebpackPlugin
  3. 第一个loader(less)
    该例子背景是重新创建一个react-app

    D:>create-react-app webapp --scripts-version=react-scripts-ts

    1. 修改package.json,增加两个dev依赖

      "less": "^3.0.1",
      "less-loader": "^4.0.6",

      执行 yarn install 安装

    2. 打开路径node_modules/react-scripts-ts 这是react封装好的webpack

    3. 打开scripts/build看看build过程中,是引用了那个webpack.config配置。

      const webpack = require('webpack');
      const config = require('../config/webpack.config.prod');
      
      function build(previousFileSizes) {
        console.log('Creating an optimized production build...');
      
        let compiler = webpack(config);
        ...省略...
      }
      
      

      由代码可知,build使用的config路径是"../config/webpack.config.prod"

    4. 打开这个webpack.config.prod文件,找到rules(打包规则)配置中关于css的解析,因为less本身属于css的范畴。

      {
          test: /\.css$/,
          loader: ExtractTextPlugin.extract(
            Object.assign(
              {
                fallback: {
                  loader: require.resolve('style-loader'),
                  options: {
                    hmr: false,
                  },
                },
                use: [
                  {
                    loader: require.resolve('css-loader'),
                  },
                  {
                    loader: require.resolve('postcss-loader'),
                  },
                ],
              },
            )
          ),
      },
      

      test:目前只支持css文件,所以需要增加对less文件的支持,

      test: /.(css|less)$/,

      loader:目前只用了postcss-loader-->css-loader-->style-loader 需要增加ls-loader支持

      loader: require.resolve('less-loader')

      修改后的配置

      {
          test: /\.(css|less)$/,
          loader: ExtractTextPlugin.extract(
            Object.assign(
              {
                fallback: {
                  loader: require.resolve('style-loader'),
                  options: {
                    hmr: false,
                  },
                },
                use: [
                  {
                    loader: require.resolve('css-loader'),
                  },
                  {
                    loader: require.resolve('postcss-loader'),
                  },
                  {
                    loader: require.resolve('less-loader'),  
                  }
                ],
              },
            )
          ),
      },
      

      同理,我们修改start脚本的配置(因为start脚本用的是另一个config)。

    测试是否配置成功

    1. 将原来的 App.css 修改为 App.less
    2. 修改App.tsx文件中的 import './App.css'; 修改为 import './App.less';
      运行即可。

四、TypeScript简单介绍

官网:https://www.tslang.cn/
TypeScript是JavaScript的超集,它的存在并不是为了取代JavaScript,而是辅助JavaScript实现类型检查,直到ES支持强类型,TypeScript就是可以光荣退休了。
未来:TypeScript不存在淘汰的问题,因为它采用的是JavaScript语法,他所做的仅仅是增加了强类型。
比如:

let a:string = "test";

编译成ES6

let a = "test";

编译成ES5

var a = "test";

所有的强类型在编译成JavaScript后都会消失。

TypeScript类型的定义

基础数据类型

  • string 字符串
  • number 数字
  • object 对象
  • undefined 未定义
  • {} 对象 可以指定成员
  • any 任意对象,懒人专用,定义后,typescript下岗了。

组合使用

  • {[key:string]:number} key为string value为number的对象

第一种:

interface IPerson{
    name:string;
}

interface IStudent extends IPerson{
    score?:string;
}

let student:Iprop;

第二种

type IStudent = {
    name:string;
    score?:string;
} 

let student:Iprop;

第三种

let student:{name:string;score?:string;};

? 表示这个属性可有可无 student.score===undefined || string

四、辅助工具

  1. cmder(推荐)
    一款支持复制粘贴,支持linux命令的windows命令行工具,选用。
    相关文章 http://blog.csdn.net/wnma3mz/article/details/78268463
  2. WebStorm
    1. WebStorm 的破解
      下载地址:https://www.jetbrains.com/webstorm/
      license server http://idea.ibdyr.com
    2. WebStorm配置
      开发语言的选择 File | Settings | Languages & Frameworks | JavaScript 默认的开发语言是ES6 修改为React JSX即可。
  3. 其它开发工具
    • VSCode(免费) VS的压缩版本
    • VS
    • Notepad+ 或 记事本
    • Xcode

五、其它

  1. 我的tsconfig配置文件

    {
      "compilerOptions": {
        "outDir": "build/dist",
        "module": "esnext",
        "target": "es5",
        "lib": ["esnext","dom"],
        "jsx": "react",
        "rootDir": "src",
        "forceConsistentCasingInFileNames": true,
        "noImplicitReturns": true,
        "suppressImplicitAnyIndexErrors": true,
        "removeComments": true,
        "preserveConstEnums": true,
        "moduleResolution": "node",
        "experimentalDecorators": true,
        "noImplicitAny": false,
        "allowSyntheticDefaultImports": true,
        "noUnusedParameters": false,
        "noUnusedLocals": false,
        "strictNullChecks": true,
        "noImplicitThis": true,
        "sourceMap": true,
        "allowJs": false,
        "allowUnreachableCode": true,
        "allowUnusedLabels": true,
        "checkJs": false,
        "baseUrl": "."
      },
      "exclude": [
        "node_modules",
        "build",
        "scripts",
        "acceptance-tests",
        "webpack",
        "jest",
        "src/setupTests.ts"
      ],
      "include": [
        "src"
      ],
      "compileOnSave": true
    }
    
    

    outDir:编译后 build/dist目录下会出现编译后的文件。
    module:模块化规范(commonjs等),好几种。
    target:编译后的es版本,通常就是es5,浏览器支持es5,es6没有普及。
    lib:开发过程中使用的es版本,看心情。
    jsx:react或者react-native,native是移动端混合APP开发用的。
    rootDir:项目所在位置。
    sourceMap:如果没有souceMap,浏览器出现的一堆压缩过后的Js文件,很难受。没法调试。
    其它有道词典走一波即可。

  2. React的编程思想

    • 响应式
    • 组件化
  3. React更新组件的算法
    倘若git采用的算法叫做逐行算法,那么React采用的算法是同行算法。

日记本
Web note ad 1