Javaweb - 14.1 - 前端工程化
目录
前端工程化
什么是前端工程化
前端工程化实现技术栈
ECMA6Script
ES6 介绍
ES6 的变量和模板字符串
ES6 的解构表达式
ES6 的箭头函数
rest 和 spread
ES6 的对象创建和拷贝
对象创建的语法糖
对象的深拷贝和浅拷贝
ES6 的模块化处理
模块化介绍
分别导出
统一导出
默认导出
完!
前端工程化
什么是前端工程化
前端工程化是使用软件工程的方法来单独解决前端的开发流程中模块化,组件化,规范化,自动化的问题。
我们在前面的案例中,前后端都在同一个容器中实现。但为了实现模块化,组件化,规范化,自动化,前后端会使用到不同的工具,后端使用 maven,前端使用 nodejs,npm,vite...
但前后端的工具,又无法在同一个容器中部署,所以,前端工程化,就是将 app 中的前端相关的代码剥离出来,形成一个独立的工程,使用相关的专门的技术来实现前端代码模块化,组件化,规范化,自动化
前后端分离模式,要做到:1. 开发分离 2. 部署分离
前端工程化实现技术栈
前端工程化实现的技术栈很多,本文采用 ES6 + Nodejs + npm + Vite + VUE3 + Router + Axios + Element-plus 组合来实现
- ES6 VUE3 中大量使用 ES6 语法
- Nodejs 前端项目运行环境
- npm 依赖下载工具
- Vite 前端项目构建工具
- VUE3 渐进式前端框架
- Router 通过路由实现页面切换
- PInia 通过状态管理实现组件数据传递
- Axios ajax 异步请求封装技术实现前后端数据交互
- Element-plus 可用提供丰富的快速构建网页的组件仓库
ECMA6Script
ES6 介绍
ECMAScript6,是 JavaScript 语言的一次重大更新。带来了大量的新特性,大大提升了 JavaScript 的开发。
ES6 的变量和模板字符串
ES6 新增了 let 和 const,用来声明变量
let 和 var 的区别:
1. let 不能重复声明
2. let 有块级作用域,非函数的花括号遇见 let 会有块级作用域,也就是只能在花括号里面访问
3. let 不会预解析进行变量提升
4. let 定义的全局变量,不会被作为 window 的属性
5. let 在 es6 中推荐更优先使用~
示例
1.
2.
3.
4.
const 和 var 的差异
const 和 let 类似,不过 const 定义的变量不能再被修改。注意理解:不能再被修改,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。
示例:
模板字符串(template string)是增强版的字符串,用反引号(`)标识
1. 字符串中可用出现换行符
2. 可以使用 ${xxx} 形式输出变量和拼接变量
示例:
ES6 的解构表达式
可以快速将数组或对象中的值拆分并赋值给变量。使用花括号表示对象,方括号表示数组。
数组解构赋值:
该语句将数组[1,2,3]中的第一个值赋值给 a 变量,第二个值赋给 b 变量,第三个值赋给 c 变量。可以使用默认值为变量提供备选值,再数组中缺失对应位置的值时使用该默认值
对象解构赋值:
新增变量名必须和属性名相同
函数参数解构赋值:
ES6 的箭头函数
ES6 中允许使用“箭头”函数。语法类似 Java 中的 Lambda 表达式
使用特点:
箭头函数中的 this 关键字
在 JS 中,this 关键字通常用来引用函数所在的对象
或者在函数本身作为构造函数,来引用新对象的实例
但在箭头函数中有所不同
是由箭头函数定义时的上下文来决定的,而不是函数调用时的上下文决定
箭头函数没有自己的this,this指向的是外层上下文环境的this
this 使用:
这个例子中,我们定义了一个 Counter 构造函数,初始化 count = 0
使用 setInterval 设置了一个定时器,每隔 1 秒执行一次回调函数
箭头函数作为回调,并不会创建自己的 this 绑定,而是继承自外层作用域的 this
因此这里的 this 指向的是 Counter 的实例对象(通过 new 创建的 counter)
当我们执行代码:let counter = new Counter() ,会创建一个 counter 实例,启动定时器,每秒将实例的 count 属性增加1并且打印。
这种行为,是因为箭头函数没有自己的 this 绑定,它会捕获所在上下文的 this 值,如果这里使用普通函数作为 setInterval 的回调,this 会指向全局对象,而不是 Counter 实例
箭头函数的练习:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>#xdd{display: inline-block;width: 200px;height: 200px;background-color: red;}</style></head>
<body><div id = "xdd"></div><script>var xdd = document.getElementById("xdd")// 方案 1xdd.onclick = function() {console.log(this)let _this = this // this 是 xdd// 开启定时器window.setTimeout(function() {_this.style.backgroundColor="yellow"},2000)}// 方案 2xdd.onclick = function(){console.log(this)// 开启定时器setTimeout(()=>{console.log(this) // 使用 setTimeout 方法所在环境上下文的 this 对象this.style.backgroundColor="yellow"},2000) }</script>
</body>
</html>
方案1:onclick 的回调是普通函数,其 this 指向绑定事件的 DOM 元素(xdd)
setTimeout 的回调也是普通函数,普通函数的this在非严格模式下默认指向全局对象,因此需要用 _this 保存外层的 this(xdd 元素)
方案2:onclick 的回调仍然是普通函数,其 this 依然指向 xdd 元素
但 setTimeout 的回调改为了箭头函数,箭头函数没有自己的 this,会继承外层作用域的 this,这里的外层作用域是 onclick 的回调函数,其 this 是 xdd,因此箭头函数中的 this 也指向 xdd,无需额外变量保存
总结:箭头函数的 this 是”静态“的,在定义时候就确定了(继承自外层)。而普通函数的 this 是”动态“的,在调用时根据上下文确定。
rest 和 spread
rest 剩余的,解决剩余的参数接收问题。rest 参数在一个参数列表中的最后一个,这也就要求一个参数列表中只能有一个 rest 参数
spread 展开 rest 在实参上的应用
不可以 let arrSpread = ...arr ...arrr 必须在调用方法时作为实参进行使用
应用:
ES6 的对象创建和拷贝
对象创建的语法糖
ES6 中新增了对象创建的语法糖,支持了 class extends constructor 等关键字,让 ES6 的语法和面向对象的语法更接近
<script>class Person {// 属性n;age;// getter setterget name() {console.log("getter")return this.n;}set name(n) {console.log("setter")this.n = n;}// 实例方法eat(food) {console.log(`${this.age}岁的${this.n}正在吃${food}`)}// 静态方法static sum(a,b) {return a + b}// 构造器constructor(name, age) {this.n = namethis.age = age}}let person = new Person('小明',8)console.log(person.name)person.eat('火锅')console.log(Person.sum(10,20))</script>
补充:如果在属性前加 # 代表该属性为私有属性~
也可以再进行继承:
对象的深拷贝和浅拷贝
浅拷贝时候,arr 和 arr2 指向相同的地址引用,是将 arr 的地址引用赋值给了 arr2
在深拷贝中,是形成一个新的对象
ES6 的模块化处理
模块化介绍
模块化是一种组织和管理前端代码的方式,将代码拆分成小的模块单元,使得代码更易于维护,扩展和复用。包括了定义,导出,导入以及管理模块的方法和规范。
ES6 模块化的几种暴露(导入)方式:分别导出,统一导出,默认导出
!!!ES6 中,无论以何种方式导出,导出的都是一个对象,导出的内容都可以理解为是向这个对象中添加属性或者方法!!!
分别导出
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 = namethis.age = age}sayHello() {console.log(`Hello my name is ${this.name},I am ${this.age} years old`)}
}
app.js:
// * 代表module.js 中的所有成员
// m1 代表所有成员所属的对象import * as m1 from './module.js'// 使用暴露的属性
console.log(m1.PI)// 使用暴露的方法
let result = m1.sum(10,20)
console.log(result)// 使用暴露的 Person 类
let person = new m1.Person('张三',18)
person.sayHello()
index.html 作为程序启动的入口,导入 app.js
统一导出
module.js
// 2. 统一暴露
// 模块想对外导出,export 统一暴露想暴露的内容// 定义一个常量
const PI = 3.14// 定义一个函数
let sum = (a, b) => a + b// 定义一个类
class Person {constructor(name, age) {this.name = namethis.age = age}sayHello = () => {console.log(`Hello my name is ${this.name},I am ${this.age} years old`);}
}// 统一对外导出
export {PI,sum,Person
}
app.js
/*** {} 中导入要使用的来自 module.js 中的成员* {} 中导入的数据的名称要和 module.js 中导出的保持一致,也可以在此处起别名* {} 中如果定义了别名,那么在当前模块中,就只能使用别名了* {} 导入的成员顺序可以不是暴露的顺序* 一个模块中可以同时导入多个 import* 多个 import 可以导入多个不同的模块,也可以是同一个模块*/import {PI, Person, sum, PI as pi, Person as People, sum as add} from './module.js'// 使用暴露的属性
console.log(PI)
console.log(pi)// 使用暴露的方法
let result = sum(10,20)
let result2 = add(10,30)
console.log(result)
console.log(result2)// 使用暴露的 Person 类
let person1 = new Person('张三',10)
let person2 = new People('李四',15)
person1.sayHello()
person2.sayHello()
index.html:
默认导出
module.js:
// 3. 默认导出
/*** 默认导出语法: export default sum* 默认导出相当于是在暴露的对象中增加了一个名字为 default 的属性* 三种导出方式可以在一个 module 中混合使用*/// 分别导出变量
export const PI = 3.14// 定义一个函数
let sum = (a,b) => a+b// 定义一个类
class Person {constructor(name, age) {this.name = namethis.age = age}sayHello() {console.log(`Hello my name is ${this.name},I am ${this.age} years old`);}
}
// 默认导出 一个 js 中只能有一个
export default sum
// 统一导出
export {Person
}
app.js:
/*** app.js 中的 * 代表 module.js 中的所有成员* m1 代表所有成员所属的对象*/import * as m1 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,30)
console.log(result2)let result3 = add2(10,40)
console.log(result3)// 引入其他方式暴露的内容
import {PI,Person} from './module.js'// 使用暴露的Person
let person = new Person('zzz',18)
person.sayHello()// 使用暴露的属性
console.log(PI)
index.html: