js模块化操作及购物车案例
购物车案例
先上一个原生js版的
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>购物车</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<table>
<caption>
购物车
</caption>
<thead>
<tr>
<!-- 全选复选框 -->
<th>
<input
type="checkbox"
name="checkAll"
id="check-all"
checked
/><label for="check-all">全选</label>
</th>
<th>图片</th>
<th>品名</th>
<th>单位</th>
<th>单价</th>
<th>数量</th>
<th>金额/元</th>
</tr>
</thead>
<tbody>
<tr>
<td><input type="checkbox" name="item" value="SN-1020" checked /></td>
<td>
<a href=""><img src="images/p1.jpg" alt="" /></a>
</td>
<td>iPhone 11</td>
<td>台</td>
<td class="price">4799</td>
<td><input type="number" min="1" value="1" /></td>
<td class="amount">***</td>
</tr>
<tr>
<td><input type="checkbox" name="item" value="sn-1020" checked /></td>
<td>
<a href=""><img src="images/p2.jpg" alt="" /></a>
</td>
<td>小米pro 11</td>
<td>台</td>
<td class="price">3999</td>
<td><input type="number" min="1" value="1" /></td>
<td class="amount">***</td>
</tr>
<tr>
<td><input type="checkbox" name="item" value="SN-1030" checked /></td>
<td>
<a href=""><img src="images/p3.jpg" alt="" /></a>
</td>
<td>MacBook pro</td>
<td>台</td>
<td class="price">18999</td>
<td><input type="number" min="1" value="1" /></td>
<td class="amount">***</td>
</tr>
<tr>
<td><input type="checkbox" name="item" value="SN-1040" checked /></td>
<td>
<a href=""><img src="images/p4.jpg" alt="" /></a>
</td>
<td>小米75电视</td>
<td>台</td>
<td class="price">5999</td>
<td><input type="number" min="1" value="1" /></td>
<td class="amount">***</td>
</tr>
<tr>
<td><input type="checkbox" name="item" value="SN-1050" checked /></td>
<td>
<a href=""><img src="images/p5.jpg" alt="" /></a>
</td>
<td>Canon 90D单反</td>
<td>台</td>
<td class="price">9600</td>
<td><input type="number" min="1" value="1" /></td>
<td class="amount">***</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="5">总计:</td>
<td id="sum">***</td>
<td id="total-amount">***</td>
</tr>
</tfoot>
</table>
<div class="closing-cost">
<button class="cost">结算</button>
</div>
<script>
//1. 获取全选复选框,所有独立商品的复选框
const checkAll = document.querySelector("#check-all");
const checkItems = document.getElementsByName("item");
//2. 为全选复选框添加事件:change,当值改变时触发
checkAll.onchange = (ev) =>
checkItems.forEach((item) => (item.checked = ev.target.checked));
//3. 为每个单独商品的复选框添加change事件
checkItems.forEach(
(item) =>
(item.onchange = () =>
(checkAll.checked = [...checkItems].every((item) => item.checked)))
);
const numbs = document.querySelectorAll("tbody input[type=number]");
numbs.forEach((item) => (onchange = autoCalculate)); //方法赋值给方法不能有()
checkAll.addEventListener("change",autoCalculate);
checkItems.forEach(item => addEventListener("change",autoCalculate));
//购物车刚加载完成时也应该触发自动计算
window.onload = autoCalculate;
function autoCalculate() {
//获取购物车所有商品的单价
const price = document.querySelectorAll("tbody .price");
const priceArr = [...price].map((item) => item.textContent * 1);
//获取购物车所有商品的数量
const numbers = document.querySelectorAll("tbody input[type=number]");
const numbersArr = [...numbers].map((item) => item.value * 1);
//计算每件商品金额
let amountArr = [priceArr, numbersArr].reduce((total, cur) =>
total.map((item, index) => item * cur[index])
);
//获取选中商品数量,使用filter 筛选出 选中的checkbox
const checkedNumbersArr = [...numbersArr].filter(
(item, index) => [...checkItems][index].checked
);
//计算选中商品的总金额
const checkedAmountArr = [...amountArr].filter(
(item, index) => [...checkItems][index].checked
);
//计算商品总数
let sum = checkedNumbersArr.reduce((pre, cur) => pre + cur,0);
//计算总金额
let totalAmount = checkedAmountArr.reduce((prev, cur) => prev + cur,0);
//将计算结果渲染到购物车中
//总数量
document.querySelector("#sum").textContent = sum;
//总金额
document.querySelector("#total-amount").textContent = totalAmount;
//每个商品的金额
let amountTarget = document.querySelectorAll(".amount");
amountTarget.forEach(
(item, index) => (item.textContent = amountArr[index])
);
}
</script>
</body>
</html>
有个奇怪的点:checkItems.forEach(item => addEventListener("change",autoCalculate));
此条语句注释程序也可以正常运行。
此处全勾选和全不勾选使用 数组的 every很巧妙;使用filter过滤出已选中的商品的数组,进行数量和金额累加也很赞。
reduce方法真的很强大,再次赞叹。
jQuery版购物车
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>购物车</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<table>
<caption>
购物车
</caption>
<thead>
<tr>
<!-- 全选复选框 -->
<th>
<input
type="checkbox"
name="checkAll"
id="check-all"
checked
/><label for="check-all">全选</label>
</th>
<th>图片</th>
<th>品名</th>
<th>单位</th>
<th>单价</th>
<th>数量</th>
<th>金额/元</th>
</tr>
</thead>
<tbody>
<tr>
<td><input type="checkbox" name="item" value="SN-1020" checked /></td>
<td>
<a href=""><img src="images/p1.jpg" alt="" /></a>
</td>
<td>iPhone 11</td>
<td>台</td>
<td class="price">4799</td>
<td><input type="number" min="1" value="1" /></td>
<td class="amount">***</td>
</tr>
<tr>
<td><input type="checkbox" name="item" value="sn-1020" checked /></td>
<td>
<a href=""><img src="images/p2.jpg" alt="" /></a>
</td>
<td>小米pro 11</td>
<td>台</td>
<td class="price">3999</td>
<td><input type="number" min="1" value="1" /></td>
<td class="amount">***</td>
</tr>
<tr>
<td><input type="checkbox" name="item" value="SN-1030" checked /></td>
<td>
<a href=""><img src="images/p3.jpg" alt="" /></a>
</td>
<td>MacBook pro</td>
<td>台</td>
<td class="price">18999</td>
<td><input type="number" min="1" value="1" /></td>
<td class="amount">***</td>
</tr>
<tr>
<td><input type="checkbox" name="item" value="SN-1040" checked /></td>
<td>
<a href=""><img src="images/p4.jpg" alt="" /></a>
</td>
<td>小米75电视</td>
<td>台</td>
<td class="price">5999</td>
<td><input type="number" min="1" value="1" /></td>
<td class="amount">***</td>
</tr>
<tr>
<td><input type="checkbox" name="item" value="SN-1050" checked /></td>
<td>
<a href=""><img src="images/p5.jpg" alt="" /></a>
</td>
<td>Canon 90D单反</td>
<td>台</td>
<td class="price">9600</td>
<td><input type="number" min="1" value="1" /></td>
<td class="amount">***</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="5">总计:</td>
<td id="sum">***</td>
<td id="total-amount">***</td>
</tr>
</tfoot>
</table>
<div class="closing-cost">
<button class="cost">结算</button>
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.js"></script>
<script>
//1. 获取全选复选框,所有独立商品的复选框
const checkAll = $("#check-all");
const checkItems = $("input[name=item]");
//2. 为全选复选框添加事件:change,当值改变时触发
checkAll.change((ev) =>
checkItems.each(function () {
$(this).prop("checked", $(ev.target).is(":checked"));
})
);
//3. 为每个单独商品的复选框添加change事件
checkItems.change(() => {
checkAll.prop(
"checked",
[...checkItems].every((item) => $(item).checked)
);
});
$("tbody input[type=number]").each((index, item) =>
$(item).change(autoCalculate)
);
// 改变独立复选框状态时触发
$("input[name=item]").each((index, item) => $(item).change(function(){
autoCalculate();
}));
// 全选状态改变时触发
$('#check-all').change(function(){
autoCalculate();
});
// 页面载入完成后触发
$(function () {
autoCalculate();
});
//购物车刚加载完成时也应该触发自动计算
window.onload = autoCalculate;
function autoCalculate() {
//获取购物车所有商品的单价
let priceArr = $("tbody .price").map(
(index, item) => $(item).text() * 1
);
//获取购物车所有商品的数量
let numbersArr = $("input[type=number]").map(
(index, item) => $(item).val() * 1
);
//计算每件商品金额
let amountArr = [priceArr, numbersArr].reduce((total, cur) =>
total.map((index, item) => item * cur[index])
);
//获取选中商品数量,使用filter 筛选出 选中的checkbox
let checkedNumbersArr = numbersArr.filter((index, item) =>
$(checkItems[index]).is(":checked")
);
//计算选中商品的总金额
let checkedAmountArr = amountArr.filter((index, item) =>
$(checkItems[index]).is(":checked")
);
//计算商品总数
let sum = [...checkedNumbersArr].reduce((pre, cur) => pre + cur);
//计算总金额
let totalAmount = [...checkedAmountArr].reduce(
(prev, cur) => prev + cur
);
console.log(sum);
//将计算结果渲染到购物车中
//总数量
$("#sum").text(sum);
//总金额
$("#total-amount").text(totalAmount);
//每个商品的金额
$(".amount").each((index, item) => $(item).text(amountArr[index]));
}
</script>
</body>
</html>
效果相同,不同点也很明显先比之下,个人还是觉得原生的写起来要顺手很多。jQ中方法很强大,吐槽点可能是不太熟练的原因,经常忘记jq中的参数和原生是反的。比如filter,map等。回头看时才了然。希望以后用的熟练了可以避免这种低级错误。
模块的知识
- 模块就是一个js代码块
- 封装成模块的js文件,内部成员对外不见,除非导出来
- 模块解决了js的模块化开发与代码封装问题
模块解决了什么问题
- 可扩展性: 每个模块是独立的,各写各的互不影响,出错直接定位责任人
- 可复用性: 只需要一条import 指令就可以导入
- 避免污染全局空间:模块处在自己的命名空间内
模块是一个js文件,显示不能像之前一样,将js代码写到html中
模块要写到一个独立的js文件中,并使用一些特别的语法和关键字
那怎么写呢?
<script type="module">
// 第二步 使用 import {和模块中的导出变量一致} from "需要导入的模块路径"
// 路径前面的 ./ 不能省略 import 必须放在首行
import { userName, hello, User } from "./module1.js";
console.log(userName);
console.log(hello(userName));
let user = new User("Mac book", 18999);
console.log(user.print());
//1. 禁止重复声明模块成员
// let userName;
//2. 模块成员也不允许更新
// userName = "phone";
//模块成员在当前作用域相当于常量
</script>
注意:一定要先在script 标签中 申明type=”module”
js文件中必须要有export导出,导出可以分别导出和统一导出
例如:module1.js文件
// // 分别到导出
// export let userName = 'I';
// export function hello(userName) {
// return `Hello PHP,${userName} have a dream.`;
// }
// export class User {
// constructor(name,price){
// this.name=name;
// this.price=price;
// }
// print(){
// return this.name +" => " +this.price;
// }
// }
// //没有export 关键字是私有成员,外部不能访问
// let salary = 123;
// 还可以统一导出
let userName = 'I';
function hello(userName) {
return `Hello PHP,${userName} have a dream.`;
}
class User {
constructor(name,price){
this.name=name;
this.price=price;
}
print(){
return this.name +" => " +this.price;
}
}
//推荐使用方法
export {userName,hello,User}
导入时可以使用别名导入,这样就不担心重名了。
// 别名导入
import {myName as userName,echo} from "./module2.js"
还可以使用别名导出
例如可以在module的js文件中使用
export {userName as myName,hello as echo,User as FirstUser}
如果只有一个默认成员导出时可以不加大括号
let userName = "I";
function hello(userName) {
return `Hello PHP,${userName} have a dream.`;
}
class User {
constructor(name, price) {
this.name = name;
this.price = price;
}
print() {
return this.name + " => " + this.price;
}
}
//默认导出的成员不要加大括号
// export default hello;
//导出列表中既有默认成员,又有普通成员 怎么做
//email 是非静态成员
let email = "admin@php.cn";
//认为hello是默认成员
export { userName, email, hello as default };
<script type="module">
// 导入默认模块与普通模块的最大区别在于没有大括号
// import userName from "./module3.js";
// console.log(userName);
// import hello from "./module3.js";
// console.log(hello("I"));
// 既有默认成员,又有普通成员
import hello, {email} from "./module3.js"
console.log(email);
console.log(hello("有default又有普通成员"));
</script>
还可以使用命名空间的方法
<script type="module">
//命名空间:是一个容器,内部可以包括任何类型的数据
//命名空间是一个对象,可以挂载到当前的全局中
// let ns ={
// a:1,b:2,c:3
// }
//ns:就是一个
// console.log(ns.a,ns.b,ns.c);
import * as namespace from "./module1.js";
let userName;
let hello = () => {};
class User {};
console.log(namespace);
console.log(namespace.userName);
console.log(namespace.hello(namespace.userName));
console.log(new namespace.User("Mac book",18999).print());
</script>
使用命名空间好处明显:层次清晰,有效减少命名冲突。