聊聊公司团队前端交互需要注意的一些要点。
- 一般上 Tab 切换的页面都应该通过路由定义,切换选项卡即切换路由,而且要用 replace 而不是 push 切换。
- 页面分页、侧栏分类切换分类等会重新加载列表的操作都要通过路由进行切换。
- 链接必须使用 a 标签+设置 href 属性声明跳转,允许用户右键新选项卡打开,不要通过 click 事件进行跳转。
- 引入外部插件、添加 DOM 事件到 document/body 时,一定要销毁,不做很容易导致内存泄漏。
- 能用框架提供的接口/方法解决就用框架,不要自己另外实现一份。
- 页面/组件的每一个状态都要处理。
组件状态处理
重点说一下这个,以页面路由组件为例子。
例如打开一个页面,状态大致可以分为数据加载中/加载完成/没有数据三种。
在写每一个页面的时候,都需要针对这 3 种状态进行显示 Loading/骨架图、渲染、空数据提示的处理。
另外说一个例子,一个页面包含多个相互独立的业务组件(每一个业务组件就可以当作子系统)。
需要考虑到每一个业务组件可能出现的状态:
有一些组件比较简单,直接渲染 UI 就好了;
有一些组件较为复杂,有 Ajax 请求,那么要考虑到请求中、请求成功、失败、没有数据等等状态;
有一些组件可能引入了很大的第三方库(如Echarts),这些第三方库必须异步加载,这时就需要考虑异步加载第三方库时的状态,比如显示 Loading 之类;
其他诸如屏幕适配也可以当作状态的一种,不过屏幕适配一般是在<App />
中做,平时不需要太注意。
平时开发组件的时候一定要想好到底有多少种状态,哪些状态要处理,哪些状态不必处理。
另外说下组件状态的理解
对比下下面几种写法的优劣势:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| <template> <div> <skeleton v-if="aData.loading && bData.loading && cData.loading"></skeleton> <div v-else-if="aData.error || bData.error || cData.error">页面发生错误</div> <template v-else> <part-a :data="aData.list"></part-a> <part-b :data="bData.list"></part-b> <part-c :data="cData.list"></part-c> </template> </div> </template> <script> export default { data() { return { aData: { loading: true, error: false, list: [] }, bData: { loading: true, error: false, list: [] }, cData: { loading: true, error: false, list: [] } } }, async created() { this.loadA(); this.loadB(); this.loadC(); } }; </script>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| <template> <div> <skeleton v-if="loading"></skeleton> <div v-else-if="hasError">页面发生错误</div> <template v-else> <part-a :data="aData"></part-a> <part-b :data="bData"></part-b> <part-c :data="cData"></part-c> </template> </div> </template> <script> export default { data() { return { loading: true, hasError: false, aData: [], bData: [], cData: [] } }, async created() { try { await Promise.all([this.loadA(), this.loadB(), this.loadC()]); } catch(err) { this.hasError = true; } finally { this.loading = true; } } }; </script>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| <template> <div> <skeleton v-if="loading"></skeleton> <div v-else-if="hasError">页面发生错误</div> <template v-else> <part-a :data="aData"></part-a> <part-b :data="bData"></part-b> <part-c :data="cData"></part-c> </template> </div> </template> <script> export default { data() { return { aData: { loading: true, error: false, list: [] }, bData: { loading: true, error: false, list: [] }, cData: { loading: true, error: false, list: [] } } }, computed: { loading() { return aData.loading && bData.loading && cData.loading; }, hasError() { return aData.error || bData.error || cData.error; } }, async created() { this.loadA(); this.loadB(); this.loadC(); } }; </script>
|
其实一个组件可以当作一个有限状态机。
然后就可以把页面复杂的逻辑问题简化为几个状态机自身的状态转移问题,可以有效简化问题又失灵活,以应对多变的需求。