一、前端工程化
1.什么是前端工程化:
前端工程化是使用软件工程的方法来单独解决前端的开发流程中模块化、组件化、规范化、自动化 的问题,以提高效率和降低成本。
2.前端工程化实现技术栈:
ES6+Nodejs+npm+Vite+VUE3+Router+Pinia+Axios+Element-plus
ECMAScript6:VUE3中大量使用ES6语法;
Nodejs:前端项目运行环境
npm:依赖下载工具
Vite:前端项目构建工具
VUE3:优秀的渐进式前端框架;
Router:通过路由实现页面切换功能;
Pinia:通过状态管理实现组件数据传递;
Axios:ajax异步请求封装技术实现前后端数据交互;
Element-plus:可以提供丰富的快速构建网页的组件仓库;
二、ECMA6Script
1.ES6介绍:
ECMAScript6简称ES6,是JavaScript语言的一次重大更新,是原来的ECMAScript标准的第六个版本。ES6带来了大量的新特性,包括箭头函数、模板字符串、let和const关键字、解构、默认参数值、模块系统等等,大大提升了JavaScript的开发体验。ES6是学习VUE3的门槛之一;
(1).更加简洁:ES6引入了一些新的语法,如箭头函数、类和模板字符串等,使代码更加简洁易懂
(2).更强大的功能:ES6引入了一些新的AP1、解构语法和迭代器等功能,从而使得JavaScript更加强大;
(3).更好的适用性:ES6引入的模块化功能为JavaScript代码的组织和管理提供了更好的方式,不仅提高了程序的可维护性,还让JavaScript更方便地应用于大型的应用程序;
2.es6的变量和模板字符串
ES6 新增了let 和const,用来声明变量,使用的细节上也存在诸多差异
let和var的差别:
(1).let不能重复声明;
(2).let有块级作用域,非函数的花括号遇见let会有块级作用域,也就是只能在花括号里面访问
(3).let不会预解析进行变量提升
(4).let定义的全局变量不会作为window的属性;
(5).let在es6中推荐优先使用;
// 1.let只有在当前代码块有效代码块:代码块、函数、全局
{let a = 1var b = 2
}
console.log(a);// a is not defined 花括号外面无法访问
console.log(b);// 可以正常输出
// 2.不能重复声明
let name = '天真'
let name = '无邪'
// 3.不存在变量提升(先声明,再使用)
console.log(test) //可以 但是值为undefined
var test ='test'
console.log(test1)// 不可以 let命令改变了语法行为,它所声明的变量一定要在声明后使用,否则报错
let test1 = 'test1'
// 4.不会成为window的属性
var a = 100
console.log(window.a) // 100
let b = 200
console.log(window.b) // undefined
//5.循环中推荐使用
for (let i=0;i<10;i++){
}
console.log(i);
const和var的差异:
(1).新增const和let类似,只是const定义的变量不能修改;
(2).并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动;
//1.声明常量
const PI = 3.1415926;
PI=3.14 // 报错
//2.对应数组和对象元素修改不算常量修改,修改值不修改地址
const TEAM = ['刘德华','张学友','郭富城'];
TEAM.push('黎明');
TEAM = [] // 报错
console.log(TEAM)
模板字符串(template string)是增强版的字符串,用反引号(``) 标识:
(1).字符串中可以出现换行符;
(2).可以使用${xxx}形式输出变量和拼接变量;
// 1 多行普通字符串
let ulStr ='<ul>'+'<li>JAVA</li>'+'<li>html</li>'+'<li>VUE</li>'+'</ul>'
console.log(ulStr)
// 多行模板字符串
let ulStr2 = `<ul><li>JAVA</li><li>html</li><li>VUE</li></u1>`
console.log(ulStr2)
//3.普通字符串拼接
let name ='张小明'
let infoStr = name+'被评为本年级优秀学员'
console.log(infoStr)
//4.模板字符串拼接
let infoStr2 = `${name}被评为本年级优秀学员`
console.log(infoStr2)
3.es6的解构表达式
ES6 的解构赋值是一种方便的语法,可以快速将数组或对象中的值拆分并赋值给变量。解构赋值的语法使用花括号(} 表示对象,方括号[]表示数组。通过解构赋值,函数更方便进行参数接受;
数组解构赋值:
let[a,b,c,d = 4] = [1, 2, 3];
console.log(a,b,c,d);
该语句将数组 [1,2,3]中的第一个值赋值给a变量,第二个值赋值给b变量,第三个值赋值给c变量。可以使用默认值为变量提供备选值,在数组中缺失对应位置的值时使用该默认值。
对象解构赋值:
let {a, b} = {a:1, b:2};
//新增变量名必须和属性名相同,本质是初始化变量的值为对象中同名属性的值
//等价于let a = 对象.a let b = 对象.b
console.log(a,b);
该语句将对象{a: 1,b:2}中的a属性值赋值给a变量,b属性值赋值给b变量。可以为标识符分配不同的变量名称,使用:操作符指定新的变量名。
let {a:x, b:y} = {a:1, b:2};
console.log(x, y);
函数参数解构赋值:解构赋值也可以用于函数参数
function add([x,y]){return x+y;
}
add([1,2]);
该函数接受一个数组作为参数,将其中的第一个值赋给x,第二个值赋给y,然后返回它们的和;ES6 解构赋值让变量的初始化更加简单和便捷。通过解构赋值,可以访问到对象中的属性,并将其赋值给对应的变量,从而提高代码的可读性和可维护性;
4. es6的箭头函数
ES6允许使用“箭头”定义函数,语法类似Java中的Lambda表达式
//ES6允许使用"箭头"(=>)定义函数
//1.函数声明
let fn1 = function(){}
let fn2 = ()=>{} //箭头函数,此处不需要书写function关键字
let fn3 = x=>{} //单参数可以省略(),多参数无参数不可以
let fn4 = x=>console.log(x)//只有一行方法体可以省略{};
let fun5 = x=>x+1 //当函数体只有一句返回值时可以省略花括号和return语句
//2.使用特点:箭头函数this关键字
//在JavaScript中this关键字通常用来引用函数所在的对象;或者在函数本身作为构造函数时来引用新对象的实例
//但是在箭头函数中this的含义与常规函数定义中的含义不同,并且是由箭头函数定义时的上下文来决定的,而不是由函数调用时的上下文来决定的;箭头函数没有自己的this,this指向的是外层上下文环境的this
let person = {name :"张三",showName:function(){console.log(this)//这里的this是personconsole.log(this.name)},viewName:()=>{console.log(this) //这里的this是windowconsole.log(this.name)}
}
person.showName()
person.viewName()
function Counter(){this.count = 0;setInterval(()=>{// 这里的this是上一层作用域中的this,即Counter实例化对象this.count++;console.log(this.count);},1000);
}
let counter =new Counter();
rest和spread:rest参数在形参上使用,和JAVA中的可变参数几乎一样
//1参数列表中多个普通参数 普通函数和箭头函数中都支持
let fun1 = function(a,b,c,d=10){console.log(a,b,c,d)
}
let fun2 = (a,b,c,d=10)=>{console.log(a,b,c,d)}
fun1(1,2,3)
fun2(1,2,3,4)
//2...作为参数列表,称之为rest参数;普通函数和箭头函数中都支持
//因为箭头函数中无法使用arguments, rest是一种解决方案
let fun3 = function(...args){console.log(args)}
let fun4 = (...args)=>{console.log(args)}
fun3(1,2,3)
fun4(1,2,3,4)
//rest参数在一个参数列表中的最后一个,这也就无形之中要求一个参数列表中只能有一个rest参数
spread参数,在实参上使用:
let arr = [1,2,3]
//let arrSpread = ...arr;// 这样不可以,...arr必须在调用方法时作为实参使用
let fun1 = (a,b,c)=>{console.log(a,b,c)}
// 调用方法时对arr进行转换,转换为1,2,3
fun1(...arr)
//应用场景1:合并数组
let arr2 = [4,5,6]
let arr3 = [...arr,...arr2]
console.log(arr3)
//应用场景2:合并对象属性
let p1 = {name:"张三"}
let p2 = {age:10}
let p3 = {gender:"boy" }
let person = {..p1,...p2,...p3}
console.log(person)
5.es6的对象创建和拷贝
(1).对象创建的语法糖
ES6中新增了对象创建的语法糖,支持了class extends constructor等关键字,让ES6的语法和面向对象的语法更加接近。
class Person{// 属性#n;age;get name(){return this.#n;}set name(n){this.#n =n;}// 实例方法eat(food){console.log(this.age+"岁的"+this.#n +"用筷子吃"+food)}// 静态方法static sum(a,b){return a+b;}// 构造器constructor(name,age){this.#n=name;this.age = age;}
}
let person =new Person("张三",10);
// 访问对象属性
// 调用对象方法
console.log(person.name)
console.log(person.#n)// 私有报错
person.name="小明"
console.log(person.age)
person.eat("火锅")
console.log(Person.sum(1,2))
class Student extends Person{grade;score;study(){}constructor(name,age){super(name,age);}
}
let stu = new student("学生小李",18);
stu.eat("面条");
(2)对象的深拷贝和浅拷贝
对象的拷贝是指快速获得一个和已有对象相同的对象的方式:
a.浅拷贝:
let arr =['java','c','python']
let person = {name:'张三',language:arr
}
let person2 = person;
person2.name = "小黑"
console.log(person.name)
b.深拷贝:
let arr =['java','c','python']
let person = {name:'张三',language:arr
}
//深拷贝:通过JSON和字符串的转换形成一个新的对象
let person2 = JSON.parse(JSON.stringify(person));
person2.name = "小黑"
console.log(person.name)
console.log(person2.name)
6.es6的模块化处理
(1)模块化介绍:模块化是一种组织和管理前端代码的方式,将代码拆分成小的模块单元,使得代码更易于维护扩展和复用。它包括了定义、导出、导入以及管理模块的方法和规范。前端模块化的主要优势如下:
a.提高代码可维护性:通过将代码拆分为小的模块单元,使得代码结构更为清晰,可读性更高,便于开发者阅读和维护;
b.提高代码可复用性:通过将重复使用的代码变成可复用的模块,减少代码重复率,降低开发成本;
c.提高代码可扩展性:通过模块化来实现代码的松耦合,便于更改和替换模块,从而方便地扩展功能;
(2).ES6模块化的几种暴露和导入方式:无论以何种方式导出的都是一个对象,导出的内容都可以理解为是向这个对象中添加属性或者方法
a.分别导出:
// module.js向外分别暴露成员:
// 1.分别暴露
// 模块想对外导出,添加export关键字即可
// 导出一个变量
export const PI =3.14
// 导出一个函数
export function sum(a,b){return a+b;
}
// 导出一个类
export class Person{constructor(name,age){this.name = name;this.age = age;}sayHello(){console.log("Hello,my name is ${this.name}, I'm ${this.age} years old.");}
}
// app.js导入module.js中的成员:
/**代表module.js中的所有成员m1代表所有成员所属的对象
*/
import * as ml from './module.js'
// 使用暴露的属性
console.log(m1.PI)
//调用暴露的方法
let result = m1.sum(10,20)
console.log(result)
// 使用暴露的Person类
let person = new m1.Person('张三',10)
person.sayHello()
index.html作为程序启动的入口,导入app.js:
<!-- 导入JS文件 添加type='module'属性,否则不支持ES6的模块化 -->
<script src = "./app.js" type="module"/>
b.统一导出:
// module.js向外统一导出成员:
// 2.统一暴露
// 模块想对外导出,export统一暴露想暴露的内容
// 定义一个常量
const PI =3.14
// 定义一个函数
function sum(a,b){return a+b;
}
// 定义一个类
class Person{constructor(name,age){this.name = name;this.age = age;}sayHello(){console.log(`Hello, my name is ${this.name}, I'm ${this.age} years old.`);}
}
//统一对外导出(暴露)
export {PI,sum,Person
}
// app.js导入module.js中的成员:
/*{}中导入要使用的来自于module.js中的成员{}中导入的名称要和module.js中导出的一致,也可以在此处起别名{}中如果定义了别名,那么在当前模块中就只能使用别名{}中导入成员的顺序可以不是暴露的顺序一个模块中可以同时有多个import多个import可以导入多个不同的模块,也可以是同一个模块
*/
//import {PI,Person,sum} from './module.js'
//import {PI as pi,Person as People,sum as add} from './module.js'
import {PI ,Person ,sum,PI as pi,Person as People,sum as add} from './module.js'
// 使用暴露的属性
console.log(PI)
console.log(pi)
// 调用暴露的方法
let result1 = sum(10,20)
console.log(result1)
let result2 = add(10,20)
console.log(result2)
// 使用暴露的Person类
let person1 =new Person('张',10)
person1.sayHello()
let person2 =new People('李四',11)
person2.sayHello()
c.默认导出:
// modules混合向外导出:
// 3.默认和混合暴露
/*默认暴露语法 export default sum默认暴露相当于是在暴露的对象中增加了一个名字为default的属性三种暴露方式可以在一个module中混合使用
*/
export const PI=3.14
// 导出一个函数
function sum(a,b){return a + b;
}
// 导出一个类
class Person{constructor(name,age){this.name = name;this.age = age;}sayHello(){console.log(`Hello,my name is ${this.name}, I'm ${this.age} years old.`);}
}
//导出默认
export default sum
// 统一导出
export {Person
}
// app.js的default和其他导入写法混用:
/**代表module.js中的所有成员m1代表所有成员所属的对象
*/
import *as ml from './module.js'
import {default as add} from'./module.js' // 用的少
import add2 from './module.js' //等效于 import {default as add2} from'./module.js'
// 调用暴露的方法
let result = m1.default(10,20)
console.log(result)
let result2 = add(10,20)
console.log(result2)
let result3 = add2(10,28)
console.log(result3)
// 引入其他方式暴露的内容
import {PI,Person} from './module.js'
// 使用暴露的Person类
let person = new Person('张三',10)
person.sayHello()
// 使用暴露的属性
console.log(PI)
三、前端工程化环境搭建
3.1.Nodejs的简介和安装
Node.js 是一个基于Chrome V8引擎的JavaScript运行时环境,可以使JavaScript运行在服务器端。使用Node.js可以方便地开发服务器端应用程序,如Web 应用、API、后端服务,还可以通过 Node.js构建命令行工具等。相比于传统的服务器端语言(如 PHP、Java、Python 等)Node.js 具有以下特点:
单线程:但是采用了事件驱动、异步IO模型,可以处理高并发请求
轻量级:使用 C++编写的 V8 引擎让 Node.js 的运行速度很快;
模块化:内置了大量模块,同时也可以通过第三方模块扩展功能;
跨平台:可以在Windows、Linux、Mac等多种平台下运行;
Node.js的核心是其管理事件和异步IO的能力。Node.js的异步IO使其能够处理大量并发请求并且能够避免在等待IO资源时造成的阻塞。此外Node.js还拥有高性能网络库和文件系统库可用于搭建 Websocket服务器、上传文件等。在 Node.js中可以使用JavaScript来编写服务器端程序,这使得前端开发人员可以利用自己已经熟悉的技能来开发服务器端程序,同时也让Javascript成为一种全栈语言;
3.2.npm配置和使用:
(1)npm介绍:
NPM全称Node Package Manager,是Node.is包管理工具,是全球最大的模块生态系统,里面所有的模块都是开源免费的;也是Node.js的包管理工具,相当于后端的Maven的部分功能