1、原生AJAX
1.1、AJAX 简介
AJAX 全称为Asynchronous Javascript And XML,就是异步的 JS 和 XML。通过AJAX可以在浏览器中向服务器发送异步请求,最大的优势:页面无刷新获取数据。AJAX 不是新的编程语言,而是一种将现有的标准组合在一起使用的新方式。1.2、
1.2、XML简介
- XML 可扩展标记语言。
- XML 被设计用来传输和存储数据。
- XML和HTML类似,不同的是HTML中都是预定义标签,而XML中没有预定义标签,全都是自定义标签,用来表示一些数据。
实例:
// 用XML表示一个学生数据:
<student>
<name>孙悟空</name>
<age>18</age>
<gender>男</gender>
</student>
现在已经被JSON取代了。
//用JSON表示:
{"name":"孙悟空","age":18,"gender":"男"}
1.3、AJAX的特点
-
优点
- 可以无需刷新页面而与服务器端进行通信。
- 允许你根据用户事件来更新部分页面内容。
-
缺点
- 没有浏览历史,不能回退;
- 存在跨域问题;
- SEO不友好;
1.4、AJAX的使用
1.4.1 核心对象
- XMLHttpRequest,AJAX的所有操作都是通过该对象进行的。
1.4.2 使用步骤
-
创建XMLHttpRequest实例对象
const xhr = new XMLHttpRequest() -
设置请求信息
xhr.open(method,url) //配置请求 xhr.setRequestHeader(key,value)//设置请求头(可选) -
发送请求
xhr.send(body) //get请求不传body参数,只有post请求使用 -
接收响应
xhr.onreadystatechange = ()=>{ if(xhr.readyState === 4 && xhr.status === 200 ){ console.log(xhr.response) } }
1.4.3 解决IE缓存问题
-
问题:
- 在一些浏览器中(IE),由于缓存机制的存在,ajax只会发送的第一次请求,剩余多次请求不会在发送给浏览器而是直接加载缓存中的数据。
-
解决方式
- 浏览器的ajax缓存是根据url地址来记录的,所以我们只需要修改url地址即可避免缓存问题
-
xhr.open('get','url/t='+Date.now())
1.4.4 AJAX请求状态
xhr.readyState 可以用来查看请求当前的状态
| 状态码 | 描述 |
|---|---|
| 0 | 表示XMLHttpRequest实例已经生成,但open未调用。 |
| 1 | pen已调用,但send还未调用,此时仍然可以修改请求头信息。 |
| 2 | 表示send()方法已经执行,并且头信息和状态码已经收到。 |
| 3 | 表示正在接收服务器传来的部分数据。 |
| 4 | 表示数据已经接收完毕 |
1.5 AJAX常用API
-
追加响应头用于标识携带请求体参数的编码形式--json
xhr.setRequestHeader('Content-type','application/json') -
追加响应头用于标识携带请求体参数的编码形式--urlencoded
xhr.setRequestHeader('Content-type','application/x-www-form-urlencoded') -
用于指定返回数据的格式
xhr.responseType = 'json' -
配置出错的回调
xhr.onerror = ()=>{ alert('当前网络不稳定,请稍后重试'); } -
设定超时时间
xhr.timeout = 2000 -
超时的回调
xhr.ontimeout = ()=>{ alert('网速不给力,请切换网络重试'); } -
取消请求
xhr.abort()
1.6、和AJAX相关的后台知识
-
使用中间件解析urlencoded编码形式的请求体参数
app.use(express.urlencoded({extended:true}))//路由需要添加相关的响应头 app.get('/test_get',(request,response)=>{ console.log('有人请求test_get了--携带的query参数是:',request.query); response.setHeader('Access-Control-Allow-Origin','*') response.setHeader('Access-Control-Expose-Headers','*') response.send('hello_test_get') }) -
使用中间件解析json编码形式的请求体参数
app.use(express.json()) -
cors包可以解决数据接收编码格式问题(urlencoded和json)
const cors = require('cors') app.use(cors()) -
注意:接收params参数时,路由地址中需要有占位符
app.get('/test_get2/:name/:age',(request,response)=>{ console.log('有人请求test_get2',request.params); response.send('hello_test_get2') })
2、jQuery中的AJAX
2.1、get请求
$.get(url, [data], [callback], [type])
- url: 请求的URL地址。
- data: 请求携带的参数。
- callback: 载入成功时回调函数。
- type: 设置返回内容格式,xml, html, script, json, text, _default。
实例:
//使用jQuery发送ajax-get(完整版)
$.ajax({
url:'http://127.0.0.1:8080/test_jquery_get', //请求地址
method:'GET',//请求方式(默认值是GET)
data:{school:'atguigu'},//携带的数据
dataType:'json',//配置响应数据格式
timeout:2000,//指定超时的时间
success:(result,reponseText,xhr)=>{
console.log(result,reponseText,xhr);
},//成功的回调
error:(xhr)=>{console.log('请求出错了',xhr);} //失败的回调
})
//使用jQuery发送ajax-get(精简版)
$.get('http://127.0.0.1:8080/test_jquery_get',{school:'atguigu'},(data)=>{
console.log(data);
},'json')
2.2、post请求
$.post(url, [data], [callback], [type])
- url: 请求的URL地址。
- data: 请求携带的参数。
- callback: 载入成功时回调函数。
- type: 设置返回内容格式,xml, html, script, json, text, _default。
实例:
//使用jQuery发送ajax-post(完整版)
$.ajax({
url:'http://127.0.0.1:8080/test_jquery_post', //请求地址
method:'POST',//请求方式(默认值是GET)
data:{school:'atguigu'},//携带的数据
dataType:'json',//配置响应数据格式
timeout:2000,//指定超时的时间
success:(result,reponseText,xhr)=>{
console.log(result,reponseText,xhr);
},//成功的回调
error:(xhr)=>{console.log('请求出错了',xhr);} //失败的回调
})
//使用jQuery发送ajax-post(精简版)
$.post('http://127.0.0.1:8080/test_jquery_post',{school:'atguigu'},(data)=>{
console.log(data);
},'json')
3、跨域
3.1、同源策略
同源策略(Same-Origin Policy)由 Netscape (网景)公司提出,是浏览器的一种安全策略。
- 同源的规则: 协议、域名、端口号 必须完全相同。
- 违背同源策略又称:跨域。
3.2、如何解决跨域
3.2.1 JSONP
-
JSONP是什么
- JSONP(JSON with Padding),是一个非官方的跨域解决方案,纯粹凭借程序员的聪明才智开发出来,只支持get请求。
-
JSONP怎么工作的?
-
在网页有一些标签天生具有跨域能力,比如:img link iframe script等。
JSONP就是利用了script标签发送请求不受同源策略的限制的特点。
-
//前端代码
btn.onclick = ()=>{
//1.创建script节点
const scriptNode = document.createElement('script')
//2.给节点指定src属性(请求地址)
scriptNode.src = 'http://localhost:8080/test_jsonp? callback=peiqi'
//3.将节点放入页面
document.body.appendChild(scriptNode)
//4.准备好一个函数
window.peiqi = (a)=>{
console.log(a);
}
//5.移除已经使用过的script节点
document.body.removeChild(scriptNode)
}
//后端代码
app.get('/test_jsonp',(request,response)=>{
const {callback} = request.query
console.log(callback);
const person = [{name:'tom',age:18},{name:'老刘',age:5}]
response.send(`${callback}(${JSON.stringify(person)})`)
})
3.2.2 CORS(后端技术)
-
CORS是什么?
- CORS(Cross-Origin Resource Sharing),跨域资源共享。CORS是官方的跨域解决方案,它的特点是不需要在客户端做任何特殊的操作,完全在服务器中进行处理,支持所有常见的请求。
-
CORS怎么工作的?
- CORS是通过设置一个响应头来告诉浏览器,该请求允许跨域,浏览器收到该响应以后就会对响应放行。
-
服务器端常用设置
response.set("Access-Control-Allow-Origin", "*"); response.set("Access-Control-Expose-Headers", "*"); response.set("Access-Control-Allow-Methods", "*");备注:后端一般借助:cors库批量编写响应头
4 、补充知识
4.1 函数和回调
4.1.1 区别实例对象与函数对象
- 实例对象: new 函数产生的对象, 称为实例对象, 简称为对象
- 函数对象: 将函数作为对象使用时, 简称为函数对象
4.1.2 同步回调
- 理解: 立即执行, 完全执行完了才结束, 不会放入回调队列中
- 例子: 数组遍历相关的回调函数 / Promise的excutor函数
4.1.3 异步回调
- 理解: 不会立即执行, 会放入回调队列中将来执行
- 例子: 定时器回调 / ajax回调 / Promise的成功|失败的回调
4.2、js的error处理
4.2.1错误的类型
- Error: 所有错误的父类型
- ReferenceError: 引用的变量不存在
- TypeError: 数据类型不正确的错误
- RangeError: 数据值不在其所允许的范围内
- SyntaxError: 语法错误
4.2.2 错误处理
-
捕获错误: try ... catch
try { //这里写可能出现错误的代码 console.log(a); } catch (error) { console.log('@',error); } -
抛出错误: throw error
4.2.3 error对象的结构
- message属性: 错误相关信息
- stack属性: 记录信息
5 、Promise
5.1、Promise是什么?
-
抽象表达:
- Promise是一门新的技术(ES6提出的)
- Promise是JS中异步编程的新方案(旧方案是谁?---纯回调)
-
具体表达:
- 从语法上来说: Promise是一个内置构造函数
- 从功能上来说: Promise的实例对象可以用来封装一个异步操作,并可以获取其成功/失败的值
5.2、Promise基本使用
5.2.1、重要语法
- new Promise(executor) 构造函数
- Promise.prototype.then 方法
5.2.2、基本编码流程
-
创建Promise的实例对象(pending状态), 传入executor函数
-
在executor中启动异步任务(定时器、ajax请求)
-
根据异步任务的结果,做不同处理:
-
如果异步任务成功了:
- 我们调用resolve(value), 让Promise实例对象状态变为成功(fulfilled),同时指定成功的value
-
如果异步任务失败了:
- 我们调用reject(reason), 让Promise实例对象状态变为失败(rejected),同时指定失败的reason
-
-
通过then方法为Promise的实例指定成功、失败的回调函数,来获取成功的value、失败的reason(注意:then方法所指定的:成功的回调、失败的回调,都是异步的回调。)
5.2.3、关于状态的注意点:
-
三个状态
- pending: 未确定的------初始状态
- fulfilled: 成功的------调用resolve()后的状态
- rejected: 失败的-------调用reject()后的状态
-
两种状态改变
- pending ==> fulfilled
- pending ==> rejected
-
状态只能改变一次!!
-
一个promise指定多个成功/失败回调函数, 都会调用
5.3、用Promise封装一个简单的AJAX
function sendAjax(url,data){
return new Promise((resolve,reject)=>{
//实例xhr
const xhr = new XMLHttpRequest()
//绑定监听
xhr.onreadystatechange = ()=>{
if(xhr.readyState === 4){
if(xhr.status >= 200 && xhr.status < 300) resolve(xhr.response);
else reject('请求出了点问题');
}
}
xhr.send()
})
}
sendAjax('https://api.apiopen.top/getJoke')
.then(
(data)=>{console.log('成功了',data);},
(reason)=>{console.log('失败了',reason);}
)
5.4、Promise常用API
-
Promise构造函数:
- executor函数: 是同步执行的,(resolve, reject) => {}
- resolve函数: 调用resolve将Promise实例内部状态改为成功(fulfilled)。
- reject函数: 调用reject将Promise实例内部状态改为失败(rejected)。
- 说明: excutor函数会在Promise内部立即同步调用,异步代码放在excutor函数中。
//executor里面两个形参resolve、reject new Promise (executor) //实例 const p = new Promise((resolve, reject) => { resolve('成功') }) -
Promise.prototype.then方法: Promise实例.then(onFulfilled,onRejected)
- then方法会返回一个新的Promise实例对象
-
//实例 p.then( value => {console.log('成功了',value);}, reason => {console.log('失败了',reason);} )
-
Promise.prototype.catch方法: Promise实例.catch(onRejected)
- catch方法是then方法的语法糖, 相当于: then(undefined, onRejected)
-
//专用于失败的回调 p.catch( reason => {console.log('失败了',reason);} )
-
Promise.resolve方法: Promise.resolve(value)
- 说明: 用于快速返回一个状态为fulfilled或rejected的Promise实例对象
- 备注:value的值可能是:(1)非Promise值 (2)Promise值
-
const p0 = Promise.reject(-100) const p = Promise.resolve(p0) p.then( value => {console.log('成功了',value);}, reason => {console.log('失败了',reason);} )
-
Promise.reject方法: Promise.reject方法(reason)
- 说明: 用于快速返回一个状态必为rejected的Promise实例对象
-
const p0 = Promise.reject(100) const p = Promise.resolve(p0) p.then( value => {console.log('成功了',value);}, reason => {console.log('失败了',reason);} )
-
Promise.all方法: Promise.all(promiseArr)
- promiseArr: 包含n个Promise实例的数组
- 返回一个新的Promise实例, 只有所有的promise都成功才成功, 只要有一个失败了就直接失败。
-
const p1 = Promise.resolve('a') const p2 = new Promise((resolve,reject)=>{ setTimeout(()=>{ reject('b') },500) }) const p3 = new Promise((resolve,reject)=>{ setTimeout(()=>{ reject('c') },2000) }) Promise.all([p1,p2,p3]) .then( value => {console.log('成功了',value);}, reason => {console.log('失败了',reason);} )
-
Promise.race方法: Promise.race(promiseArr)
- promiseArr: 包含n个Promise实例的数组
- 返回一个新的Promise实例, 成功还是很失败?以最先出结果的promise为准。
-
const p1 = Promise.resolve('a') const p2 = new Promise((resolve,reject)=>{ setTimeout(()=>{ reject('b') },500) }) const p3 = new Promise((resolve,reject)=>{ setTimeout(()=>{ reject('c') },2000) }) Promise.all([p1,p2,p3]) .then( value => {console.log('成功了',value);}, reason => {console.log('失败了',reason);} )
5.5、Promise关键问题
5.5.1、如何改变一个Promise实例的状态?
- 执行resolve(value): 如果当前是pending就会变为fulfilled
- 执行reject(reason): 如果当前是pending就会变为rejected
- 执行器函数(executor)抛出异常: 如果当前是pending就会变为rejected
5.5.2、Promise实例.then()返回的是一个【新的Promise实例】,它的值和状态由什么决定?
-
简单表达: 由then()所指定的回调函数执行的结果决定
-
详细表达:
- 如果then所指定的回调返回的是非Promise值a,那么【新Promise实例】状态为:成功(fulfilled), 成功的value为a
- 如果then所指定的回调返回的是一个Promise实例p,那么【新Promise实例】的状态、值,都与p一致
- 如果then所指定的回调抛出异常,那么【新Promise实例】状态为rejected, reason为抛出的那个异常
const p = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('a')
},1000)
})
p.then(
value => {console.log('1',value); return Promise.reject('a')},
reason => {console.log('2',reason);}
).then(
value => {console.log('3',value);return true},
reason => {console.log('4',reason); return 100}
).then(
value => {console.log('成功了3',value);throw 900},
reason => {console.log('失败了3',reason); return false}
).then(
value => {console.log('成功了4',value);return -100},
reason => {console.log('失败了4',reason);}
)
5.5.3、Promise如何串连多个异步任务?
通过then的链式调用
//发送第1次请求
sendAjax('https://api.apiopen.top/getJoke',{page:1})
.then(
value => {
console.log('第1次请求成功了',value);
//发送第2次请求
return sendAjax('https://api.apiopen.top/getJoke',{page:1})
},
reason => {console.log('第1次请求失败了',reason);}
)
.then(
value => {
console.log('第2次请求成功了',value);
//发送第3次请求
return sendAjax('https://api.apiopen.top/getJoke',{page:1})
},
reason => {console.log('第2次请求失败了',reason);}
)
.then(
value => {console.log('第3次请求成功了',value);},
reason => {console.log('第3次请求失败了',reason);}
)
5.5.4、中断promise链
- 当使用promise的then链式调用时, 在中间中断, 不再调用后面的回调函数。
- 办法: 在失败的回调函数中返回一个pendding状态的Promise实例。
-
sendAjax('https://api.apiopen.top/getJoke',{page:1}) .then( value => { console.log('成功了',value); //发送第2次请求 return sendAjax('https://api.apiopen.top/getJoke2',{page:1}) }, reason => {console.log('失败',reason);return new Promise(()=>{})} )
5.5.5、promise错误穿透:
- 当使用promise的then链式调用时, 可以在最后用catch指定一个失败的回调,
- 前面任何操作出了错误, 都会传到最后失败的回调中处理了
- 备注:如果不存在then的链式调用,就不需要考虑then的错误穿透。
-
const p = new Promise((resolve,reject)=>{ setTimeout(()=>{ reject(-100) },1000) }) p.then( value => {console.log('成功了1',value);return 'b'}, reason => {throw reason}//这一行我们不用写,底层帮我们补上的这个失败的回调 ) .then( value => {console.log('成功了2',value);return Promise.reject(-108)}, reason => {throw reason}//这一行我们不用写,底层帮我们补上的这个失败的回调 ) .catch( reason => {throw reason} )
5.6、Promise的优势
-
指定回调函数的方式更加灵活:
- 旧的: 必须在启动异步任务前指定
- promise: 启动异步任务 => 返回promie对象 => 给promise对象绑定回调函数(甚至可以在异步任务结束后指定)
-
支持链式调用, 可以解决回调地狱问题
-
什么是回调地狱:
- 回调函数嵌套调用, 外部回调函数异步执行的结果是嵌套的回调函数执行的条件
-
回调地狱的弊病:
- 代码不便于阅读、不便于异常的处理
-
一个不是很优秀的解决方案:
- then的链式调用
-
终极解决方案:
- async/await(底层实际上依然使用then的链式调用)
-
6、async和await
6.1、async修饰的函数
- 函数的返回值为promise对象
- Promise实例的结果由async函数执行的返回值决定
6.2、await表达式
await右侧的表达式一般为Promise实例对象, 但也可以是其它的值
- 如果表达式是Promise实例对象, await后的返回值是promise成功的值
- 如果表达式是其它值, 直接将此值作为await的返回值
6.3、注意:
- await必须写在async函数中, 但async函数中可以没有await
- 如果await的Promise实例对象失败了, 就会抛出异常, 需要通过try...catch来捕获处理
//实例1
;(async()=>{
try {
const result1 = await p1
console.log(result1);
const result2 = await p2
console.log(result2);
const result3 = await p3
console.log(result3);
} catch (error) {
console.log(error);
}
})()
//实例2
function sendAjax(url,data){
return new Promise((resolve,reject)=>{
//实例xhr
const xhr = new XMLHttpRequest()
//绑定监听
xhr.onreadystatechange = ()=>{
if(xhr.readyState === 4){
if(xhr.status >= 200 && xhr.status < 300) resolve(xhr.response);
else reject(`请求出了点问题`);
}
}
xhr.send()
})
}
(async()=>{
try {
const result1 = await sendAjax('https://api.apiopen.top/getJoke',{page:1})
console.log('第1次请求成功了',result1);
const result2 = await sendAjax('https://api.apiopen.top/getJoke',{page:1})
console.log('第2次请求成功了',result2);
const result3 = await sendAjax('https://api.apiopen.top/getJoke',{page:1})
console.log('第3次请求成功了',result3);
} catch (error) {
console.log(error);
}
})()
6.4、补充
若我们使用async配合await这种写法:
- 表面上不出现任何的回调函数
- 但实际上底层把我们写的代码进行了加工,把回调函数“还原”回来了。
- 最终运行的代码是依然有回调的,只是程序员没有看见。
async function demo(){
//程序员“轻松”的写法
const result = await p
console.log(result);
console.log(100);
console.log(200);
//浏览器翻译后的代码
/* p.then(
result => {
console.log(result);
console.log(100);
console.log(200);
},
) */
}
demo()
console.log(1);
7、宏队列和微队列
宏队列: [宏任务1,宏任务2.....]
- 比如: 定时器回调/DOM事件回调/ajax回调
微队列: [微任务1,微任务2.....]
- 比如: promise的回调 MutationObserver的回调
规则:
-
每次要执行宏队列里的一个任务之前,先看微队列里是否有待执行的微任务
- 如果有,先执行微任务
- 如果没有,按照宏队列里任务的顺序,依次执行
//测试题
setTimeout(()=>{
console.log('timeout1')
Promise.resolve(5).then(
value => console.log('成功了5')
)
})
setTimeout(()=>{
console.log('timeout2')
})
Promise.resolve(3).then(
value => console.log('成功了3')
)
Promise.resolve(4).then(
value => console.log('失败了4')
)
8、HTTP补充
8.1、常见的响应状态码
| 状态码 | 描述 |
|---|---|
| 200 OK | 请求成功。一般用于GET与POST请求 |
| 201 Created | 已创建。成功请求并创建了新的资源 |
| 401 Unauthorized | 未授权/请求要求用户的身份认证 |
| 404 Not Found | 服务器无法根据客户端的请求找到资源 |
| 500 Internal Server Error | 服务器内部错误,无法完成请求 |
8.2、请求方式和请求参数
8.2.1、请求方式
| 方式 | 描述 |
|---|---|
| GET | 从服务器端读取数据 ----- 查(R) |
| POST | 向服务器端添加新数据 ------ 增(C) |
| PUT | 更新服务器端已存在的数据 ------- 改(U) |
| DELETE | 删除服务器端数据 ---------删(D) |
8.2.2、请求参数
-
query参数(查询字符串参数)
- 参数包含在请求地址中,格式为:/xxxx?name=tom&age=18
- 敏感数据不要用query参数,因为参数是地址的一部分,比较危险。
- 备注:query参数又称查询字符串参数,编码方式为urlencoded
-
params参数
- 参数包含在请求地址中,格式如下:http://localhost:3000/add_person /tom/18
- 敏感数据不要用params参数,因为参数是地址的一部分,比较危险。
-
请求体参数
-
参数包含在请求体中,可通过浏览器开发工具查看
-
常用的两种格式:
-
格式一:urlencoded格式
- 例如:name=tom&age=18
- 对应请求头:Content-Type: application/x-www-form-urlencoded
-
格式二:json格式
- 例如: {"name": "tom", "age": 12}
- 对应请求头:Content-Type: application/json
-
-
8.2.3、注意事项
-
GET请求不能携带请求体参数,因为GET请求没有请求体。
-
理论上一次请求可以随意使用上述3种类型参数中的任何一种,甚至一次请求的3个参数可以用3种形式携带,但一般不这样做。
-
一般来说我们有一些“约定俗成”的规矩:
- 例如form表单发送post请求时: 自动使用请求体参数,用urlencoded编码。
- 例如jQuery发送ajax-post请求时:自动使用请求体参数,用urlencoded编码。
-
开发中请求到底发给谁?用什么请求方式?携带什么参数?----要参考项目的API接口文档。
9、API相关
9.1、API的分类
-
REST API ( restful风格的API )
- 发送请求进行CRUD哪个操作由请求方式来决定
- 同一个请求路径可以进行多个操作
- 请求方式会用到GET/POST/PUT/DELETE
-
非REST API ( restless风格的 API )
- 请求方式不决定请求的CRUD操作
- 一个请求路径只对应一个操作
- 一般只有GET/POST
9.2、使用json-server搭建REST API
9.2.1、1.1.1. json-server是什么?
用来快速搭建REST API的工具包
9.2.2、1.1.1. 使用json-server
- 在线文档: github.com/typicode/js…
- 下载: npm i json-server -g
- 目标根目录下创建数据库json文件
- 启动服务器执行命令: json-server filename.json
9.2.3、使用浏览器访问测试
9.2.4、使用postman测试接口
测试GET / POST / PUT / DELETE请求
9.3、一般http请求与ajax请求
-
ajax请求是一种特别的http请求
-
对服务器端来说, 没有任何区别, 区别在浏览器端
-
浏览器端发请求: 只有XHR或fetch发出的才是ajax请求, 其它所有的都是非ajax请求
-
浏览器端接收到响应
- 一般请求: 浏览器一般会直接显示响应体数据, 也就是我们常说的自动刷新/跳转页面
- ajax请求: 浏览器不会对界面进行任何更新操作, 只是调用监视的回调函数并传入响应相关数据
10、axios
10.1、axios是什么?
- 前端最流行的ajax请求库
- react / vue官方都推荐使用axios发ajax请求
- 文档: github.com/axios/axios
10.2、axios特点
- 基本promise的异步ajax请求库
- 浏览器端/node端都可以使用
- 支持请求/响应拦截器
- 支持请求取消
- 请求/响应数据转换
- 批量发送多个请求
10.3、使用axios发ajax请求
-
//(完整版) axios({ //请求地址, url:'http://localhost:5000/person', //携带params参数 url:`http://localhost:5000/person/${value}` // 请求方式GET POST PUT DELETE method:'GET', //请求携带query参数 params:{id:personId.value} , //请求体,(json编码) data:{id: value,name: value,age: value}, //请求体,(urlencoded编码) //data:`name=${value}&age=${value}` }).then( response => {console.log('成功了',response.data);}, error => {console.log('失败了',error);} ) -
//精简版 axios.get( 'http://localhost:5000/person', //携带pramas参数 `http://localhost:5000/person/${value}` //携带query参数 {params:{id:personId.value}}) //请求体,携带json编码参数 {id:value,name:value,age:value} //请求体,携带urlencoded编码参数 `name=${value}&age=${value}` .then( response => {console.log('成功了',response.data);}, error => {console.log('失败了',error);} )
10.4、axios常用语法
| api | 描述 |
|---|---|
| axios(config): | 通用/最本质的发任意类型请求的方式 |
| axios(url, config]): | 可以只指定url发get请求 |
| axios.request(config) | 等同于axios(config) |
| axios.get(url, config]): | 发get请求 |
| axios.post(url, data, config]): | 发post请求 |
| axios.put(url, data, config]): | 发put请求 |
| axios.delete(url, config]): | 发delete请求 |
| axios.defaults.xxx = xxx; | 请求的默认全局配置 |
| axios.interceptors.request.use(): | 添加请求拦截器 |
| axios.interceptors.response.use(): | 添加响应拦截器 |
| axios.create([config]): | 创建一个新的axios |
| axios.CancelToken(): | 用于创建取消请求的token对象 |
| axios.isCancel(): | 是否是一个取消请求的错误 |
| axios.all(promises): | 用于批量执行多个异步请求 |
10.5、axios.create(config)
-
根据指定配置创建一个新的axios, 也就就每个新axios都有自己的配置
-
新axios只是没有取消请求和批量发请求的方法, 其它所有语法都是一致的
-
为什么要设计这个语法?
- 需求: 项目中有部分接口需要的配置与另一部分接口需要的配置不太一样, 如何处理
- 解决: 创建2个新axios, 每个都有自己特有的配置, 分别应用到不同要求的接口请求中
const axios2 = axios.create({
timeout:3000,
//headers:{name:'tom'},
baseURL:'https://api.apiopen.top'
})
//给axios配置默认属性
axios.defaults.timeout = 2000
axios.defaults.headers = {school:'atguigu'}
axios.defaults.baseURL = 'http://localhost:5000'
10.6、axios拦截器
10.6.1、axios请求拦截器
-
是什么?
- 在真正发请求前执行的一个回调函数
-
作用:
- 对所有的请求做统一的处理:追加请求头、追加参数、界面loading提示等等
axios.interceptors.request.use((config)=>{ console.log('请求拦截器1执行了'); if(Date.now() % 2 === 0){ config.headers.token = 'atguigu' } return config })
10.6.2、axios响应拦截器
-
是什么?
- 得到响应之后执行的一组回调函数
-
作用:
- 若请求成功,对成功的数据进行处理
- 若请求失败,对失败进行统一的操作
axios.interceptors.response.use( response => { console.log('响应拦截器成功的回调执行了',response); if(Date.now() % 2 === 0) return response.data else return '时间戳不是偶数,不能给你数据' }, error => { console.log('响应拦截器失败的回调执行了'); alert(error); return new Promise(()=>{}) } )
10.7、axios取消请求
10.7.1、基本流程
- 配置cancelToken对象
- 缓存用于取消请求的cancel函数
- 在后面特定时机调用cancel函数取消请求
- 在错误回调中判断如果error是cancel, 做相应处理
10.7.2、实现功能
- 点击按钮,取消某个正在请求中的请求
- 在请求一个接口前, 取消前面一个未完成的请求
const {CancelToken,isCancel} = axios //CancelToken能为一次请求“打标识”
let cancel
axios.interceptors.request.use((config)=>{
if(cancel) cancel('取消了')
config.cancelToken = new CancelToken((c)=> cancel= c)
return config
})
axios.interceptors.response.use(
response => {return response.data},
error => {
if(isCancel(error)){
//如果进入判断,证明:是用户取消了请求
console.log('用户取消了请求,原因是:',error.message);
}else{
console.log('失败了',error);
}
return new Promise(()=>{})
}
)
btn.onclick = async()=>{
const result = await axios.get('http://localhost:5000/test1?delay=3000')
console.log(result);
}
btn2.onclick = ()=>{
cancel('任性,就是不要了')
}
10.8、axios批量发送请求
btn.onclick = async()=>{
axios.all([
axios.get('http://localhost:5000/test1'),
axios.get('http://localhost:5000/test2?delay=3000'),
axios.get('http://localhost:5000/test3'),
]).then(
response => {console.log(response);},
error => {console.log(error);}
)
}
11、JavaScript模块化
11.1、模块化入门
11.1.1、理解什么是模块
- 将一个复杂的程序依据一定的规则拆分成单个文件, 并最终组合在一起
- 这些拆分的文件就是模块,模块内部数据是私有的, 只是向外部暴露一些方法与外部其它模块通信
11.1.2、为什么要模块化?
- 降低复杂度,提高解耦性
- 避免命名冲突
- 更好的分离, 按需加载
- 更高复用性,高可维护性
11.1.3、模块化概念带来的问题
- 请求过多
- 依赖模糊
- 难以维护
11.2、模块化规范
前言:一个大的项目必定会使用模块化技术,使用模块化就会使用相应的模块化规范,现在比较流行的模块化规范有以下2种:CommonJS、ES6
11.3、 CommonJS
11.3.1、规范
- 官网:wiki.commonjs.org/wiki/Module…
- 每个文件都是一个模块。
- CommonJS模块化的代码既可在服务端运行,也可在浏览器端运行
- 服务器端: 模块化的代码可直接运行。
- 浏览器端: 模块化的代码要经过Browserify(browserify.org)编译。
11.3.2、基本语法
-
暴露模块:
module.exports = value //第一种方式 exports.xxx = value //第二种方式 -
引入模块
require(xxx) //引入第三方模块 xxx为模块名 require(xxx) //引入自定义模块 xxx为模块文件路径
11.3.3、编写自定义板块
-
module1.js
//使用module.exports暴露一个加法函数 module.exports = function increment(a,b){ console.log(a+b); } -
module2.js
//使用exports.xxxx暴露一个减法函数、再暴露一个乘法函数 exports.decrement = function (a,b){ console.log(a-b); } exports.mul = function (a,b){ console.log(a*b); } -
module3.js
//使用module.exports暴露一个对象 module.exports = { data:'atguigu', demo(){ console.log('module3---demo1'); } }
11.3.4、下载第三方模块
-
执行命令:
npm i uniq
11.3.5、主文件引入模块
-
app.js
const module1 = require('./module1') const module2 = require('./module2') const {data,demo} = require('./module3') const uniq = require('uniq') module1(1,2) module2.decrement(3,4) module2.mul(5,6) demo() console.log(data); const result = uniq([1,2,3,4,4,4,3,5,8,8,9,7,6,10,11]) console.log(result);
11.3.6、运行app.js
-
【在Node环境下运行】,直接使用命令:
node app.js -
【在浏览器环境下运行】,需要如下操作:
-
全局安装Browserify
npm install browserify -g -
编译指定文件
browserify a.js -o build.js -
html页面中引入build.js
-
11.4、ES6模块化规范
11.4.1、规范
- 每个文件都是一个模块。
- 要借助Babel和Browserify依次编译代码,才能在浏览器端运行。
- Babel中文网:www.babeljs.cn/
11.4.2、基本语法
-
暴露模块
-
分别暴露: export 暴露内容
export const data = 'atguigu' export const msg = 'hello,0826' export function showData(){ console.log(data); } -
统一暴露: export { 暴露内容1, 暴露内容2 }
const school = '尚硅谷' function getLaoliu(){ console.log(person); } export {school,getLaoliu} -
默认暴露: export defalut 暴露内容(只能暴露一次!!!!)
export default { name:"wc", age:5, }
-
-
引入模块
-
方法1 : import{xxx,yyy} from './module1'
-
方法2:importmodule3 from './module3'
//引入【分别暴露】的模块 import {data,showData,showMsg} from './module1' //引入【分别暴露】的模块+重命名 import {data as data2} from './module2' //引入【分别暴露】的模块+打包引入 import * as module1 from './module1' //引入【统一暴露】的模块(统一暴露和分别暴露,最后引入的方式都是一样的) import {school,person,getLaoliu} from './module3' //引入【默认暴露】的模块 import module4 from './module4' //引入多种暴露方式的模块 import module5,{teacher1,teacher2,stu1,stu2} from './module5'
-
-
使用规则
- 若使用分别暴露、统一暴露的方式暴露内容,那么就要用方法1引入。
- 若使用默认暴露的方式暴露内容,那么就要用方法2引入。
11.4.3、ES6模块化教程
1.编写自定义模块
-
建立文件结构:
-src -module1.js -module2.js -module3.js -module4.js -app.js -
module1.js,使用分别暴露
//module1中,我们使用【分别暴露】 export const data = 'atguigu' export const msg = '你好,0826!' -
module2.js,使用统一暴露
let arr = [1,3,5,7,9] function showPerson(){ console.log('module2中的人为:',person); } export {arr,showPerson} -
module3.js,使用默认暴露
const schoolInfo = { name:'尚硅谷', address:'北七家镇-宏福科技园', subject:'前端·大数据·java' } export default schoolInfo -
module4.js,各种暴露方式混用
//分别暴露 export let date = '2020年11月20日' export function showRandom() { console.log(Math.random); } //统一暴露 let str = 'hello,atguigu' let dog = {name:'旺财',age:4} export {str,dog} //默认暴露 export default ['刺激战场','穿越火线','王者农药']
2.下载第三方模块
-
执行命令(以uniq为例):
npm i uniq
3.主文件引入
-
app.js引入各个模块
//在es6的模块化规范中,用哪一种方式引入,取决于用何种方式暴露的。 //引入【分别暴露】的模块方法1 import {data,msg,showData,showMsg} from './module1' //引入【分别暴露】的模块方法2 import * as obj from './module1' //引入【统一暴露】的模块方法1 import {showArr,showPerson} from './module2' //引入【统一暴露】的模块方法2 import * as obj from './module2' //引入【默认暴露】的模块 import module3 from './module3' //引入module4,module4里用了多种暴露的方式 import games,{date,showRandom,str,dog} from './module4' //引入uniq第三方模块 import uniq from 'uniq'
4.准备相关依赖包(为编译代码做准备)
-
全局安装:babel-cli、Browserify,执行命令如下:
npm install babel-cli browserify -g- 局部安装:babel-preset-es2015
npm install babel-preset-es2015 -
定义.babelrc文件
{ "presets": ["es2015"] }
5.编译代码
-
进入目录,使用Babel将ES6编译为ES5代码 :
babel ./src -d ./build -
继续使用Browserify编译上一步的js :
browserify ./build/app.js -o ./build/index.js
6.页面中引入测试
<script type="text/javascript" src="./build/index.js"></script>
12、webpack
12.1.、初识Webpack
-
什么是webpack?
- Webpack是一个模块打包器。
- 在Webpack中会将前端的所有资源文件(js/json/css/img/less/...)都作为模块处理。
- 它将根据模块的依赖关系进行分析,生成对应的资源
-
五个核心概念:
- 【入口(entry)】 :指示 webpack 应该使用哪个模块,来作为构建其内部依赖图的开始。
- 【输出(output)】 :在哪里输出文件,以及如何命名这些文件。
- 【Loader】 :处理那些非 JavaScript 文件(webpack 自身只能解析 JavaScript和json)。
- 【插件(plugins)】 执行范围更广的任务,从打包到优化都可以实现。
- 【模式(mode)】 ,有生产模式production和开发模式development
-
对loader的理解
- webpack 本身只能处理JS、JSON模块,如果要加载其他类型的文件(模块),就需要使用对应的loader 。
- 它本身是一个函数,接受源文件作为参数,返回转换的结果。
- loader 一般以 xxx-loader 的方式命名,xxx 代表了这个 loader 要做的转换功能,比如 css-loader。
-
对plugins的理解
- 插件可以完成一些loader不能完成的功能。
-
配置文件
- webpack.config.js : 用于存储webpack配置信息。
12.2、开启项目
-
初始化项目:
-
使用
npm init或yarn init生成一个package.json文件{ "name": "webpack_test", "version": "1.0.0" }
-
-
安装webpack
-
npm install webpack@4 webpack-cli@3 -g //全局安装,作为指令使用 -
npm install webpack@4 webpack-cli@3 -D //本地安装,作为本地依赖使用
-
12.3、 处理js和json文件
-
创建js文件
- src/js/app.js
- src/js/module1.js
- src/js/module2.js
-
创建json文件
- src/json/data.json
-
创建主页面:
- src/index.html
-
运行指令
-
打包指令(开发):
webpack ./src/js/app.js -o ./build/js/app.js --mode=development- 功能: webpack能够打包js和json文件,并且能将es6的模块化语法转换成浏览器能识别的语法
-
打包指令(生产):
webpack ./src/js/app.js -o ./build/js/app.js --mode=production- 功能: 在开发配置功能上加上一个压缩代码的功能
-
index.html页面中引入:build/js/app.js
-
-
结论:
- webpack能够编译打包js和json文件
- 能将es6的模块化语法转换成浏览器能识别的语法
- 能压缩代码
-
缺点:
- 不能编译打包css、img等文件
- 不能将js的es6基本语法转化为es5以下语法
-
改善:使用webpack配置文件解决,自定义功能
12.4、 webpack配置文件
- 目的:在项目根目录定义配置文件,通过自定义配置文件,还原以上功能
- 文件名称:webpack.config.js
- 文件内容:
const { resolve } = require('path'); //node内置核心模块,用来设置路径。
module.exports = {
entry: './src/js/app.js', // 入口文件配置(精简写法)
/*完整写法:
entry:{
main:'./src/js/app.js'
}
*/
output: { //输出配置
filename: './js/app.js',//输出文件名
path: resolve(__dirname, 'build')//输出文件路径(绝对路径)
},
mode: 'development' //开发环境(二选一)
//mode: 'production' //生产环境(二选一)
};
- 运行指令: webpack
12.5、 打包less、css资源
-
概述:less、css文件webpack不能解析,需要借助loader编译解析
-
创建less文件
- src/css/demo1.less
- src/css/demo2.css
-
入口app.js文件
- 引入less、css资源
-
安装loader
-
npm install css-loader style-loader less-loader less -D
-
-
配置loader
{ // 处理less资源 test: /.less$/, use: [ 'style-loader', //创建style标签,将js中的样式资源插入进行,添加到head中生效 'css-loader', //将css文件变成commonjs模块加载js中,里面内容是样式字符串 'less-loader' //将less文件编译成css文件 ] }, { // 处理css资源 test: /.css$/, use: [ // use数组中loader执行顺序:从右到左,从下到上 依次执行 'style-loader',// 创建style标签,将js中的样式资源插入进行,添加到head中生效 'css-loader'// 将css文件变成commonjs模块加载js中,里面内容是样式字符串 ] }, -
运行指令:webpack
12.6、 打包html文件
-
概述:借助html-webpack-plugin插件打包html资源
-
创建html文件
- src/index.html
- 注意:不要在该html中引入任何css和js文件
-
安装插件:html-webpack-plugin
-
npm install html-webpack-plugin -D
-
-
在webpack.config.js中引入插件(插件都需要手动引入,而loader会自动加载)
-
const HtmlWebpackPlugin = require('html-webpack-plugin')
-
-
配置插件Plugins
plugins: [ new HtmlWebpackPlugin({ // 以当前文件为模板创建新的HtML(1. 结构和原来一样 2. 会自动引入打包的资源) template: './src/index.html', }), ] -
运行指令:webpack
12.7、 打包样式中的图片
-
概述:图片文件webpack不能解析,需要借助loader编译解析
-
添加2张图片:
- 小图, 小于8kb: src/images/vue.png
- 大图, 大于8kb: src/images/react.jpg
-
在less文件中通过背景图的方式引入两个图片
-
安装loader
-
npm install file-loader url-loader file-loader -D - 补充:url-loader是对象file-loader的上层封装,使用时需配合file-loader使用。
-
-
配置loader
{ // 处理图片资源 test: /.(jpg|png|gif)$/, loader: 'url-loader', //url-loader是对file-loader的上层封装 options: { limit: 8 * 1024, //临界值为8KB,小于8KB的图片会被转为base64编码 name: '[hash:10].[ext]', //加工后图片的名字 outputPath: 'imgs' //输出路径 } },
12.8、打包html中的图片
-
概述:html中的
<img>标签url-loader没法处理,需要引入其他loader处理。 -
添加图片
- 在src/index.html添加一个img标签,src/images/angular.png
-
安装loader
-
npm install html-loader --save-dev
-
-
配置loader
{ // 处理html中<img>资源 test: /.html$/, loader: 'html-loader' }, -
可能出现的坑:打包后html文件中的图片的src变成了:[object Module],
-
解决办法:url-loader中加入一个配置:esModule:false即可
12.9、打包其他资源
-
概述:其他资源(字体、音视频等)webpack不能解析,需要借助loader编译解析
-
以处理几个字体图标的字体为例,font下添加几个下载好的字体文件:
- src/font/iconfont.eot
- src/font/iconfont.svg
- src/font/iconfont.ttf
- src/font/iconfont.woff
- src/font/iconfont.woff2
-
修改incofont.css中字体的url
@font-face { font-family: 'iconfont'; src: url('../media/iconfont.eot'); src: url('../media/iconfont.eot?#iefix') format('embedded-opentype'), url('../media/iconfont.woff2') format('woff2'), url('../media/iconfont.woff') format('woff'), url('../media/iconfont.ttf') format('truetype'), url('../media/iconfont.svg#iconfont') format('svg'); } .iconfont { font-family: "iconfont" !important; font-size: 16px; font-style: normal; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } -
修改html,添加字体图标,例如:
<span class="iconfont icon-icon-test"></span> -
配置loader
{ // 处理其他资源(字体、音视频等等) exclude: /.(html|js|css|less|jpg|png|gif)/, //排除的文件 loader: 'file-loader', options: { name: '[hash:10].[ext]', //命名 outputPath: 'media' //输出路径 } } -
运行指令:webpack
12.10、devServer
-
安装webpack-dev-server
-
npm i webpack-dev-server -D -
npm i webpack-dev-server -g - 详细配置见官网:指南 -> 开发环境 -> 使用webpack-dev-server
-
-
修改webpack配置对象,追加devServer配置(注意不是loader中追加)
//devServer配置(开发模式所特有的配置) devServer: { contentBase: resolve(__dirname, 'build'),//本地打包文件的位置 port: 3000, //端口号 open: true //自动打开浏览器 } -
修改package.json中scripts指令
"dev": "webpack-dev-server",
-
运行指令:npm run dev 或者 yarn dev
至此,你已经完成了用webpack搭建一个简单的开发环境,但这套配置只适用于开发过程中调试代码,项目上线并不能运用这套配置,因为你还有很多的问题没有处理,比如:css还不是单独的文件,css、js还有很多兼容性问题等等,接下来我们开始去搭建生产环境。
13、webpack生产环境准备
1.新建config文件夹,重命名webpack.config.js为webpack.dev.js,放入config文件夹 2.复制webpack.dev.js,重命名为webpack.prod.js,删除其中的devServer配置,因为这是开发环境特有的,生产环境是不需要的 3.修改package.json中scripts指令:
"start": "webpack-dev-server --config ./config/webpack.dev.js",
"build": "webpack --config ./config/webpack.prod.js"
4.修改output中path为:path: resolve(__dirname, '../build')
13.1、提取css为单独文件
-
安装插件
-
npm install mini-css-extract-plugin -D
-
-
引入插件
-
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
-
-
配置loader
//引入mini-css-extract-plugin,用于提取css为单独文件 const MiniCssExtractPlugin = require('mini-css-extract-plugin'); { // 处理less资源 test: /.less$/, use: [ MiniCssExtractPlugin.loader, 'css-loader', 'less-loader', ] }, { // 处理css资源 test: /.css$/, use: [ MiniCssExtractPlugin.loader, 'css-loader', ] } -
配置插件
//提取css为单独文件 new MiniCssExtractPlugin({ // 对输出的css文件进行重命名 filename: 'css/built.css', }) -
运行指令
-
npm run build
-
-
备注:由于在提取了独立的文件,要从外部引入,所以可能会有路径问题,解决方案是在output配置中,添加:
publicPath:'/'publicPath根据实际情况自行调整,若上线运行值为:/imgs,若本地右键运行值为:/build/imgs
13.2、css兼容性处理
-
安装loader
-
npm install postcss postcss-loader postcss-preset-env -D
-
-
因为css和less样式文件都要进行兼容性处理,所以我们定义好一个通用的配置:
// 配置一个commonCssLoader,处理less和css时都会使用
const commonCssLoader = [
MiniCssExtractPlugin.loader, //提取css为单独的文件
'css-loader', //将css文件变成commonjs模块加载js中,里面内容是样式字符串
{
//注意:想让postcss-loader工作,还需在package.json中定义browserslist配置兼容程度
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: () => [require('postcss-preset-env')()]
}
}
];
- 修改css-loader和less-loader配置
{
// 处理css资源
test: /.css$/,
use: [...commonCssLoader]
},
{
// 处理less资源
test: /.less$/,
use: [...commonCssLoader, 'less-loader']
},
-
配置package.json,在其中追加browserslist配置,通过配置加载指定的css兼容性样式
"browserslist": { // 开发环境 "development": [ "last 1 chrome version", "last 1 firefox version", "last 1 safari version" ], // 生产环境:默认是生产环境 "production": [ ">0.2%", //兼容市面上99.8%的浏览器 "not dead", //"死去"的浏览器不做兼容,例如IE8 "not op_mini all",//不做opera浏览器mini版的兼容 "ie 10" //兼容IE10 ] } -
备注1: browserslist 是一套描述产品目标运行环境的工具,它被广泛用在各种涉及浏览器/移动端的兼容性支持工具中,详细配置规则参考:github.com/browserslis…
-
备注2:若出现版本不兼容,或配置不正确的错误,那么需更换依赖包版本:
npm i less-loader@5 postcss-loader@3 -
运行指令:
-
npm run build
-
13.3、JS语法检查
-
概述:对js基本语法错误/隐患,进行提前检查
-
安装loader
-
npm install eslint-loader eslint -D
-
-
安装检查规则库:
-
npm install eslint-config-airbnb-base eslint-plugin-import -D
-
-
备注:eslint-config-airbnb-base定制了一套标准的、常用的js语法检查规则,推荐使用
-
配置loader
module: { rules: [ { // 对js进行语法检查 test: /.js$/, exclude: /node_modules/,//排除这个文件 // 优先执行 enforce: 'pre',//优先执行 只要webpack启动时 尽可能先执行 可不写 loader: 'eslint-loader', options: { fix: true //若有问题自动修复,重要!!!! } } ] } -
修改package.json 库的底层实现
"eslintConfig": { "extends": "airbnb-base", //直接使用airbnb-base提供的规则 需要下载的 "env": { "browser": true//如果运行环境不是浏览器 则运行环境为node 此时需要将这个改为false } } -
运行指令:webpack
-
备注:若出现:warning Unexpected console statement no-console警告,意思是不应该在项目中写console.log(),若想忽略,就在要忽略检查代码的上方输入一行注释:// eslint-disable-next-line即可。
琳-黄色的警告证明打印了log 可在上面加eslint-disable-next-line 也可以不管它
琳-红色
13.4、 js语法转换
-
概述:将浏览器不能识别的新语法转换成原来识别的旧语法,做浏览器兼容性处理
-
安装loader
- 琳--webpack4版本以上可直接安装 3版本的还需定义@后面的版本 借鉴官网
-
npm install babel-loader @babel/core @babel/preset-env -D
-
配置loader
module: { rules: [ { test: /.js$/, exclude: /node_modules/, use: { loader: "babel-loader", options: { presets: ['@babel/preset-env'] } } } ] } -
运行指令:webpack
13.5、JS兼容性处理
-
安装包
-
npm install @babel/polyfill琳-用于高级兼容 可以解决babel中无法解决的 大项目没有不用的 记住 可以解决ie中不认识promise
琳-在app.js中引入
-
-
使用
- app.js import '@babel/polyfill'; // 包含ES6的高级语法的转换
13.6、压缩html、压缩js
- 直接修改webpack.prod.js中的model为production即可。
- 备注:若设置了模式为production,必须在new HtmlWebpackPlugin时添加配置minify: false:
new HtmlWebpackPlugin({
// 以当前文件为模板创建新的HtML(1. 结构和原来一样 2. 会自动引入打包的资源)
template: './src/index.html',
minify: false
}),
13.7、压缩css
webpack 4.0版本以上可以自动压缩html js 不能压缩css optimize优化 assets静态资源
-
安装插件
npm install optimize-css-assets-webpack-plugin -D -
引入插件
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin'); -
配置插件
new OptimizeCssAssetsPlugin({ cssProcessorPluginOptions: { preset: ['default', { discardComments: { removeAll: true } }],//移出所有注释 }, })以上就是webpack生产环境的配置,可以生成打包后的文件。
14、webpack总结
14.1、webpack配置文件
module.exports = {
entry: '', //入口
output: {},//输出
module: { //配置loader
rules: []
},
plugins: [], //配置plugin
model:工作模式
}
14.2、webpack打包的基本流程
- 连接: webpack从入口JS开始,递归查找出所有相关的模块, 并【连接】起来形成一个图(网)的结构
- 编译: 将JS模块中的模块化语法【编译】为浏览器可以直接运行的模块语法(当然其它类型资源也会处理)
- 合并: 将图中所有编译过的模块【合并】成一个或少量的几个文件, 浏览器真正运行是打包后的文件
14.3、比较loader与plugins
- loader: 用于加载特定类型的资源文件, webpack本身只能打包js和json。
- plugin: 用来扩展webpack其它方面的功能, 一般loader处理不了的资源、完成不了的操作交给插件处理。
14.4、live-reload(自动刷新)与HMR(热模替换)
- 相同点: 代码修改后都会自动重新编译打包
- 不同点: live-reload: 刷新整体页面, 从而查看到最新代码的效果, 页面状态全部都是新的。 HMR: 没有刷新整个页面, 只是加载了修改模块的打包文件并运行, 从而更新页面的局部界面, 整个界面的其它部分的状态还在
14.5、webpack常用loader与plugin汇总
常用loader:
1.【less-loader】用于将less文件翻译成为css
2.【css-loader】用于将css以CommonJs语法打包到js中
3.【style-loader】用于动态创建一个style标签,将css引入页面
4.【file-loader】用于处理其他资源,也可以处理图片资源,核心操作就是:提取资源到指定位置,且可修改文件名等操作。
5.【url-loader】与file-loader功能几乎一致,优势是可以对图片进行动态转换base64编码(控制limit属性值可以控制阈值)。
备注:上述两个loader中url-loader应用比file-loader广泛,且url-loader是file-loader的上层封装。
6.【html-loader】用于处理html中<img>标签的图片。
6.【esint-loader】对项目中的js语法进行检查。
7.【babel-loader】将es6语法转换为es5语法
备注1:直接使用只能处理简单的语法,Promise等无法处理。
备注2:借助polyfill完成高级es6语法的转换,缺点:所有都转换,无法按需转换,生成的js体积大。
备注3:使用core-js配合polyfill完成按需转换。
8.【postcss-loader】经典的loadr,用于处理css兼容性问题,需要与【postcss】和【postcss-preset-env】配合使用
备注1:使用的时机为:["css-loader","postcss-loader","less-loader"]。
备注2:需要在package.json中配置browserslist属性指定具体的兼容规则
备注3:browserslist是一个单独的库,被广泛用在各种涉及浏览器/移动端的兼容性支持工具中
常用pulgins:
1.【mini-css-extract-plugin】:用于提取项目中的css为一个单独的文件。
备注1:使用的时机为:[MiniCssExtractPlugin.loader,"css-loader","postcss-loader",less-loader"]。
备注2:使用是可以在new时传入具体配置例如:
{
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: ["postcss-preset-env"]
}
}
}
2.【html-webpack-plugin】:根据指定模板,自动创建html文件,且自动引入外部资源
3.【eslint-plugin-import】:用于配合eslint-loader
4.【eslint-config-airbnb-base】用于引入airbnb配置好的语法检查规则,否则需要一个一个配置,比较麻烦
5.【@babel/polyfill】用于处理JS兼容性问题,例如IE浏览器不支持Promise
6.【optimize-css-assets-webpack-plugin】用于压缩css
14.6. webpack中的tree-shaking
-
概述:有些时候,我们一个模块向外暴露了n个函数、对象、或其他一些数据,但是我们只是用到了其中的一个或几个,那在最终打包的时候,我们只希望把我们所用的打包进去,这时候就要tree-shaking,即:去除无用代码。
-
配置:同时满足两个条件webpack会自动开启tree-shaking
- 使用ES6模块化
- 开启production环境