js编码原则

107 阅读4分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 3 天

各司其职

案例: 深夜食堂实现白天模式和夜晚模式切换的三个版本

1.版本一,使用js来操作样式

const btn=document.querySelector('button');
btn.addEventListener('click',(e)=>{
  const body=document.body
  if(e.target.innerHTML=='白天'){
    body.style.color='black';
    body.style.backgroundColor='white';
    e.target.innerHTML="夜晚";
  }else{
    body.style.color='white';
    body.style.backgroundColor='black';
    e.target.innerHTML="白天";
  }
})

代码链接code.juejin.cn/pen/7189589…

2.版本二,使用js操作className

const btn=document.querySelector('button');
btn.addEventListener('click',(e)=>{
  const body=document.body;
  if(e.target.innerHTML=='夜晚'){
    body.className='night';
    e.target.innerHTML='白天'
  }else{
    body.className='';
    e.target.innerHTML='夜晚';
  }
})

代码链接 code.juejin.cn/pen/7189819…

3.版本三,零js

1.使用checkbox来实现状态切换
2.使用label标签的for属性将状态切换绑定到id为modeBtn的元素上
3.使用::after,通过content的设置来实现modeBtn元素内容的改变
4.通过动画transition: all 1s实现动画过渡效果

代码链接 code.juejin.cn/pen/7189827…

结论:
让html、css、js各司其职,html负责页面框架构建,css负责样式渲染和动画效果,js负责交互和操作事件的。尽量避免不必要的由js直接操作样式,可以用className来表示状态,纯展示类的可以用零js

组件封装

组件是指web页面上抽出的一个个包含模板(html)、样式(css)、功能(js)的单元,好的组件具有封装性、正确性、扩展性、复用性这四大特性

轮播图

1.组件封装的方法:

  • 结构设计
  • 展现效果
  • 行为设计
    API(功能)
    event(控制流)

实践

1.通过将不是第一张的图片设置为 transition: opacity 1s; opacity: 0; 第一张图片设置为 transition: opacity 1s; opacity: 1; 将图片实现重叠
2.通过改变className来实现轮播
3.通过index来实现控制

轮播图-1 - 码上掘金 (juejin.cn)

4.用自定义事件实现小圆点和图片之间的状态跟随

const detail = { index: idx }
const event = new CustomEvent('slide', { bubbles: true, detail })
this.container.dispatchEvent(event)
this.container.addEventListener('slide', evt => {
 const idx = evt.detail.index
 const selected = controller.querySelector('.slide-list_control-button--selected');
 if (selected) selected.className = 'slide-list_control-button';
 buttons[idx].className = 'slide-list_control-button--selected';
 })

自定义事件: juejin.cn/post/699015…
轮播图-2 - 码上掘金 (juejin.cn)

2.优化

1.优化考虑的方向可以是组件的灵活性,如:是否可以删除这个两边的按钮。为了达到这个目的,我们可以采用解耦的方式,将控制元素抽取成插件,插件与组件之间通过依赖注入的方式建立联系
定义注册插件方法

registerPlugins(...plugins) {
    plugins.forEach(plugins => plugins(this));
}

调用注册插件方法

slider.registerPlugins(pluginController, pluginPrevious, pluginNext)

标签有id就可以直接用id来得到元素,但是该id不能被占位

轮播图-3 - 码上掘金 (juejin.cn)

2.由于如果有html的改变我们还需要去改动html代码,所以,为了进一步优化,我们可以将html模板化
code.juejin.cn/pen/7192066…

3.这就是优化的终点了吗?当然不是,我们还可以将组件通用模型给抽象出来(组件框架)
4.扩展点:1.插件和组件可以融合在一起,子组件作为父组件的插件 2.这里只对html进行了模板化,没有对css进行模板化,如果我们后续打算更改样式可能会有一定的麻烦,所以我们还可以对css进行模板化

过程抽象

定义

通过高阶函数来将过程抽象,如:为了实现只执行一次这个过程,我们实现一个名为once的高阶函数。

高阶函数:参数为函数,返回值也是函数的函数,通常用作装饰函数,如HOF0就是一个等价高阶函数

function FOF0(fn){
    return function(...args){
        return fn.apply(this,args);
    }
}
//其返回的函数和传入的函数一样

常用高阶函数

  • once
  • Throttle 节流函数
  • Debounce 防抖函数
  • Consumer/2 延时调用函数
  • Iterative 可迭代函数

once

function once(fn){
    return function(...args){
        if(fn){
            const ret=fn.apply(this.args);
            fn=null;
            return ret;
        }
    }
}

Throttle

节流函数,用于页面滚动,mmoucemove等事件,对频率做出限制

Debounce

防抖函数,用于自动保存,当页面内容一直在发生变化的时候不会自动保存,只有当页面的内容在多少秒内不变的时候才会保存

Consumer

如果这里有一个按钮,设置的延时时间是1s,你要连续点击它100下,那么它会每隔一秒加一下,一直加到100下为止

Iterative

实现批量操作

为什么要使用高阶函数

纯函数:结果是可以预料的,输出是确定的,在任何时候输入结果A都能得到结果B,其输入输出简单,对它的测试也简单;
非纯函数是有副作用的,结果是不可估量的,输出是由外部环境所决定的,对它的测试相对复杂,在测试时需要创建特定的环境。

一般来说高阶函数是纯函数。大量使用纯函数有助于提升一个库的可维护性

命令式和声明式

编程范式分为命令式和声明式,声明式比命令式有更多的可扩展性