360前端星计划——JavaScript从入门到放弃

268 阅读3分钟

HTML/JS/CSS各司其职

实例:页面的白天/夜间模式实现

方法1: JS直接修改dom元素样式;

方法2:避免代码功能耦合,JS不去修改dom元素的样式,而是修改class名,让css改变样式;

方法3:如果只涉及到展示层的改变,可以不用js,直接用css实现;

label+input+兄弟选择器

复杂UI组件的设计

实例:轮播图

轮播图有哪些功能点:

  1. 几张图片可轮播
  2. 下面有导航点可以选择某一张图(此时不自动播放)
  3. 选好之后继续自动播放

如何实现该组件:

结构设计:

  1. 图片结构是一个列表型结构,主体用ul标签
  2. 使用css绝对定位将图片重叠在同一个位置
  3. 轮播图切换的状态使用修饰符(modifier)
  4. 轮播图的切换动画使用css transition

API设计:

  1. getSelectedItem()
  2. getSelectedItemIndex()
  3. slideTo()
  4. slideNext()
  5. slidePrevious()

具体实现:

  1. 定义一个名为slider的class,里面是具体的API方法实现
  2. setInterval实现自动播放
  3. 导航控制结构
  4. 自定义事件+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:

  1. 各司其职:JavaScript尽量只做状态管理;
  2. 结构、API、控制流分离设计UI组件;
  3. 插件和模板化,并抽象出组件模型;
  4. 运用过程抽象的技巧来抽象并优化局部API;