【前端实战】基础二 - hippowc/hippowc.github.io GitHub Wiki
可以通过框架更加明确的看下需要哪些实战基础,譬如antd pro
- typescript/es6 语法
- react,jsx,tsx语法
- dva 路由与数据
尽管每次都学,但过一段时间似乎有有点不懂,所以感觉应该是某些原理的地方还没有入门,这次尽量理解好,把几个不理解的点都摘出来,从这个点深挖进去。
本次技术选型考虑到了项目实现成本,优先使用简单而基础的,框架性的或者具有趋势型的技术先不去考虑,譬如:
- 使用标准es6 而不使用typescript
- 获取数据使用比较简单的ajax,而先不使用dva框架
elements 元素指的是最终在屏幕看到的,元素是不可以修改的。元素不会直接被使用,而是应该在组件中获得
const element = <h1>Hello, world</h1>;
而react组件是一个可重用的代码片段,特点是组件会返回一个react的元素,它可以通过一个js函数定义,也可以使用es6继承React.Component
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都可以作为组件
- 使用构造函数
// 当一个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中传递到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
你可以在函数参数列表之后使用与变量相同的样式来注解返回类型
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均可用于导出常量、函数、文件、模块等,你可以在其它文件或模块中通过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定义一个组件的语法
export default (): React.ReactNode => (
<PageHeaderWrapper>
<div>new page</div>
</PageHeaderWrapper>
);
解析
首先使用的是函数定义React组件,函数返回的为React的element
该组件是一个箭头函数,返回值为一个字面量,使用()包裹,也可以使用{},返回值为React.ReactNode类型
import $ from 'jquery';
$.ajax({
type:'get',
url:'data/data.json',
success:function(res){
console.log(res);
}
})
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);
});
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');
});
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 });
},
},
});