webpack
November 21, 2017
react是一个前端库,着力在创造新的前端渲染的工作模式。 react单个可以做为开发库来单独使用,但是一般我们会将其与其他的相关库一起使用,在使用的时候,基本的react概念是不变的,只是在某些环节发生一些变化。 react 组件的生命周期 # 对于一个react的组件,其生命周期决定了其思维方式。 一个组件什么时候产生实例,在什么时候调用什么函数,决定了这个组件在什么时候做什么动作。 react严格的定义了组件的生命周期,分别是装载过程,更新过程,卸载过程。我们重点是要清楚,在装载和更新的过程中,react组件都要做哪些动作。 装载过程 # 当组件第一次被装载的时候,一次被调用的分别是: constructor componentWillMount render componentDidMount 这里我们使用的是ES6语法的方式来创建组建,所以有一些老的函数就没有被用到,也就不用再提了。 contructor # 并不是每个组件都需要构造函数,当有构造函数的时候,一定要先执行父类的构造函数。 class Sample extends React.Component{ contructor(props){ super(props); this.state = {foo: 'bar'}; this.onClickSubmitButton = this.onClickSubmitButton.bind(this); // this.onClickSubmitButton = ::this.onClickSubmitButton; } } 在构造函数中,我们要: 给内部数据初始化,也就是赋值state。 将函数绑定this,否则内部使用的数据会是错的,绑定以后使用的数据就是本组件中的数据。 componentWillMount & componentDidMount # 这是一组函数,分别在render之前和之后调用。 WillComponent这个函数其实有点多余,因为所有在这里做的事情我们都可以在构造函数中做,因此基本不会用到这个函数。 DidMount 就是在render函数过后调用,此时页面已经渲染出来。 在这个函数的执行时间上,还是比较有文章的。 举个例子,组件A有三个子组件B,B都有实现这个函数,在A的render函数中,会执行B的render函数,但是当一个B的render函数执行完后,并不立即执行这个B的DidMount函数,而是等所有的B的render函数被调用完毕后才一起调用。当然也是按顺序的。 由于此时已经渲染出来,所以我们也就可以获得DOM树上的节点。因此我们可以在这时候请求服务器去填充数据。这在我们使用其他的一些前端库的时候比较方便。例如在使用jQuery的时候,jQuery只能对已经存在的元素进行操作,所以此时正是调用jquery的时候。 render # 但凡React组件都要实现这个函数,因为这个函数在Component中没有默认的实现。 render函数并不直接操作渲染,而是返回一个JSX语法的描述。最终的渲染动作由react来做。如过没有什么要渲染就可以返回一个null或者false。 render应该是一个纯函数,只接受state和props并产生返回值,没有其他任何的副作用。 更新过程 # 要实现交互,就需要更新: componentWillReceiveProps shouldComponentUpdate componentWillUpdate render componentDidUpdate 在父组件发生Update调用render函数的时候,子组件就会经历更新过程。 componentWillReceiveProps # 这个函数在使用this.setState更改数据的时候不会被调用。因为这个函数根据新props的值来计算是不是要更新内部状态state。更新内部状态使用setState,因此不会产生循环调用。 这个函数接受一个参数, 新传进来的nextProps。 ...
在使用react做大型开发的时候,我们习惯使用redux来做数据管控。但是redux实在是太过繁琐,流程很长,不利于快速开发。 mobx是redux作者非常推荐的一个替代产品。 基本概念 # 与redux的长流程,严管控不同,mobx采用一种更直接的方式,和更自动化的方式管理应用的数据。 状态驱动页面更新 # 应用的state也就是其中的数据,就是应用此时所在的状态,状态的改变驱动页面的改变,这是共识,问题就是怎么设计使状态的改变驱动页面更新。 在mobx中,数据将被监视,当数据发生变化的时候,mobx会自动的知道哪些部分需要被刷新,而不需要程序员来指明更新什么。 应用的状态分成两种, 一种是数据本身组成的基本状态。还有一种是在数据基础上计算得出的衍生状态。在下面我们会讨论这些。 observable # 对于需要被监视的数据,就将他用注解的方式注明,需要被监视。 import { observable, computed } from "mobx"; class OrderLine { @observable price = 0; @observable amount = 1; @computed get total() { return this.price * this.amount; } } 就这样简单的就将一个变量纳入mobx的观察体系中。 被观察的可以是几乎所有的javascript的数据结构,但是,对于对象,建议一律转成map来观察,因为对象只会观察此时有的字段,对于未来新添加的字段需要手动加入观察, 但是map就可以将添加进来的新key一并纳入观察。 observer # 有了被观察的,就要有观察者, 这里的观察者会是前端的组件。通常来说,每个组件都应该是可以观察自身数据的,响应式的应用。 一个观察者,会在数据发生变化的时候自动更新自己的视图。 import {observer} from 'mobx-react'; @observer class TimerView extends React.Component { render() { return (<button onClick={this.onReset.bind(this)}> Seconds passed: {this.props.appState.timer} </button>); } onReset () { this. ...
这是一个react router的V4版本。被广泛使用, 非常流行。 Installation # react router 被包含在三个包中: react-router, react-router-dom, react-router-native. 你几乎不会直接安装react-router这个包,因为这是一个核心包,提供的是基础的react应用的路由库。 另外的两个分别提供了特殊的使用环境,前者是浏览器端, 后者是在react-native中。他们都完全包含react-router这个包中的内容。 npm install --save react-router-dom The Router # 在多页应用中,路由的设计尤为重要。 在react应用中,其实不存在真正的多页应用,而是使用一些浏览器操作来使得单页应用具备多页应用的功能。所谓的多页应用,应该是当你输入一个固定的链接时,得到的页面应该是一致的,并且具备后退的功能。但是在react应用中,页面并不会完全重新加载,而是以特殊的方式来组织,做局部加载,实现多页面的效果。 在浏览器应用中,我们使用的是<BrowserRouter>和<HashRouter>组件。当我们的请求的网站是一个动态的页面(Know how to respond to any possible URI)的时候,我们使用前者;如果是个纯静态的页面,我们使用后者。很明显我们大多数使用前者。 History # 每个router组件都有一个history的对象,用于记录浏览的记录,用于保持跟踪。 关于这一部分,暂时还没有研究。待研究。 Rendering a <Router> # <BrowserRouter> 只允许一个子组件的存在,所以跟react一个尿性的,我们可以将我们的应用封装成<App \>的形式来使用。 For example: import { BrowserRouter } from 'react-router-dom' ReactDOM.render(( <BrowserRouter> <App /> </BrowserRouter> ), document.getElementById('root')) The App # 在<App>中,我们就可以开始做布局的工作,我们可以不直接添加我们的路由体系,直接在<App>组件中添加一些组件。当我们添加我们的路由体系的时候,每个路由信息,都由一个<Route>组件来组织。 <Switch> <Route exact path='/' component={Home}/> {/* both /roster and /roster/:number begin with /roster */} <Route path='/roster' component={Roster}/> <Route path='/schedule' component={Schedule}/> </Switch> 其中, <Switch>组件用于列表匹配,从上到下依次匹配,直到匹配成功退出。 ...
如果想用react开发功能丰富,易于代码管理,易于扩展维护的项目,我们最好使用react的一整个体系。当然你单独使用react的部分也是可以的,写一些简单的应用也非常方便,没有必要把这个体系用上。 这篇文章作为入门的一篇略深一些的介绍,在全面学习之前我们至少要知道我们学的每个部分是做什么,为什么要在这里用这个。所以本文不讲一些代码的细节,而是从整体上把握这个技术栈,帮助你快速理解整个体系。 总结 # 一个比较好的实践是使用: react + redux + router, 来实现复杂的应用。 其中, react 承担了前端渲染控制的功能,redux承担了数据流转的控制功能,router则实现了路由控制的功能。 这三项,基本上可以保证我们做出的项目,结构清晰,易于维护。 在初学这写东西的时候,可能会觉得比较痛苦,因为你需要记住太多的东西和条条框框来开发,这好像限制了你的自由发挥,但是对于一个大型应用来说,或者即便是小应用,没有清晰的结构,就连你自己可能都看不懂自己的代码,后期维护的时候就基本上没有多少好体验了。 真正优秀的作品,都是在严格的限制下完成的,因为人的思维不应该去思考那些复杂的页面组成的逻辑关系,而是在实现自己的业务逻辑和计算算法上,那些结构,按照一个科学的方式来做就好。而react正是提供了这样一个科学的流程和方式。 react # 总的来说,在前端渲染方面,react非常出众, 一般我们将前端渲染的部分交给react来做。 react使用虚拟DOM的方式,来管理渲染。并且将一个页面分成多个组件进行管理,我们一般将组件按功能分开。当页面发生变化的时候,由react来决定哪里需要渲染,哪里不动,react来操纵浏览器的渲染。 与jQuery相比,更进一步, jQuery是将浏览器的一些操作封装出来,提供一组新的调用函数。但是react已经将浏览器的相关接口完全封装,并且屏蔽在react内部,提供了一种更自然和智能的方式来满足我们的需求。我们直接和react打交道,剩余的事情由react去和浏览器做。 react这个框架可以运行在浏览器端,也可以运行在服务器端, 我们称之为Universal渲染,这里的服务器端,就是Nodejs的平台了。我用的基本上都是浏览器端运行。只要将react的脚本发送到浏览器进行执行就可以了。 初的渲染是后端的事情,有了ajax后前端也可以进行渲染了。但是前端渲染会有个问题,就是搜索引擎不友好,因为搜索引擎并不会去跟js代码交互,只会爬取静态页面,所以使用后端渲染十分重要。这关乎你的网站被检索到的可能性。 基本构造 # react的逻辑很简单,一切都是组件。一个页面是由多个组件堆砌而成,每个组件之间互相隔离,仅通过数据交互,而数据交互的接口是固定的。所以对于一个组件内部来说,与外界是完全隔离的。 一个组件包括,样式和数据。数据我们通过请求服务器去获得。 每个组件通过提供一个render函数来返回这个组件,这个render函数只能返回一个元素。如果有多个元素需要被包起来成为一个元素返回。 组件之间可以是嵌套关系,这个和类的概念有点像,在一个类中可以包含另一个类作为自己的成员。也就是方便我们做组件封装,功能划分。 当我们写完一个react应用后,可以通过npm start的方式启动起来,当我们在浏览器访问对应的地址时,默认访问index.js, 所以我们的应用最终在这里组织起来。 ES6 & Javascript & JSX # 在写应用的时候,我们使用JSX 语法来写,JSX以ES6为基础,react提供了JSX解释器。在写完以后,JSX语法的代码会被编译成JS代码,最终在浏览器中运行的是Javascript的代码,关于webpack等工具,我暂时没有去学习,因为react提供了一个创建react应用的脚本,能够一键创建好这些配置文件,所以我就暂时直接使用了。 npm install --global create-react-app 这个命令就可以安装这个脚本,然后我们执行: create-react-app myapp, 就可以创建一个文件夹, 里面已经配置了一个简单的react应用。 然后我们进入这个文件夹,使用npm start就可以启动服务。 start的过程中,代码就会被编译,并打包成webpack中配置的打包形式。 如果此时我们改变代码,这个页面会动态更新。 react 原生的数据控制 # 在react中,数据主要有三个载体,分别是state、prop、context。其中state是组件内部状态数据,prop用于层层传递,context可以跨层传递。 react要实现各个组件独立,并且具有统一的结构,最重要的就是控制数据。在JS这种弱类型的语言中,这一点很好实现,重点就是设计一个接口形式。 react中的组件,都有两个变量,一个是prop, 一个是state。 其中,state是描述组件内部数据的,prop是对外的接口。也就是说,别人传过来的数据,会存在prop中。同时,prop是不可变的,组件自己不能更改prop的值, 只能改变state的值。 这种数据控制的方式,比较原始,从接口统一的角度来看挺好的,也符合基本的思维方式。 但是这种控制方式也存在很多问题,不用去深究这个框架的细节,我们也可以提出很多问题: 每个组件之间的数据冗余问题,很多数据统计的模块,该怎么存储数据? 复制一份出来?当要更改数据的时候,是不是还要传参给另一个组件通知他改变数据? ...