Javaweb - 14.1 - 前端工程化

目录

前端工程化

什么是前端工程化

前端工程化实现技术栈

ECMA6Script

[ES6 介绍](#ES6 介绍)

[ES6 的变量和模板字符串](#ES6 的变量和模板字符串)

[ES6 的解构表达式](#ES6 的解构表达式)

[ES6 的箭头函数](#ES6 的箭头函数)

[rest 和 spread](#rest 和 spread)

[ES6 的对象创建和拷贝](#ES6 的对象创建和拷贝)

对象创建的语法糖

对象的深拷贝和浅拷贝

[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 新增了 letconst,用来声明变量

let 和 var 的区别:

  1. let 不能重复声明

  2. let 有块级作用域,非函数的花括号遇见 let 会有块级作用域,也就是只能在花括号里面访问

  3. let 不会预解析进行变量提升

  4. let 定义的全局变量,不会被作为 window 的属性

  5. let 在 es6 中推荐更优先使用~

示例

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 实例

箭头函数的练习:

html 复制代码
<!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")
        // 方案 1
        xdd.onclick = function() {
            console.log(this)
            let _this = this // this 是 xdd
            // 开启定时器
            window.setTimeout(function() {
                _this.style.backgroundColor="yellow"
            },2000)
        }
        // 方案 2
        xdd.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 的语法和面向对象的语法更接近

html 复制代码
    <script>
        class Person {
            // 属性
            n;
            age;

            // getter setter
            get 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 = name
                this.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:

javascript 复制代码
// 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 am ${this.age} years old`)
    }
}

app.js:

javascript 复制代码
// * 代表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

javascript 复制代码
// 2. 统一暴露
// 模块想对外导出,export 统一暴露想暴露的内容

// 定义一个常量
const PI = 3.14

// 定义一个函数
let sum = (a, b) => a + b

// 定义一个类
class Person {
  constructor(name, age) {
    this.name = name
    this.age = age
  }

  sayHello = () => {
    console.log(`Hello my name is ${this.name},I am ${this.age} years old`);
  }
}

// 统一对外导出
export {
    PI,
    sum,
    Person
}

app.js

javascript 复制代码
/**
 * {} 中导入要使用的来自 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:

javascript 复制代码
// 3. 默认导出
/**
 * 默认导出语法: export default sum
 * 默认导出相当于是在暴露的对象中增加了一个名字为 default 的属性
 * 三种导出方式可以在一个 module 中混合使用
 */

// 分别导出变量
export const PI = 3.14

// 定义一个函数
let sum = (a,b) => a+b

// 定义一个类
class Person {
    constructor(name, age) {
        this.name = name
        this.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:

javascript 复制代码
/**
 *  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:

完!

相关推荐
Lsx_8 分钟前
分不清RAG 、Function Call、MCP、Agent?一文秒懂它们的区别和联系
前端·agent·mcp
毕了业就退休28 分钟前
websocket 的心跳机制你知道几种
前端·javascript·http
子林super29 分钟前
aiforcast集群单节点CPU使用率100%问题
前端
CF14年老兵31 分钟前
为什么 position: absolute 在 Flexbox 里会失效?
前端·css·trae
xianxin_35 分钟前
CSS 选择器
前端
徐小夕36 分钟前
花3个月时间,写了一款协同文档编辑器
前端·vue.js·算法
Nicholas6843 分钟前
flutter滚动视图之ScrollDirection、ViewportOffset源码解析(一)
前端
Dream耀1 小时前
FitKick 电商APP项目总结二
前端·javascript·react.js
ZsTs1191 小时前
一篇通关:从 MVVM 到渲染优化,Vue 基础核心 5 大模块全解析
前端·vue.js·面试
石小石Orz1 小时前
视差悬停特效:鼠标跟随的沉浸式交互体验
前端·css·trae