【前端实战】基础二 - hippowc/hippowc.github.io GitHub Wiki

概览

可以通过框架更加明确的看下需要哪些实战基础,譬如antd pro

  • typescript/es6 语法
  • react,jsx,tsx语法
  • dva 路由与数据

尽管每次都学,但过一段时间似乎有有点不懂,所以感觉应该是某些原理的地方还没有入门,这次尽量理解好,把几个不理解的点都摘出来,从这个点深挖进去。

关于技术选型

本次技术选型考虑到了项目实现成本,优先使用简单而基础的,框架性的或者具有趋势型的技术先不去考虑,譬如:

  • 使用标准es6 而不使用typescript
  • 获取数据使用比较简单的ajax,而先不使用dva框架

一些不易理解的点

react 组件(Components),元素(Elements)

elements 元素指的是最终在屏幕看到的,元素是不可以修改的。元素不会直接被使用,而是应该在组件中获得

const element = <h1>Hello, world</h1>;

而react组件是一个可重用的代码片段,特点是组件会返回一个react的元素,它可以通过一个js函数定义,也可以使用es6继承React.Component

react组件定义语法

React 允许将代码封装成组件(component),然后像插入普通 HTML 标签一样,在网页中插入这个组件。元素是React的最小元素,React中的元素是普通的js对象,不是html的Dom元素,但是react Dom可以保证react的元素和htm的dom元素保持一致,从实现来讲,我们把react的元素和html的元素都传递给ReactDOM.render()方法,实现两边渲染的一致。

var myDivElement = <div className="foo" />;
ReactDOM.render(myDivElement, document.getElementById('example'));
// 我们使用jsx来描述React元素,jsx是js的扩展,render方法负责对jsx语句进行解析,jsx就像是一种模板语言

组件的定义方法

  • 通过函数
function HelloMessage(props) {
    return <h1>Hello World!</h1>;
}
  • 通过ES6 class
class Welcome extends React.Component {
  render() {
    return <h1>Hello World!</h1>;
  }
}

以上 HelloMessage和Welcome都可以作为组件

state的声明方式

  • 使用构造函数
// 当一个class组件创建之后,constructor会首先被调用,
// 所以在construct中可以来初始化所有值,包括state。class实例在内存中已经被创建,所以可以使用this来为state赋值
class Son extends React.Component {
    constructor(props) {
          super(props)
          this.state = {date: new Date()}
    }
}
  • 直接在class中以属性的方式定义
class App extends React.Component {
  state = {
    loggedIn: false,
    currentState: "not-panic",
    someDefaultThing: this.props.whatever
  }

  render() {
    // whatever you like
  }
}
// state属性是直接引用的,并不是通过this.state来引用的
// state的作用域是在Class内部,并不是一个方法的内部

组件灵活的编写方式

jsx是一个非常好用且灵活的js扩展语言,同时也是一个模板语言,使用react最方便的地方在于,我们可以灵活的使用jsx编写组件。

我们已经知道在render的return方法中,返回值就是一个jsx语言描述的组件,包含了标签和js脚本。其实我们在jsx文件的任何方法中都可以使用jsx混写标签和js的语法编写,而不仅仅是在render方法中。其实有两点

  • jsx扩展了es6的模板语言,任何地方不仅可以写es6,也可以和标签混编。React会将标签编译为js对象
  • React关心的就是最终的render方法返回的是一个jsx的模板语言就好了
// 这个函数的返回值可以直接放在return方法中,实际就是构造了两个input
buildCom() {
    let c1 = <input />
    let c2 = <input />
    let c3 = [c1, c2]
    return c3
}

父子组件信息传递

借助引用改变子组件中数据,也可以改变父组件数据

在项目开发中发现,因为js中对象传递的都是引用,当我将父组件中定义的state中的对象,通过props传递给子组件,子组件修改了该对象的某个属性后,父组件state对象的属性其实也是被修改了。其实props和state之间的传值以及组件中setState方法应该都是传递的引用,而没有深度拷贝。

知道这个信息的我,感到无比方便。

通过props传值,props改变子组件未收到更新

有两种思路,一种是先把props中传递到state中,但是这种方式,state后续无法接受更新,需要配置使用方法一。第二种方法是直接在render方法中直接使用this.props,而不是在构造函数中先把props值传递给state,这两种都可以

  • 方法一
// 需要通过这个方法让子组件监听父组件传递props的变化
componentWillReceiveProps() {
   this.setState({ props})
}

目前react已经不推荐使用上述方法了改用这个:

static getDerivedStateFromProps(props, state) {
    return {
      srcStruct: props.srcStruct, // srcStruct为state中的属性
    }
  }
  • 方法二

在render方法中直接使用this.props.xxx

函数

ts中的函数

你可以在函数参数列表之后使用与变量相同的样式来注解返回类型

interface Foo {
  foo: string;
}

// Return type annotated as `: Foo`
function foo(sample: Foo): Foo {
  return sample;
}

对象中的函数

对象中的函数定义可以省略function

{
  reducers: {
    add() {}  // 等同于 add: function() {}
  },
  effects: {
    *addRemote() {}  // 等同于 addRemote: function*() {}
  },
}

胖箭头函数

  • 通过=>定义;属于匿名函数,即没有函数名称;
  • 箭头函数还会继承当前上下文的 this 关键字。
let fun = arg => arg;
解析:上面代码定义了一个叫fun的的函数,等号右侧(箭头左侧)是这个箭头函数的参数,右侧是函数;只有一个参数时,小括号省略;否则应写成:let fun = (a,b)=>a+b;
如果没有规定参数,那么小括号也不能省略: let fun = () => 5;
如果返回的是字面量,需要用小括号或大括号包裹。
let fun = () => ({name: ‘lzm’});
或者
Let fun = () => {return {name: ‘lzm’}}

:: 方法引用

箭头函数可以绑定 this 对象,大大减少了显式绑定 this 对象的写法(call、apply、bind)。但是,箭头函数并不适用于所有场合,所以现在有一个提案,提出了“函数绑定”(function bind)运算符,用来取代 call、apply、bind 调用。

函数绑定运算符是并排的两个冒号(::),双冒号左边是一个对象,右边是一个函数。改运算符会自动将左边的对象,作为上下文环境(即 this 对象),绑定到右边的函数上面。

在jdk8中也有双冒号运算符,叫做方法引用。其实是差不多的意思,但是使用方法绑定这个概念更容易理解。因为这个概念通过支持函数式编程的语言来理解比较方便。

当函数可以单独存在的时候,就会遇到这样一个问题,函数体内部有可能会有状态,引用 this 关键字,也可以理解为函数的上下文环境,同一个函数在不同的上下文中是不同的,那么函数在运行时就需要确定这个上下文,简单来说就是this。那么:: 左边就是用来绑定这个上下文。

java中也类似,不同的对象都有toString方法,但具体是哪个对象,toString是不一样的,所以执行时也需要绑定这个方法

获取组件属性的最佳实践

有时候,我们希望将函数绑定到组件,以获取到存放在组件上的某些属性,譬如 希望在onClick的方法中奖key作为参数传入。这个时候需要将函数绑定到这个组件。同时我们的click方法中又希望绑定的是上层组件,以获得state进行操作,这个时候,可以先在上层组件绑定原组件,在onClick后面使用箭头函数再次封装原方法,将组件参数传递给原函数

这样在原函数内部,可以使用this访问原来的state,又可以使用参数访问被操作的组件的参数

handleClick = (e) => {xxx}
<Foo onClick={e => {this.handleClick(e)}}></Foo>

export 和 export default

  • export与export default均可用于导出常量、函数、文件、模块等,你可以在其它文件或模块中通过import+(常量 | 函数 | 文件 | 模块)名的方式,将其导入,以便能够对其进行使用
  • 在一个文件或模块中,export、import可以有多个,export default仅有一个
  • 通过export方式导出,在导入时要加{ },export default则不需要;export 对应的 import 需要知道 export抛出的变量名或函数名 import{a,b} export default对应的 import 不需要知道 export抛出的变量名或函数名 import anyname
1.export
//a.js
export const str = "blablabla~";
export function log(sth) { 
  return sth;
}
对应的导入方式:

//b.js
import { str, log } from 'a'; //也可以分开写两次,导入的时候带花括号

2.export default
//a.js
const str = "blablabla~";
export default str;
对应的导入方式:

//b.js
import str from 'a'; //导入的时候没有花括号
  • 使用export default命令,为模块指定默认输出,这样就不需要知道所要加载模块的变量名
//a.js
let sex = "boy";
export default sex(sex不能加大括号)
//原本直接export sex外部是无法识别的,加上default就可以了.但是一个文件内最多只能有一个export default。
其实此处相当于为sex变量值"boy"起了一个系统默认的变量名default,自然default只能有一个值,所以一个文件内不能有多个export default

// b.js
本质上,a.js文件的export default输出一个叫做default的变量,然后系统允许你为它取任意名字。所以可以为import的模块起任何变量名,且不需要用大括号包含
import any from "./a.js"
import any12 from "./a.js" 
console.log(any,any12)   // boy,boy

antd pro中的例子

antd pro 中的 组件定义

下面这个是antd pro定义一个组件的语法

export default (): React.ReactNode => (
    <PageHeaderWrapper>
        <div>new page</div>
    </PageHeaderWrapper>
);
解析
首先使用的是函数定义React组件,函数返回的为React的element

该组件是一个箭头函数,返回值为一个字面量,使用()包裹,也可以使用{},返回值为React.ReactNode类型

数据与路由

数据获取方式

$.ajax()

import $ from 'jquery';
$.ajax({
            type:'get',
            url:'data/data.json',
            success:function(res){
                console.log(res);
            }
        })

axios

axios是独立的ajax插件,不依赖于react,在VUE中甚至原生JS开发的项目中也可以用,现在已经是前端主流的ajax插件。

安装axios npm install axios --save

import axios from 'axios';
axios.get('/user', {
    params: {
      ID: 12345
    }
  })
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });

axios.post('/user', {
    firstName: 'Fred',
    lastName: 'Flintstone'
  })
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });

解决异步的组件

Promises

Promise 用于更优雅地处理异步请求。比如发起异步请求:

fetch('/api/todos')
  .then(res => res.json())
  .then(data => ({ data }))
  .catch(err => ({ err }));
定义 Promise 
const delay = (timeout) => {
  return new Promise(resolve => {
    setTimeout(resolve, timeout);
  });
};

delay(1000).then(_ => {
  console.log('executed');
});

Generators

dva 的 effects 是通过 generator 组织的。Generator 返回的是迭代器,通过 yield 关键字实现暂停功能。这是一个典型的 dva effect,通过 yield 把异步逻辑通过同步的方式组织起来。

app.model({
  namespace: 'todos',
  effects: {
    *addRemote({ payload: todo }, { put, call }) {
      yield call(addTodo, todo);
      yield put({ type: 'add', payload: todo });
    },
  },
});
⚠️ **GitHub.com Fallback** ⚠️