Microsoft OCR Form Tool前端技术文档

前端技术文档

依着在北航软工NameNotFound小组里二次开发Microsoft OCR Form Tool开源项目的机会,前端程序猿零零碎碎写一点技术文档,目的是让新手尽快看懂项目代码。一些基础的东西我并不打算在技术文档里面将,只提一些最关键最常见的地方。如有错误,望不吝赐教。

技术栈

React

Redux

sass(scss)、css

office-ui-fabric-react

前言

当我还是新手的时候,总是看不懂html代码,自然而然在这上面倾注过多心血,比如

<div style={{display: "flex", width: "100px", background: "red", paddingLeft: "20px"}}>
    <span className="span-class"><strong>hello world</strong></span>
</div>

各种标签、各种样式,头都大了。这是前端工作人员的必经之路,但是把太多时间花在html和css上面是不可取的,我认为这叫捡了芝麻丢了西瓜。我的建议是:这些东西不需要记忆,只需要在遇到不懂的时候查百度就可以了,html标签和css样式毕竟太多太杂,需要时间和经验的积累。

React

React 是一个用于构建用户界面的Javascript库(当然Typescript也可以)。React部分大部分内容都可以通过看组件名、参数和返回值,揣测其含义来看懂;就像我们刚刚接触到一个全新的语言时一样:先注重语义,再去研究它的语法。

当我还是新手时,被javascript的箭头函数()=>{}困扰了足足一个星期,后来硬是靠着从大量的代码中猜,猜出了它的含义:

const func1 = (para1,para2) => {
    ;;
};

function func1(para1,para2) {
    ;;
}

其实可以认为是等价的,箭头函数实际上还就是一个函数,在调用的时候,一样通过func1()来调用。

React组件

React组件是React前端代码的积木,也是React的核心和精髓。一个典型的react组件可以通过函数或者ES6 class来定义。

function HelloMessage(props:any) {
    return (<h1>Hello World!</h1>);
}
interface IHelloMessageProps {
    array: [];
    numb: number;
    str: string;
    things: any;
}
class HelloMessage extends React.Component<IHelloMessageProps> {
  public render() {
    return (
       	<div>
            <h5>{this.props.numb}</h5>
            <h1>Hello World!</h1>
        </div>
    );
  }
}

上述两者均可。在第二种实现中,组件可以包含三大要素:state(表示组件当前的状态)、props(表示外界传给组件的参数)、render(渲染前端页面的函数)。

props在实现时最好定义一个接口(必须以大写字母I开头),外界在调用这个HelloMessage类(组件)的时候必须传入IHelloMessageProps接口定义的参数,而后就可以在组件内部几乎任意位置使用this.props.XXX来调用名为XXX的props。

render则是组件必须要实现的一个函数,用于渲染前端页面,调用组件时就相当于把render里面的前端代码渲染到被调用的位置。一个调用组件和渲染的实例如下:

import HelloMessage from '路径';
class AnotherComponent extends React.Component {
	render (){
        return (<HelloMessage array={[1,2,3]} numb={1} str={"hello"} things:undefined/>);
    }
}

React状态

React 把组件看成是一个状态机(State Machines)。通过与用户的交互,实现不同状态,然后渲染 UI,让用户界面和数据保持一致。React 里,只需更新组件的 state,然后根据新的 state 重新渲染用户界面(不要操作 DOM)。

一个典型的例子如下:

class Machine extends React.Component {
    constructor(props) {
        super(props);
        this.state = {count: 1};
    }
    public componentDidMount() {
        console.log("the counter inits", this.state.count);
    }
    public componentDidUpdate() {
        console.log("the counter has increment", this.state.count);
    }
    public render() {
		return (
            <div>
				<span>{this.state.count}</span>
				<Button onClick={handleClick}></Button>
			</div>
        );
    }
    private handleClick() {
        this.setState({count: this.state.count + 1});
    }
}

在组件被构建时,会执行constructor,导入调用方的给Machine的参数props,并且设定Machine 自身的状态为{count: 1};在准备执行render()进行渲染界面时,会执行componentDidMount() ,这个函数常用于为页面状态设定一些初始值,比如从数据库取数据、对数据进行整理。

用户在触发一些操作后,会调用一些定义好的处理该操作的函数。比如上方<Button onClick={handleClick}></Button>以为点击该按钮,就会调用我们自己定义的一个handleClick函数。

handleClick函数内,我们定义“用户点击了按钮之后我们要干什么”的逻辑,比如修改state,this.setState({count: this.state.count + 1});则是点击了按钮之后把count值由原来的count值加上1。

在发现自生状态发生改变之后,Machine组件会调用componentDidUpdate()方法,这个函数常用于页面状态修改后的一些操作,比如重新从数据库更新数据。然后页面会重新渲染,屏幕上显示的数字马上就从1变成了2。

异步处理

在JavaScript的世界中,所有代码都是单线程执行的。

在Microsoft OCR Form Tool中,由于前端需要直接访问Microsoft Azure,故在数据交互部分使用了很多异步处理。首先讲一下Promise:

function function1(resolved, rejected) => {
    const a = 1;
    wait 1000 years
    if (a === 1){resolved();}
    else{rejected();}
}
function good(){console.log("good");}
function bad(){console.log("bad");}
funtion main() {
	new Promise(function1).then(good).catch(bad);
    console.log("我先去忙别的");
}

Promise意味“承诺”,这个语句的意思就是:我保证你给我的函数能执行完,到时候把结果告诉你,现在你先去忙别的。上面main函数执行逻辑是这样的:

  1. 进入main函数

  2. 把执行function1的任务交给Promise,然后告诉他:“你做完了马上告诉我,失败了也及时打我电话”,然后main就不管了,直接执行下一条console

  3. 可怜的Promise被迫背锅,帮main执行function1

  4. 一千年后(wait 1000 years),Promise终于完成了任务,如果完成了,就执行resolved函数,告诉main:俺完成了你交给我的任务;或者失败了,也通知main。

在项目的数据交互中,常常用到这样的异步操作。但是ES6更新了await/async之后,异步的写法就更加简便了。如下是一个数据交互结构的伪代码:

public main = () => {
    console.log("before fetch");
    onFetch();
    console.log("behind fetch");
}
public onFetch = async (): Promise<void> => {
    const ret = await fetch('https://www.baidu.com/', {method: "GET"});
    const ret2 = await fetch('https://www.google.com/', {method: "GET"});
    console.log("after fetch");
    return ret.data;
}

这里的async说明onFetch函数是异步的,其中的await其实就是Promise。onFetch函数看到await就知道这是别人交代给我的委托,要我帮他完成,但是我先不妨碍他干下一步,于是onFetch()函数就先返回,让main执行 console.log("behind fetch")这一句。

onFetch()中await的第二个作用就能体现出来,那就是等待,await能强制fetch百度fetch谷歌两个操作按顺序进行,即使fetch百度中还有别的嵌套的Promise,也会在fetch百度之后再fetch谷歌

所以上述结构的执行顺序应该是这样:

  1. console.log("before fetch");
  2. 进入onFetch,开始fetch百度
  3. console.log("behind fetch");
  4. fetch百度结束,开始fetch谷歌
  5. fetch谷歌结束
  6. console.log("after fetch");
  7. onFetch()返回

结语

前端真实爆肝,积累十分重要,没事去看文档,天天项目练手,吃饭思索组件,喝水考虑异步,睡觉琢磨样式,努力跳出桎梏,早日不当码农。

posted @ 2020-05-26 09:20  lzhmark  阅读(209)  评论(0编辑  收藏  举报