Skip to content

JavaScript模块化、异常处理和正则

内容概述

这一讲主要讲解JavaScript模块化,然后补充两个前面没有讲到的知识:异常处理和正则

教学目标

  • 理解JavaScript模块化是什么以及为什么需要模块化
  • 掌握ES6模块化规划语法
  • 理解CommonJS模块化语法
  • 理解异常处理,掌握try/catch/finally语句使用
  • 掌握正则表达式的基本使用,能编写简单的正则

具体内容

  • JavaScript模块化:CommonJS和ES6模块
  • 异常处理
  • 正则表达式:创建正则表达式,特殊字符,正则相关方法

JavaScript模块化

JavaScript 模块化是一种将代码分割成独立、可复用的单元(称为“模块”)的技术。这种技术有助于组织代码、提高代码的可维护性、促进代码重用,并有助于管理依赖关系。JavaScript 模块化经历了几个发展阶段,从最初的简单函数封装,到使用立即执行函数表达式(IIFE),再到现代的 ES6 模块(ECMAScript 2015 引入)和 CommonJS、AMD 等规范。

没有模块化的问题

比如,我们网页中引入JavaScript代码,经常是这样的。

javascript
<script src="./utils.js"></script>
<script src="./main.js"></script>
javascript
// utils.js
let a = 1

const sum = function (a, b) {
	return a + b
}
// main.js
let a = 1

console.log(sum(1, 2))

问题

  1. 变量容易冲突
  2. script引入顺序要根据依赖关系

所以需要模块化解决

  1. 需要实现每个模块都要有自己单独的作用域,每个模块之间的内部变量不会产生冲突;
  2. 需要实现每个模块需要能导入其它模块的功能,也能导出自己的功能供其它模块调用。

CommonJS

CommonJS 是一种主要用于 Node.js 的模块规范。它使用 require() 函数来同步加载模块,并使用 module.exportsexports 对象来导出模块。

导出模块

javascript
// math.js  
function add(a, b) {  
  return a + b;  
}  
  
function subtract(a, b) {  
  return a - b;  
}  
  
module.exports = { add, subtract };  
  
// 或者使用 exports  
// exports.add = add;  
// exports.subtract = subtract;

导入模块

javascript
// app.js  
const { add, subtract } = require('./math.js');  
  
console.log(add(2, 3)); // 5  
console.log(subtract(5, 2)); // 3

ES6 模块

ES6 引入了原生的模块系统,使得在浏览器和 Node.js 环境中都可以使用 importexport 语句来导入和导出模块。

浏览器如果需要加载ES6模块,需要在<script>标签上加上type="module"属性。

javascript
<script type="module" src="./main.js"></script>

export命令

一个模块就是一个独立的文件。该文件内部的所有变量,外部无法获取。如果你希望外部能够读取模块内部的某个变量,就必须使用export关键字输出该变量。下面是一个 JS 文件,里面使用export命令输出变量。

javascript
// user.js
export const name = 'js';
export const age = 18;

export的写法,除了像上面这样,还有另外一种。

javascript
// user.js
const name = 'js';
const age = 18;

export { name, age };

export命令除了输出变量,还可以输出函数或类(class)。

javascript
export function multiply(x, y) {
  return x * y;
};

import命令

  • 使用export命令定义了模块的对外接口以后,其他 JS 文件就可以通过import命令加载这个模块。
javascript
// main.js
import { name, age } from './user.js';

console.log(name)
console.log(age)
  • 如果想为输入的变量重新取一个名字,import命令要使用as关键字,将输入的变量重命名。
js
import { name as userName } from './user.js';
console.log(userName)
  • 如果多次重复执行同一句import语句,那么只会执行一次,而不会执行多次。

模块的整体加载

除了指定加载某个输出值,还可以使用整体加载,即用星号(*)指定一个对象,所有输出值都加载在这个对象上面。

下面是一个circle.js文件,它输出两个方法areacircumference

javascript
// circle.js

export function area(radius) {
  return Math.PI * radius * radius;
}

export function circumference(radius) {
  return 2 * Math.PI * radius;
}

现在,加载这个模块。

javascript
// main.js

import { area, circumference } from './circle';

console.log('圆面积:' + area(4));
console.log('圆周长:' + circumference(14));

上面写法是逐一指定要加载的方法,整体加载的写法如下。

javascript
import * as circle from './circle';

console.log('圆面积:' + circle.area(4));
console.log('圆周长:' + circle.circumference(14));

默认导出

javascript
// export-default.js
export default function () {
  console.log('foo');
}

其他模块加载该模块时,import命令可以为该匿名函数指定任意名字。

javascript
// main.js
import customName from './export-default';
customName(); // 'foo'

export default命令用在非匿名函数前,也是可以的。

js
// export-default.js
export default function foo() {
  console.log('foo');
}

// 或者写成

function foo() {
  console.log('foo');
}

export default foo;

异常处理

在JavaScript中,异常处理是一种重要的编程实践,它可以帮助你捕获和处理在运行时可能发生的错误,从而避免程序因未处理的错误而完全崩溃。JavaScript通过try...catch...finally语句来实现异常处理。

基本语法

javascript
try {  
  // 尝试执行的代码块  
  // 如果这里面的代码抛出了异常,则控制权会转移到catch块  
} catch (error) {  
  // 捕获异常并处理  
  // error对象包含了异常的信息  
} finally {  
  // 无论是否捕获到异常,finally块中的代码都会执行  
  // 通常用于执行清理工作,如关闭文件、释放资源等  
}

示例

javascript
try {
  // 尝试执行一个可能会抛出异常的操作
  const result = 10 / 0; // 这将抛出一个Infinity值,但JavaScript不会因此抛出异常
  // 如果这里尝试访问一个未定义的属性,比如null.someProperty,则会抛出TypeError
  // 故意制造一个错误以演示try...catch
  let obj = null
  console.log(obj.a);
  throw new Error('这是一个自定义的错误');
} catch (error) {
  // 捕获到异常,并执行这里的代码
  console.error('捕获到错误:', error.name);
} finally {
  // 无论是否捕获到异常,finally块都会执行
  console.log('finally块执行了');
}

正则表达式

JavaScript 中的正则表达式(Regular Expressions)是一种强大的文本处理工具,它允许你定义搜索文本的模式,并根据这些模式执行匹配、搜索、替换等操作。

创建正则表达式

方式一:通过调用RegExp对象的构造函数创建

javascript
const regexp = new RegExp(/123/);

方式二:利用字面量创建 正则表达式

javascript
const reg = /123/;

测试正则表达式

test() 正则对象方法,用于检测字符串是否符合该规则,该对象会返回 true 或 false,其参数是测试字符串。

javascript
const reg = /123/;
console.log(reg.test(123));//匹配字符中是否出现123  出现结果为true
console.log(reg.test('abc'));//匹配字符中是否出现123 未出现结果为false

正则表达式中的特殊字符

修饰符

修饰符描述
i执行对大小写不敏感的匹配。
g执行全局匹配(查找所有匹配而非在找到第一个匹配后停止)。
m执行多行匹配。

方括号

方括号用于查找某个范围内的字符:

表达式描述
[abc]查找方括号之间的任何字符。
[^abc]查找任何不在方括号之间的字符。
[0-9]查找任何从 0 至 9 的数字。
[a-z]查找任何从小写 a 到小写 z 的字符。
[A-Z]查找任何从大写 A 到大写 Z 的字符。
[A-z]查找任何从大写 A 到小写 z 的字符。
[adgk]查找给定集合内的任何字符。
[^adgk]查找给定集合外的任何字符。
(red|blue|green)查找任何指定的选项。
javascript
const reg1 = /[abc]/; // 只要包含有a 或者 包含有b 或者包含有c 都返回为true
const reg2 = /^[abc]$/; // 三选一 只有是a 或者是 b  或者是c 这三个字母才返回 true

// - 短横线 表示一个范围
const reg3 = /^[a-z]$/ //26个英文字母任何一个字母返回 true  - 表示的是a 到z 的范围  
// 字符组合
const reg4 = /^[a-zA-Z0-9]$/; // 26个英文字母(大写和小写都可以)任何一个字母返回 true  
//取反 方括号内部加上 ^ 表示取反,只要包含方括号内的字符,都返回 false 。
const reg5 = /^[^a-zA-Z0-9]$/;

元字符

元字符(Metacharacter)是拥有特殊含义的字符:

元字符描述
.查找单个字符,除了换行和行结束符。
\w查找数字、字母及下划线。
\W查找非单词字符。
\d查找数字。
\D查找非数字字符。
\s查找空白字符。
\S查找非空白字符。
\b匹配单词边界。
\B匹配非单词边界。
\0查找 NULL 字符。
\n查找换行符。
\f查找换页符。
\r查找回车符。
\t查找制表符。
\v查找垂直制表符。
\xxx查找以八进制数 xxx 规定的字符。
\xdd查找以十六进制数 dd 规定的字符。
\uxxxx查找以十六进制数 xxxx 规定的 Unicode 字符。

量词

量词描述
n+匹配任何包含至少一个 n 的字符串。例如,/a+/ 匹配 "candy" 中的 "a","caaaaaaandy" 中所有的 "a"。
n*匹配任何包含零个或多个 n 的字符串。例如,/bo*/ 匹配 "A ghost booooed" 中的 "boooo","A bird warbled" 中的 "b",但是不匹配 "A goat grunted"。
n?匹配任何包含零个或一个 n 的字符串。例如,/e?le?/ 匹配 "angel" 中的 "el","angle" 中的 "le"。
n{X}匹配包含 X 个 n 的序列的字符串。例如,/a{2}/ 不匹配 "candy," 中的 "a",但是匹配 "caandy," 中的两个 "a",且匹配 "caaandy." 中的前两个 "a"。
n{X,}X 是一个正整数。前面的模式 n 连续出现至少 X 次时匹配。例如,/a{2,}/ 不匹配 "candy" 中的 "a",但是匹配 "caandy" 和 "caaaaaaandy." 中所有的 "a"。
n{X,Y}X 和 Y 为正整数。前面的模式 n 连续出现至少 X 次,至多 Y 次时匹配。例如,/a{1,3}/ 不匹配 "cndy",匹配 "candy," 中的 "a","caandy," 中的两个 "a",匹配 "caaaaaaandy" 中的前面三个 "a"。注意,当匹配 "caaaaaaandy" 时,即使原始字符串拥有更多的 "a",匹配项也是 "aaa"。
n$匹配任何结尾为 n 的字符串。
^n匹配任何开头为 n 的字符串。
?=n匹配任何其后紧接指定字符串 n 的字符串。
?!n匹配任何其后没有紧接指定字符串 n 的字符串。
javascript
// 量词符:用来设定某个模式出现的次数
//简单理解:就是让下面的a这个字符重复多少次
 
//* 相当于 >=0 可以出现0次或者很多次
const reg1 = /^a*$/;
 
//+相当于 >=1 可以出现1次或者很多次
 const reg2 = /^a+$/;
 
//?相当于 1 || 0 重复1次或者0次
 const reg3 = /^a?$/;
 
//{3}就是重复3次
const reg4 = /^a{3}$/;
 
//{3,}大于等于3 >=3
const reg5 = /^a{3,}$/;
 
//{3,16} 等于等于3 并且 小于等于16
const reg6 = /^a{3,16}$/;

边界符

正则表达式中的边界符(位置符)用来提示字符所处的位置,主要有两个字符

边界符说明
^表示匹配行首的文本(以谁开始)
$表示匹配行尾的文本(以谁结束)

如果 ^和 $ 在一起,表示必须是精确匹配。

javascript
const reg1 = /abc/; // 只要包含有abc这个字符串返回的都是true
const reg2 = /^abc/;  // 必须以abc开头
const reg3 = /^abc$/; // 精确匹配 要求必须是 abc字符串才符合规范

正则相关方法

String 对象的方法

  1. match()
    • 用法:str.match(regexp)
    • 描述:执行一个搜索匹配,返回一个数组,其中存放匹配的结果。如果没有找到任何匹配项,则返回null。如果使用了全局匹配(即正则表达式中包含g标志),则返回的数组包含字符串中的所有匹配项,但不包括捕获组。
  2. search()
    • 用法:str.search(regexp)
    • 描述:执行一个搜索匹配,如果找到匹配项,则返回第一个匹配项的索引;否则返回-1search()方法不返回匹配的字符串,也不使用捕获组。
  3. replace()
    • 用法:str.replace(regexp|substr, newSubStr|function)
    • 描述:在字符串中搜索匹配项,然后替换它们。replace()可以接受一个字符串或一个正则表达式作为第一个参数,第二个参数可以是一个字符串或一个函数,用于指定替换内容。
  4. split()
    • 用法:str.split([separator[, limit]])
    • 描述:使用指定的分隔符(可以是一个字符串或正则表达式)将字符串分割成子字符串数组,并返回该数组。可选的limit参数可以限制返回的数组中的元素数量。

RegExp 对象的方法

虽然RegExp对象本身不直接提供与字符串操作相关的方法(除了它的exec()test()方法),但它们与字符串方法紧密相关,因为字符串方法可以接受正则表达式作为参数。不过,这里还是列出RegExp对象的两个与匹配相关的方法:

  1. exec()
    • 用法:regexp.exec(str)
    • 描述:在字符串中执行一个搜索匹配。返回一个数组,其中存放匹配的结果。如果没有找到任何匹配项,则返回null。返回的数组包含匹配的文本和任何捕获组的匹配项,以及索引和输入字符串。与String.prototype.match()类似,但在没有全局标志(g)时,exec()在每次调用时都会从字符串的开头开始搜索,而不是从上次匹配的末尾开始。
  2. test()
    • 用法:regexp.test(str)
    • 描述:执行一个正则表达式匹配测试。如果字符串str中含有匹配正则表达式的文本,则返回true;否则返回false

案例

作业

  1. 使用正则识别手机号,手机号规则
  • 11个数字
  • 1开头
  • 第2位只能是3、4、5、7、8的其中一个
javascript
function isPhone (str) {

}
  1. 使用正则判断时间格式 yyyy-mm-dd
javascript
function isDate (str) {

}
  1. 使用正则去除前后空格
javascript
function trim (str) {

}

let str = "   Hello, World!   ";  
let trimmedStr = trim(str);  
console.log(trimmedStr); // 输出: "Hello, World!"

作业讲解

  1. 实现函数,函数接收一个参数arg,类型未知,如果是数字返回该数字的平方,如果是字符串将该字符串重复5次并返回,如果是其他类型直接返回该参数
javascript
function test1(arg) {
  // 补充代码
  if (typeof arg === 'number' && !Number.isNaN(arg)) {
    return arg * arg
  } else if (typeof arg === 'string') {
    return arg.repeat(5)
  }
}

console.log(test1(10)); // 100
console.log(test1('abc')); // 'abcabcabcabcabc'
  1. 实现函数,函数接收一个数组且每个元素都是数字,分别计算数组的最小值、最大值、平均值按顺序放入一个长度为3的数组中并返回
javascript
function test2(arr) {
  // 补充代码
  let min = arr[0]
  let max = arr[0]
  let total = 0;
  for (let i = 0; i < arr.length; i++) {
    if (min > arr[i]) {
      min = arr[i]
    }
    if (max < arr[i]) {
      max = arr[i]
    }
    total = total + arr[i]
  }
  return [min, max, total / arr.length]
}

console.log(test2([1, 7, 10])); // [1, 10, 6]
  1. 将下列函数转换为箭头函数
javascript
function getSum (a, b) {
  return a + b;
}

function square (a) {
  return a * a;
}

function say (name, message) {
  console.log(name);
  console.log(message);
  console.log(name + ':' + message)
}

4.实现函数test1,参数接收一个数组表示所有学生考试分数,数组每个元素是个对象,对象有两个属性id和score分数,筛选出分数大于等于60的学生然后求平均分(取整数部分)

javascript
function test1 (arr) {
  // 实现代码
  const filterArr = students.filter(item => item.score >= 60)
  const total = filterArr.reduce((pre, current) => {
    return pre + current.score
  }, 0)
  return Math.floor(total / filterArr.length)
}

// 参数示例
const students = [
  {
    id: 1,
    score: 90
  },
  {
    id: 2,
    score: 45
  },
  {
    id: 3,
    score: 70
  },
  {
    id: 4,
    score: 65
  },
  {
    id: 5,
    score: 55
  }
]

console.log(test1(students)); // 75
  1. 实现函数test2,参数接收一个数组表示所有学生考试分数,数组每个元素是个对象,对象有两个属性id和score分数,去掉一个最低分,去掉一个最高分,然后求平均分(取整数部分)
javascript
function test2 (arr) {
  // 实现代码
  const sortArr = arr.sort((a, b) => a.score - b.score)
  const filterArr = sortArr.slice(1, sortArr.length - 1)
  console.log(filterArr);
  const total = filterArr.reduce((pre, current) => {
    return pre + current.score
  }, 0)
  return Math.floor(total / filterArr.length)
}

// 参数示例
const students = [
  {
    id: 1,
    score: 90
  },
  {
    id: 2,
    score: 45
  },
  {
    id: 3,
    score: 70
  },
  {
    id: 4,
    score: 65
  },
  {
    id: 5,
    score: 55
  }
]

console.log(test2(students)); // 63
  1. 写一个函数,入参是一个日期对象,实现格式化日期对象,返回yyyy-MM-dd HH:mm:ss的形式
javascript
function addZero (str) {
  if (String(str).length < 2) {
    return '0' + str;
  } else {
    return str
  }
}

function date2str (date) {
  // yyyy-MM-dd HH:mm:ss
  const year = date.getFullYear();
  const month = date.getMonth() + 1;
  const day = date.getDate();
  const hour = date.getHours();
  const minute = date.getMinutes();
  const second = date.getSeconds();
  return `${year}-${addZero(month)}-${addZero(day)} ${addZero(hour)}:${addZero(minute)}:${addZero(second)}`;
}

console.log(date2str(new Date()));

7.写一个函数实现单词逆转,如,输入Welcome to Shenzhen 返回Shenzhen to Welcome

javascript
function reverseWords (str) {
  const words = str.split(' ');
  return words.reverse().join(' ')
}

console.log(reverseWords('Welcome to Shenzhen'));

8.写一个函数,入参是3个字符串,函数内部实现1s之后打印第1个字符串,再过2s之后打印第2个字符串,再过3s之后打印第3个字符串

javascript
function test (str1, str2, str3) {
  setTimeout(() => {
    console.log(str1);
    setTimeout(() => {
      console.log(str2);
      setTimeout(() => {
        console.log(str3);
      }, 3000)
    }, 2000)
  }, 1000)
}

test('a', 'b', 'c')

9.编写一个异步函数sleep实现定时效果,接收一个参数表示需要定时的时间,单位ms

javascript
function sleep (time) {
  return new Promise(reslove => {
    setTimeout(() => {
      reslove()
    }, time)
  })
}

sleep(3000).then(() => {
  console.log('3s后执行该行代码');
})
  1. 在题目1实现sleep函数的基础上,写一个函数,入参是3个字符串,函数内部实现1s之后打印第1个字符串,再过2s之后打印第2个字符串,再过3s之后打印第3个字符串,写出两种方式(Promise链式编程和async/await语法形式)
javascript
function sleep (time) {
  return new Promise(reslove => {
    setTimeout(() => {
      reslove()
    }, time)
  })
}

function test1 (str1, str2, str3) {
  sleep(1000).then(() => {
    console.log(str1);
    return sleep(2000)
  }).then(() => {
    console.log(str2);
    return sleep(3000)
  }).then(() => {
    console.log(str3);
  })
}
  1. 使用正则识别手机号,手机号规则 11个数字 1开头 第2位只能是3、4、5、7、8的其中一个
javascript
function isPhone (str) {
  return /^1[34578]\d{9}$/.test(str)
}

console.log(isPhone('18674584978'));
  1. 使用正则判断时间格式 yyyy-mm-dd
javascript
function isDate (str) {
  return /^\d{4}-\d{2}-\d{2}$/.test(str);
}
  1. 使用正则去除前后空格
javascript
function trim (str) {
  return str.replace(/^\s+|\s+$/g, '');
}
html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8"/>
  <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
  <title>文章列表页面</title>
  <style>
      /* styles.css */
      body,
      html {
          margin: 0;
          padding: 0;
          font-family: Arial, sans-serif;
      }

      .container {
          max-width: 800px;
          margin: 0 auto;
          padding: 20px;
      }

      .header {
          text-align: center;
          margin-bottom: 20px;
      }

      .articles {
          display: flex;
          flex-direction: column;
          gap: 20px;
      }

      .article {
          display: flex; /* 启用Flex布局 */
          align-items: flex-start; /* 垂直对齐方式:项目在交叉轴上的起点对齐 */
          border: 1px solid #ccc;
          border-radius: 5px;
          padding: 10px; /* 根据需要调整内边距 */
          margin-bottom: 20px; /* 文章项之间的间距 */
          position: relative;
      }

      .cover-image {
          width: 150px; /* 或者使用百分比,根据需要调整 */
          height: auto;
          border-radius: 5px 0 0 5px; /* 仅在左侧添加圆角 */
          margin-right: 15px; /* 封面和简介之间的间距 */
      }

      .content {
          position: relative;
          flex-grow: 1; /* 使得内容区域能够填充剩余空间 */
      }

      /* 其他CSS样式保持不变或根据需要调整 */
      .article h2 {
          margin: 0 0 10px 0;
          font-size: 1.5em;
      }

      .article img {
          width: 150px;
          height: 80px;
      }

      .article p {
          margin: 0;
          color: #666;
      }

      .date {
          position: absolute; /* 绝对定位 */
          right: 15px;
          top: 15px;
          font-size: 0.8em;
          color: #aaa;
      }

      /* 响应式设计,当屏幕较小时,可以调整Flex方向或文章样式 */
      @media (min-width: 600px) {
          .articles {
              flex-direction: row;
              flex-wrap: wrap;
              justify-content: space-between;
          }

          .article {
              flex: 1 1 calc(50% - 10px); /* 宽度自适应,但保留间隙 */
              position: relative;
          }
      }

      #addForm {
          text-align: right;
      }

      .form-row {
          display: flex;
          margin-bottom: 20px;
      }

      .form-row > label {
          margin-right: 5px;
      }

      .article-title,.article-content {
          flex: 1;
          padding: 10px;
          border-radius: 5px;
          margin-right: 10px;
          box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
      }

      .submit-button {
          padding: 10px 20px;
          border: none;
          border-radius: 5px;
          background-color: #8e9bf0;
          color: #fff;
          cursor: pointer;
          margin-bottom: 10px;
      }

      .article-delete {
          padding: 5px 10px;
          border: none;
          border-radius: 5px;
          background-color: #ff6b6b;
          color: #fff;
          cursor: pointer;
          transition: background-color 0.3s;
          position: absolute;
          right: 5px;
          bottom: 5px;
      }

      .article-delete:hover {
          background-color: #ff4f4f;
      }
  </style>
</head>
<body>
<div class="container">
  <header class="header">
    <h1>文章列表</h1>
  </header>
  <form class="form" id="addForm">
    <div class="form-row">
      <label for="articleTitle">文章标题</label>
      <input
        type="text"
        class="article-title"
        placeholder="请输入文章标题"
        id="articleTitle"
      />
    </div>
    <div class="form-row">
      <label for="articleContent">文章内容</label>
      <textarea
        rows="3"
        cols="30"
        class="article-content"
        placeholder="请输入文章内容"
        id="articleContent"></textarea>
    </div>
    <button type="submit" class="submit-button">提交</button>
  </form>
  <div class="articles">
    <div class="article">
      <img
        src="https://api.likepoems.com/img/nature"
        alt="文章封面 1"
        class="cover-image"
      />
      <div class="content">
        <h2>文章标题 1</h2>
        <p>这是一篇关于HTML和CSS的入门级文章,适合初学者。</p>
        <span class="date">发布日期:2023-08-06 12:00:00</span>
      </div>
      <button class="article-delete">删除</button>
    </div>
    <div class="article">
      <img
        src="https://api.likepoems.com/img/nature"
        alt="文章封面 2"
        class="cover-image"
      />
      <div class="content">
        <h2>文章标题 2</h2>
        <p>这是一篇关于HTML和CSS的入门级文章,适合初学者。</p>
        <span class="date">发布日期:2023-08-06 13:00:00</span>
      </div>
      <button class="article-delete">删除</button>
    </div>
    <div class="article">
      <img
        src="https://api.likepoems.com/img/nature"
        alt="文章封面 3"
        class="cover-image"
      />
      <div class="content">
        <h2>文章标题 3</h2>
        <p>这是一篇关于HTML和CSS的入门级文章,适合初学者。</p>
        <span class="date">发布日期:2023-08-06 14:00:00</span>
      </div>
      <button class="article-delete">删除</button>
    </div>
    <!-- 更多文章 -->
  </div>
</div>
<script>

</script>
</body>
</html>