写好JavaScript | 青训营笔记

72 阅读7分钟

写好JavaScript | 青训营笔记

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

写好JavaScript的一些准则

  • 各司其职
  • 组件封装
  • 过程抽象

各司其职

HTML(Hyper Text Markup Language),是页面的骨架;

CSS则是使用各种选择器和各种dom关系以及标签状态等为不同的标签设置各自的样式,它来进行样式的控制

JavaScript则是在浏览器上运行的编程语言,浏览器编译运行js代码对用户的各种行为做出反应,比如点击按钮提交表单后跳转到新的页面,验证用户在浏览某些网页之前是否登录了账号等。

各司其职就是使用其本身的特性解决问题,例如能够使用css就不使用js去操作DOM,

实现控制网页深色和浅色浏览模式的这个例子你能很好的说明HTML CSS JavaScript各司其职的好处下面是代码。

html:

<header>
    <button id="modeBtn">🌞</button>
    <h1>深夜食堂</h1> 
</header>
<main> 
    <div class="pic">
    <img src="https://p5.ssl.qhimg.com/t015b445bef7dede221.jpg" alt="">
    </div> 
    <div class="content">
        <p> 这是一间营业时间从午夜十二点到早上七点的特殊食堂。这里的老板,不太爱说话,却总叫人吃得热泪盈 眶。在这里,自卑的舞蹈演员偶遇隐退多年舞界前辈,前辈不惜讲述自己不堪回首的经历不断鼓舞年轻人,最终令其重拾自信;轻言绝交的闺蜜因为吃到共同喜爱的美食,回忆起从前的友谊,重归于好;乐观的绝症患者遇到同命相连的女孩,两人相爱并相互给予力量,陪伴彼此完美地走过了最后一程;一味追求事业成功的白领,在这里结交了真正暖心的朋友,发现真情比成功更有意义。食物、故事、真情,汇聚了整部剧的主题,教会人们坦然面对得失,对生活充满期许和热情。每一个故事背后都饱含深情,情节跌宕起伏,令人流连忘返。</p>
    </div>
</main>

css:

body, html { width: 100%; height: 100%; padding: 0; margin: 0; overflow: hidden; } body { padding: 10px; box-sizing: border-box; } div.pic img { width: 100%; } #modeBtn { font-size: 2rem; float: right; border: none; background: transparent; }

js:

const btn=document.getElementBuId('bottonName');
//为该按钮绑定点击事件,实现点击切换页面颜色
btn.addEventListener('click',(e)=>{
    const body=dcument.body;
    if(e.target.innerHTML==='🌞'){
        //修改背景颜色为黑色 
        body.style.backgroundColor='black'; 
        //修改文字颜色为白色 
        body.style.color='white'; 
        e.target.innerHTML='🌙';
    }else{
        //修改背景颜色为白色
        body.style.bakckgroundColor='white';
        //修改文字颜色为黑色 
        body.style.color='black';
        e.target.innerHTML='🌞'
    }
});

js中我们通过给按钮添加点击事件直接修改CSS样式,实现了点击页面让背景颜色变化的功能但是违背各司其职的原则。
这段js代码存在问题:

  1. 后续想要添加更多样式的话只能在这里堆砌代码,会对后期维护造成困难.
  2. 性能问题,每一次触发点击事件都会查询DOM树找到Body节点,为其修改样式后触发重绘.

改进方法:纯CSS实现,通过使用伪类选择器来完成功能。

第一我们在HTML中设置一个checkbox的input组件,css样式设置为display: none;

其次,通过使用label组件的for属性来修改这个checkbox的状态,来进行背景颜色的改变

最后,在CSS中指定一个 伪类+兄弟选择器,在checkbox的状态为checked的情况下选中后面类名为content的组件修改样式,从而实现暗黑模式。

选中暗黑模式时,由于该选择器最特殊,因此该选择器指定的样式会被选择。
代码如下:

<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> 这是一间营业时间从午夜十二点到早上七点的特殊食堂。这里的老板,不太爱说话,却总叫人吃得热泪盈 眶。在这里,自卑的舞蹈演员偶遇隐退多年舞界前辈,前辈不惜讲述自己不堪回首的经历不断鼓舞年轻人,最终令其重拾自信;轻言绝交的闺蜜因为吃到共同喜爱的美食,回忆起从前的友谊,重归于好;乐观的绝症患者遇到同命相连的女孩,两人相爱并相互给予力量,陪伴彼此完美地走过了最后一程;一味追求事业成功的白领,在这里结交了真正暖心的朋友,发现真情比成功更有意义。食物、故事、真情,汇聚了整部剧的主题,教会人们坦然面对得失,对生活充满期许和热情。每一个故事背后都饱含深情,情节跌宕起伏,令人流连忘返 [6] 。 
            </p> 
        </div> 
    </main>
</div>

#modeCheckBox {
    display: none;
}

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

#modeBtn {
    font-size: 2rem;
    float: right;
}

#modeBtn::after {
    content: '🌞';
}

#modeCheckBox:checked+.content #modeBtn::after {
    content: '🌜';
}

这是因为我们使用label属性与input绑定了,当我们点击页面上方时触发input,然后利用css的伪类以及兄弟选择器修改页面颜色。

好处:

  1. HTML |CSS |JavaScript各司其职,代码简洁方便易懂。
  2. 避免由js直接操作样式。

组件封装

好的UI组件必须具备正确性、拓展性、复用性

以轮播图举例,代码如下: html:

<div id="my-slider" class="slider-list">
      <ul>
        <li class="slider-list__item--selected">
          <img src="https://p5.ssl.qhimg.com/t0119c74624763dd070.png" />
        </li>
        <li class="slider-list__item">
          <img src="https://p4.ssl.qhimg.com/t01adbe3351db853eb3.jpg" />
        </li>
        <li class="slider-list__item">
          <img src="https://p2.ssl.qhimg.com/t01645cd5ba0c3b60cb.jpg" />
        </li>
        <li class="slider-list__item">
          <img src="https://p4.ssl.qhimg.com/t01331ac159b58f5478.jpg" />
        </li>
      </ul>
    </div>

css:

  /* 
  -使用 CSS 绝对定位将图片重叠在同一个位置 
  - 轮播图切换的状态使用修饰符(modifier) 
  - 轮播图的切换动画使用 CSS transition  
  */
  #my-slider {
      position: relative;
      width: 790px;
  }

  .slider-list ul {
      list-style-type: none;
      position: relative;
      padding: 0;
      margin: 0;
  }

  .slider-list__item,
  .slider-list__item--selected {
      position: absolute;
      transition: opacity 1s;
      opacity: 0;
      text-align: center;
  }

  .slider-list__item--selected {
      transition: opacity 1s;
      opacity: 1;
  }

js:

用类封装Slider组件,设计API

    -   Slider

        -   +getSelectedItem() // 获取被选中的项
        -   +getSelectedItemIndex() // 获取被选中的项的下标
        -   +slideTo() // 滑到某一项
        -   +slideNext() // 滑到后一项
        -   +slidePrevious() // 滑到前一项

        可以使用自定义事件来实现 控制Slider的组件的状态 和 Slider类 之间的解耦

    class Slider{
                constructor(id){
                  this.container = document.getElementById(id);
                  this.items = this.container
                  .querySelectorAll('.slider-list__item, .slider-list__item--selected');
                }
                getSelectedItem(){
                  const selected = this.container
                    .querySelector('.slider-list__item--selected');
                  return selected
                }
                getSelectedItemIndex(){
                  return Array.from(this.items).indexOf(this.getSelectedItem());
                }
                slideTo(idx){
                  const selected = this.getSelectedItem();
                  if(selected){
                    selected.className = 'slider-list__item';
                  }
                  const item = this.items[idx];
                  if(item){
                    item.className = 'slider-list__item--selected';
                  }
                }
                slideNext(){
                  const currentIdx = this.getSelectedItemIndex();
                  const nextIdx = (currentIdx + 1) % this.items.length;
                  this.slideTo(nextIdx);
                }
                slidePrevious(){
                  const currentIdx = this.getSelectedItemIndex();
                  const previousIdx = (this.items.length + currentIdx - 1)
                    % this.items.length;
                  this.slideTo(previousIdx);
                }
              }

              const slider = new Slider('my-slider');
              slider.slideTo(3);

轮播图 重构:插件化

  • 为什么要插件化?
    之前的设计修改一处,其他地方也要修改,麻烦

  • 方法:

    将控制元素抽取成插件

    插件与组件之间通过依赖注入方式建立联系

    将控制插件依赖的Slider类实例注入到控制插件中

    方便添加或减少控制插件

轮播图 模板化重构

  • 将HTML模板化,更易于扩展,用数据进行驱动更符合实际的开发

轮播图 组件框架

  • 将组件通用模型抽象出来
  • 将组件和插件统一用一个类来实现从而实现父插件可以有子插件的嵌套

总结

  • 组件设计的原则:封装性、正确性、扩展性、复用性

  • 实现组件的步骤:结构设计、展现效果、行为设计

  • 三次重构

    1. 插件化
    2. 模板化
    3. 抽象化(组件框架)

过程抽象

过程抽象是用来处理局部细节控制的一些方法,是函数式编程思想的基础应用.
为了能够让“只执行一次“的需求覆盖不同的事件处理,我们可以将这个需求剥离出来。这个过程我们称为——过程抽象

高阶函数

HOF

  • 以函数作为参数
  • 以函数作为返回值
  • 常用于作为函数装饰器 例如:节流、防抖

为什么使用高阶函数? 使用高阶函数可以大大减少我们使用非纯函数的可能性

个人总结

通过本次课程个人受益非浅,对于前端有了更进一步的认知。且月影老师对于JS的讲解非常细致,容易理解,这些准则对于我进行前端开发很有帮助。