# 组件的代码设计基本思路 我们以一个分页组件为例,阐述一下FineUI中组件代码的基本思路 ## 控件功能 上一页,下一页,带个输入框,有总页数.四个按钮带disabled逻辑,无上一页或者下一页时候灰化 ![示例](../../../images/9.png) ## 第一步: 明确对外提供的props 总页数,当前页数 就这两个属性够了 ```javascript const props = { total: 200, page: 10, } ``` ## 第二步: 明确要对外提供哪些方法 一个getValue方法加一个populate就行了 ```javascript // 获取当前页码 const page = this.page.getValue() // 设置总页数和页码 this.pager.attr("total", 100) this.pager.attr("page", 1) this.pager.populate() ``` ## 第三步: 明确对外提供哪些事件 事件只需要一个就够了,通知外部使用者当前page,养成良好习惯,事件尽量带上参数 ```javascript this.fireEvent("EVENT_CHANGE", page) ``` ## 生命周期 万物皆render,能render一遍就执行好的,尽量别用其他生命周期,mounted之类的,避免没必要的回流重绘 如果组件是异步的, beforeInit/beforeRender.那么你在render中一定可以拿到所有的状态了,就没必要再通过一次watch来实现效果.当然,有些事情必须在mounted中做(例如获取组件宽高)那就另说了. ## 抛砖引玉 比如这个pager组件按钮的灰化,我就借用最新的响应式写个例子 [自动相应变更](http://fanruan.design/doc.html?post=afe4c84120) ```demo const Pager = BI.inherit(BI.Widget, { props: { total: 1, page: 1, }, init: function () { this.state = Fix.define({ currentPage: this.options.page, total: this.options.total, }); }, render: function () { return { type: "bi.vertical_adapt", columnSize: [24, 24, "fill", "", 24, 24], hgap: 5, items: [ { type: "bi.icon_button", cls: "pre-page-h-font", disabled: () => { return this.state.currentPage === 1; }, handler: () => { this.state.currentPage = 1; this.fireEvent("EVENT_CHANGE", this.getValue()); }, }, { type: "bi.icon_button", cls: "pre-page-h-font", disabled: () => { return this.state.currentPage === 1; }, handler: () => { this.state.currentPage--; this.fireEvent("EVENT_CHANGE", this.getValue()); }, }, { type: "bi.text_editor", ref: ref => this.editor = ref, value: () => this.state.currentPage, validationChecker: (v) => (BI.parseInt(v) >= 1) && (BI.parseInt(v) <= this.state.total), errorText: "error", listeners: [ { eventName: "EVENT_CHANGE", action: () => { this.state.currentPage = BI.parseInt(this.editor.getValue()); this.fireEvent("EVENT_CHANGE", this.getValue()); }, }, ], }, { type: "bi.label", text: () => "/" + this.state.total, }, { type: "bi.icon_button", cls: "next-page-h-font", disabled: () => { return this.state.currentPage === this.state.total; }, handler: () => { this.state.currentPage++; this.fireEvent("EVENT_CHANGE", this.getValue()); }, }, { type: "bi.icon_button", cls: "next-page-h-font", disabled: () => { return this.state.currentPage === this.state.total; }, handler: () => { this.state.currentPage = this.state.total; this.fireEvent("EVENT_CHANGE", this.getValue()); }, }, ], }; }, getValue: function () { return this.state.currentPage; }, populate: function () { this.state.currentPage = this.options.page; this.state.total = this.options.total; }, }); BI.shortcut("pager", Pager); let pager; BI.createWidget({ type: "bi.vertical", vgap: 20, items: [ { type: "pager", ref: ref => pager = ref, height: 24, width: 250, total: 100, page: 2, }, { type: "bi.button", text: "populate", handler: () => { pager.attr("total", 500); pager.attr("page", 400); pager.populate(); }, }, ], }); ```