跟着月影学 JavaScript | 青训营笔记

80 阅读3分钟

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

【知之非艰,行之惟难。】


一、本堂课重点内容:

围绕写好JavaScript的三原则对JavaScript讲解。

三原则:各司其职、组件封装、过程抽象。

二、详细知识点介绍:

写好JS的一些原则
  • 各司其职:让HTML、CSS和JavaScript三者分离,负责自己应该做的事
  • 组件封装:一个组件应当具有正确性、可拓展性和复用性
  • 过程抽象:利用函数闭包、函数编程式思想
示例:
  1. 各司其职:深夜食堂(在网页中实现深色模式与浅色模式的自由切换)
// 版本一
const btn = document.getElementById('modeBtn');
btn.addEventListener('click', (e) => {
    const body = document.body;
    if(e.target.innerHTML === '🌞') {
        body.style.backgroundColor = 'black';
        body.style.color = 'white';
        e.target.innerHTML = '🌜';
    } else {
        body.style.backgroundColor = 'white';
        body.style.color = 'black';
        e.target.innerHTML = '🌞';
    }
});

// 版本二
const btn = document.getElementById('modeBtn');
btn.addEventListener('click', (e) => {
    const body = document.body;
    if(body.className !== 'night') {
        body.className = 'night';
    } else {
        body.className = '';
    }
});

版本三

<input id="modeCheckBox" type="checkbox">
<div class="content">
    <header>
        <label id="modeBtn" for="modeCheckBox"></label>
        <h1>深夜食堂</h1>
    </header>
    <main>
        <div class="pic">
            <img src="https://p2.ssl.qhimg.com/t0120cc20854dc91c1e.jpg">
        </div>
        <div class="description">
            <p>
            这是一间营业时间从午夜十二点到早上七点的特殊食堂。这里的老板,不太爱说话,却总叫人吃得热泪盈眶。
            </p>
        </div>
    </main>
</div>
#modeCheckBox {
    display: none;
}

#modeCheckBox:checked + .content {
    background-color: black;
    color: white;
    transition: all 1s;
}

在版本一的代码中,直接由JS直接控制样式,没有使用CSS。版本二中使用JS控制元素的class标签,再通过css设置元素样式,但是此时的JS代码为一次性代码,可复用性低。版本三中仅使用HTML和CSS,没有JS代码。

  1. 组件封装:轮播图 在最开始中使用HTML、CSS、JS去描述一个组件的相关内容,但是并不符合一个组件应该具备的特性,因此对其进行改进。
// 模板化
class Slider{
    constructor(id, opts = {images:[], cycle: 3000}){
        this.container = document.getElementById(id);
        this.options = opts;
        this.container.innerHTML = this.render();
        this.items = this.container.querySelectorAll('.slider-list__item, .slider-list__item--selected');
        this.cycle = opts.cycle || 3000;
        this.slideTo(0);
    }
    render(){
        const images = this.options.images;
        const content = images.map(image => `
        <li class="slider-list__item">
          <img src="${image}">
        </li>    
      `.trim());

        return `<ul>${content.join('')}</ul>`;
    }
    ...
}

// 插件化
function pluginController(slider){
    const controller = slider.container.querySelector('.slide-list__control');
    if(controller){
        const buttons = controller.querySelectorAll('.slide-list__control-buttons, .slide-list__control-buttons--selected');
        controller.addEventListener('mouseover', evt=>{
            const idx = Array.from(buttons).indexOf(evt.target);
            if(idx >= 0){
                slider.slideTo(idx);
                slider.stop();
            }
        });

        controller.addEventListener('mouseout', evt=>{
            slider.start();
        });

        slider.addEventListener('slide', evt => {
            const idx = evt.detail.index
            const selected = controller.querySelector('.slide-list__control-buttons--selected');
            if(selected) selected.className = 'slide-list__control-buttons';
            buttons[idx].className = 'slide-list__control-buttons--selected';
        });
    }  
}

function pluginPrevious(slider){
    const previous = slider.container.querySelector('.slide-list__previous');
    if(previous){
        previous.addEventListener('click', evt => {
            slider.stop();
            slider.slidePrevious();
            slider.start();
            evt.preventDefault();
        });
    }  
}

function pluginNext(slider){
    const next = slider.container.querySelector('.slide-list__next');
    if(next){
        next.addEventListener('click', evt => {
            slider.stop();
            slider.slideNext();
            slider.start();
            evt.preventDefault();
        });
    }  
}

将组件通用模型抽象出来的方法

class Component{
    constructor(id, opts = {name, data:[]}){
        this.container = document.getElementById(id);
        this.options = opts;
        this.container.innerHTML = this.render(opts.data);
    }
    registerPlugins(...plugins){
        plugins.forEach(plugin => {
            const pluginContainer = document.createElement('div');
            pluginContainer.className = `.${name}__plugin`;
            pluginContainer.innerHTML = plugin.render(this.options.data);
            this.container.appendChild(pluginContainer);

            plugin.action(this);
        });
    }
    render(data) {
        /* abstract */
        return ''
    }
}

在组件实现过程中要考虑组件的设计原则(封装性,正确性,可扩展性,可复用性),对于设计出的组件可进行插件化、模板化等优化。

  1. 过程抽象:

image.png 剥离项目开发中的大量重复代码(实现相同功能),将其过程进行抽象。

过程抽象,将那些需要重复的操作抽象出来,利用函数闭包的语法,实现类似装饰器的效果,将业务代码加上这些功能。


【left-pad事件】 leftpad是一个简单处理字符串的函数,主要功能:当字符串的长度没有达到我们要的长度时,就在它的左边补若干个字符,把它的长度补齐。

module.exports = leftpad;
function leftpad (str, len, ch) {
  str = String(str);
  var i = -1;
  if (!ch && ch !== 0) ch = ' ';
  len = len - str.length;//期望的长度-字符串的长度 = 要拼接的长度
  while (++i < len) {//通过循环,把用来替代的字符添加到字符串前面去
    str = ch + str;
  }
  return str;
}

小八卦:【Left-pad事件】就是当年 NPM 圈发生的“一个十几行代码的模块引发的血案”。left-pad工具模块被作者 Azer从 NPM 上撤下,所有直接或者间接依赖这个模块的 NPM 包就都忧伤的挂掉了,其中就包括 babel 、React等。作者为什么要删包?事情是这样的,Azer 写了一个工具叫 kik 发布在 npm 上,这天有个同名的公司律师找上门要求他删掉,Azer 不从,这律师就找上 npm,npm 把包的管理权限转给了这家公司——当然,Azer 就怒了,从 npm 上解放了所有自己发布的包。

三、实践练习例子:

四、课后个人总结:

进一步熟悉掌握了JS,不过对于JS的深入了解还需要进一步去细致学习。

五、引用参考: