大前端技能最详细的笔记,快速入门必看!NodeJS、Npm、Es6、Babel、Webpack、模块化超详细!

作者:FL

初稿日期:2024年1月18日

版本:2


主要内容:

  • 前端开发与前端开发工具
  • Node.js安装与入门
  • Es6新语法糖
  • Npm包管理工具
  • Babel
  • 模块化管理
  • webpack

简介

  • 本笔记旨在打通html+css+js ——> vue.js/react ,补充中间的知识,利于更好的学习vue.js和react
  • 本笔记知识只补充各个大前端知识的基础,具体细节参考各个技术的官网
  • 本笔记以狂神的大前端技能的视频为大纲,各个知识通过查阅相关资料和询问ai,知识多数比视频更详尽、更好操作。
  • 如有遗漏或问题,欢迎各位读者批判指正。
  • 看完本篇,请继续看vue3教程

一、Node.js

1.安装与配置

node.js安装与配置

2.介绍

简单的说 Node.js 就是运行在服务端的 JavaScript,如果你熟悉 Javascript,那么你将会很容易的学会 Node.js。

如果你是一个前端程序员,Node.js可以使用js开发后端,无需额外学习java等后端语言。

当然,如果你是后端程序员,想部署一些高性能的服务,那么学习 Node.js 也是一个非常好的选择。

前端技术流:HTML+CSS -> JS -> node.js -> webpack ->react -> vue -> uniapp

用node.js运行js

// 输出Hello World!
console.log("Hello World!");
$node hello.js
> Hello World!

3.Node.js实现请求响应

  • node.js将js变为了服务端语言

编写app.js

// 导入内置的 http 模块
const http = require('http');

// 创建一个名为 httpServer 的 Node.js 服务器实例
const httpServer = http.createServer();

// 定义一个名为 on 的回调函数,该函数将在服务器启动时被调用
// 监听请求事件,对所有请求做出响应
httpServer.on('request', (req, res) => {
  // 设置 HTTP 响应状态码为 200,表示请求成功
  res.statusCode = 200;
  // 设置响应的 Content-Type 为 text/plain,表示响应的内容是纯文本
  res.setHeader('Content-Type', 'text/plain');
  // 使用 res.end 方法发送响应正文 "Hello World"
  res.end('Hello World\n');
});

// 使用 http 模块的 listen 方法启动 HTTP 服务器,监听在 3000 端口上
httpServer.listen(3000, () => {
  // 打印出服务器正在端口 3000 上监听的消息
  console.log('Server is running and listening on port 3000');
});

使用Node.js运行

node app.js

浏览器效果:

//在http://localhost:3000/
Hello World

4.Node.js连接Mysql

在 Node.js 中操作 MySQL 数据库通常需要使用一个外部库,例如 mysqlmysql2。以下是使用这些库操作 MySQL 数据库的完整指南:

1. 安装 Node.js 包

准备工作:

1.再vscode中新建文件夹,右键文件夹选择集成终端中打开(需要用管理员权限打开)

2.输入初始化命令:npm init -y

3.安装express 模块:npm i express

4.安装mysql模块:npm i mysql/mysql2

使用 mysql:
npm install mysql
使用 mysql2:

在 Node.js 环境中,有一个名为 mysql2 的库,它是对 MySQL 官方库的一个封装,提供了一些额外的功能和改进,如异步查询支持。这个库并不是 MySQL 或 MySQL2 的官方 Node.js 绑定,而是一个第三方库,它使得在 Node.js 应用程序中使用 MySQL 变得更加容易。

npm install mysql2

2. 创建 MySQL 数据库和表

在你开始之前,确保你的 MySQL 服务器上已经创建了数据库,并且需要操作的表也已经存在。例如,你可以使用以下 SQL 命令来创建一个名为 students 的表:

CREATE TABLE students (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(50) NOT NULL,
    age INT NOT NULL
);

3. 编写 Node.js 代码来操作数据库

以下是使用 mysqlmysql2 库的 Node.js 示例代码。

使用 mysql:
const express = require('express'); //引入express 模块
const app = express(); //创建实例
const mysql = require('mysql'); //引入mysql 模块
// 创建数据库连接 填入数据库信息
//填自己数据库的信息!!!!!!!!!!!
const conn = mysql.createConnection({
  host: 'localhost', // MySQL服务器地址
  user: 'root', // MySQL用户名
  password: '123456', // MySQL密码
  database: 'test_db', // 要连接的数据库
});
// 测试连接
conn.connect(err => {
  if (err) throw err;
  console.log('连接成功');
});
// 开启服务器
app.listen(3000, () => {
  console.log('服务器在3000端口开启...');
});

  • 具体对数据库的操作见mysql2,mysql和mysql2的操作是类似的
使用 mysql2:
const express = require('express'); //引入express 模块
const app = express(); //创建实例
const mysql2 = require('mysql2'); //引入mysql 模块
// 创建数据库连接 填入数据库信息
//填自己数据库的信息!!!!!!!!!!!
const conn = mysql2.createConnection({
  host: 'localhost', // MySQL服务器地址
  user: 'root', // MySQL用户名
  password: '123456', // MySQL密码
  database: 'test_db', // 要连接的数据库
  waitForConnections: true,
  connectionLimit: 10,
  queueLimit: 0,
});
// 测试连接
conn.connect(err => {
  if (err) throw err;
  console.log('连接成功');
});
//进行数据库操作

// 开启服务器
app.listen(3000, () => {
  console.log('服务器在3000端口开启...');
});

插入:

  • 运行后可以在mysql中或http://localhost:3000/insert查看
// 定义路由(说白了就是网址)
app.get('/insert', (req, res) => {
  let sqlStr = "INSERT INTO students ( name, age )VALUES('张三',18)";
  //执行mysql 语句
    conn.query(sqlStr, err => {
        if (err) throw err;
        console.log('插入成功');
  });
  //成功后的页面显示
  res.send('插入成功');
});

修改:

// // 更新操作
app.get('/update', (req, res) => {
    let sqlStr = "UPDATE students SET name = '李四', age = 20 WHERE id = 1";
    conn.query(sqlStr, err => {
      if (err) throw err;
      console.log('更新成功');
    });
    res.send('更新成功');
  });

删除:

// 删除操作
  app.get('/delete', (req, res) => {
    let sqlStr = "DELETE FROM students WHERE id = 1";
    conn.query(sqlStr, err => {
      if (err) throw err;
      console.log('删除成功');
    });
    res.send('删除成功');
  });
  • 注意:以上对数据库的操作只有在访问对应路由才会执行,且每访问一次就会执行一次

每次访问这些路由时,都会独立地执行相应的SQL操作。这对于测试和小型应用程序可能是可行的,但如果你正在构建一个高流量的应用程序,你可能需要考虑更高效的方法来处理这些操作,比如使用队列系统或者在后台进行数据操作,以避免对数据库的频繁请求可能导致性能问题。
此外,对于生产环境,还需要考虑事务处理、错误处理、安全性(如使用预编译语句来防止SQL注入)以及数据的完整性和一致性。


二、ES6新语法

1.介绍

CMAScript 6(ES6),也称为ECMAScript 2015,是JavaScript语言的一个重要版本,于2015年6月正式发布。ES6在语言的核心中引入了许多新特性,这些特性旨在改善代码的清晰度、性能,以及开发体验。

2.let 、const和var

在ES6(ECMAScript 2015)中,letconstvar是用于变量声明的关键字,它们之间有几个关键的区别:

2.1 作用域(Scope)

  • var声明的变量具有函数作用域(function scope),这意味着如果var变量在一个函数内部声明,它只能在这个函数内部被访问
  • let声明的变量具有块作用域(block scope),它仅在声明它的块(如一个if语句或for循环)内部有效
  • const声明的变量也是块作用域的,但其值在声明后不能被重新赋值,这意味着它等同于最终的常量。

2.2 提升(Hoisting)

  • var声明的变量会被提升到其作用域的顶部,但在初始化之前不能使用,访问未初始化的变量会得到undefined
  • letconst声明的变量也会被提升,但它们在声明之前不可用,如果尝试访问未声明的变量,将会导致一个引用错误(ReferenceError)。

2.3 数据类型

  • varletconst都可以用来声明任何数据类型,包括基本类型(如number、string、boolean)和对象。
  • const特别之处在于,一旦声明,它所引用的对象或数组不能被重新分配,但是其引用的对象或数组内部的属性可以被修改
// 基本数据类型的不可变性
const number = 42;
// 错误,不能重新赋值
// number = 24;

// 对象引用的不可变性
const person = { name: "Alice" };
// 错误,不能重新分配到一个新的对象
// person = { name: "Bob" };
// 但是可以修改对象的属性
person.name = "Bob"; // 允许

// 数组引用的不可变性
const numbers = [1, 2, 3];
// 错误,不能重新分配到一个新的数组
// numbers = [4, 5, 6];
// 但是可以修改数组的元素
numbers.push(4); // 允许

2.4 重新赋值

  • var声明的变量可以被重新赋值。
  • let声明的变量也可以被重新赋值,但只能在它声明的块内部。
  • const声明的变量不能被重新赋值,它的值在声明后是固定的。

示例:

// 使用var
function exampleVar() {
    if (true) {
        var x = 10;
    }
    console.log(x); // 输出10,x被提升了
}
exampleVar();
// 使用let
function exampleLet() {
    if (true) {
        let x = 10;
    }
    console.log(x); // 报错,x没有在当前作用域外可用
}
// exampleLet(); // ReferenceError: x is not defined
// 使用const
function exampleConst() {
    if (true) {
        const x = 10;
    }
    console.log(x); // 报错,x没有在当前作用域外可用
}
// exampleConst(); // ReferenceError: x is not defined
// 另一个例子,展示const不可重新赋值
const y = 20;
y = 30; // 报错,不能重新赋值

2.5性能影响

  • 在性能上,varletconst声明的变量在内存中没有显著差异。它们的主要区别在于作用域和提升行为,而不是性能。
  • 然而,由于letconst避免了变量提升,它们在处理复杂代码时可以减少错误和意外行为,从而可能提高代码的可预测性和可维护性。

在现代JavaScript开发中,推荐尽可能使用letconst,因为它们提供了更好的作用域管理和数据安全,有助于编写更清晰、更可靠的代码。

3.模板字符串

在ES6(ECMAScript 2015)中,模板字符串(template strings)是一种增强版的字符串字面量,允许你创建包含变量、表达式和多行字符串的字符串。模板字符串使用反引号(`)来包裹字符串内容,而不是使用单引号(')或双引号(")。

基本语法

模板字符串的基本语法如下:

`模板字符串 ${表达式}`
  • ${expression} 表示一个模板表达式,它可以是任何有效的JavaScript表达式,包括变量、函数调用、运算符等。
  • 模板字符串中的变量会被JavaScript引擎替换为对应表达式的结果。
  • 如果模板表达式包含空格或换行符,这些空白字符会被保留在生成的字符串中。

使用示例

// 声明变量
let name = "Alice";
let age = 25;
// 使用模板字符串插入变量
console.log(`Hello, ${name}! You are ${age} years old.`); // 输出: Hello, Alice! You are 25 years old.
// 嵌入表达式
console.log(`The square of ${name.toUpperCase()} is ${age * age}.`); // 输出: The square of ALICE is 625.
// 插入多行字符串
let introduction = `Hello,
my name is ${name}
and I am ${age} years old.`;
console.log(introduction); // 输出:
// Hello,
// my name is Alice
// and I am 25 years old.
// 使用模板字符串构造函数
function greet(person) {
    return `Hello, ${person}!`;
}
console.log(greet("Alice")); // 输出: Hello, Alice!
// 嵌套模板字符串
console.log(`${greet(name)}. Welcome to the party!`); // 输出: Hello, Alice. Welcome to the party!

4.默认参数

在ES6中,如果定义一个函数时没有提供任何显式参数,那么函数将使用提供的默认参数。默认参数允许你为函数的参数指定一个默认值,这样在调用函数时,如果传入的参数缺失,函数将使用默认值。
下面是一个定义了默认参数的greet函数的例子:

function greet(name = "Alice", greeting = "Hello") {
    console.log(`${greeting}, ${name}!`);
}

在这个例子中,greet函数有两个参数:namegreeting。每个参数都有一个默认值:

  1. name参数的默认值是"Alice"。如果调用函数时没有提供name参数,函数将使用默认值"Alice"
  2. greeting参数的默认值是"Hello"。如果调用函数时没有提供greeting参数,函数将使用默认值"Hello"
    现在,我们可以调用greet函数,无论是否提供参数:
  • 如果没有提供任何参数,greet函数将使用默认参数"Alice""Hello",输出"Hello, Alice!"

  • 如果只提供了name参数,例如greet("Bob"),函数将使用"Bob"作为name的值,"Hello"作为greeting的值,输出"Hello, Bob!"

  • 如果提供了greeting参数,例如greet("Charlie", "Hi"),函数将使用"Charlie"作为name的值,"Hi"作为greeting的值,输出"Hi, Charlie!"

  • 如果提供了所有参数,例如greet("David", "Hey"),函数将使用提供的值,输出"Hey, David!"

  • 需要注意的是,如果函数的参数列表中有默认参数,那么在调用函数时,必须从左到右按顺序提供非默认参数,否则会导致undefined值的错误。

    例如:

greet("David", "Hey", "How are you?") // 错误:因为"How are you?"没有对应的名字参数

在上面的错误示例中,我们试图跳过name参数并直接提供greeting和第三个参数,这将导致一个TypeError,因为greet函数只有两个参数,而且它们都有默认值。

5.箭头函数

  • ES6(ECMAScript 2015)引入了箭头函数(Arrow Functions),这是一种写法更简洁、表达性更强的函数表示形式。箭头函数主要用于回调函数、局部函数和设置对象属性的函数值。
  • 当使用箭头函数时,它会捕获外部作用域的 this 值,并将其绑定到函数体内部。这意味着箭头函数中的 this 始终与包含它的外部作用域的 this 值相同,而不受函数调用方式的影响。

5.1定义与语法结构

箭头函数的语法结构如下:

(参数1, 参数2, ..., 参数N) => { 函数声明 }

或者如果只有一个参数并且不使用大括号编写函数体(单个表达式):

参数 => 表达式

如果函数直接返回一个表达式的结果,无需写 return 语句,箭头函数会自动返回该表达式的结果。

5.2与传统函数的不同之处

  1. 没有自己的 this:箭头函数不绑定自己的 this,它继承自父作用域。在对象方法和回调函数中使用时,其 this 值将根据函数的词法作用域来确定,而不是调用时所在的作用域。
const obj = {
  method: function() {
    setTimeout(() => { // 箭头函数不绑定自己的this,而是继承自父作用域(这里指向obj)
      console.log(this); // this 指向 obj
    }, 1000);
  }
};
obj.method();
  1. 不适用 arguments 对象:箭头函数没有自己的 arguments 对象,可以使用剩余参数(rest parameters)代替。
// 普通函数可以使用arguments对象
function regularFunction() {
  console.log(arguments); // arguments对象
}
regularFunction(1, 2, 3);

// 箭头函数需要使用rest参数
const arrowFunction = (...args) => {
  console.log(args); // rest参数的数组形式
};
arrowFunction(1, 2, 3);

  1. 没有原型链继承this:由于箭头函数没有自己的 this,所以它们不会像普通函数那样通过原型链继承 this
// 定义一个对象
const obj = {
  name: 'John',
  sayHello: function () {
    // 普通函数
    setTimeout(function () {
      console.log('Hello, ' + this.name); // 这里的 this 指向全局对象(或 undefined)
      // Hello, undefined
    }, 1000);

    // 箭头函数
    setTimeout(() => {
      console.log('Hello, ' + this.name); // 这里的 this 指向 obj 对象
      // Hello, John
    }, 1000);
  },
};

obj.sayHello();

  1. 函数体内的 this 绑定:在普通函数中,this 的值取决于函数被调用时的上下文,而在箭头函数中,this 的值取决于函数被定义时的上下文。
// 定义一个对象
const obj = {
  name: 'John',
  sayHello: function() {
    console.log('Hello, ' + this.name);
  }
};

// 定义一个全局函数
function globalFunction() {
  console.log('Hello, ' + this.name);
}

// 在 obj 上调用 sayHello 方法
obj.sayHello(); // 输出:Hello, John
//当我们在 obj 上调用 sayHello 方法时,普通函数中的 this 指向了调用该方法的对象 obj,因此它可以正确地访问到 obj 的 name 属性。

// 将 obj.sayHello 赋值给一个变量
const helloFunc = obj.sayHello;

// 在全局作用域中调用 helloFunc
helloFunc(); // 输出:Hello, undefined
//当我们将 obj.sayHello 赋值给变量 helloFunc 并在全局作用域中调用它时,普通函数中的 this 指向了全局对象,因此无法访问到 obj 的 name 属性,输出结果是 undefined。

// 定义一个箭头函数
const arrowFunc = () => {
  console.log('Hello, ' + this.name);
};

// 在 obj 上调用 arrowFunc
obj.sayHello = arrowFunc;
obj.sayHello(); // 输出:Hello, John
//箭头函数 arrowFunc 在定义时就捕获了外部作用域的 this 值,它指向了定义时的上下文。因此,无论是在 obj 上调用 arrowFunc 还是在全局作用域中调用它,箭头函数都能正确地访问到 obj 的 name 属性。

// 在全局作用域中调用 arrowFunc
arrowFunc(); // 输出:Hello, undefined

5.3使用方法和示例

1. 回调函数
[1, 2, 3].map((num) => num * 2); // [2, 4, 6]
2. 局部函数
function outer() {
    let outerVar = 'I am outside';
    function inner() {
        let innerVar = 'I am inside';
        return () => console.log(outerVar, innerVar);
    }
    return inner();
}
outer()(); // 输出: I am outside I am inside
3. 对象属性
const object = {
    method: function() {
        return () => console.log('Hello World!');
    }
};
object.method()(); // 输出: Hello World!
4. 箭头函数与传统函数混用
function handleClick(event) {
    // 传统函数
    function logMessage() {
        console.log('Clicked!');
    }
    // 箭头函数
    [1, 2, 3].map((num) => {
        console.log(num);
        return num * 2;
    });
    logMessage();
}
// 调用函数
handleClick();

5.4实际应用场景

  1. 事件处理:在处理点击、键盘事件等DOM事件时,可以使用箭头函数提供简洁的语法,并且确保事件处理函数的 this 指向正确的元素。
  2. 数组处理函数:在处理数组时(如 map, filter, reduce 等),箭头函数可以提供一种非常简洁的方式来编写回调函数。
  3. 异步代码:箭头函数可以用于处理异步任务,如使用 Promiseasync/await
  4. 模块导出:在模块化编程中,箭头函数可以用来简化模块导出的语法。
    箭头函数提供了一种更现代、更清晰的函数编写方式,使得代码更加简洁和易于理解,但在使用时也要注意其与传统函数的不同点,以免在特定情境下导致意外的行为。

6.对象字面量简写

对象字面量简写语法是一种方便的语法现象,可以在对象字面量中省略属性的值,让 JavaScript 自动根据属性名称查找变量或常量的值,并将其赋给属性。

使用对象字面量简写语法可以更简洁地定义对象,省略重复的代码,使代码更加简洁易读。

// 定义变量
const name = 'John';
const age = 30;

// 使用对象字面量简写语法定义对象
const person = {
  name,
  age,
    //在对象的属性定义中,我们只写了属性的名称,而没有显式地指定属性的值。
    //JavaScript会根据属性的名称从当前作用域中查找相同名称的变量或常量,并将其值作为属性的值。因此,person 对象中的 name 属性的值为 'John',age 属性的值为 30。
  sayHello() {
    console.log('Hello, ' + this.name);
  }
};

console.log(person.name); // 输出:John
console.log(person.age); // 输出:30
person.sayHello(); // 输出:Hello, John

7.对象解构与...

在JavaScript(ES6)中,对象解构(Object Destructuring)是一种方便的语法,用于从对象中提取属性并将其赋值给变量。

7.1基本用法和原理

对象解构使用花括号 {} 来声明要提取的属性,并使用等号 = 将属性解构给对应的变量。解构过程是基于属性的键名来匹配和赋值的。

// 定义一个对象
const person = {
  name: 'John',
  age: 30,
  address: {
    city: 'New York',
    country: 'USA'
  }
};

// 对象解构
const { name, age } = person;
//通过对象解构,我们将 name 和 age 属性的值提取出来,并分别赋值给同名的变量。现在我们可以直接使用 name 和 age 这两个变量来访问对应的属性值。
console.log(name); // 输出:John
console.log(age); // 输出:30

7.2通过键名直接访问属性值:

在对象解构中,我们可以使用键名来直接访问属性值。

例如,如果我们想获取 person 对象中 address 属性的 city 值,可以通过嵌套的解构来实现:

const { address: { city } } = person;
//通过嵌套的对象解构,使用键名 address 来获取 person 对象中的 address 属性。然后,再使用键名 city 直接访问 address 属性中的 city 值。
console.log(city); // 输出:New York

7.3合并不同来源的对象属性

  • 对象解构还可以用于合并不同来源的对象属性,将它们解构到一个新的对象中。这种方式可以用于合并默认选项和用户提供的选项,以及合并多个配置对象等。
  • 对象解构和对象展开运算符 ... 通常一起使用,可以轻松地提取对象的属性并将其赋值给变量,以及合并对象的属性到一个新的对象中。

对象展开运算符 ... 用于将一个对象的属性展开到另一个对象中。

它可以用于创建新对象,将已有对象的属性展开并复制到新对象中。对象展开运算符在对象字面量中使用,以 ... 加上要展开的对象的变量名的方式表示。

const defaults = {
  host: 'localhost',
  port: 8080,
  timeout: 5000
};

const userConfig = {
  port: 3000,
  timeout: 10000
};

// 合并默认选项和用户选项
//通过对象解构和对象展开运算符 `...`,我们将两个对象的属性解构到一个新的对象 config 中,从而合并了它们的属性
const config = { ...defaults, ...userConfig };

console.log(config);
// 输出:
// {
//   host: 'localhost',
//   port: 3000,
//   timeout: 10000
// }

7.4应用场景和用途:

对象解构在许多方面都有应用场景和用途:

  1. 对象属性获取和赋值: 对象解构使得从对象中获取属性值更加简洁和直观。可以用于提取所需的属性值,并将它们赋值给对应的变量。
  2. 合并对象属性: 对象解构结合对象展开运算符可以用于合并多个对象的属性,方便地创建一个包含多个来源属性的新对象。
  3. 对象初始化和创建新对象: 对象解构可以用于快速初始化对象或创建新对象,将已有对象的属性解构给新对象。
  4. 继承和原型链: 对象解构可用于继承属性,从一个对象中继承属性并赋值给另一个对象,实现属性的继承。
  5. 事件处理: 在事件处理中,对象解构可以方便地提取事件对象中的属性,使代码更简洁易读。
// 模拟事件对象
const event = {
  type: 'click',
  target: 'button',
  pageX: 100,
  pageY: 200
};

// 对象解构提取属性
function handleClick({ type, pageX, pageY }) {
  console.log(`Clicked at (${pageX}, ${pageY})`);
}

// 调用事件处理函数
handleClick(event);
// 输出:Clicked at (100, 200)

8.数组map

在JavaScript(ES6)中,mapreduce 是数组的两个常用方法,用于对数组进行转换和聚合操作。

数组map方法

map 方法用于创建一个新数组,该数组的元素是源数组经过指定函数的映射后的结果。它会遍历源数组的每个元素,对每个元素应用指定的函数,并将函数的返回值作为新数组的对应元素。

map 方法的基本语法如下:

array.map(callback(currentValue, index, array), thisArg)
  • array:源数组。
  • callback:要对每个元素执行的回调函数,该函数接收三个参数:
    • currentValue:当前正在处理的元素。
    • index(可选):当前元素的索引。
    • array(可选):调用 map 方法的源数组。
  • thisArg(可选):执行回调函数时使用的 this 值。

map 方法返回一个新数组,其中包含源数组经过回调函数映射后的结果。

下面是一个使用 map 方法的示例,将数组中的每个元素转换为它们的平方值:

const numbers = [1, 2, 3, 4, 5];
//使用 `map` 方法将数组 `numbers` 中的每个元素进行平方运算,并将结果存储在新数组 `squaredNumbers` 中。
const squaredNumbers = numbers.map((num) => num * num);

console.log(squaredNumbers); // 输出:[1, 4, 9, 16, 25]

数组reduce方法

reduce 方法用于从左到右对数组中的元素进行累积操作,最终得到一个值。它接收一个回调函数作为参数,该函数定义了累积逻辑和初始累积值。

reduce 方法的基本语法如下:

array.reduce(callback(accumulator, currentValue, index, array), initialValue)
  • array:源数组。
  • callback:对每个元素执行的回调函数,该函数接收四个参数:
    • accumulator:累积值,初始值为 initialValue 或数组的第一个元素(如果没有提供初始值)。
    • currentValue:当前正在处理的元素。
    • index(可选):当前元素的索引。
    • array(可选):调用 reduce 方法的源数组。
  • initialValue(可选):作为累积值的初始值。

reduce 方法返回最终的累积值。

下面是一个使用 reduce 方法的示例,计算数组中所有元素的总和:

const numbers = [1, 2, 3, 4, 5];
//使用 `reduce` 方法对数组 `numbers` 中的所有元素进行累加操作,并将结果存储在变量 `sum` 中。
const sum = numbers.reduce((accumulator, currentValue) => accumulator + currentValue, 0);

console.log(sum); // 输出:15

应用场景和示例代码

  • map 方法的应用场景:

    • 转换数组中的每个元素,生成一个新的数组。
    • 根据数组中的每个元素创建一个新的对象。
    • 格式化数组中的数据,生成特定格式的字符串。

    示例代码:

    // 将数组中的每个元素转换为字符串并添加前缀
    const numbers = [1, 2, 3, 4, 5];
    
    const prefixedNumbers = numbers.map((num) => `Number: ${num}`);
    
    console.log(prefixedNumbers);
    // 输出:['Number: 1', 'Number: 2', 'Number: 3', 'Number: 4', 'Number: 5']
    ```
    
  • reduce 方法的应用场景:

    • 计算数组中的总和、平均值或其他聚合值。
    • 对数组中的元素进行筛选、条件过滤。
    • 将数组中的元素转换为其他形式的数据结构,如对象或 Map。

    示例代码:

    // 计算数组中的总和
    const numbers = [1, 2, 3, 4, 5];
    
    const sum = numbers.reduce((accumulator, currentValue) => accumulator + currentValue, 0);
    
    console.log(sum); // 输出:15
    
    // 筛选出数组中大于 2 的元素
    const numbers = [1, 2, 3, 4, 5];
    
    const filteredNumbers = numbers.reduce((accumulator, currentValue) => {
      if (currentValue > 2) {
        accumulator.push(currentValue);
      }
      return accumulator;
    }, []);
    
    console.log(filteredNumbers); // 输出:[3, 4, 5]
    
    // 将数组中的元素转换为对象
    const people = [
      { name: 'Alice', age: 25 },
      { name: 'Bob', age: 30 },
      { name: 'Charlie', age: 35 }
    ];
    
    const peopleMap = people.reduce((accumulator, person) => {
      accumulator[person.name] = person.age;
      return accumulator;
    }, {});
    
    console.log(peopleMap);
    // 输出:
    // {
    //   Alice: 25,
    //   Bob: 30,
    //   Charlie: 35
    // }
    ```
    

通过 mapreduce 方法,我们可以对数组进行各种转换和聚合操作,从而简化代码并实现更高效的数据处理。它们是处理数组数据的常用工具,在实际编程中具有广泛的应用。

9.Symbol

介绍

JavaScript ES6 引入了一种新的原始数据类型 Symbol,它代表独一无二的值。Symbol 是一种用于创建对象私有属性的标识符,它们在对象中作为属性名,确保了属性名的唯一性,避免了属性名冲突的问题。

细节

以下是 Symbol 的一些细节拓展:

  1. 创建 Symbol:
let sym1 = Symbol();
let sym2 = Symbol('description');

console.log(sym1); // Symbol()
console.log(sym2); // Symbol(description)

可以通过 Symbol 函数创建一个新的 Symbol 值。可选的字符串参数 description 用来描述这个 Symbol,但它并不影响 Symbol 的值。

  1. 每个 Symbol 都是唯一的:
    即使描述相同,两个 Symbol 值也不相等。
console.log(Symbol('foo') !== Symbol('foo')); // true
  1. 作为对象属性的键:
    Symbol 可以用作对象属性的键,这使得这些属性不会被常规的方法(如 for...in 循环和 Object.keys())枚举。
let obj = {};
obj[Symbol('foo')] = 'bar';

console.log(obj); // { [Symbol(foo)]: 'bar' }
  1. 全局 Symbol 注册表:
    可以通过 Symbol.for(key) 方法注册一个全局的 Symbol,它会在全局注册表中创建或检索一个以该键为名的 Symbol
let globalSym = Symbol.for('foo'); // 如果 'foo' 不存在,则创建一个新的 Symbol
let otherGlobalSym = Symbol.for('foo'); // 如果 'foo' 已存在,则返回已存在的 Symbol
globalSym === otherGlobalSym; // true
  1. 内置 Symbol 值:
    ES6 还定义了一些内置的 Symbol 值,如 Symbol.iteratorSymbol.asyncIteratorSymbol.toStringTag 等,它们用于表示对象的特殊行为。
class MyClass {
  [Symbol.toStringTag] = 'MyClass';
}
console.log(Object.prototype.toString.call(new MyClass())); // '[object MyClass]'
  1. 类型转换:
    Symbol 值不能与其他类型的值进行运算,也不能转换成数字,但可以转换成字符串和布尔值。
String(sym1); // 'Symbol(description)'
Boolean(sym1); // true
  1. 属性名的遍历:
    Object.getOwnPropertySymbols() 方法可以获取一个对象所有用 Symbol 做为键的属性名。
let obj = {};
let a = Symbol('a');
let b = Symbol('b');
obj[a] = 'foo';
obj[b] = 'bar';

let symbols = Object.getOwnPropertySymbols(obj);
console.log(symbols); // [Symbol(a), Symbol(b)]
console.log(obj[a]); // foo
console.log(obj[b]); // bar

示例

// 创建两个Symbol类型的变量,用作不同属性的私有标识
const id = Symbol('id');
const name = Symbol('name');

// 创建一个对象,使用Symbol作为属性名
const user = {
  [id]: 123, // id属性
  [name]: '张三' // name属性
};

// 尝试访问私有属性
console.log(user[id]); // 输出: 123
console.log(user[name]); // 输出: 张三

// 尝试修改私有属性
user[id] = 456;
console.log(user[id]); // 输出: 456

// 使用Symbol属性模拟私有方法
const privateMethod = Symbol('privateMethod');
user[privateMethod] = function() {
  return `${this[name]}的ID是${this[id]}`;
};

// 调用私有方法
console.log(user[privateMethod]()); // 输出: 张三的ID是456

// 尝试枚举对象属性
for (let key in user) {
  console.log(key); // 不会输出Symbol属性,因为它们不会出现在for...in循环中
}

// 使用Object.getOwnPropertySymbols获取所有的Symbol属性
let symbolKeys = Object.getOwnPropertySymbols(user);
console.log(symbolKeys); // 输出: [Symbol(id), Symbol(name), Symbol(privateMethod)]

// 遍历Symbol属性
symbolKeys.forEach(sym => {
  console.log(user[sym]);
});

10.类

介绍

在JavaScript ES6中,class 关键字被引入,为JavaScript提供了一个类(class)的语法糖,使得JavaScript的面向对象编程变得更加直观和易于理解。尽管这并不是JavaScript引入类的真正语法,但它提供了一种声明类的简洁方式,同时保持了JavaScript基于原型的继承机制。

细节

  • class关键字: 使用 class 关键字来声明一个类,后面跟着类的名称。
  • 构造函数: 类的构造函数是使用 constructor关键字定义的,它会在创建类的新实例时被调用。构造函数中的 this 关键字指向新创建的实例。
  • 实例方法: 类的主体内部可以定义实例方法。这些方法会被添加到类的原型上,因此所有实例都能访问这些方法。
  • 静态方法: 使用 static关键字定义静态方法。静态方法属于类本身,而不是类的实例。调用静态方法时不需要类的实例,直接通过类来调用。
  • 继承: 使用 extends关键字来创建一个类,该类继承另一个类的属性和方法。子类的构造函数中必须调用 super(),它会执行父类的构造函数。
  • 原型链: 类的继承仍然基于原型链。子类的原型是父类的一个实例,这意味着子类可以访问父类原型上的属性和方法。
  • 方法遮蔽: 如果子类定义了一个与父类相同名称的方法,那么子类的方法会遮蔽(override)父类的方法。在子类的方法内部,可以使用 super 关键字来调用父类的同名方法。
  • getter和setter: 类的主体内部可以定义getter和setter,用于控制对类属性的访问和修改。
// 定义一个基类
class Animal {
  name;
  sound;

  // 构造函数
  constructor(name, sound) {
    this.name = name;
    this.sound = sound; 
  }

  // 实例方法
  speak() {
    return `${this.name} says${this.sound}`;
  }

  // 静态方法
  static describe() {
    return 'This is an animal.';
  }
}

// 定义一个继承自Animal的子类
class Dog extends Animal {
  // 构造函数
  constructor(name, sound, breed) {
    super(name, sound); // 调用父类的构造函数
    this.breed = breed;
  }

  // 方法遮蔽
  speak() {
    return `${super.speak()}WOOF!`; // 调用父类的speak方法
  }

  // getter和setter
  get sound() {
    return this.sound;
  }

  set sound(newSound) {
    this.sound = newSound;
  }
}

// 创建类的实例
const dog = new Dog('Buddy', 'Bark', 'Golden Retriever');
console.log(dog.speak()); // 输出: Buddy says BarkWOOF!
console.log(Animal.describe()); // 输出: This is an animal.

// 使用getter和setter
console.log(dog.sound); // 输出: Bark
dog.sound = 'Yip';
console.log(dog.sound); // 输出: Yip
  • 公有和私有字段:

    • 共有字段:在ES2020之前,可以在构造函数中或者类的外部使用 this 来添加公有字段。从ES2020开始,可以直接在类主体内部声明公有字段,而不需要在构造函数中。
    • 私有字段:私有字段是在ES2020中引入的,使用 # 前缀来声明。私有字段只能在类的内部访问,包括构造函数、实例方法和私有方法中。
// ES2020+ 公有字段语法
class Person {
  name; // 公有字段
  age; // 公有字段

  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
}

const person = new Person('Alice', 30);
console.log(person.name); // 输出: Alice
console.log(person.age); // 输出: 30
// ES2020+ 私有字段语法
class Person {
  #name; // 私有字段
  #age; // 私有字段

  constructor(name, age) {
    this.#name = name;
    this.#age = age;
  }

  greet() {
    return `Hello, my name is ${this.#name} and I am${this.#age} years old.`;
  }

  getAge() {
    return this.#age;
  }
}

const person = new Person('Alice', 30);
console.log(person.greet()); // 输出: Hello, my name is Alice and I am 30 years old.
// console.log(person.#name); // 错误: SyntaxError: Private field '#name' must be declared in an enclosing class
console.log(person.getAge()); // 输出: 30
  • 类表达式: 类也可以定义为类表达式,它可以是命名或匿名的,并且可以立即执行。

11.模块

介绍

静态结构:ES6模块支持静态导入和导出语句,这意味着模块的依赖关系在编译时就已经确定,而不是在运行时。

默认导出与命名导出:可以使用export default来导出默认成员,每个模块只能有一个默认导出。同时,还可以使用export来命名导出多个成员。

导入整个模块:可以使用import * as name from 'module’来导入整个模块。

模块提升:模块中的import和export语句会首先被解析,这意味着它们位于模块的顶级作用域,并且有提升的效果。

使用ES6模块

导出模块:

// 导出默认成员
export default function f1() {
  console.log('This is f1');
}
// 命名导出
export const v = 'someValue';
export function f2() {
  console.log('This is f2');
}

导入模块:

// 导入默认导出
import f1 from './myModule.js';
// 导入命名导出
import { v, f2 } from './myModule.js';
// 导入整个模块
//import * as myModule from './myModule.js';

//使用模块
// 使用默认导出
f1();
// 使用命名导出
console.log(v);
f2();

注意事项:

  • 在浏览器中使用ES6模块时,需要服务器支持,或者文件使用.mjs扩展名。
  • 在Node.js中,ES6模块需要.mjs扩展名或者在package.json中设置"type": “module”。
{
  "name": "es6",
  "version": "1.0.0",
  "description": "",
  "main": "main.js",
  "type": "module",//主要是这句
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  //...
}

当你设置了"type": "module"后,你就可以在项目中使用ES6模块语法,例如使用import和export语句,而不需要将文件命名为.mjs。

  • 导入和导出的语句必须位于模块的顶层作用域,不能嵌套在条件语句或其他语句中。
// 导入默认导出
let sum = 0;
for(let i = 0;i <10;i++){
  import f1 from './myModule.js';//error
  sum += i;
}
f1();

12.Promise

JavaScript ES6中的Promise是一种用于处理异步操作的对象。它代表了一个尚未完成但预期将来会完成的操作的结果。Promise是对未来某个时刻可能知道的结果的一个代理。

介绍

  1. 状态Promise对象有三种状态:
    • pending(进行中):初始状态,既不是成功,也不是失败状态。
    • fulfilled(已成功):意味着操作成功完成。
    • rejected(已失败):意味着操作失败。
  1. 状态转换pending可以转换为fulfilledrejected,一旦状态转换,就不会再变。
  2. Promisefulfilled状态有一个不可变的值,rejected状态有一个原因(错误信息)。
  3. 消费Promise:可以通过.then().catch()方法来使用或处理Promise的值或错误。
  4. 几个静态方法:
  • Promise.all(iterable): 等待所有Promise都解决后再解决。
  • Promise.race(iterable): 等待第一个解决的Promise。
  • Promise.resolve(value): 返回一个成功的Promise。
  • Promise.reject(reason): 返回一个拒绝的Promise。

创建和使用

使用 new Promise()创建Promise

// 创建一个返回 Promise 的函数,该函数在一段时间后解决或拒绝
function simulateAsyncOperation(success) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (success) {//根据success的值处理Promise
        resolve('操作成功!'); // 成功解决 Promise
      } else {
        reject(new Error('操作失败!')); // 拒绝 Promise 并传递错误
      }
    }, 2000); // 模拟 2 秒的延迟
  });
}

// 使用 Promise
var promise = simulateAsyncOperation(true);
console.log(promise);

Promise链和错误处理

Promise可能处于不同状态,我们可以使用 then接收成功状态,使用 catch接收失败状态

// 使用 Promise
simulateAsyncOperation(true)
  .then((message) => {
    console.log(message); // 如果操作成功,将打印出“操作成功!”
  })
  .catch((error) => {
    console.error(error.message); // 如果操作失败,将打印出“操作失败!”
  });

注意事项

  • Promise一旦被解决或拒绝,它的状态就不再改变,这意味着多次调用resolvereject只有第一次调用有效。
  • Promise中的错误应该总是通过reject来处理,而不是使用throw语句。
  • Promise链中返回的非Promise值会自动包装成Promise
    Promise是处理异步JavaScript操作的一种强大方式,它提供了一种更加简洁和可读的代码结构,尤其是在处理多个异步操作时。

13.asysc函数

async函数是一种特殊的函数,它允许使用async关键字声明,使得函数返回一个Promiseasync函数可以让异步代码的编写看起来更像是同步代码,使得代码更加清晰和易于理解。async函数内部可以使用await关键字,这使得异步操作看起来像是在同步顺序中执行。

async函数的特点

  1. 返回Promiseasync函数始终返回一个Promise。如果函数返回一个非Promise的值,该值会自动包装成一个解决的Promise。如果函数抛出异常,则返回一个被拒绝的Promise
  2. 使用await:在async函数内部,你可以使用await关键字来暂停代码的执行,直到等待后面的Promise解决。这允许你编写看起来像同步代码的异步逻辑。
  3. 错误处理async函数可以通过try...catch语句来捕获await后面的Promise拒绝时的错误。

如何定义和使用async函数

  • 在函数前添加 async定义async函数,之后可以像使用普通函数一样使用
  • 在async函数内部,使用await关键字来等待一个Promise解决,允许编写看起来像同步代码的异步逻辑。
// 定义一个async函数
async function getUserInfo(userId) {
  try {
    // 使用await等待Promise解决
    const response = await fetch(`https://api.example.com/users/${userId}`);//fetch()用于发起网络请求
    if (!response.ok) {
      throw new Error('Network response was not ok');
    }
    const userInfo = await response.json();
    return userInfo;
  } catch (error) {
    // 处理错误
    console.error('There was a problem with the fetch operation:', error);
    throw error;
  }
}

// 使用async函数
getUserInfo(123)
  .then((userInfo) => {
    console.log(userInfo);
  })
  .catch((error) => {
    console.error('Failed to get user info:', error);
  });

注意事项:

  • await关键字只能在async函数内部使用。
  • async函数可以包含多个await表达式,每个表达式都会按顺序执行。
  • async函数可以与其他异步模式(如回调函数、事件监听器、Promise链等)一起使用。
    async函数是ES6及以后版本JavaScript中处理异步编程的一种非常方便和强大的方式,它提供了一种更简洁、更易读的异步代码编写模式。

14.fetch()

fetch() 是一个全局的 JavaScript 函数,用于发起网络请求。它返回一个 Promise,这个 Promise 在请求完成时会解决为一个 Response 对象。fetch() 是 Web API 的一部分,旨在提供一种更简单、更现代的方式来处理网络请求。

JavaScript 实现了多种 Web API,这些 API 可以分为多个类别,如文档对象模型 (DOM) API、浏览器对象模型 (BOM) API、网络 API等,使得开发者能够构建丰富的Web应用程序。

基本用法

fetch('https://api.example.com/data')
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Fetch error:', error));

传递选项

fetch() 函数可以接受第二个参数,一个配置对象,用于自定义请求。这个配置对象可以包含诸如方法、头部、主体内容等信息。

fetch('https://api.example.com/data', {
  method: 'POST', // 指定请求方法
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer your-token'
  },
  body: JSON.stringify({ key: 'value' }) // 发送的数据
})
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Fetch error:', error));

处理响应状态

在处理 fetch() 返回的 Response 对象时,你可能需要检查响应的状态码来确定请求是否成功。

fetch('https://api.example.com/data')
  .then(response => {
    if (!response.ok) {
      throw new Error('Network response was not ok');
    }
    return response.json();
  })
  .then(data => console.log(data))
  .catch(error => console.error('Fetch error:', error));

三、NPM包管理器

1.介绍

官网:https://www.npmjs.com/

我们可以通过模块化的方式来封装自己的代码,或者封装一个工具,这个工具可以让同事通过导入的方式来使用,甚至可以分享给全世界的程序员来使用。

npm全程是Node Package Manager,也就是Node包管理器。但是它目前已经不仅仅是Node包管理器了,在前端项目中我们也使用它来管理依赖的包,包括:vue,vue-router,koa,axios,webpack等等。

2.下载与配置

本文不多阐述,可以参考别人的博客或自行搜索。

3.n工具和nvm工具

n工具

安装n

npm install -g n

n工具常用命令

arduino复制代码n <version>  // 下载某一版本号node    e.g:n 10.16.0
n latest     // 安装最新版本
n stable     // 安装最新稳定版
n lts        //安装最新长期维护版(lts)
n rm <version>  // 删除某个版本   e.g:n  rm 10.16.0
n   // 输入命令后直接使用上下箭头选择版本

nvm

nvm可以方便的在同一台设备上进行多个node版本之间切换。

  1. 1.卸载node.js(如果有),下载nvm安装包。安装包下载地址:https://github.com/coreybutler/nvm-windows/releases,windows系统下载nvm-setup.zip安装包
  2. 安装nvm
  3. 配置淘宝镜像。找到你下载的位置,然后打开settings.txt,那里边有两行代码,换行添加下边两行代码。
image-20240116084402177
node_mirror: https://npm.taobao.org/mirrors/node/
npm_mirror: https://npm.taobao.org/mirrors/npm/

nvm工具常用命令

nvm off                     // 禁用node.js版本管理(不卸载任何东西)
nvm on                      // 启用node.js版本管理
nvm install <version>       // 安装node.js的命名 version是版本号 例如:nvm install 8.12.0
nvm uninstall <version>     // 卸载node.js是的命令,卸载指定版本的nodejs,当安装失败时卸载使用
nvm ls                      // 显示所有安装的node.js版本
nvm list available          // 显示可以安装的所有node.js的版本
nvm use <version>           // 切换到使用指定的nodejs版本
nvm v                       // 显示nvm版本
nvm install stable          // 安装最新稳定版
  1. 下载指定版本,例如:nvm install 20.11.0
  2. 使用指定版本,例如:nvm use 20.11.0
  3. 检查node是否安装成功
node -v  
npm -v
  • 如果以上步骤出现错误,可能需要配置环境变量(一般会自动配置)

4.package.json

4.1介绍

  • package.json会记录着你项目的名称、版本号、项目描述等,也会记录着你项目所依赖的其他库的信息和依赖库的版本号

4.2获取

  • 当我们从零开始创建项目的时候,我们需要:
npm init -y

生成的package.json如下(仅供参考):

{
  "name": "es6",
  "version": "1.0.0",
  "description": "",
  "main": "main.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "FL",
  "license": "ISC"
}

  • 如果是通过脚手架创建项目,脚手架会帮助我们生成package.json,并且配置有相关信息

4.3常见属性

name :必填,为项目的名称。

version: 必填,当前项目的版本号。

description:描述信息,很多时候是作为项目的基本描述

author:作者的相关信息(发布时会用到)

license:开源协议(发布的时候用到)

private属性:private属性记录当前项目是否私有。当值为true时,npm无法发布,这是为了防止私有项目或者模块发布出去的方式。

module属性:main属性为文件的入口。这个非常重要,我们引入某个库require(‘xxxx’)就是自动从main找找对应的入口文件。

script属性:script属性支持用户配置一些自定义的脚本命令(定制化操作)。

type:设置为module,实现CommonJS规范。

4.4依赖的版本管理

在依赖的版本前面,会有~^这连个符号,通常都是^。这是因为npm的包通常都要遵从semever版本规范

semver版本的规范是X.Y.Z

  • X主版本号(major):当你做了不兼容的API修改(可能不兼容之前的版本)
  • Y次版本号(minor):当你做了向下兼容的功能性新增(新功能增加,但是兼容之前的版本)
  • Z修订号(patch):当你做了 向下兼容的问题修真(没有新功能,修复了之前版本的bug)

关于^和~ 连个符号

  • ^x.y.z:表示x是保持不变的,y和z永远安装最新的版本。 这种我们一般称之为大概的版本
  • ~x.y.z:表示x和y不变,z永远安装最新的版本。

5.NPM常用指令

5.1 install

  • 在使用 npm install 命令时,可以使用 --save-dev 标志将依赖项添加到开发依赖项中,使用 --save 标志将依赖项添加到生产依赖项中。
  • 此外,还可以使用 --global/-g 标志进行全局安装,但这在大多数情况下不被推荐,可能导致版本冲突,或者在不同项目中使用不同版本的包时出现问题。因此,许多开发人员更倾向于使用本地安装,特别是当工具或库的版本需要根据项目不同而变化时。
  • 在现代开发实践中,随着npx的引入,全局安装的必要性进一步减少了。npx可以在不全局安装包的情况下运行CLI工具,它会临时安装包到一个临时目录中,使用后即删除。这种方式可以确保你总是使用最新版本的包,并且避免了全局安装可能带来的问题。
# 全局安装 
npm install package -g 
# 项目(局部)安装 
npm install package 

示例:

# 全局安装 
npm install webpack -g 
# 项目(局部)安装 
npm install webpack 

仅以下情况推荐全局安装:

  • 命令行工具。如:webpack、create-react-app、Vue CLI 等脚手架工具。
  • 全局命令行工具集。如:像Gulp、Grunt这样的任务运行器,或者像Angular CLI这样的全局命令行工具集。
  • 全局服务。如:数据库服务(如MongoDB、MySQL)或开发服务器(如live-server)。

5.2 uninstall

# 让 npm 根据 package.json 中的定义自动判断依赖类型
npm uninstall package
# 显式地说明你正在移除一个开发依赖
npm uninstall package -D

5.3 强制rebuild

npm rebuild

在以下情况下,你应该执行npm rebuild:

  1. 安装了新的npm模块或更新了现有的模块
  2. 更改了Node.js版本
  3. 升级了操作系统或更改了系统环境
  4. 发生了构建失败或模块损坏的情况

5.5 清除缓存

npm cache clean

在以下情况下,你应该执行npm cache clean

  1. 占用空间过多
  2. 安装了错误的依赖版本
  3. 更新了npm版本
  4. 修复依赖关系冲突

5.6 其他命令

其他命令

6. cnpm工具

对于大多数人来说,并不希望将npm镜像去修改。

  • 第一,我们不太愿意随意修改npm原本从官方下来包的渠道(其他渠道会有一些延迟)。
  • 第二,担心某天比如淘宝的镜像不维护了,又要改来改去。

这个时候我们就可以使用cnpm工具了,并且将cnpm设置为淘宝的镜像

# 安装cnpm
npm install -g cnpm --registry=https://registry.npm.taobao.org
# 检查版本
cnpm -v

7.npx工具

作用:方便的使用局部的package工具

npx package --version

适用场景:

  1. 检查软件包的版本
  2. 确认软件包是否已安装
  3. 调试和故障排除

8.发布自己的包

  1. 注册
npm login
  1. 发布我们的包
npm publish

适用场景:

  1. 对某个软件包进行了重要的更新或修复,并且希望将这些更改发布到公共软件包注册表时
  2. 使用私有注册表或其他工具来发布软件包,而现在决定将其发布到公共注册表(如npm)中
  3. 需要更改软件包的发布配置时,例如更新发布的访问权限、标记稳定版本等

其他的命令:

# 删除发布的包
npm unpublish 
# 让发布的包过期
npm deprecate 

四、Babel转码器

1.介绍

Babel是一个广泛使用的JavaScript转码器,用于将新版本的JavaScript代码转换为向后兼容的旧版本代码

它实现了ECMAScript规范中的各种转换规则,例如箭头函数、解构赋值、模板字符串、类和模块等。

2.安装与使用

  • 将Babel作为项目的开发依赖项进行本地安装比全局安装更可取。
  1. 安装Babel的核心模块和所需的转换插件
npm install --save-dev @babel/core @babel/cli @babel/preset-env
这将安装Babel的核心模块(`@babel/core`)以及命令行工具模块(`@babel/cli`)和用于转换ES6+语法的预设模块(`@babel/preset-env`)。这些模块将作为开发依赖项安装,并在`package.json`文件的`devDependencies`部分中列出。
  1. 创建一个名为.babelrc的文件,并在其中指定Babel的配置。
{
  "presets": ["@babel/preset-env"]
}
这将告诉Babel使用`@babel/preset-env`预设来进行转换,该预设将根据目标环境自动确定需要的转换规则。
  1. 使用Babel来转换你的JavaScript代码
npx babel src --out-dir dist
这将使用Babel将`src`目录中的JavaScript代码转换并输出到`dist`目录中。你可以根据你的项目结构和需求进行相应的调整。

3.自定义脚本

详细的配置选项和用法可以在 Babel 官方文档 中找到

  1. .babelrc 文件中的 plugins 字段中添加插件名称或配置对象
{
  "presets": ["@babel/preset-env"],
  "plugins": ["log-function-calls"]
}
  1. 创建一个名为 log-function-calls.js 的文件,用于编写自定义 Babel 插件的逻辑
module.exports = function logFunctionCalls({ types }) {
  return {
    visitor: {
      CallExpression(path) {
        console.log('Function call:', path.node.callee.name);
        path.traverse({
          Identifier(path) {
            console.log('Argument:', path.toString());
          }
        });
      }
    }
  };
};
自定义插件 log-function-calls 会在每个函数调用前后打印日志。它通过 Babel 的访问器对象 path 来访问和修改 AST,使用 types 提供的方法来操作节点。
  1. 在命令行中运行 Babel CLI 来转换代码
npx babel src --out-dir dist

五、WebPack

1.介绍

  • Webpack是一个现代JavaScript应用程序的静态模块打包器(module bundler)。当你在浏览器中加载一个应用程序时,你希望尽可能减少HTTP请求的数量,以提高加载速度和性能
  • Webpack的作用就是将你的应用程序中的所有模块(例如,你的JavaScript文件、CSS文件、图片、字体等)打包成一个或多个bundle

本教程只记录常用功能,其他详情可见官网

2.安装与使用

  1. 全局安装WevPack(不推荐)
npm install --global webpack webpack-cli
  1. 初始化npm项目
npm init
  1. 本地安装Webpack(推荐)
npm install --save-dev webpack webpack-cli
  1. 执行Webpack打包
npm run build

3.创建Webpack配置文件

  • 在项目根目录中创建一个名为webpack.config.js的文件。在这个文件中,你需要配置Webpack的入口点和输出文件。
  • 注意,webpack.config.js必须在根目录下

例如,如果你有一个名为index.js的入口文件,你可以在webpack.config.js中这样配置:

// webpack.config.js
const path = require('path');

module.exports = {
  entry: './index.js',
  output: {
    path: path.resolve(__dirname, 'dist'), // string
    filename: 'main.js',
  },
  mode: 'development',
};

4.webpack合并JS

假设你的项目目录结构如下:

project/
├── dist/
├── src/
│   ├── index.js
│   └── test.js
├──	package.json
└── webpack.config.js
  1. 创建工程,初始化
npm init
  1. 局部安装webpack
npm i webpack webpack-cli -D
  1. 新建 webpack.config.js 配置文件(在根目录)
// webpack.config.js
const path = require('path');
module.exports = {    
    // 入口
    entry: path.resolve(__dirname, 'src/index.js'),    
    // 出口
    output: {        
        path: path.resolve(__dirname, 'dist'),        
        filename: 'bundle.js',        
        publicPath: './'    
    }
}

  1. 定义模块:
    在你的 JavaScript 文件中,你可以使用 import 语句来引入其他模块。Webpack 会解析这些导入语句,并将它们打包在一起。

例如,在index.js中:

//入口文件:src/index.js
const test = require('./test');
const a = 12
const b = 12
function add(x, y) {  
    return x + y
}
const c = add(a,b)
console.log(c)
test();

test.js中定义相应的功能或变量。

//非入口文件:src/test.js
function  test(){
    console.log(2);
}

module.exports = test;
  1. package.json 下的 scripts 中添加打包命令:
"build": "webpack --mode development"
  • 这里说明一下,不加 –mode development,默认打包是生产环境,打包出来的代码会默认压缩
  1. 进行打包
npm run build
  1. 打包完成后,你会在配置的输出目录(在上面的例子中是dist目录)中找到一个或多个打包文件(bundle.js)。

5.处理CSS

  • Webpack 本身只能处理 JavaScript 文件,但是通过加载器(loaders),Webpack 可以调用外部的工具来处理其他类型的文件,例如 CSS、图片等。
  • 加载器是运行在 Node.js 中的函数,它接收资源文件作为参数,并返回转换后的结果。

假设你的项目目录结构如下:

demo
├─ src
│   ├─ css
│   │   └─ index.css
│   ├─ js
│   │  └─ idnex.js
│   │  └─ test.js
├─ webpack.config.js
└─ package.json

处理CSS步骤如下(示例):

  1. 创建工程,初始化
npm init
  1. 局部安装webpack:
npm i webpack webpack-cli -D
  1. 安装CSS加载器:Webpack 本身只能理解 JavaScript,所以需要安装 css-loader 和 style-loader 来处理 CSS 文件。
npm install css-loader style-loader -D
  1. 新建 webpack.config.js 配置文件(在根目录),在 webpack.config.js 文件中,添加一个模块(module)部分来配置加载器(loader)。
const path = require('path');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');//方便清理 dist 目录中的旧文件
module.exports = {
  // 入口
  entry: path.resolve(__dirname, 'src/js/index.js'),
  // 出口
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js',
    publicPath: './',
  },
  module: {
    rules: [
      {
        test: /\.css$/, // 匹配CSS文件
        use: ['style-loader', 'css-loader'], // 使用style-loader和css-loader
      },
    ],
  },
  plugins: [
    new CleanWebpackPlugin(), // 实例化插件
  ],
};

  • publicPath:指定基础路径,开发环境一般是项目的根路径,上线之后一般是CDN的路径。
  • __dirname:表示项目所在目录的根路径。
//入口文件:src/js/index.js
import './css/index.css'; // 在顶部导入CSS文件
const test = require('./test');
const a = 12;
const b = 12;

function add(x, y) {
  return x + y;
}

const c = add(a, b);
console.log(c);
test();

//非入口文件:src/js/test.js
function  test(){
    console.log(2);
}

module.exports = test;
/* src/css/index.css */
body {
  background-color: #f2f2f2;
}
  1. 在 package.json 中已经定义了 build 脚本:
"scripts": {
  "build": "webpack --mode production"
}
  1. 进行打包
 npm run build

6.处理图片

假设你的项目目录结构如下:

demo
├─ src
│   ├─ css
│   │   └─ index.css
│   ├─ js
│   │  └─ idnex.js
│   │  └─ test.js
│   └─ image
│      └─ image.jpeg
├─ index.html
├─ webpack.config.js
└─ package.json
  1. 安装加载器:
npm install file-loader url-loader -D

file-loader 用于处理图片文件,而 url-loader 可以将小图片转换为 base64 编码的字符串,从而减少 HTTP 请求。

  1. 配置 webpack.config.js
// webpack.config.js
const path = require('path');

module.exports = {
  // ...其他配置
  module: {
    rules: [
      // ...其他加载器配置
      {
        test: /\.(png|jpg|jpeg|gif)$/i, // 匹配图片文件
        use: [
          {
            loader: 'url-loader', // 使用 url-loader
            options: {
              limit: 8192, // 图片大小小于8KB时转换为base64
              name: 'images/[name].[hash:8].[ext]', // 设置输出图片的目录和名称
            },
          },
        ],
      },
    ],
  },
};

  1. 在 JavaScript 中导入图片:

    在 src/js/index.js 中,你可以使用 import 语句来导入图片。

代码如下:

// src/js/index.js
import image from './image/image.jpeg';

// 使用图片,例如将其作为背景图
const imgElement = document.createElement('img');
imgElement.src = image;
document.body.appendChild(imgElement);
  1. 打包项目
npm run build
  1. 在 index.html 中引用打包后的资源:
    由于图片文件是通过 url-loader 处理的,并且 file-loader 会将图片输出到 dist 目录中,你不需要在 index.html 中显式引用图片。Webpack 会处理图片路径,并在打包后的 JavaScript 或 CSS 文件中更新图片的引用路径。

7.clean-webpack-plugin

如果你之前已经运行过 npm run build,那么在重新构建之前,你可能想要清理 dist 目录中的旧文件。这可以通过使用一个插件如 clean-webpack-plugin 来自动完成。

  1. 安装插件
npm install clean-webpack-plugin -D
  1. 配置插件
    在你的 webpack.config.js 文件中,首先需要引入 CleanWebpackPlugin,然后在插件(plugins)数组中实例化它。
// 引入插件
const { CleanWebpackPlugin } = require('clean-webpack-plugin');

module.exports = {
  // ...其他配置
  plugins: [
    // ...其他插件
    new CleanWebpackPlugin() // 实例化插件
  ]
};

  • lean-webpack-plugin 默认会删除 output.path 目录中的所有文件。如果你需要自定义要删除的文件或目录,可以在实例化插件时传递一个对象作为配置。

    例如,如果你只想删除 dist 目录中的特定文件或目录,可以这样做:

new CleanWebpackPlugin({
  cleanOnceBeforeBuildPatterns: ['**/*', '!static-files/**'] // 这里配置要保留的文件或目录
})
  1. 重新执行构建
    配置好插件后,当你再次运行 npm run build 时,clean-webpack-plugin 将在构建开始之前自动清理指定的文件和目录。
npm run build

六、其他

前端开发与前端开发工具

vscode安装与配置

模块化

1.介绍

JavaScript 模块化编程是一种将代码组织为可重用、可维护和可扩展模块的开发方法。

它通过将代码拆分为独立的模块来解决传统的脚本编程的一些问题,并提供了一种更好的方式来管理代码的结构和依赖关系。

在传统的脚本编程中,所有的代码都放在一个全局命名空间中,这导致了以下问题:

  1. 命名冲突
  2. 代码复用困难
  3. 可维护性差
  4. 协作困难
  5. 安全性低

通过使用 JavaScript 模块化编程,可以解决上述问题并带来以下优势:

  1. 封装和隔离:模块之间的接口通过明确定义的导出和导入来进行通信,减少了全局命名冲突的可能性。
  2. 代码复用:使得代码可以按照功能或特定的业务逻辑进行划分,并在需要的地方导入和使用。
  3. 维护性和可扩展性:每个模块都专注于特定的功能,可以方便地添加、删除或更新特定的功能模块
  4. 团队合作:有助于团队分工和协作,使得代码共享变得容易
  5. 系统安全性:有助于实施最小权限原则,即每个模块只拥有执行其任务所需的最少权限。确保应用程序使用的是安全的库和框架版本。

2.CommonJS规范

  • CommonJS是一种JavaScript模块规范,它允许开发者将每个文件作为一个模块来使用。在CommonJS中,每个模块都有自己的作用域,这意味着在一个文件中定义的变量、函数和类默认是私有的,不会污染全局作用域。

  • 模块内部的属性和方法可以通过exportsrequire进行导入导出函数、类、变量,私有的模块不能被外部访问。

  • CommonJS规范最初是为服务器端JavaScript设计的,特别是为了在Node.js中使用。但在浏览器端可能会导致性能问题,浏览器端通常使用异步加载的模块系统,如ES6模块(见3)。

示例:

myModule.js

// 定义一个简单的函数,这个函数是模块私有的
function privateFunction() {
  console.log('这是一个私有函数');
}

// 定义一个变量,这个变量也是模块私有的
const privateVariable = '这是一个私有变量';

// 使用exports将一个函数暴露给外部
exports.publicFunction = function() {
  console.log('这是一个公开的函数');
};

// 使用module.exports将一个对象暴露给外部
module.exports.someObject = {
  key: '这是一个公开的属性',
  publicMethod: function() {
    console.log('这是一个公开的方法');
    // 在模块内部调用私有函数
    privateFunction();
  }
};

// 可以直接替换整个module.exports对象
module.exports = {
  anotherPublicFunction: function() {
    console.log('这是另一个公开的函数');
  }
  // 注意:一旦直接替换了module.exports,之前的exports将不再有效
};

main.js

// 导入上面定义的模块
const myModule = require('./myModule.js');

// 调用模块暴露的公开函数
myModule.publicFunction(); // 输出:这是一个公开的函数

// 调用模块暴露的公开方法
myModule.someObject.publicMethod(); // 输出:这是一个公开的方法

// 注意:如果myModule.js中直接替换了module.exports,这里的调用方式可能会有所不同

  • 如果提示Warning: To load an ES module, set "type": "module" in the package.json,就需要在package.json中添加下面语句
"type": "module"
  • 如果提示require is not defined in ES module scope, you can use import instead,在你要require的代码前引入如下代码即可
import { createRequire } from 'module';
const require = createRequire(import.meta.url);

3.ES6模块化规范

  • ES6模块化规范(也称为ECMAScript 2015模块)是JavaScript官方的模块化系统。与CommonJS不同,ES6模块是静态的,意味着模块的依赖关系是在编译时确定的,而不是在运行时。ES6模块也支持异步加载和编译,这使得它们适合在浏览器中使用
  • 在ES6模块中,使用export关键字来导出函数、类、变量等,使用import关键字来导入其他模块导出的成员。
// 导出变量
export const pi = 3.14159;

// 导出函数
export function sum(x, y) {
  return x + y;
}

// 导出类
export class Circle {
  constructor(radius) {
    this.radius = radius;
  }
  
  area() {
    return pi * this.radius * this.radius;
  }
}

// 默认导出
export default function(x, y) {
  return x - y;
}

// 在同一个模块中,也可以组合导出
export { pi, sum, Circle };

// 在另一个文件中,可以这样导入上面的模块
import { pi, sum, Circle } from './myModule.js';
import subtract from './myModule.js'; // 导入默认导出的函数

// 使用导入的模块成员
console.log(sum(pi, pi)); // 输出:6.28318
const circle = new Circle(10);
console.log(circle.area()); // 输出:314.159

// 使用默认导出的函数
console.log(subtract(10, pi)); // 输出:6.85841
  • ES6模块在浏览器和现代JavaScript环境中得到了广泛支持,但为了兼容性,有时需要使用打包工具(如Webpack)或转换工具(如Babel)来处理模块。

案例

vue3-antd-admin

  • vue3-antd-admin 基于vue-cli5.x/vite2.x + vue3.x + ant-design-vue3.x + typescript hooks 的基础后台管理系统模板 RBAC的权限系统, JSON Schema动态表单,动态表格,漂亮锁屏界面。
  • 本案例只将Vue-element-admin部署到本地,体验前面的知识
  1. GitHub或gitee下载。github需要梯子,gitee可以直接访问

    1. 选择压缩包比较方便

    2. 也可以通过git克隆到本地,参考

  2. 使用vscode或其他IDE打开,下载依赖

npm config set registry https://registry.npm.taobao.org # 如果依赖下载慢就执行
npm install / npm install -f(强制安装)
  1. 下载完依赖后,使用下面命令运行
npm run dev
  • 大家可以多在github或gitee找一些开源项目运行到本地,提前熟悉节约以后的开发时间

附录

参考文献:

  1. 关于npm包管理工具的完全详解(超详细)
  2. webpack 打包原理及流程解析,超详细!
  • 25
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值