在学习javascript中感觉这个语言的变化太快了,发现系统学的效率很低,所以在学完传统三大件之后就自己整理了一个ES6到ES11常用的改变,内容比较杂可以看目录

观前提醒:

本文创作的目的是搭建一个传统三大件和框架之间的桥梁,少绕弯路,省去大部分的前端学习时间

本文默认读者会使用简单的html,css,javascript,并且由于我自己的方向主要是后端java,所以有些内容都会用java比较,会一点java语法最好了

至于前后端交互的内容,如果学习过ajax等会观看得更加轻松,没学过也可以适当跳过文中我建议跳过的部分,可以在学过之后再来补充,相信你会有不一样的感受

let和const

let

声明变量

1
2
3
4
let a;
let b,c,d;
let e = 100;
let f = 521, g = 'iloveyou', h = [];

特点:

  1. 变量不能重复声明
  2. 块儿级作用域
  3. 不存在变量提升
1
2
console.log(singer);
var singer = "日向文";

上面这种就叫做变量提升,即在输出后定义,var可行而let不可行

  • 以后尽量都用let替代var,方便读代码

const

1
const SINGER = "鹿乃"
  • 一定要赋初始值

  • 常量一般使用大写(注意这是潜规则,也可以不遵守)

  • 常量的值不能修改

  • 块儿级作用域

  • 对于数组和对象的元素修改, 不算做对常量的修改, 不会报错

1
2
3
const TEAM = ['UZI','MXLG','Ming'];

TEAM.push('Meiko');//不报错

解构赋值

ES6 允许按照一定模式从数组和对象中提取值,对变量进行赋值

  • 数组的解构
1
2
3
4
5
6
7
8
9
10
11
const F4 = ['小沈阳','刘能','赵四','宋小宝'];

let [xiao, liu, zhao, song] = F4;

console.log(xiao);//输出小沈阳

console.log(liu);//输出刘能

console.log(zhao);//输出赵四

console.log(song);//输出宋小宝
  • 对象的解构
1
2
3
4
5
6
7
8
9
 const zhao = {
name: '赵本山',
age: '不详',
xiaopin: function(){
console.log("我可以演小品");
}
};
let {xiaopin} = zhao;
xiaopin();//输出 我可以演小品

注意上面对象的解构需要属性名一致

模板字符串

ES6 引入新的声明字符串的方式:反引号

声明

1
2
let str = `我也是一个字符串哦!`;
console.log(str, typeof str);
  • 其内容中可以直接出现换行符
1
2
3
4
5
6
let str = `<ul>
<li>沈腾</li>
<li>玛丽</li>
<li>魏翔</li>
<li>艾伦</li>
</ul>`;//如果是单引号或者双引号必须内容所有都一行下来不能换行

变量拼接

1
2
3
let lovest = '魏翔';

let out = `${lovest}是我心目中最搞笑的演员!!`;//固定格式,和jsp里面的EL表达式长相一致

简化对象写法

ES6 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法,这样的书写更加简洁

1
2
3
4
5
6
7
8
9
10
11
12
  let name = '尚硅谷';
let change = function(){
console.log('我们可以改变你!!');
}

const school = {
name,
change,
improve(){//function的省略也是es6的特性
console.log("我们可以提高你的技能");
}
}

箭头函数

  • ES6 允许使用「箭头」(=>)定义函数。
1
2
3
4
5
6
let fn = (a,b) => {
return a + b;
}
//调用函数
let result = fn(1, 2);
console.log(result);
  • this 是静态的,this 始终指向函数声明时所在作用域下的 this 的值。
1
2
3
4
5
6
7
8
9
10
11
12
    function getName(){
console.log(this.name);
}
let getName2 = () => {
console.log(this.name);
}
window.name = '哈哈哈';
const kaixin = {
name = "hahaha"
}
getName.call(kaixin);//输出hahaha
getName2.call(kaixin);//输出哈哈哈
  • 不能作为构造实例化对象,即不能作为构造函数
1
2
3
4
5
6
let Person = (name, age) => {
this.name = name;
this.age = age;
}
let me = new Person('xiao',30);
console.log(me);//这里就报错了

箭头函数的简写

省略小括号, 当形参有且只有一个的时候

1
2
3
4
let add = n => {
return n + n;
}
console.log(add(9));

省略花括号, 当代码体只有一条语句的时候, 此时 return 必须省略,而且语句的执行结果就是函数的返回值

1
2
let pow = n => n * n;
console.log(pow(8));

参数默认值

ES6 允许给函数参数赋值初始值

1. 形参初始值 具有默认值的参数, 一般位置要靠后(潜规则)

1
2
3
4
5
function add(a,b,c=10) {
return a + b + c;
}
let result = add(1,2);
console.log(result);

2. 与解构赋值结合

1
2
3
4
5
6
7
8
9
10
11
12
 function connect({host="127.0.0.1", username,password, port}){
console.log(host)
console.log(username)
console.log(password)
console.log(port)
}
connect({
host: 'atguigu.com',
username: 'root',
password: 'root',
port: 3306
})

rest参数

ES6 引入 rest 参数,用于获取函数的实参,用来代替 arguments

  • ES5 获取实参的方式
1
2
3
4
function date(){
console.log(arguments);//这个arguments是一个对象
}
date('白芷','阿娇','思慧');
  • ES6里的rest 参数
1
2
3
4
5
6
7
8
function date1(...args){
console.log(args);//args是一个数组,故filter等方法都可以使用
}
function date2(a,...args){//rest参数必须要放到参数最后
console.log(args);
}
date1('阿娇','柏芝','思慧');
date2('阿娇','柏芝','思慧');//这里就把阿娇给了a,而剩下的给了args

spread扩展运算符

简介

『…』 扩展运算符能将『数组』转换为逗号分隔的『参数序列』

1
2
3
4
5
6
const three = ['死神','火影','海贼王'];
//声明了一个数组,若想把它转化为参数序列'死神','火影','海贼王'而不是放到数组里面该怎么做呢
function jingdian(){
console.log(arguments);
}
jingdian(...three);//等同于jingdian('死神','火影','海贼王')

应用

  • 数组的合并
1
2
3
4
5
const kuaizi = ['王太利','肖央'];
const fenghuang = ['曾毅','玲花'];
const zuixuanxiaopingguo = kuaizi.concat(fenghuang);
const zuixuanxiaopingguo = [...kuaizi, ...fenghuang];
console.log(zuixuanxiaopingguo);
  • 数组的克隆
1
2
3
const sanzhihua = ['E','G','M'];
const sanyecao = [...sanzhihua];// ['E','G','M']
console.log(sanyecao);
  • 将伪数组转为真正的数组
1
2
3
const divs = document.querySelectorAll('div');//这里拿回来的是一个对象而不是一个数组
const divArr = [...divs];
console.log(divArr);// arguments

Symbol

说实在的,讲的不好,这块不是很重点,推荐两个博客

在看之前明确几个概念

  • javascript里的对象本质就是键值对,左侧是键右侧是值,即左侧其实是一个字符串,如name: “张三”其实也可以写作”name”: “张三”,只是平常忽略了双引号
  • javascript中的对象调用内部属性和方法不仅可以用.还可以用[],如student.name和student[“name”]效果相同

以上知识不是ES6的内容,如果有疑问建议重学javascript高级

放推荐博客

https://www.cnblogs.com/sker/p/5474591.html

https://blog.csdn.net/qq_41694291/article/details/103322409?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522167016723116782425175146%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=167016723116782425175146&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-1-103322409-null-null.142

看完他们对Symbol有个大概了解即可

至此,java里的七大基本类型字符串(String)、数字(Number)、布尔(Boolean)、空(Null)、未定义(Undefined)、Symbol已经全部出现,其他的均为引用类型,论本质是对象(数组本质也是对象,这点也是javascript本篇的内容)

迭代器

和java很像但接口调用不一样,这里其实也讲得一般但是不重要

迭代器声明使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//声明一个数组
const xiyou = ['唐僧','孙悟空','猪八戒','沙僧'];
//使用 for...of 遍历数组,这个是es6的新特性
for(let v of xiyou){
console.log(v);
}
//let v in xiyou,v是0,1,2,3
//let v of xiyou,v是数组里的内容

let iterator = xiyou[Symbol.iterator]();//xiyou对象里有一个Symbol对象,内部有一个iterator方法,返回一个对象
//调用对象的next方法
//next方法返回的是一个对象,里面有value和done两个属性
console.log(iterator.next());//输出value唐僧(默认指针是第一个变量)
console.log(iterator.next());//输出value为孙悟空
console.log(iterator.next());//输出value为猪八戒
console.log(iterator.next());//输出value为沙僧
console.log(iterator.next());//输出value为undefined,同时done变为从false变为true代表遍历完成

迭代器的自定义遍历

这里内容有点难,而且用处不大,可以以后再看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
 const banji = {
name: "终极一班",
stus: [
'xiaoming',
'xiaoning',
'xiaotian',
'knight'
],
[Symbol.iterator]() {
//索引变量
let index = 0;
let _this = this;
return {
//这里相当于自己实现了next方法
next: function () {
if (index < _this.stus.length) {
const result = { value: _this.stus[index], done: false };
//下标自增
index++;
//返回结果
return result;
}else{
return {value: undefined, done: true};
}
}
};
}
}

//遍历这个对象,这里for...in可以直接访问到每个键名称,但是for...of就需要上述的自定义遍历,因为对象里没有iterator接口方法
for (let v of banji) {
console.log(v);
}

生成器

生成器函数

这里需要一定的异步编程基础,如果没学可以先跳过

  • 生成器其实就是一个特殊的函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//定义时和普通函数不同的是需要加一个*
function * leimu(){
console.log("leimu");
}
let iterator = leimu();//返回的是一个迭代器,函数不会执行
console.log(iterator.next());//这样函数才会输出leimu

//特性是可以在函数中写yield
//如果你没学过异步编程可以理解为yield是函数的一个中断,函数的执行暂时停到了这里
function * gen(){
console.log(111);
yield '一只没有耳朵';
console.log(222);
yield '一只没有尾部';
console.log(333);
yield '真奇怪';
console.log(444);
}
let iterator = gen(); console.log(iterator.next());
//输出111
console.log(iterator.next());
//输出222
console.log(iterator.next());
//输出333
console.log(iterator.next());
//输出444

生成器函数参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function * gen(arg){
console.log(arg);
let one = yield 111;
console.log(one);
let two = yield 222;
console.log(two);
let three = yield 333;
console.log(three);
}

//执行获取迭代器对象
let iterator = gen('AAA');
console.log(iterator.next());//输出AAA

//next方法可以传入实参,传入的实参为yield语句的返回结果
console.log(iterator.next('BBB'));//输出BBB,证明one里存储了111
console.log(iterator.next('CCC'));//输出CCC
console.log(iterator.next('DDD'));//输出DDD

案例就不写了,感觉这个用处真的不大

Promise

划重点!!!这个对异步编程很重要,学没学过都可以看看

这里打算参考另一篇博客写下

Promise基本语法

定义

1
2
3
4
5
6
7
8
//promise对象的实例化
let p = new Promise((resolve, reject) => {
//做一些异步操作,这里拿定时器举例
setTimeout(() => {
console.log('执行完成');
resolve('我是成功!!');//状态一旦被改变后面就不会再执行其他代码了
}, 2000);
});复制代码

如上,我们已经发现了Promise其实就是一个对象,对象的构造函数参数是一个函数用来实现异步操作,而作为参数的这个函数中又要有两个参数,分别是

  • resolve :异步操作执行成功后的回调函数
  • reject:异步操作执行失败后的回调函数

其实就是无限套娃,对象的参数是一个函数,函数的参数又是两个函数

该对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)

  • 如果Promise的参数函数中调用了resolve,promise状态置为已成功
  • 如果Promise参数函数中调用了reject,promise状态置为已失败

调用 promise 对象的 then 方法

Promise对象中有一个至关重要的方法叫then

Promise,顾名思义,是一个承诺,而之所以叫承诺就是因为在Promise确定状态后会调用then里面的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
let p = new Promise((resolve, reject) => {
//做一些异步操作
setTimeout(function(){
var num = Math.ceil(Math.random()*10); //生成1-10的随机数
if(num<=5){
resolve(num);
}
else{
reject('数字太大了');
}
}, 2000);
});//Promise对象被new出来的时候就会执行一次

//then方法返回了一个Promise对象,参数是两个函数或者一个函数,当参数为一个函数的时候该函数就是Promise成功执行后回调的函数,这个函数的参数为resolve的参数;当参数为两个函数的时候第一个函数同上述函数,而第二个函数是Promise执行失败后执行的函数,这个函数的参数是reject的函数
p.then((data) => {
console.log('resolved',data);//这里输出num的值
},(err) => {
console.log('rejected',err);//输出 数字太大了
}
);

//then方法可以连续注册
p.then((data1) => {
console.log(data1);
})
.then((data2) => {
console.log(data2);
})
.then((data3) => {
console.log(data3);
});

//再举个例子,下面代码中,Promise 新建后就会立即执行,所以首先输出的是Promise。然后,then方法指定的回调函数,将在当前脚本所有同步任务执行完才会执行,所以resolved最后输出。
let promise = new Promise(function(resolve, reject) {
console.log('Promise');
resolve();
});
promise.then(function() {
console.log('resolved.');
});
console.log('Hi!');
// Promise
// Hi!
// resolved

这里如果还是没听懂,那建议还是先学完前后端交互再来看,了解了实际的应用场景后可能会对Promise有更深的理解

Promise的catch方法

Promise对象除了then方法,还有一个catch方法,它和then的第二个参数一样,用来指定reject的回调,用法如下

1
2
3
4
5
p.then((data) => {
console.log('resolved',data);
}).catch((err) => {
console.log('rejected',err);
});

Promise的all方法

Promise的all方法提供了并行执行异步操作的能力,并且在所有异步操作执行完后才执行回调。

1
2
3
4
5
6
7
8
9
10
11
let Promise1 = new Promise(function(resolve, reject){})
let Promise2 = new Promise(function(resolve, reject){})
let Promise3 = new Promise(function(resolve, reject){})
//all接收一个数组参数,里面的值最终都算返回Promise对象
let p = Promise.all([Promise1, Promise2, Promise3])

p.then(funciton(){
// 三个都成功则成功
}, function(){
// 只要有失败,则失败
})

到这其实大致Promise对象用法就明确了,强烈建议在学完前后端交互之后再来看看,会有不一样的体会

Set集合

和java类似的机制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//声明一个 set
let s = new Set();
let s2 = new Set(['大事儿','小事儿','好事儿','坏事儿','小事儿']);

//元素个数
console.log(s2.size);
//添加新的元素
s2.add('喜事儿');
//删除元素
s2.delete('坏事儿');
//检测
console.log(s2.has('糟心事'));//输出false
//清空
s2.clear();
console.log(s2);//输出空集合

for(let v of s2){
console.log(v);
}//集合也有iterator接口

Map集合

类似于对象也是键值对的集合,但是键的范围不限于字符串,各种类型的值(包括对象都可以当做键,和java类似)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
 //声明 Map
let m = new Map();
//添加元素
m.set('name','尚硅谷');
m.set('change', function(){
console.log("我们可以改变你!!");
});
let key = {
school : 'ATGUIGU'
};
m.set(key, ['北京','上海','深圳']);
//size
console.log(m.size);
//删除
m.delete('name');
//获取
console.log(m.get('change'));
console.log(m.get(key));
//清空
m.clear();
//遍历
for(let v of m){
console.log(v);
}
console.log(m);

class类

这下和java几乎一样的

学习之前如果没有搞清楚隐式原型对象和显式原型对象建议看这篇博客https://blog.csdn.net/cc18868876837/article/details/81211729?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522167025642416800186588381%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=167025642416800186588381&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-1-81211729-null-null.142

介绍和基本使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//先用es5的方法来创建对象
//构造函数
function Phone(brand, price){
this.brand = brand;
this.price = price;
}

//添加方法
Phone.prototype.call = function(){
console.log("我可以打电话!!");
}

//实例化对象
let Huawei = new Phone('华为', 5999);
Huawei.call();
console.log(Huawei);



//用es6中class的方法来创建对象
class Shouji{
//构造方法,名字不能修改,必须叫constuctor
constructor(brand, price){
this.brand = brand;
this.price = price;
}
//方法必须使用该语法, 不能使用 es5 的对象完整形式
call(){
console.log("我可以打电话!!");
}
}

let onePlus = new Shouji("1+", 1999);
console.log(onePlus);

类的静态成员

和java一致,都是static,不多说了

类的继承

ES5构造函数的继承

复习一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function Phone(brand, price){
this.brand = brand;
this.price = price;
}

Phone.prototype.call = function(){
console.log("我可以打电话");
}

function SmartPhone(brand, price, color, size){ Phone.call(this, brand, price);
this.color = color;
this.size = size;
}

//设置子级构造函数的原型
SmartPhone.prototype = new Phone;
SmartPhone.prototype.constructor = SmartPhone;

//声明子类的方法
SmartPhone.prototype.photo = function(){
console.log("我可以拍照")
}
SmartPhone.prototype.playGame = function(){
console.log("我可以玩游戏");
}
const chuizi = new SmartPhone('锤子',2499,'黑色','5.5inch');
console.log(chuizi);

ES6-Class类继承

这个也和java一模一样…

class里的get和set

这里和java有一点点不一样,java的class没这么智能,得自己调用get和set方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// get 和 set  
class Phone{
get price(){
console.log("价格属性被读取了");
return 'iloveyou';
}
set price(newVal){//这个必须有参数
console.log('价格属性被修改了');
}
}
//实例化对象
let s = new Phone();
console.log(s.price);//输出iloveyou
s.price = 'free';//输出价格属性被修改了

模块化

重点又来了

模块化是指将一个大的程序文件,拆分成许多小的文件,然后将小文件组合起来

ES6 模块化语法

模块功能主要由两个命令构成:export 和 import

  • export 命令用于规定模块的对外接口
  • import 命令用于输入其他模块提供的功能

导入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//通用的导入方式
import * as m1 from "./src/js/m1.js";
console.log(m1);//这是个Module对象

import * as m2 from "./src/js/m2.js";
console.log(m2);

import * as m3 from "./src/js/m3.js";
console.log(m3);
m3.defalut.love();//只有默认暴露有个中间商default,其他的直接调用就可以
----------------------------------------------------分割线-----------------------------------------------------------------
//解构赋值的形式
import{school,love} from "./src/js/m1.js";
import{school as jiu,love as you} from "./src/js/m2.js";//由于重复冲突,需要使用别名
import{default as m3} from "./src/js/m3.js";//默认暴露必须使用别名,这是固定写法
console.log(school);
console.log(jiu);
console.log(m3);//这里就是默认暴露的东西了
----------------------------------------------------分割线-----------------------------------------------------------------
//简便形式(只能针对默认暴露)Vue中最常用
import m3 from "./src/js/m3.js"
console.log(m3);//这里也是默认暴露的东西了

暴露

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//分别暴露
在m1文件下
export let school = "jiu";

export function love(){
console.log("you");
}
//统一暴露
在m2文件下
let school = "jiu";
function love(){
console.log("you");
}

export {school,findJob}
//默认暴露
在m3文件下
export default{//一般默认暴露都是暴露对象,但也可以暴露别的
school = "jiu"
love:function(){
console.log("you");
}
}

引入具体方式

  • 内部脚本引入
1
2
3
<script type="module">
在这里面写import语句
</script>
  • 外部脚本引入
1
2
<script src="./src/js/app.js" type="module"></script>
然后在app.js文件里面写import语句

async和await

又是重点,不过到这里已经是ES8的内容了,并且涉及到一定的异步编程知识

async和await的出现是为了让异步代码像同步代码一样

async函数

  • async函数的返回值是Promise对象
  • Promise对象的结果有async函数执行的返回值决定
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//async函数定义
async function fn1(){
return "不想学习";
}
//可以抛错误
async function fn2(){
throw new Error("出错了!");
}
//也可以直接返回Promise对象
async function fn3(){
return new Promise(() => {
resolve("成功的数据");
});
}
const result1 = fn1();
const result2 = fn2();
const result3 = fn3();
console.log(result1);//value是返回的内容,状态成功
console.log(result2);//value是错误信息,状态失败
console.log(result3);//和async里的Promise对象一致

如上,输出的Promise对象里只要async函数return后面不是Promise对象且不是抛出错误,返回的Promise对象就是成功的;抛出错误肯定是失败的;async函数return后面的是Promise对象就和这个Promise的状态一致

await表达式

awiat表达式只能放在async函数里面,但async函数中可以没有await

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const p1 = new Promise((resolve,reject) => {
resolve("我是成功的值!");
})

const p2 = new Promise((resolve,reject) => {
reject("我是失败的值!");
})

async function main(){
let result1 = await p1;//await返回的结果就是Promise对象成功的值
console.log(result1)//输出 我是成功的值!
try{//失败就需要try catch才能抓住失败信息
let result2 = await p2;
}catch(e){
consolo.log(e);//输出 我是失败的值!
}
}
main();

结合ajax对比Promise和async,await组合区别

这里如果没学过前后端交互可以跳过,只是为了更方便理解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
function sendAJAX(url){
return new Promise(() => {
//创建对象
const x = new XMLHttpRequest();
//初始化
x.open('GET',url);
//发送
x.send();
//事件绑定
x.onreadystatechange = function(){
if(x.readystate === 4){
if(x.status >= 200 && x.status < 300){//200到299状态码才说明网络交互成功
//成功了
resolve(x.response);
}else{
//失败了
reject(x.status);
}
}
}
})
}
//使用Promise方法
sendAJAX("http://...").then(value =>{
console.log(value);
}, reason => {});

//使用async await组合
async function main(){
let result = await sendAJAX("http://...");
console.log(result);
}
//两个方法输出结果相同

私有属性

这是ES11新增的

1
2
3
4
5
6
7
function Person(name,age,weight){
this.name = name;
this.#age = age;
this.#weight = weight;
}
const girl = new Person("小红", 18, "45kg");
console.log(girl.#age);//无法访问,会告诉你这个是私有属性

ES6的class也是同理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Person{
//共有属性
name;
//私有属性,效果和java里面的private一致
#age;
#weight;
constructor(){
this.name = name;
this.#age = age;
this.#weight =weight;
}
}

const girl = new Person("小红",18,"45kg");
console.log(girl.#age);//无法这么访问必须在类里自己写get set方法

那么到这里本文就暂且告一段落了,如果没有学习前后端交互的可以去学习前后端交互,学过了建议直接跳去框架Vue等进行学习,在掌握了本文内容后框架的学习将变得格外轻松

欢迎大家评论区交流学习