diff --git a/README.md b/README.md index 18ede4a..e056b9f 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,15 @@ FineUI入门100题,带你走进FineUI的世界 此系列内容来源于日常开发积累与沉淀,用于大家学习交流与项目中踩坑 -## 题目 +## 100-Questions列表 -### 项目基础 +--- +### 基础 + +- [0、前端工作进阶需要哪些必备技能](./questions/0.前端工作进阶需要哪些必备技能.md) - [1、前端如何正确书写资源路径](./questions/50.前端如何正确书写资源路径.md) +- [2、组件的代码设计基本思路](./questions/2.组件的代码设计基本思路.md) ### 布局篇 @@ -16,8 +20,15 @@ FineUI入门100题,带你走进FineUI的世界 - [3、我们为什么要设计el这个属性](./questions/40.我们为什么要设计el这个属性.md) - [4、绝对布局的隐藏知识点](./questions/41.绝对布局的隐藏知识点.md) +### Fix数据流篇 + +- [4、Fix中对于对象属性的监听,为什么要先定义属性才可以正常watch,如何解决](./questions/55.Fix中对于对象属性的监听,为什么要先定义属性才可以正常watch,如何解决.md) + ### 进阶 - [1、如何获取当前时间](./questions/1.如何获取当前时间.md) - [2、如何格式化输出日期](./questions/2.如何格式化输出日期.md) -- [3、为什么传递时间信息时候推荐使用时间戳](./questions/3.为什么传递时间信息时候推荐使用时间戳.md) \ No newline at end of file +- [3、为什么传递时间信息时候推荐使用时间戳](./questions/3.为什么传递时间信息时候推荐使用时间戳.md) +- [4、BI.config都可以做那些事情](./questions/9.BI.config都可以做那些事情.md) +- [5、BI.config的执行顺序是什么,为什么这么设计](./questions/10.BI.config的执行顺序是什么,为什么这么设计.md) +- [6、combo的一些特性详解](./questions/80.combo的一些特性详解.md) diff --git a/images/8.png b/images/8.png new file mode 100644 index 0000000..a9dc2b8 Binary files /dev/null and b/images/8.png differ diff --git a/images/9.png b/images/9.png new file mode 100644 index 0000000..a18df6e Binary files /dev/null and b/images/9.png differ diff --git a/questions/2.组件的代码设计基本思路.md b/questions/2.组件的代码设计基本思路.md new file mode 100644 index 0000000..222854e --- /dev/null +++ b/questions/2.组件的代码设计基本思路.md @@ -0,0 +1,174 @@ +# 组件的代码设计基本思路 + +我们以一个分页组件为例,阐述一下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(); + }, + }, + ], +}); + +``` \ No newline at end of file diff --git a/questions/30.Fix中对于对象属性的监听,为什么要先定义属性才可以.md b/questions/30.Fix中对于对象属性的监听,为什么要先定义属性才可以.md deleted file mode 100644 index afe629f..0000000 --- a/questions/30.Fix中对于对象属性的监听,为什么要先定义属性才可以.md +++ /dev/null @@ -1 +0,0 @@ -# Fix中对于对象属性的监听,为什么要先定义属性才可以 \ No newline at end of file diff --git a/questions/41.绝对布局的隐藏知识点.md b/questions/41.绝对布局的隐藏知识点.md index 317954e..0ceee7a 100644 --- a/questions/41.绝对布局的隐藏知识点.md +++ b/questions/41.绝对布局的隐藏知识点.md @@ -105,9 +105,36 @@ BI.createWidget({ ![示例](../images/7.png) -4. 绝对布局支持shorthand写法inset +4. 位置属性使用负值会有起效 -使用Chrome审查元素的同学可能发现了,绝对布局的元素在chrome中会显示`inset: 0`这种形式,实际上这是top right bottom left属性的简写,规则遵循顺时针方向.需要注意的是只有第一种情况支持属性名为数字,其余情况需要加引号,毕竟我们是写js而不是css +在开发过程中会遇到有些按钮图标为了位置美观,会脱离正常位置的情况,此时利用负值属性可以很轻松实现 + +```demo +BI.createWidget({ + type: "bi.absolute", + width: 300, + height: 300, + css: { + background: "red", + }, + items: [ + { + el: { + type: "bi.button", + text: "保存", + }, + top: -30, + right: 10, + }, + ], +}); +``` + +![示例](../images/8.png) + +5. 绝对布局支持shorthand写法inset + +使用Chrome审查元素的同学可能发现了,绝对布局的元素在chrome中会显示`inset: 0`这种形式,实际上这是top right bottom left属性的简写,规则遵循顺时针方向.FineUI同样支持了这种写法,需要注意的是只有第一种情况支持属性名为数字,其余情况需要加引号,毕竟我们是写js而不是css ``` inset: 10 /* value applied to all edges */ diff --git a/questions/55.Fix中对于对象属性的监听,为什么要先定义属性才可以正常watch,如何解决.md b/questions/55.Fix中对于对象属性的监听,为什么要先定义属性才可以正常watch,如何解决.md new file mode 100644 index 0000000..b52475a --- /dev/null +++ b/questions/55.Fix中对于对象属性的监听,为什么要先定义属性才可以正常watch,如何解决.md @@ -0,0 +1,55 @@ +# Fix中对于对象属性的监听,为什么要先定义属性才可以正常watch,如何解决? + + +先看如下代码示例,很明显按钮被点击后并不会输出phone的值,其实这和Vue2类似,是因为Fix在响应式处理的时候,遍历对象的每一个属性添加依赖的,并不能检测到新增的属性. + +```javascript +const state = Fix.define({ + name: "小明", + details: { + age: 18, + address: "北京", + }, +}); + +Fix.watch(state, "details.phone", phone => { + console.log(phone); +}); + + +const widget = BI.createWidget({ + type: "bi.button", + text: "button", + handler: () => { + state.details.phone = "123456789"; + }, +}); +``` + +在日常实践中我们一般采用两种方式来处理这种情况 + +1. 修改整个对象的引用 + +``` +const widget = BI.createWidget({ + type: "bi.button", + text: "button", + handler: () => { + state.details = { ...state.details, phone: "123456789"}; + }, +}); +``` + +2. 使用Fix.set方法 + +类似vue的$set方法 + +``` +const widget = BI.createWidget({ + type: "bi.button", + text: "button", + handler: () => { + Fix.set(state.details, "phone", "123456789"); + }, +}); +``` \ No newline at end of file diff --git a/questions/81.combo的一些特性.md b/questions/81.combo的一些特性.md deleted file mode 100644 index e69de29..0000000