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函数执行逻辑是这样的:
-
进入main函数
-
把执行function1的任务交给Promise,然后告诉他:“你做完了马上告诉我,失败了也及时打我电话”,然后main就不管了,直接执行下一条console
-
可怜的Promise被迫背锅,帮main执行function1
-
一千年后(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谷歌
。
所以上述结构的执行顺序应该是这样:
console.log("before fetch");
- 进入
onFetch
,开始fetch百度
console.log("behind fetch");
fetch百度
结束,开始fetch谷歌
fetch谷歌
结束console.log("after fetch");
onFetch()
返回
结语
前端真实爆肝,积累十分重要,没事去看文档,天天项目练手,吃饭思索组件,喝水考虑异步,睡觉琢磨样式,努力跳出桎梏,早日不当码农。