react 给代码指定规范_react native 代码规范 - CSDN
  • React 基础知识介绍 hello world 以下是一个最简单的demo,将一个最简单的组件渲染到页面上。 import React from 'react' import { render } from 'react-dom' // 定义组件 class Hello extends React....

    React 基础知识介绍

    hello world

    以下是一个最简单的demo,将一个最简单的组件渲染到页面上。

    import React from 'react'
    import { render } from 'react-dom'
    
    // 定义组件
    class Hello extends React.Component {
        render() {
            // return 里面写jsx语法
            return (
                <p>hello world</p>
            )
        }
    }
    
    // 渲染组件到页面
    render(
        <Hello/>,
        document.getElementById('root')
    )

    深入一下,这里import React from 'react'引用的是什么?

    这里的'react'对应的就是./package.json文件中dependencies中的'react',即在该目录下用npm install安装的 react 。npm 安装的 react 的物理文件是存放在 ./node_modules/react中的,因此引用的东西肯定就在这个文件夹里面。

    打开./node_modules/react/package.json找到"main": "react.js",,这里的main即指定了入口文件,即./node_modules/react/react.js这个文件。那么,问题的答案自然就出来了。

    jsx 语法

    React 里面写模板要使用 jsx 语法,它其实和 html 很相似但是又有那么几点不一样。下面简单介绍一下 jsx 语法的一些特点:

    使用一个父节点包裹

    jsx 中不能一次性返回零散的多个节点,如果有多个请包涵在一个节点中。例如,

    // 三个 <p> 外面必须再包裹一层 <div>
    return (
      <div>
        <p>段落1</p>
        <p>段落2</p>
        <p>段落3</p>
      </div>
    )

    再例如:

    // { } 中返回的两个 <p> 也要用 <div> 包裹
    return (
      <div>
        <p>段落1</p>
        {
          true 
          ? <p>true</p>
          : <div>
            <p>false 1</p>
            <p>false 2</p>
          </div>
        }
      </div>
    )

    注释

    jsx 中用{/* */}的注释形式

            return (
                // jsx 外面的注释
                <div>
                    {/* jsx 里面的注释 */}
                    <p>hello world</p>
                </div>
            )

    样式

    对应 html 的两种形式,jsx 的样式可以这样写:
    css样式:<p className="class1">hello world</p>,注意这里是className,而 html 中是class
    内联样式:<p style={{display: 'block', fontSize: '20px'}}>hello world</p>,注意这里的{{...}},还有fontSize的驼峰式写法

    事件

    拿 click 事件为例,要在标签上绑定 click 事件,可以这样写

    class Hello extends React.Component {
        render() {
            return (
                <p onClick={this.clickHandler.bind(this)}>hello world</p>
            )
        }
    
        clickHandler(e) {
            // e 即js中的事件对象,例如 e.preventDefault()
            // 函数执行时 this 即组件本身,因为上面的 .bind(this)
            console.log(Date.now())
        }
    }

    注意,onClick是驼峰式写法,以及.bind(this)的作用

    循环

    在 jsx 中使用循环,一般会用到Array.prototype.map(来自ES5标准)

    class Hello extends React.Component {
        render() {
            const arr = ['a', 'b', 'c']
            return (
                <div>
                    {arr.map((item, index) => {
                        return <p key={index}>this is {item}</p>
                    })}
                </div>
            )
        }
    }

    注意,arr.map是包裹在{}中的,key={index}有助于React的渲染优化,jsx中的{}可放一个可执行的 js 程序或者变量

    判断

    jsx中使用判断一般会用到三元表达式(表达式也是放在{}中的),例如:

    return (
      <div>
        <p>段落1</p>
        {
          true 
          ? <p>true</p>
          : <p>false</p>
          </div>
        }
      </div>
    )

    也可以这样使用:

    <p style={{display: true ? 'block' ? 'none'}}>hello world</p>

    代码分离

    之前的demo代码都是在一个文件中,实际开发中不可能是这样子的,因此这里就先把组件的代码给拆分开。我们将使用 es6 的模块管理规范。

    page 层

    创建./app/containers/Hello/index.jsx文件,将之前创建组件代码复制进去

    import React from 'react'
    
    class Hello extends React.Component {
        render() {
            return (
                 <p>hello world</p>
            )
        }
    }
    
    export default Hello

    然后./app/index.jsx中代码就可以这样写。

    import Hello from './containers/Hello';
    
    render(
        <Hello/>,
        document.getElementById('root')
    )

    注意,代码import Hello from './containers/Hello';这里可以写成./containers/Hello/index.jsx也可以写成./containers/Hello/index

    subpage 层

    如果Hello组件再稍微复杂一点,那么把代码都放一块也会变得复杂,接下来我们再拆分。

    创建./app/containers/Hello/subpage目录,然后在其下创建三个文件Carousel.jsx Recommend.jsx List.jsx,分别写入相应的代码(看代码文件即可),然后./app/containers/Hello/index.js中即可这样写

    import Carousel from './subpage/Carousel'
    import Recommend from './subpage/Recommend'
    import List from './subpage/List'
    
    class Hello extends React.Component {
        render() {
            return (
                <div>
                    <p>hello world</p>
                    <hr/>
                    <Carousel/>
                    <Recommend/>
                    <List/>
                </div>
            )
        }
    }

    注意,这里import.jsx后缀省略了。

    component 层

    以上介绍的是页面和复杂页面的拆分,但那都是页面层级的,即page层。这里复杂页面拆分为subpage其实没啥特别的,就是把复杂页面的代码拆分一下,会更加符合开放封闭原则。而且,只有复杂页面才有必要去拆分,简单页面根本没必要拆分。因此,无论是page还是subpage它都是页面级别的。

    页面的特点是其独特性,一个页面就需要创建一个文件(如果两个页面可以共用一个文件,这是设计不合理,得治)。而页面其中的内容,就不一定是这样子了。例如,现在的APP每个页面最上面都会有个 header ,即可以显示标题,可以返回。每个页面都有,样子差不多,难道我们要为每个页面都做一个?——当然不是。

    创建./app/components/Header/index.jsx文件,简单写入一个组件的代码(见源码文件),然后在./app/containers/index.jsx中引用

    import Header from '../../components/Header'
    
    class Hello extends React.Component {
        render() {
            return (
                <div>
                    <Header/>
                    {/* 省略其他内容 */}
                </div>
            )
        }
    }

    Hello 页面会用到 Header,以后的其他页面也会用到 Header ,我们把多个页面都可能用到的功能,封装到一个组件中,代码放在./app/components下。

    数据传递 & 数据变化

    props

    接着刚才 Header 的话题往下说,每个页面都会使用 Header ,但是 Header 上显示的标题每个页面肯定是不一样的。我们需要这样解决:页面中引用Header时,这样写 <Header title="Hello页面"/>,即给 Header 组件设置一个 title 属性。而在 Header 组件中可以这样取到

        render() {
            return (
                 <p>{this.props.title}</p>
            )
        }

    在 React 中,父组件给子组件传递数据时,就是以上方式,通过给子组件设置 props 的方式,子组件取得 props 中的值即可完成数据传递。被传递数据的格式可以是任何 js 可识别的数据结构,上面demo是一个字符串。React 中,props 一般只作为父组件给子组件传递数据用,不要试图去修改自己的 props ,除非你想自找麻烦

    props && state

    上面提到了 props 不能被自身修改,如果组件内部自身的属性发生变化,该怎么办?—— React 为我们提供给了 state,先看一个demo:

    class Hello extends React.Component {
        constructor(props, context) {
            super(props, context);
            this.state = {
                // 显示当前时间
                now: Date.now()
            }
        }
        render() {
            return (
                <div>
                    <p>hello world {this.state.now}</p>
                </div>
            )
        }
    }

    还有一点非常重要,React 会实时监听每个组件的 props 和 state 的值,一旦有变化,会立刻更新组件,将结果重新渲染到页面上,下面demo演示了state的变化,props也是一样的

    class Hello extends React.Component {
        constructor(props, context) {
            super(props, context);
            this.state = {
                // 显示当前时间
                now: Date.now()
            }
        }
        render() {
            return (
                <div>
                    <p onClick={this.clickHandler.bind(this)}>hello world {this.state.now}</p>
                </div>
            )
        }
        clickHandler() {
            // 设置 state 的值的时候,一定要用 this.setState ,不能直接赋值修改
            this.setState({
                now: Date.now()
            })
        }
    }

    智能组件 & 木偶组件

    这是用 React 做系统设计时的两个非常重要的概念。虽然在 React 中,所有的单位都叫做“组件”,但是通过以上例子,我们还是将它们分别放在了./app/containers./app/components两个文件夹中。为何要分开呢?

    • 智能组件 在日常开发中,我们也简称“页面”。为何说它“智能”,因为它只会做一些很聪明的事儿,脏活累活都不干。它只对数据负责,只需要获取了数据、定义好数据操作的相关函数,然后将这些数据、函数直接传递给具体实现的组件即可。
    • 木偶组件 这里“木偶”一词用的特别形象,它总是被人拿线牵着。它从智能组件(或页面)那里接受到数据、函数,然后就开始做一些展示工作,它的工作就是把拿到的数据展示给用户,函数操作开放给用户。至于数据内容是什么,函数操作是什么,它不关心。

    以上两个如果不是理解的很深刻,待把课程学完再回头看一下这两句话,相信会理解的。

    生命周期

    React 详细的生命周期可参见这里,也可查阅本文档一开始的视频教程。这里我们重点介绍这个项目开发中常用的几个生命周期函数(hook),相信你在接下来的 React 开发中,也会常用这些。

    以下声明周期,也没必要每个都写demo来解释,先简单了解一下,后面会根据实际的例子来解释,这样会更加易懂。

    • getInitialState

    初始化组件 state 数据,但是在 es6 的语法中,我们可以使用以下书写方式代替

    class Hello extends React.Component {
        constructor(props, context) {
            super(props, context);
            // 初始化组件 state 数据
            this.state = {
                now: Date.now()
            }
        }
    }
    • render

    最常用的hook,返回组件要渲染的模板。

    • comopentDidMount

    组件第一次加载时渲染完成的事件,一般在此获取网络数据。实际开始项目开发时,会经常用到。

    • shouldComponentUpdate

    主要用于性能优化,React 的性能优化也是一个很重要的话题,后面一并讲解。

    • componentDidUpdate

    组件更新了之后触发的事件,一般用于清空并更新数据。实际开始项目开发时,会经常用到。

    • componentWillUnmount

    组件在销毁之前触发的事件,一般用户存储一些特殊信息,以及清理setTimeout事件等。

    展开全文
  • React Native命名规范

    2018-09-13 13:59:05
    React/JSX代码规范 文件与组件命名 扩展名:使用.js作为js文件的扩展名。如果同一个文件夹下有同名而不同作用的js文件,则通过中缀(小写)进一步区分,例如:HomeView.component.js,HomeView.style.js,HomeView...

    React/JSX代码规范

    文件与组件命名

    • 扩展名:使用.js作为js文件的扩展名。如果同一个文件夹下有同名而不同作用的js文件,则通过中缀(小写)进一步区分,例如:HomeView.component.js,HomeView.style.js,HomeView.action.js等。

    • 文件名: 使用驼峰命名法且首字母大写,如HomeView.js。

    • 组件命名: 与文件名(除中缀外)完全一致。如果组件单独放置在目录中,则目录名也一致。

    //下面的写法都是正确的
    
    import Footer from './Component/Footer/Footer' //在不同文件夹下
    
    import Footer from './Footer' //同一文件夹下

    组件声明

    使用class与extends关键字。需要导出的组件直接在class关键字前使用export default。

    export default class HomeView extends React.Component {
    }

    对齐

    组件的属性要对齐,如果能一行摆下的props,最好摆在一行(个人建议超过两个就换行)。

    <Foo
      superLongParam="bar"
      anotherSuperLongParam="baz"
    />
    
    // 如果一行能摆下props,那就摆在一行
    <Foo bar="bar" />
    
    // 子组件照常缩进
    <Foo
      superLongParam="bar"
      anotherSuperLongParam="baz"
    >
      <Spazz />
    </Foo>

    引号

    对于JSX的字符串属性使用双引号(“),其他情况下使用单引号。

    <Foo bar="bar" />
    
    <Foo style={{ left: '20px' }} />

    空格

    在自闭合的标签中包含一个空格。

    <Foo />

    state/props

    • 对于多个单词组成的pros,使用驼峰命名法。不使用下划线或连接线。
    <Foo
      userName="hello"
      phoneNumber={12345678}
    />
    • 读取state和props时,使用const与解构,必要时可使用let。不使用var。
    const { userName, age, sex } = this.props;
    const { checked } = this.state;

    括号

    当JSX标签超过一行时,使用括号包裹。单行时,不必有括号。

    render() {
      return (
        <MyComponent className="long body" foo="bar">
          <MyChild />
        </MyComponent>
      );
    }
    
    
    render() {
      const body = <div>hello</div>;
      return <MyComponent>{body}</MyComponent>;
    

    标签

    对于没有子组件的JSX标签,始终自闭合。

    <Foo className="stuff" />

    如果组件有多行属性,则另起一行进行自闭合。

    <Foo
      bar="bar"
      baz="baz"
    />

    方法

    为方法命名时,不使用下划线开头(哪怕是想用作私有方法)。

    class extends React.Component {
      onClickSubmit() {
        // do stuff
      }
    
      // other stuff
    });

    方法声明的顺序

    原则上按如下顺序排列React组件的各个方法(生命周期):

    1. constructor
    2. 静态方法(static methods)
    3. getChildContext
    4. componentWillMount
    5. componentDidMount
    6. componentWillReceiveProps
    7. shouldComponentUpdate
    8. componentWillUpdate
    9. componentDidUpdate
    10. componentWillUnmount
    11. 点击处理或事件处理函数,比如onClickSubmit()onChangeDescription()
    12. 用在render中的getter方法,比如getSelectReason()getFooterContent()
    13. 可选的render方法,比如renderNavigation()renderProfilePicture()
    14. render
    • 按如下方式定义propTypes, defaultProps, contextTypes等
    import React, { Component, PropTypes } from 'react';
    
    const propTypes = {
      id: PropTypes.number.isRequired,
      url: PropTypes.string.isRequired,
      text: PropTypes.string,
    };
    
    const defaultProps = {
      text: 'Hello World',
    };
    
    class Link extends Component {
      static methodsAreOk() {
        return true;
      }
    
      render() {
        return <a href={this.props.url} data-id={this.props.id}>{this.props.text}</a>
      }
    }
    
    Link.propTypes = propTypes;
    Link.defaultProps = defaultProps;
    
    export default Link;

    ES6代码规范

    变量与常量声明

    1 变量

    • 尽量使用let来代替var
    • 对于全局变量声明,采用 global.xx = xxx,但应避免声明过多全局变量污染环境

    2 常量

    • 对于常量应使用const进行声明,命名采用驼峰写法
    • 对于使用 immutable 数据应用const进行声明
    const someNum = 123;
    const anotherStr = '不变的字符串';
    const arr = ['不', '变', '数', '组'];
    const anotherObj = {
      '不变对象': true
    };

    字符串

    1 处理多行字符串,使用模板字符串

    以反引号( ` )标示,可读性更强,代码更易编写。
    注意排版引起空格的问题,使用场景为声明HTML模板字符串

    const tmpl = `
    <div class="content">
      <h1>这是换行了。</h1>
    </div>`;

    2 处理字符串拼接变量时,使用模板字符串

     function sayHi(name) {
       return `How are you, ${name}?`;
     }

    解构

    ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。

    var a = 1;
    var b = 2;
    var c = 3;
    
    ES6允许写成下面这样,就是解构
    
    var [a, b, c] = [1, 2, 3];

    1 解构语句中不使用圆括号

    let [a, b] = [11, 22];

    2 对象解构

    对象解构 元素与顺序无关

    对象指定默认值时仅对恒等于undefined ( !== null ) 的情况生效

    • 若函数形参为对象时,使用对象解构赋值
    function someFun(opt) {
      let { opt1, opt2 } = opt;
      console.log(`$(opt1) 加上 $(opt2)`);
    }
    
    function someFun({ opt1, opt2 }) {
      console.log(opt1);
    }
    • 若函数有多个返回值时,使用对象解构,不使用数组解构,避免添加顺序的问题
    function anotherFun() {
      const one = 1, two = 2, three = 3;
      return { one, two, three };
    }
    const { one, three, two } = anotherFun(); // 不用管顺序
    // one = 1, two = 2, three = 3
    • 已声明的变量不能用于解构赋值(语法错误)

    3 数组解构

    数组元素与顺序相关

    • 交换变量的值
    let x = 1;
    let y = 2;
    
    [x, y] = [y, x]; // 交换变量
    • 将数组成员赋值给变量时,使用数组解构
    const arr = [1, 2, 3, 4, 5];
    
    const [one, two] = arr;

    数组

    1 将类数组(array-like)对象与可遍历对象(如Set, Map)转为真正数组

    采用Array.from进行转换

    function foo() {
      let args = Array.from(arguments);
    }

    2 数组去重

    结合Set结构与Array.from
    使用indexOf,HashTable等形式,不够简洁清晰

    function deduplication(arr) {
      return Array.from(new Set(arr));
    }

    3 数组拷贝

    采用数组扩展 ==== 形式

    const items = [1, 2, 3];
    
    let copyTemp = [...items];

    4 将一组数值转为数组

    采用Array.of进行转换

    let arr1 = Array.of(2);  // [2]
    let arr2 = Array.of(1, 2, 3); // [1, 2, 3]

    函数

    1 当要用函数表达式或匿名函数时,使用箭头函数(Arrow Functions)

    箭头函数更加简洁,并且绑定了this

    const foo = (x) => {
      console.log(foo.name); // 返回'foo'
    };
    
    [1, 2, 3].map( (x) => {
      return x + 1;
    });
    
    var testObj = {
      name: 'testObj',
      init() {
        // 箭头函数自动绑定定义时所在的对象
        document.addEventListener('click', () => this.doSth(), false);
      },
      doSth() {
        console.log(this.name);
      }
    };

    箭头函数书写约定

    函数体只有单行语句时,允许写在同一行并去除花括号

    当函数只有一个参数时,允许去除参数外层的括号

    const foo = x => x + x; // 注意此处会隐性return x + x
    
    const foo = (x) => {
      return x + x; // 若函数体有花括号语句块时须进行显性的return
    }; 
    
    [1, 2, 3].map( x => x * x);

    用箭头函数返回一个对象,应用括号包裹

    let test = x => ({ x: x }); // 使用括号可正确return {x:x}

    2 立即调用函数 IIFE

    使用箭头函数

    (() => {
      console.log('哈');
    })();

    3 函数参数指定默认值

    采用函数默认参数赋值语法

    function foo(opts = {}) {
      console.log('更加简洁,安全');
    }

    4 对象中的函数方法使用缩写形式

    更加简洁,函数方法不要使用箭头函数,避免this指向的混乱

    //正确写法
    const des = '对象模块写法'; // 使用对象属性值简写方式
    const shopObj = {
      des,
      foo() {
        console.log(this.des);
      }
    };

    1 类名应使用帕斯卡写法(PascalCased)

    class SomeClass {
    
    }

    2 定义类时,方法的顺序如下:

    1. constructor
    2. public get/set 公用访问器,set只能传一个参数
    3. public methods 公用方法,公用相关命名使用小驼峰式写法(lowerCamelCase)
    4. private get/set 私有访问器,私有相关命名应加上下划线 _ 为前缀
    5. private methods 私有方法
    class SomeClass {
      constructor() {
        // constructor
      }
    
      get aval() {
        // public getter
      }
    
      set aval(val) {
        // public setter
      }
    
      doSth() {
        // 公用方法
      }
    
      get _aval() {
        // private getter
      }
    
      set _aval() {
        // private setter
      }
    
      _doSth() {
        // 私有方法
      }
    }

    3 如果不是class类,不使用new

    // 不推荐❌
    function Foo() {
    }
    const foo = new Foo();
    
    // 正确✔
    class Foo {
    }
    const foo = new Foo();

    4 使用真正意思上的类Class写法,不使用prototype进行模拟扩展

    Class更加简洁,易维护

    class Dog {
      constructor(names = []) {
        this._names = [...names];
      }
      bark() {
        const currName = this._names[0];
        alert(`one one ${currName}`);
      }
    }

    5 this的注意事项

    子类使用super关键字时,this应在调用super之后才能使用,可在方法中return this来实现链式调用写法

    class Foo {
      constructor(x, y) {
        this.x = x;
        this.y = y;
      }
    }
    
    class SubFoo extends Foo {
      constructor(x, y, z) {
        super(x, y);
        this.z = z; // this 放在 super 后调用
      }
      setHeight(height) {
        this.height = height;
        return this;
      }
    }

    模块

    1 使用import / export来做模块加载导出,不使用非标准模块写法

    import { lightRed } from './colors';
    export default lightRed;

    2 import / export 后面采用花括号{ }引入模块的写法时,须在花括号内左右各保留一个空格

    import { lightRed } from './colors';



    转自:React Native 代码规范

    展开全文
  • React Native出现的目的本就是“learn once, write anywhere”,Facebook希望人们能够学习一次,到处使用,但是同样是JS代码,从前端移植到RN,或者RN移植到前端,却并没有想象的那么容易。事实上除了语法相同外,...

    React Native出现的目的本就是“learn once, write anywhere”,Facebook希望人们能够学习一次,到处使用,但是同样是JS代码,从前端移植到RN,或者RN移植到前端,却并没有想象的那么容易。事实上除了语法相同外,还有很多不一样的地方,导致我们没法做到直接copy,以下是我在做迁移代码的时候,总结的一些经验

    1 Antd Mobile

    Antd Mobile的蚂蚁金服开源的一套UI组件,已经实现了前端、iOS、Android的三端统一UI,推荐大家多多使用

    1.1 Flex

    1.1.1 使用Flex的参数

    多用direction justify align 属性,因为某些移动端浏览器不支持Flex功能,影响到适配,而antd已经帮我做过了,只是需要我们采用以下写法,否则无效 例如:

    Good:

    <Flex  direction="column" align="start" justify="startt">
    复制代码

    Bad:(这种写法并没有利用到Flex本身的适配)

    <Flex style={{  
    flexDirection: "column",  
    backgroundColor: "white",
    justifyContent: "flex-start"
    }}>
    复制代码

    这样的话,因为前端的兼容性你需要写很多适配的css代码,例如

    {
          display: flex;
          display: -webkit-flex;
          flex-direction: row;
          -webkit-flex-direction: row;
          justify-content: flex-start;
          -webkit-justify-content: flex-start;
          align-items: center;
          -webkit-align-items: center;
    }
    复制代码

    1.1.2 显示指定flexDirection

    因为某些情况下Flex无法使用,例如RN端的Touchable组件只能包裹原生组件,必须用View替代Flex,而View和Flex的默认方向是不一样的,因此显示写出flexDirection,明确告知方向,方便迁移者改动代码

    Good:

    <Flex  direction="column">
         {...}
    </Flex>
    复制代码

    Bad:(替换成View时候方向会错)

    <Flex>
         {...}
    </Flex>
    复制代码

    1.2 ListView

    在RN 0.51版本下,antd的ListView会报错,还是使用RN提供的ListView,只需要修改import ListView即可,其他写法完全一致

    // 不推荐
    import { ListView } from "antd-mobile";
    // 推荐
    import { ListView } from "react-native";
    复制代码

    2 代码规范

    2.1 尽量组件化

    某些页面很复杂,常见的一种写法是将页面拆分成若干模块,每个模块写一个moduleRender函数,再在render函数里分别调用,类似以下

    Bad:

    class SomeComponent extends Component {
          renderSubOne() {
            return <Flex>{...}</Flex>;
          }
          renderSubTwo() {
            return <Flex>{...}</Flex>;
          }
          renderSubThree() {
            return <Flex>{...}</Flex>;
          }
          render() {
             return <Flex>
                {this.renderSubOne()}
                {this.renderSubTwo()}
                {this.renderSubThree()}
              </Flex>
          }
    }
    复制代码

    Good:

    1. 因为迁移很可能是迁移某一部分,尽量拆分成组件迁移起来更灵活
    2. 这种写法性能更高,分开写的话每个组件有自己的生命周期,某个子组件刷新时不会影响父组件
    class SomeComponent extends Component {
          render() {
             return <Flex>
                <SubOne />
                <SubTwo />
                <SubThree />
              </Flex>
          }
    }
    复制代码

    2.2 不要用css

    关于样式的写法,RN和前端有个显著的差别 RN:

    import { StyleSheet } from "react-native";
    
    const styles = StyleSheet.create({
     someContainer: {
        fontSize:16,
        fontWeight: 'bold',
      },
      ...
    })
    
    <Flex style={styles.container} />
    复制代码

    前端:

    // js 文件
    import CSSModules from 'react-css-modules';
    ...
    
    @CSSModules(styles)
    ...
    
    <Flex className="someContainer">
    复制代码
    // css文件
    .someContainer{
          font-size: 16px;
          font-weight: bold;
     }
    复制代码

    由上面示例可知

    1. 前端的样式是藏在css文件内的,迁移起来需要一一去查找
    2. 关键字命名不同(font-size,fontSize)

    而这些不同都需要我们迁移的时候一一手动修改,工作量很大

    推荐的写法,是统一使用RN的写法:

    // 前端js文件
    const styles = { // 这里不需要像RN一样,使用StyleSheet.create
     someContainer: {
        fontSize:16,
        fontWeight: 'bold',
      },
      ...
    }
    
    <div style={styles.container} />
    复制代码

    2.3 第三方组件

    挑选第三方组件要注意

    1. 尽量使用原生,或者antd
    2. 尽量选择支持前端和移动端的
    3. 尽量选择有人维护的
    4. 如果git无人维护或者年代久远,建议将代码copy过来,而不是用npm管理,因为RN和React版本更新的原因,常常需要手动修改部分代码,才能运行,因此直接copy至工程里,比较方便

    3 差异

    有一些差异是前端和RN天然的差异,需要注意

    3.1 Image

    加载图片资源在两端写法并不一样,需要手动修改

    前端:

    <img style={{ width: 98, height: 82 }} src={nullImg} alt="nullImg" />
    复制代码

    RN:

    <Image style={{ height: 15, width: 15, marginLeft: 10 }} source={{ uri: 'search3' }} />
    复制代码

    3.1.1 RN的Image

    需要特别注意一下的是,React Native 0.50.3以后,Image组件不再能包裹child

    <Image> // 0.50.3以后,这种写法报错
          {...child...}
    </Image>
    复制代码

    如果确实需要,只能用绝对位置了

    3.2 Text文本

    前端渲染文本有多种标签,divspan等,但是RN端只有一种Text,这在迁移时会带来很大的工作量,文本散落在各个地方,需要人工一一替换。 推荐使用react-intl,RN端是react-intl-native这个有Yahoo提供的第三方组件,实现了在前端、RN端的统一

    Bad:

    <div>文本</div>
    <span>文本</span>
    复制代码

    Good:

      <FormattedMessage
        style={styles.valueDesc}
        id="someId"
        defaultMessage={text}
      />
    复制代码

    3.3 响应事件

    前端:

    // 可以添加在任何标签上
    <div onclick={()=>{}} />
    <img onclick={()=>{}} />
    复制代码

    RN:

    <TouchableHighlight onPress={}>// 只能有一个子元素,且必须是RN原生组件,不能是自定义组件
      <View> // 要用一个View来包裹更多的元素
          {child}
      </View
    </TouchableHighlight>
    复制代码

    由上可知,点击事件迁移时,常常需要改动较多的代码

    1. 增加TouchableHighlight标签
    2. 原标签下,如果有多个子标签,那么还需要增加View来嵌套,还要注意样式保持不变
    3. onClick改成onPress

    3.4 路由跳转

    • 前端使用react-route
    • RN端根据具体情况会有不同选择,例如react-navigationreact-native-navigation

    写法上会有不同,需要迁移者根据具体选择的库,手动修改

    3.5 PropTypes

    PropTypes是React提供的一种类型检测工具,但是随着版本的变迁,从React 15.5起,PropTypes被移出了React,形成了一个单独的库,如果前端和RN两边版本不一致,还有可能需要人工大量修改

    // react version < 15.5
    import React, { Component, PropTypes } from 'react';
    
    // react version >= 15.5
    import React, { Component } from 'react';
    import PropTypes from 'prop-types';
    复制代码

    3.6 全局变量

    前端js代码的运行环境通常是浏览器,浏览器本身提供全局变量window,而RN端则没有,因此不要在前端使用window全局变量,而是要使用导入文件

    Bad:

    window.someVar = var
    复制代码

    Good:

    // 新建constants.js文件
    const object = {
       website:'http://www.hao123.com',
       name:'好123',
    };
    export default object;
    
    // 需要使用时导入
    import constants from './constansts.js'
    <Text>{constants.name}</Text>
    复制代码
    展开全文
  • React Flow代码静态检查

    2019-04-30 01:22:01
    Flow是Facebook开源的静态代码检查工具,他的作用是在运行代码之前对React组件以及Jsx语法进行静态代码的检查以发现一些可能存在的问题。Flow可以用于所有前端开发的项目而不仅仅局限于React,码友们可以到官网仔细...

    Flow

    Flow是Facebook开源的静态代码检查工具,他的作用是在运行代码之前对React组件以及Jsx语法进行静态代码的检查以发现一些可能存在的问题。Flow可以用于所有前端开发的项目而不仅仅局限于React,码友们可以到 官网仔细了解(友情提示:可能需要VPN,非常不稳定),本文只介绍如何配合React开发使用。

    Flow仅仅是一个用于检查的工具,安装使用都很方便,使用时注意以下3点即可:

    1. 将Flow增加到我们的项目中。
    2. 确保编译之后的代码移除了Flow相关的语法。
    3. 在需要检查的地方增加了Flow相关的类型注解。(类似与Java的Annotation机制)

    接下来我们来一一说明以上三点的具体内容。码友们边阅读边操作即可。

    将Flow增加到我们的项目中

    安装最新版本的Flow:

    Npm:

    npm install --save-dev flow-bin

    安装完成之后在package.json文件中增加执行脚本:

    {
      // ...
      "scripts": {
        "your-script-name": "flow",
        // ...
      },
      // ...
    }

    然后初始化Flow:

    npm run flow init

    执行完成后,Flow会在终端输出一下内容:

    > yourProjectName@1.0.0 flow /yourProjectPath
    > flow "init"
    

    然后在根目录下生成一个名为 .flowconfig 的文件,打开之后是这样的:

    [ignore]
    
    [include]
    
    [libs]
    
    [lints]
    
    [options]
    
    [strict]
    

    基本上,配置文件没有什么特殊需求是不用去配置的,Flow默认涵盖了当前目录之后的所有文件。[include]用于引入项目之外的文件。例如:

    [include]
    
    ../otherProject/a.js
    
    [libs]
    

    他会将和当前项目平级的otherProject/a.js 文件纳入进来。关于配置文件请看这里

    编译之后的代码移除Flow相关的语法

    Flow在JavaScript语法的基础上增加了一些 注解(annotation)进行了扩展。因此浏览器无法正确的解读这些Flow相关的语法,我们必须在编译之后的代码中(最终发布的代码)将增加的Flow注解移除掉。具体方法需要看我们使用了什么样的编译工具。下面将说明一些React开发常用的编译工具

    Create React App

    如果你的项目是使用Create React App直接创建的。那么移除Flow语法的事项就不用操心了,Create React App已经帮你搞定了这个事,直接跳过这一小节吧。

    Babel

    在15.x版本之前入坑React的码友应该绝大部分都用的Babel作为语法糖编译器,那个时候毕竟Create React App完全没有成熟。如果使用Babel我们还需要安装一个Babel对于Flow的preset:

    npm install --save-dev babel-preset-flow

    然后,我们需要在项目根目录Babel的配置文件 .babelrc 中添加一个Flow相关的preset:

    {
      "presets": [
        "flow",
        //other config
      ]
    }

    其他方式

    如果你既没有使用Create React App也没使用Babel作为语法糖编译器,那么可以使用 flow-remove-types 这个工具在发布之前移除Flow代码。

    运行Flow

    完成上述步骤之后,就可以开始运行flow了:

    npm run flow

    然后会输类似一下的内容:

    > yourProjectName@1.0.0 flow /yourProjectPath
    > flow
    
    Launching Flow server for /yourProjectPath
    Spawned flow server (pid=10705)
    Logs will go to /tmp/flow/zSworkzSchkuizSone-big-website.log
    Monitor logs will go to /tmp/flow/zSworkzSchkuizSone-big-website.monitor_log
    No errors!
    

    第一次运行会生成很多临时文件比较慢,之后会快许多。

    增加Flow注解

    如果你了解C++/C#的元编程或者Java的Annotation,那么理解Flow的Annotation就会非常轻松。大概就是在文件、方法、代码块之前增加一个注解(Annotation)用来告知Flow的执行行为。

    首先,Flow只检查包含 // @flow 注解的文件。所以如果需要检查,我们需要这样编写我们的文件:

    // @flow
    import React from 'react'
    
    class MyComponent extends React.Component {
        render(){
            return (<div>MyComponent</div>)
        }
    }
    
    export default MyComponent

    然后我们再运行Flow就变成这样的风格了:

    > yourProjectName@1.0.0 flow /yourProjectPath
    > flow
    
    Error ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ dev/src/home/test.js:5:21
    
    Cannot use property Component [1] with less than 1 type argument.
    
         dev/src/home/test.js
          23import React from 'react'
          45class MyComponent extends React.Component {
          6render(){
          7return (<div>MyComponent</div>)
          8│     }
    
         /tmp/flow/flowlib_cc1898a/react.js
     [1] 26declare class React$Component<Props, State = void> {

    到这里,Flow已经算是安装成功了,接下来的事是要增加各种注解以加强类型限定或者参数检测。之后的内容将简要介绍flow的相关语法规则。

    React组件参数检查

    React组件参数检查介绍了React通过PropType机制限定使用者使用组件传递的参数类型以及范围,但是PropType是一种运行检测机制,在程序跑起来之后获取到具体数据才会执行检查。而Flow是静态检查,是在代码编译运行之前进行一次检查,两者相辅相成互不干扰。

    Props参数检查

    承接上面 MyComponent 的例子,我们引入Flow的注解对代码进行检查:

    // @flow
    // flow的例子,可以看看和PropType的差异在哪
    import React from 'react'
    
    type Props = {
        num : number,
        text : ?string
    }
    
    //通过<>引入Flow类型检查
    //可以直接写成 React.Component<{num : number, text ?: string}>这样的形式
    class MyComponent extends React.Component<Props> {
        render(){
            return (<div>{this.props.num}\{this.props.text}</div>)
        }
    }
    
    export default MyComponent

    然后在运行Flow,输出了No Error。

    然后我们使用这个组件:

    // @flow
    // flow的例子,可以看看和PropType的差异在哪
    import React from 'react'
    
    type Props = {
        num : number,
        text : ?string
    }
    
    class MyComponent extends React.Component<Props> {
        render(){
            this.props.myValue;
            return (<div>{this.props.num}\{this.props.text}</div>)
        }
    }
    
    //void 表示 undefined 不传递参数
    //这里传递类型发生错误
    const UseComponent = (props : void) =>(<MyComponent num="2" text={2}/>)
    
    export default UseComponent

    运行flow之后输出:

    Error ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ dev/src/home/test.js:12:20
    
    Cannot get this.props.myValue because property myValue is missing in Props [1].
    
          9│
     [1] 10class MyComponent extends React.Component<Props> {
         11render(){
         12this.props.myValue;
         13return (<div>{this.props.num}\{this.props.text}</div>)
         14│     }
         15│ }
    
    
    Error ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ dev/src/home/test.js:17:40
    
    Cannot create MyComponent element because:
     • string [1] is incompatible with number [2] in property num.
     • number [3] is incompatible with string [4] in property text.
    
        [2]  6│     num : number,
        [4]  7│     text : ?string
              :
            14│     }
            15│ }
            16│
     [1][3] 17const UseComponent = (props : void) =>(<MyComponent num="2" text={2}/>)
            1819export default UseComponent
    
    
    
    Found 3 errors

    输出内容可以看出一共有2个错误栏输出:

    • 第一栏表示myValue并没有声明。
    • 第二栏[1]违反了[2]的限定,[3]违反了[4]的限定。我们将组件变更为<MyComponent num={2} text="2"/>即可检查通过。

    增加对State的检查

    React的数据通过两处控制——props 和 state。Flow也提供了state数据的检查,我们在例子中增加state检查:

    // @flow
    // flow的例子,可以看看和PropType的差异在哪
    import React from 'react'
    
    type Props = {
        num : number,
        text : ?string
    }
    
    type State = {
        count: number,
    };
    
    class MyComponent extends React.Component<Props, State> {
        constructor(...props){
            super(...props)
            this.state = {count:'1'}
        }
    
        render(){
            return (<div>{this.props.num}\{this.props.text}</div>)
        }
    }
    
    const UseComponent = (props : void) =>(<MyComponent num={2} text="2"/>)
    
    export default UseComponent

    此时运行Flow会输出:

    Error ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ dev/src/home/test.js:17:29
    
    Cannot assign object literal to this.state because string [1] is incompatible
    with number [2] in property count.
    
     [2] 11│     count: number,
         12│ };
         1314class MyComponent extends React.Component<Props, State> {
         15│     constructor(...props){
         16super(...props)
     [1] 17this.state = {count:'1'}
         18│     }
         1920render(){
    

    检测出state.count在构造函数中赋值的类型错误。

    组件默认值

    使用Flow后一样可以使用默认值,但是必须要注意默认值的类型要和注解声明的一致:

    import * as React from 'react';
    
    type Props = {
      foo: number, 
    };
    
    class MyComponent extends React.Component<Props> {
      static defaultProps = {
        foo: 42, 
      };
    }

    函数类型的组件

    除了使用Class关键字,使用函数同样可以构造一个React组件,配合Flow使用:

    import React from 'react';
    
    type Props = {//参数检查
      foo: number,
      bar?: string,
    };
    
    function MyComponent(props: Props) {
      return <div>{props.bar}</div>;
    }
    
    MyComponent.defaultProps = {
      foo: 42 //指定默认值
    };

    React事件、子组件、高阶组件检查扩展

    除了对单个组件基本的检查,Flow还提供了对React事件、refs、子组件、高阶组件、Redux。本文就不一一介绍了,有需要的码友可以按照下面的资源清单去了解相关的内容:

    类型检查扩展

    Flow会检查所有的JavaScript基础类型——Boolean、String、Number、null、undefined(在Flow中用void代替)。除此之外还提供了一些操作符号,例如例子中的 text : ?string,他表示参数存在“没有值”的情况,除了传递string类型之外,还可以是null或undefined。需要特别注意的是,这里的没有值和JavaScript的表达式的“非”是两个概念,Flow的“没有值”只有null、void(undefined),而JavaScript表达式的“非”包含:null、undefined、0、false。

    除了前面的例子中给出的各种类型参数,Flow还有更丰富的检查功能,查看 这里 以了解更多内容。

    React数据类型参考

    对于Flow来说,除了常规的JavaScript数据类型之外,React也有自己特有的数据类型。比如React.Node、React.Key、React.Ref<>等。需要详细了解的,可以查看官网关于React类型的说明

    需要特别说明的是,如果所要使用React的类型,在通过ES6引入React对象时需要使用这样的方式:

    import * as React from 'react'
    //替换 import React from 'react'
    
    //或者单独引入一个类型
    //import type {Node} from 'react

    两者的差异在于ES6的星号import的特性,使用*号会将一个文件中的所有 export 内容组合成一个对象返回,而不使用星号仅仅能获取到exprot default 那个原型。而引入Flow后不会修改React的默认导出类型,因为默认导出不一定是一个对象,他会通过export为React扩展更多的类型。

    比如我们用React.Node限制render方法的返回类型:

    import * as React from 'react'
    class MyComponent extends React.Component<{}> {
      render(): React.Node {
        // ...
      }
    }

    遇到的一些问题

    我在使用的过程中目前遇到的问题之一是import 样式资源 或  图片时报 “./xxx.scss. Required module not found” 的异常,查看官方文档了解Flow只支持.js、.jsx、.mjs、.json的文件,如果需要导入其他文件需要并支持需要扩展options。在.flowconfig添加options:

    [ignore]
    [include]
    [libs]
    [lints]
    [options]
    module.file_ext=.scss
    [strict]

    此外,某些IDE对Flow的支持不是很好。我目前所使用的webstorm 2017.3.5相对还不错,不过切记要到File->Setting->Languages&Frameworks->Javascript中将version设置为Flow。

    写在最后的使用心得

    引入并按照Flow的规范去约束每一个组件会导致开发量增加不少(当然你引入不用是另外一回事,但是不用引入他做什么?)。搭建好Flow的框架仅仅是开始,之后除了团队成员要去了解flow的使用方法,早期还会遇到各种坑需要去解决。而且Flow也要比React的 PropTypes ”重“许多。

    JavaScript本来是一个类型推导的原型语言,弄个Flow进来搞得越来越像Java这种强类型语言,也不知道是好是坏,而Java10又学JavaScript等加入了val这种可以类型推导的关键字....。

    总的来说引入规范是有成本的,具体要看团队规模以及项目大小,不是引入越多的技术栈就越有逼格。如果你独立项目的前端开发人数并不多,或者代码膨胀(代码腐烂)速度也没有让你措手不及,建议慎重引入Flow。个人觉得Flow除了开发人员自检还要整合到整个测试框架中,在集成测试或某个版本的代码发布之前进行集中检查。需要思考它在项目的开发、测试、仿真、上线迭代周期中扮演的角色,甚至整合到类似与CMMI之类的管理流程去反向量化考核代码质量。

    展开全文
  • 自动保存代码的时候希望可以格式话代码,下面是我的一些配置! { "window.zoomLevel": 0, "editor.fontSize": 20, "editor.tabSize": 2, "vsicons.projectDetection.autoReload": true, "git....

    自动保存代码的时候希望可以格式话代码,下面是我的一些配置

    // 将设置放入此文件中以覆盖默认设置
    {
      "editor.wrappingColumn": -1,
      // 每行最大字符数(0 = 禁用)。
      "html.format.wrapLineLength": 0,
      // 为指定的语法定义配置文件或使用带有特定规则的配置文件。
      "emmet.syntaxProfiles": {
        "javascript": "jsx"
      },
      "window.zoomLevel": 0,
      "editor.fontSize": 20,
      "editor.tabSize": 2,
      "explorer.confirmDelete": false,
      "git.enableSmartCommit": true,
      "emmet.triggerExpansionOnTab": true,
      "search.quickOpen.includeSymbols": true,
      "workbench.startupEditor": "newUntitledFile",
      "explorer.confirmDragAndDrop": false,
      "editor.suggestSelection": "first",
      "vsintellicode.modify.editor.suggestSelection": "automaticallyOverrodeDefaultValue",
      "files.associations": {
        "*.cjson": "jsonc",
        "*.wxss": "css",
        "*.wxs": "javascript",
        "*.mina": "vue",
        "*.jsx": "javascriptreact",
        ".babalrc": "jsonc"
      },
      "emmet.includeLanguages": {
        "wxml": "html"
      },
      "minapp-vscode.disableAutoConfig": true,
      "editor.formatOnSave": true,
      "editor.formatOnType": false,
      "editor.quickSuggestions": {
        "strings": true
      },
      "[vue]": {},
      "workbench.fontAliasing": "antialiased",
      "javascript.updateImportsOnFileMove.enabled": "always",
      "[javascript]": {
        "editor.defaultFormatter": "esbenp.prettier-vscode"
      },
      "flowide.enabled": false,
      "flow.useBundledFlow": false,
      "flow.useCodeSnippetOnFunctionSuggest": false,
      "flow.useLSP": false,
      "flowide.useCodeSnippetsOnFunctionSuggest": false,
      "flow.useNPMPackagedFlow": false,
      "flow.stopFlowOnExit": false,
      "flow.runOnEdit": false,
      "flow.showStatus": false,
      "flow.enabled": false,
      "typescript.format.insertSpaceAfterKeywordsInControlFlowStatements": false,
      "javascript.format.insertSpaceAfterKeywordsInControlFlowStatements": false,
      "git.alwaysShowStagedChangesResourceGroup": true,
      "git.autoStash": true,
      "search.useGlobalIgnoreFiles": true,
      "git.allowForcePush": true,
      "git.autofetch": true,
      "git.alwaysSignOff": true,
      "git.fetchOnPull": true,
      "git.confirmSync": false,
      "typescript.updateImportsOnFileMove.enabled": "always",
      "editor.formatOnPaste": true,
      "[typescriptreact]": {
        "editor.defaultFormatter": "esbenp.prettier-vscode"
      },
    }

     

     

    展开全文
  • 版本更新背景:react-native发布最新版本:最新版本为0.49.3。 此版本最新改动是统一了入口文件,此版本不管Ios还是Android入口文件都是index.js,其中,页面元素入口为:App.js文件。 (一) 命名规约 1. 【强制】...
  • 一.react一般规范 1.基本结构(使用mobx) ├── build 构建脚本目录 │ ├── css 打包生成的css目录 │ ├── js 打包生成的js目录 │ ├──assets 打包生成的静态文件目录 │ ├── index.html 运行本地...
  • React-Native开发规范

    2017-12-14 10:35:29
    React-Native开发规范 标签(空格分隔): React-Native JavaScript ##一、编程规约 ###(一) 命名规约 【强制】 代码中命名均不能以下划线或美元符号开始,也不能以下划线或美元符号结束; `_name / $Object...
  • 前端开发规范手册(命名、HTML、CSS、JS、ES6、React)完整链接,欢迎出您的宝贵意见; GitHub源码地址,要是觉得文档还能凑合着看,欢迎 star ~ 不以规矩,不能成方圆。 对于团队而言,统一很重要,代码是写人...
  • 代码规范,也算是个老生常谈的问题。如何写出优秀漂亮的代码,是每个程序员的必修课。得益于开源伟大思想,许多大厂都制定了一系列的代码规范并发布在市场上。正所谓&amp;amp;amp;quot;前人栽树,后人乘凉&...
  • React入门简单练习

    2019-02-18 08:13:30
    利用react开发一个简单的博客系统, 首先我们简单的了解一下react的简单实用 博客的功能 信息发布系统 登录管理 博客发布 后台管理 权限管理 什么是前端开发? 将页面显示和业务逻辑独立,使用组件开发,解决传统...
  • 一般应用React Native(RN)后,随着使用页面的增加,bundle包(携带资源)会逐渐加大,这会带来以下两个缺点: 页面启动速度&内存占用增加 这是不言而喻的,一个页面启动时会加载其他无关页面的代码,自然会有...
  • Vue开发代码规范

    2019-06-11 12:08:07
    对团队协同开发造成了不小影响,在浪费时间解决冲突的同时也导致了最新代码被覆盖成 我作为其中一员也是不胜其烦,向leader提出问题后,把解决规范的任务到了我解决。虽然由于平时开发除了eslint以外,对相关内容...
  • 1、首先基于npm在全局安装create-react-app $ npm install -g create-react-app 2、在指定目录中创建一个react项目工程 $ create-react-app my-app my-app是自己设置的项目名称(遵循npm模块发布时的要求,...
  • react native 书写规范

    2017-08-03 17:16:05
    React/JSX代码规范 文件与组件命名 扩展名: 使用.js作为js文件的扩展名。如果同一个文件夹下有同名而不同作用的js文件,则通过中缀(小写)进一步区分,例如:HomeView.component.js,HomeView.style.js,Hom
  • 为什么80%的码农都做不了架构师?>>> ...
  •  可以用来规范代码的开发风格,统一代码习惯。     2、为什么使用 ESLint ?    统一代码规范 :  在开发中,可能会遇到很多不同的同事同时开发一个项目 ,或者在工作的调整中,会有接手一个项目,...
  • 本文出自《React Native学习笔记》系列文章。 React Native是基于React的,在开发React Native过程中少不了的需要用到React方面的知识。虽然官方也有相应的Document,但篇幅比较多,学起来比较枯燥。 通过《React ...
  • 于是乎,第一步,赶紧新建一个demo,飞快在 terminal 中输入 react native init yx_rnDemo ,漫长的等待后,项目成功建立。 然后用 IDE 打开 demo ,执行react-native run-android 命令,结果半路夭折,没跑起来。...
  • 原文地址:关于Google发布的JS代码规范,你需要了解什么? 相关资料:Google JS代码规范 1. 使用空格代替tab 除了每一行的终止符序列,ASCII水平空格符(0x20)是唯一一个可以出现在源文件中任意位置的空格...
1 2 3 4 5 ... 20
收藏数 7,042
精华内容 2,816
关键字:

react 给代码指定规范