一:准备工作
1、项目结构
compnents文件夹 存放整个项目公共的组件
assets文件夹 存放样式和字体图标
api文件夹 存放请求文件
pages文件夹存放项目页面
pages文件夹中有conponents存放的是页面级的公共组件
2、搭建开发环境
- 初始化项目
npm init
2)安装开发依赖
npm install --save-dev css-loader@4.2.1 style-loader@1.2.1 file-loader@6.0.0 url-loader@4.1.0
3)安装生产依赖
npm install art-template@4.13.2 swiper@6.1.1
4)修改scripts属性值
"start": "webpack-dev-server --open chrome"
3、配置webpack.config.js文件
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
// 获取绝对路径
const resolve = dir => path.resolve(__dirname, dir);
module.exports = {
mode: 'development',
// Webpack 入口文件
entry: {
index: './src/pages/index',
destination: './src/pages/destination'
},
// Webpack 输出路径
output: {
// 输出的目录
path: resolve('dist'),
// 输出的文件名
filename: 'js/[name].js'
},
// source-map,调试用的,出错的时候,将直接定位到原始代码,而不是转换后的代码
devtool: 'cheap-module-eval-source-map',
resolve: {
// 自动补全(可以省略)的扩展名
extensions: ['.js'],
// 路径别名
alias: {
api: resolve('src/api'),
icons: resolve('src/assets/icons'),
styles: resolve('src/assets/styles'),
components: resolve('src/components'),
pages: resolve('src/pages'),
utils: resolve('src/utils')
}
},
// 不同类型模块的处理规则
module: {
rules: [
// css
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
// 模板文件
{
test: /\.art$/,
loader: 'art-template-loader'
},
// 图片
{
test: /\.(png|jpe?g|gif|svg)$/,
loader: 'url-loader',
options: {
// 小于 10K 的图片转成 base64 编码的 dataURL 字符串写到代码中
limit: 10000,
// 其他的图片转移到
name: 'images/[name].[ext]',
esModule: false
}
},
// 字体文件
{
test: /\.(woff2?|eot|ttf|otf)$/,
loader: 'url-loader',
options: {
limit: 10000,
name: 'fonts/[name].[ext]'
}
}
]
},
plugins: [
// 自动将依赖注入 html 模板,并输出最终的 html 文件到目标文件夹
new HtmlWebpackPlugin({
filename: 'index.html',
template: './src/pages/index/index.art',
chunks: ['index']
}),
new HtmlWebpackPlugin({
filename: 'destination.html',
template: './src/pages/destination/destination.art',
chunks: ['destination']
})
]
};
二、首页开发
1、头部组件,下方代码是components/header/index.js
import './header.css';
// 需要添加的类名
const CHANGED_CLASS_NAME = 'header-transition';
// 初始状态,主要是为了防止多次触发的问题
const INIT_STATE = 'init';
// 改变状态,主要是为了防止多次触发的问题
const CHANGED_STATE = 'changed';
// 添加Header类
class Header {
// 需要传递四个参数
// 1:元素,2、距离,3、滚动条所在的元素,4、绑定事件的元素
constructor(el, critical_point, scrollContainer, eventEl = scrollContainer) {
this.el = el;
this.critical_point = critical_point;
// 滚动条所在的容器
this.scrollContainer = scrollContainer;
// 监听滚动事件的元素
this.eventEl = eventEl;
// 调整状态
this.setState(INIT_STATE);
// 绑定事件
this.bindEvent();
}
// 设置状态
setState(state) {
this.state = state;
}
// 绑定事件
bindEvent() {
this.eventEl.addEventListener(
'scroll',
() => {
if (this.needChange()) {
this.setState(CHANGED_STATE);
this.change();
} else if (this.needReset()) {
this.setState(INIT_STATE);
this.reset();
}
},
false
);
}
// 重置,去掉类名
reset() {
this.el.classList.remove(CHANGED_CLASS_NAME);
}
// 不是初始的状态,并且滚动的距离大于等于设定的距离
needReset() {
return (
this.state !== INIT_STATE &&
this.scrollContainer.scrollTop <= this.critical_point
);
}
// 变化,添加类名
change() {
this.el.classList.add(CHANGED_CLASS_NAME);
}
// 需要变化,不是已改变的状态,并且滚动的距离小于设定的距离
needChange() {
return (
this.state !== CHANGED_STATE &&
this.scrollContainer.scrollTop > this.critical_point
);
}
}
// 导出顶部
export default Header;
使用Header类,下方代码是pages/index/components/header/index.js
import Header from 'components/header';
const scrollContainer = document.getElementById('index-page');
const headerEl = scrollContainer.querySelector('.header');
new Header(headerEl, 0, scrollContainer);
2、幻灯片组件
下方代码是:pages/index/components/slider/config.js
// Swiper 配置
export default {
// 循环模式选项
loop: true,
// 是否需要分页器
pagination: {
el: '.swiper-pagination'
}
// 是否需要前进后退按钮
// navigation: {
// nextEl: '.swiper-button-next',
// prevEl: '.swiper-button-prev'
// },
// 是否需要滚动条
// scrollbar: {
// el: '.swiper-scrollbar'
// }
};
export const SWIPER_CONTAINER_CLASS = '.swiper-container';
使用幻灯片,下方代码是:pages/index/components/slider/index.js
// 引入css
import 'swiper/swiper-bundle.min.css';
import './slider.css';
// 引入js
import Swiper from 'swiper/swiper-bundle.min';
// 引入配置文件
import config, { SWIPER_CONTAINER_CLASS } from './config';
// https://www.swiper.com.cn/api/index.html
// 实例化
new Swiper(SWIPER_CONTAINER_CLASS, config);
3、导航组件
下方代码是:pages/index/components/nav/config.js
export const URL = 'https://www.imooc.com/api/mall-wepApp/index/nav';
export const LAYOUT_ID = 'index-nav';
下方代码是:pages/index/components/nav/index.js
import './nav.css';
// 引入模板
import render from './nav.art';
// ajax请求
import { getData, getDelayedData } from 'api/getData';
// 请求地址和盛放结构的元素
import { URL, LAYOUT_ID } from './config';
// https://www.imooc.com/api/mall-wepApp/index/nav
getData(URL).then(data => {
document.getElementById(LAYOUT_ID).innerHTML = render({
items: data
});
});
4、返回顶部
Backtop 组件,下方代码是:下方代码是components/backtop/index.js
import './backtop.css';
import 'icons/iconfont.css';
const CHANGED_CLASS_NAME = 'backtop-hidden';
const INIT_STATE = 'init';
const CHANGED_STATE = 'changed';
class Backtop {
constructor(el, critical_point, scrollContainer, eventEl = scrollContainer) {
this.el = el;
this.critical_point = critical_point;
// 滚动条所在的容器
this.scrollContainer = scrollContainer;
// 监听滚动事件的元素
this.eventEl = eventEl;
this.setState(INIT_STATE);
this.bindEvent();
}
// 设置状态
setState(state) {
this.state = state;
}
// 绑定事件
bindEvent() {
this.eventEl.addEventListener(
'scroll',
() => {
if (this.needChange()) {
this.setState(CHANGED_STATE);
this.change();
} else if (this.needReset()) {
this.setState(INIT_STATE);
this.reset();
}
},
false
);
this.el.addEventListener(
'click',
() => {
this.scrollTo();
},
false
);
}
scrollTo(top = 0, left = 0) {
this.scrollContainer.scrollTo({
top,
left,
behavior: 'smooth'
});
}
reset() {
this.el.classList.add(CHANGED_CLASS_NAME);
}
needReset() {
return (
this.state !== INIT_STATE &&
this.scrollContainer.scrollTop <= this.critical_point
);
}
// 变化
change() {
this.el.classList.remove(CHANGED_CLASS_NAME);
}
// 需要变化
needChange() {
return (
this.state !== CHANGED_STATE &&
this.scrollContainer.scrollTop > this.critical_point
);
}
}
export default Backtop;
使用返回顶部组件,下方代码是:pages/index/components/backtop/index.js
import Backtop from 'components/backtop';
const scrollContainer = document.getElementById('index-page');
const backtopEl = scrollContainer.querySelector('.backtop');
new Backtop(backtopEl, window.innerHeight, scrollContainer);