模块:一个一个的局部作用域的代码块
模块系统需要解决的主要问题
- 模块化的问题
- 消除全局变量
- 管理加载顺序
Module的基本用法
模块里面都是局部无法访问
切换幻灯片示例
Base.js
// 默认参数
const DEFAULTS = {
// 初始索引
initialIndex: 0,
// 切换时是否有动画
animation: true,
// 切换速度,单位 ms
speed: 300
};
// base
const ELEMENT_NODE = 1;
const SLIDER_ANIMATION_CLASSNAME = 'slider-animation';
// 父类
class BaseSlider {
constructor(el, options) {
console.log(options)
if (el.nodeType !== ELEMENT_NODE)
throw new Error('实例化的时候,请传入 DOM 元素!');
// 实际参数
this.options = {
...DEFAULTS,
...options
};
const slider = el;
const sliderContent = slider.querySelector('.slider-content');
const sliderItems = sliderContent.querySelectorAll('.slider-item');
// 添加到 this 上,为了在方法中使用
this.slider = slider;
this.sliderContent = sliderContent;
this.sliderItems = sliderItems;
this.minIndex = 0;
this.maxIndex = sliderItems.length - 1;
this.currIndex = this.getCorrectedIndex(this.options.initialIndex);
// 每个 slider-item 的宽度(每次移动的距离)
this.itemWidth = sliderItems[0].offsetWidth;
this.init();
}
// 获取修正后的索引值
// 随心所欲,不逾矩
getCorrectedIndex(index) {
if (index < this.minIndex) return this.maxIndex;
if (index > this.maxIndex) return this.minIndex;
return index;
}
// 初始化
init() {
// 为每个 slider-item 设置宽度
this.setItemsWidth();
// 为 slider-content 设置宽度
this.setContentWidth();
// 切换到初始索引 initialIndex
this.move(this.getDistance());
// 开启动画
if (this.options.animation) {
this.openAnimation();
}
}
// 为每个 slider-item 设置宽度
setItemsWidth() {
for (const item of this.sliderItems) {
item.style.width = `${this.itemWidth}px`;
}
}
// 为 slider-content 设置宽度
setContentWidth() {
this.sliderContent.style.width = `${
this.itemWidth * this.sliderItems.length
}px`;
}
// 不带动画的移动
move(distance) {
this.sliderContent.style.transform = `translate3d(${distance}px, 0px, 0px)`;
}
// 带动画的移动
moveWithAnimation(distance) {
this.setAnimationSpeed(this.options.speed);
this.move(distance);
}
// 设置切换动画速度
setAnimationSpeed(speed) {
this.sliderContent.style.transitionDuration = `${speed}ms`;
}
// 获取要移动的距离
getDistance(index = this.currIndex) {
return -this.itemWidth * index;
}
// 开启动画
openAnimation() {
this.sliderContent.classList.add(SLIDER_ANIMATION_CLASSNAME);
}
// 关闭动画
closeAnimation() {
this.setAnimationSpeed(0);
}
// 切换到 index 索引对应的幻灯片
to(index) {
index = this.getCorrectedIndex(index);
if (this.currIndex === index) return;
this.currIndex = index;
const distance = this.getDistance();
if (this.options.animation) {
return this.moveWithAnimation(distance);
} else {
return this.move(distance);
}
}
// 切换上一张
prev() {
this.to(this.currIndex - 1);
}
// 切换下一张
next() {
this.to(this.currIndex + 1);
}
// 获取当前索引
getCurrIndex() {
return this.currIndex;
}
}
export default BaseSlider;
slider.js
import BaseSlider from "./base.js";
// 子类
class Slider extends BaseSlider {
constructor(el, options) {
super(el, options);
this._bindEvent();
}
_bindEvent() {
document.addEventListener('keyup', ev => {
// console.log(ev.keyCode);
if (ev.keyCode === 37) {
// ←
this.prev();
} else if (ev.keyCode === 39) {
// →
this.next();
}
});
}
}
export default Slider;
index.js
import Slider from "./slider.js";
new Slider(document.querySelector('.slider'), {
initialIndex: 1,
animation: true,
speed: 1000
});
slider.css
/* css reset */
* {
padding: 0;
margin: 0;
}
a {
text-decoration: none;
outline: none;
}
img {
vertical-align: top;
}
/* layout */
.slider-layout {
width: 80%;
height: 420px;
margin: 0 auto;
}
/* slider */
.slider,
.slider-content,
.slider-item,
.slider-img {
width: 100%;
height: 100%;
}
.slider {
overflow: hidden;
}
.slider-item {
float: left;
}
.slider-animation {
transition-property: transform;
transition-duration: 0ms;
}
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>Class 的应用</title>
<link rel="stylesheet" href="css/slider.css"/>
</head>
<body>
<div class="slider-layout">
<div class="slider">
<div class="slider-content">
<div class="slider-item">
<a href="javascript:;"
><img src="imgs/1.jpeg" alt="1" class="slider-img"
/></a>
</div>
<div class="slider-item">
<a href="javascript:;"
><img src="imgs/2.jpeg" alt="1" class="slider-img"
/></a>
</div>
<div class="slider-item">
<a href="javascript:;"
><img src="imgs/3.jpeg" alt="1" class="slider-img"
/></a>
</div>
<div class="slider-item">
<a href="javascript:;"
><img src="imgs/4.jpeg" alt="1" class="slider-img"
/></a>
</div>
</div>
</div>
</div>
<script src="js/index.js" type="module"></script>
</body>
</html>
一个文件就是一个模块,只要你会用到import或export,在使用script标签加载的时候,就要加上type="module"
export default和import from注意事项
- 一个模块只能有一个export default
- 一个模块没有导出也可以将其导入 直接import就行
- 被导入的代码都会执行一遍,也仅会执行一遍
- 导出export default age,导入import aaa from '/js/module.js' 导入名字可以为aaa与age对应
export和import注意事项
- export声明或语句 export const age=18;/export {age};两种导出方式 import {age} from '/js/module.js'(不能随意命名,名字必须相同)
// 记住导出不能匿名
// 多个导出
// export function fn(){};
// export class className{};
// export const age=18;
function fn(){};
class className{};
const age=18;
export {fn as func,className,age};
export default 18;
<script type="module">
// 1.导出导入起别名
import {func, className as Person, age} from "./js/module.js";
// 2.整体导入
// 会导入所有输出,包括通过export default导出的
import * as obj from "./js/module.js";
// 3.同时导入
// 要求:一定是export default在前
// import age2,{func, className as Person, age} from "./js/module.js";
console.log(Person);
console.log(func);
console.log(obj);
</script>
Module的注意事项
- 模块顶层的注意事项
- 模块中,顶层的this指向undefined
- import关键字和import()函数
- import命令具有提升效果,会提升到整个模块的头部,率先执行(import执行时,代码块还没执行)
- import()可以按条件导入,import()返回的是一个Promise对象
- 导入导出的复合写法
//复合写法导出的,无法在当前模块中使用
export {age} from './module.js';
console.log(age);
// 等价于下面两个
//import {age} from './module.js';
//export {age} from './module.js';
//可以在当前模块中使用
//console.log(age);