HTML/JS/CSS各司其职
实例:页面的白天/夜间模式实现
方法1: JS直接修改dom元素样式;
方法2:避免代码功能耦合,JS不去修改dom元素的样式,而是修改class名,让css改变样式;
方法3:如果只涉及到展示层的改变,可以不用js,直接用css实现;
label+input+兄弟选择器
复杂UI组件的设计
实例:轮播图
轮播图有哪些功能点:
- 几张图片可轮播
- 下面有导航点可以选择某一张图(此时不自动播放)
- 选好之后继续自动播放
如何实现该组件:
结构设计:
- 图片结构是一个列表型结构,主体用ul标签
- 使用css绝对定位将图片重叠在同一个位置
- 轮播图切换的状态使用修饰符(modifier)
- 轮播图的切换动画使用css transition
API设计:
- getSelectedItem()
- getSelectedItemIndex()
- slideTo()
- slideNext()
- slidePrevious()
具体实现:
- 定义一个名为slider的class,里面是具体的API方法实现
- setInterval实现自动播放
- 导航控制结构
- 自定义事件+mouseover+mouseout实现图片的【手动切换/自动播放】
进阶
1. 复杂组件中子组件的解耦合:插件机制
可以将轮播图中下方的导航条、左右两边的点击箭头作为插件引入class slider中
registerPlugins(...plugins){
plugins.forEach(plugin=>plugin(this))
}
2. 改进插件/模板化
在JS代码中,通过调用render方法,插入Html代码进行渲染;
大部分HTML结构的都在JS中生成(封装成了一个可提供给别人的第三方组件);
3. 组件模型抽象
UI组件框架
class Component{
constructor(){
}
registerPlugins(...plugins){
}
render(data){
return '';
}
}
class Slider extends Component{
constructor(){
super(this);
}
}
一些point:
- class BEM命名方式(eg:slider-list__item--selected);
- 一个函数一般20-50行,如果函数内容过多,可以考虑拆分解耦;
- 组件的设计规范:数据驱动和行为驱动,用户只需要传入数据,HTML的生成和逻辑都由组件内部来实现;
局部细节控制
eg1:点击只能执行一次
block.addEventListener('click',()=>{
},{once:true})
eg2:异步请求获取数据
实例:有很多“只允许执行一次”的函数操作,如何进行统一的抽象?
eg3:防抖&节流&消费者
//防抖
function debounce(fn,dur){
dur = dur || 100;
var timer;
return function(){
clearTimeout(timer);
timer = setTimeout(()=>{
fn.apply(this,arguments)
},dur);
}
}
//节流
function throttle(fn,time=500){
let timer;
return function(...args){
if(timer === null){
fn.apply(this,args);
timer = setTimeout(()=>{
timer = null;
},time)
}
}
}
//消费者(连击)
function consumer(fn,time){
let tasks = [],timer;
return function(...args){
tasks.push(fn.bind(this,...args));
if(timer==null){
timer = setInterval(()=>{
tasks.shift().call(this);
if(tasks.length<=0){
clearInterval(timer);
timer = null;
}
},time)
}
}
}
eg4:指令式 vs 声明式编程
指令式:面向过程/面向对象
声明式:逻辑/函数式,把其中的指令抽象成逻辑或函数,可以充分使用高阶函数来实现;
eg5:高阶函数
自身输入函数或返回函数
//toggle 点击按钮在on和off间切换
function toggle(...args){
return function(...args){
let action = actions.shift();
actions.push(action);
return action.apply(this,args);
}
}
switcher.onclick=toggle(
evt=>evt.target.className='off',
evt=>evt.target.className='on'
);
总结
如何写好JavaScript:
- 各司其职:JavaScript尽量只做状态管理;
- 结构、API、控制流分离设计UI组件;
- 插件和模板化,并抽象出组件模型;
- 运用过程抽象的技巧来抽象并优化局部API;