原生ajax+axios请求库+webpack打包器

210 阅读36分钟

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的特点

  • 优点

    1. 可以无需刷新页面而与服务器端进行通信。
    2. 允许你根据用户事件来更新部分页面内容。
  • 缺点

    1. 没有浏览历史,不能回退;
    2. 存在跨域问题;
    3. SEO不友好;

1.4、AJAX的使用

1.4.1 核心对象
  • XMLHttpRequest,AJAX的所有操作都是通过该对象进行的。
1.4.2 使用步骤
  1. 创建XMLHttpRequest实例对象

    const xhr = new XMLHttpRequest()
    
  2. 设置请求信息

    xhr.open(method,url) //配置请求
    xhr.setRequestHeader(key,value)//设置请求头(可选)
    
  3. 发送请求

    xhr.send(body) //get请求不传body参数,只有post请求使用
    
  4. 接收响应

    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未调用。
1pen已调用,但send还未调用,此时仍然可以修改请求头信息。
2表示send()方法已经执行,并且头信息和状态码已经收到。
3表示正在接收服务器传来的部分数据。
4表示数据已经接收完毕

1.5 AJAX常用API

  1. 追加响应头用于标识携带请求体参数的编码形式--json

    xhr.setRequestHeader('Content-type','application/json')
    
  2. 追加响应头用于标识携带请求体参数的编码形式--urlencoded

    xhr.setRequestHeader('Content-type','application/x-www-form-urlencoded') 
    
  3. 用于指定返回数据的格式

    xhr.responseType = 'json'
    
  4. 配置出错的回调

    xhr.onerror = ()=>{
        alert('当前网络不稳定,请稍后重试');
    }
    
  5. 设定超时时间

    xhr.timeout = 2000 
    
  6. 超时的回调

    xhr.ontimeout = ()=>{
    	alert('网速不给力,请切换网络重试');
    }
    
  7. 取消请求

    xhr.abort()
    

1.6、和AJAX相关的后台知识

  1. 使用中间件解析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')
    })
    
  2. 使用中间件解析json编码形式的请求体参数

    app.use(express.json())
    
  3. cors包可以解决数据接收编码格式问题(urlencoded和json)

    const cors = require('cors')
    app.use(cors())
    
  4. 注意:接收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
  1. JSONP是什么

    • JSONP(JSON with Padding),是一个非官方的跨域解决方案,纯粹凭借程序员的聪明才智开发出来,只支持get请求
  2. 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(后端技术)

  1. CORS是什么?

    • CORS(Cross-Origin Resource Sharing),跨域资源共享。CORS是官方的跨域解决方案,它的特点是不需要在客户端做任何特殊的操作,完全在服务器中进行处理,支持所有常见的请求。
  2. CORS怎么工作的?

    • CORS是通过设置一个响应头来告诉浏览器,该请求允许跨域,浏览器收到该响应以后就会对响应放行。
  3. 服务器端常用设置

    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错误的类型
  1. Error: 所有错误的父类型
  2. ReferenceError: 引用的变量不存在
  3. TypeError: 数据类型不正确的错误
  4. RangeError: 数据值不在其所允许的范围内
  5. 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是什么?

  1. 抽象表达:

    1. Promise是一门新的技术(ES6提出的)
    2. Promise是JS中异步编程的新方案(旧方案是谁?---纯回调)
  2. 具体表达:

    1. 从语法上来说: Promise是一个内置构造函数
    2. 从功能上来说: Promise的实例对象可以用来封装一个异步操作,并可以获取其成功/失败的值

5.2、Promise基本使用

5.2.1、重要语法
  • new Promise(executor) 构造函数
  • Promise.prototype.then 方法
5.2.2、基本编码流程
  1. 创建Promise的实例对象(pending状态), 传入executor函数

  2. 在executor中启动异步任务(定时器、ajax请求)

  3. 根据异步任务的结果,做不同处理:

    1. 如果异步任务成功了:

      • 我们调用resolve(value), 让Promise实例对象状态变为成功(fulfilled),同时指定成功的value
    2. 如果异步任务失败了:

      • 我们调用reject(reason), 让Promise实例对象状态变为失败(rejected),同时指定失败的reason
  4. 通过then方法为Promise的实例指定成功、失败的回调函数,来获取成功的value、失败的reason(注意:then方法所指定的:成功的回调、失败的回调,都是异步的回调。)

5.2.3、关于状态的注意点:
  1. 三个状态

    • pending: 未确定的------初始状态
    • fulfilled: 成功的------调用resolve()后的状态
    • rejected: 失败的-------调用reject()后的状态
  2. 两种状态改变

    1. pending ==> fulfilled
    2. pending ==> rejected
  3. 状态只能改变一次!!

  4. 一个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

  1. Promise构造函数:

    1. executor函数: 是同步执行的,(resolve, reject) => {}
    2. resolve函数: 调用resolve将Promise实例内部状态改为成功(fulfilled)。
    3. reject函数: 调用reject将Promise实例内部状态改为失败(rejected)。
    4. 说明: excutor函数会在Promise内部立即同步调用,异步代码放在excutor函数中。
    //executor里面两个形参resolve、reject
    new Promise (executor)
    ​
    //实例
    const p = new Promise((resolve, reject) => {
        resolve('成功')
    })
    
  2. Promise.prototype.then方法: Promise实例.then(onFulfilled,onRejected)

    1. then方法会返回一个新的Promise实例对象
    2. //实例
      p.then(
      	value => {console.log('成功了',value);},
      	reason => {console.log('失败了',reason);}
      )
      
  3. Promise.prototype.catch方法: Promise实例.catch(onRejected)

    1. catch方法是then方法的语法糖, 相当于: then(undefined, onRejected)
    2. //专用于失败的回调
      p.catch(
      	reason => {console.log('失败了',reason);}
      )
      
  4. Promise.resolve方法: Promise.resolve(value)

    1. 说明: 用于快速返回一个状态为fulfilled或rejected的Promise实例对象
    2. 备注:value的值可能是:(1)非Promise值 (2)Promise值
    3. const p0 = Promise.reject(-100)
      const p = Promise.resolve(p0)
      p.then(
      	value => {console.log('成功了',value);},
      	reason => {console.log('失败了',reason);}
      )
      
  5. Promise.reject方法: Promise.reject方法(reason)

    1. 说明: 用于快速返回一个状态必为rejected的Promise实例对象
    2. const p0 = Promise.reject(100)
      const p = Promise.resolve(p0)
      p.then(
      	value => {console.log('成功了',value);},
      	reason => {console.log('失败了',reason);}
      )
      
  6. Promise.all方法: Promise.all(promiseArr)

    1. promiseArr: 包含n个Promise实例的数组
    2. 返回一个新的Promise实例, 只有所有的promise都成功才成功, 只要有一个失败了就直接失败。
    3. 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);}
      )
      
  7. Promise.race方法: Promise.race(promiseArr)

    1. promiseArr: 包含n个Promise实例的数组
    2. 返回一个新的Promise实例, 成功还是很失败?以最先出结果的promise为准。
    3. 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实例的状态?
  1. 执行resolve(value): 如果当前是pending就会变为fulfilled
  2. 执行reject(reason): 如果当前是pending就会变为rejected
  3. 执行器函数(executor)抛出异常: 如果当前是pending就会变为rejected
5.5.2、Promise实例.then()返回的是一个【新的Promise实例】,它的值和状态由什么决定?
  1. 简单表达: 由then()所指定的回调函数执行的结果决定

  2. 详细表达:

    1. 如果then所指定的回调返回的是非Promise值a,那么【新Promise实例】状态为:成功(fulfilled), 成功的value为a
    2. 如果then所指定的回调返回的是一个Promise实例p,那么【新Promise实例】的状态、值,都与p一致
    3. 如果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链
  1. 当使用promise的then链式调用时, 在中间中断, 不再调用后面的回调函数。
  2. 办法: 在失败的回调函数中返回一个pendding状态的Promise实例。
  3. 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错误穿透:
  1. 当使用promise的then链式调用时, 可以在最后用catch指定一个失败的回调,
  2. 前面任何操作出了错误, 都会传到最后失败的回调中处理了
  3. 备注:如果不存在then的链式调用,就不需要考虑then的错误穿透。
  4. 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的优势

  1. 指定回调函数的方式更加灵活:

    1. 旧的: 必须在启动异步任务前指定
    2. promise: 启动异步任务 => 返回promie对象 => 给promise对象绑定回调函数(甚至可以在异步任务结束后指定)
  2. 支持链式调用, 可以解决回调地狱问题

    1. 什么是回调地狱:

      • 回调函数嵌套调用, 外部回调函数异步执行的结果是嵌套的回调函数执行的条件
    2. 回调地狱的弊病:

      • 代码不便于阅读、不便于异常的处理
    3. 一个不是很优秀的解决方案:

      • then的链式调用
    4. 终极解决方案:

      • async/await(底层实际上依然使用then的链式调用)

6、async和await

6.1、async修饰的函数

  • 函数的返回值为promise对象
  • Promise实例的结果由async函数执行的返回值决定

6.2、await表达式

await右侧的表达式一般为Promise实例对象, 但也可以是其它的值

  1. 如果表达式是Promise实例对象, await后的返回值是promise成功的值
  2. 如果表达式是其它值, 直接将此值作为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这种写法:

  1. 表面上不出现任何的回调函数
  2. 但实际上底层把我们写的代码进行了加工,把回调函数“还原”回来了。
  3. 最终运行的代码是依然有回调的,只是程序员没有看见。
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的回调

规则:

  • 每次要执行宏队列里的一个任务之前,先看微队列里是否有待执行的微任务

    1. 如果有,先执行微任务
    2. 如果没有,按照宏队列里任务的顺序,依次执行
//测试题
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、请求参数
  1. query参数(查询字符串参数)

    • 参数包含在请求地址中,格式为:/xxxx?name=tom&age=18
    • 敏感数据不要用query参数,因为参数是地址的一部分,比较危险。
    • 备注:query参数又称查询字符串参数,编码方式为urlencoded
  2. params参数

    • 参数包含在请求地址中,格式如下:http://localhost:3000/add_person /tom/18
    • 敏感数据不要用params参数,因为参数是地址的一部分,比较危险。
  3. 请求体参数

    • 参数包含在请求体中,可通过浏览器开发工具查看

    • 常用的两种格式:

      1. 格式一:urlencoded格式

        • 例如:name=tom&age=18
        • 对应请求头:Content-Type: application/x-www-form-urlencoded
      2. 格式二:json格式

        • 例如: {"name": "tom", "age": 12}
        • 对应请求头:Content-Type: application/json
8.2.3、注意事项
  1. GET请求不能携带请求体参数,因为GET请求没有请求体。

  2. 理论上一次请求可以随意使用上述3种类型参数中的任何一种,甚至一次请求的3个参数可以用3种形式携带,但一般不这样做。

  3. 一般来说我们有一些“约定俗成”的规矩:

    1. 例如form表单发送post请求时: 自动使用请求体参数,用urlencoded编码。
    2. 例如jQuery发送ajax-post请求时:自动使用请求体参数,用urlencoded编码。
  4. 开发中请求到底发给谁?用什么请求方式?携带什么参数?----要参考项目的API接口文档。

9、API相关

9.1、API的分类

  1. REST API ( restful风格的API )

    1. 发送请求进行CRUD哪个操作由请求方式来决定
    2. 同一个请求路径可以进行多个操作
    3. 请求方式会用到GET/POST/PUT/DELETE
  2. 非REST API ( restless风格的 API )

    1. 请求方式不决定请求的CRUD操作
    2. 一个请求路径只对应一个操作
    3. 一般只有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请求

  1. ajax请求是一种特别的http请求

  2. 对服务器端来说, 没有任何区别, 区别在浏览器端

  3. 浏览器端发请求: 只有XHR或fetch发出的才是ajax请求, 其它所有的都是非ajax请求

  4. 浏览器端接收到响应

    1. 一般请求: 浏览器一般会直接显示响应体数据, 也就是我们常说的自动刷新/跳转页面
    2. ajax请求: 浏览器不会对界面进行任何更新操作, 只是调用监视的回调函数并传入响应相关数据

10、axios

10.1、axios是什么?

  1. 前端最流行的ajax请求库
  2. react / vue官方都推荐使用axios发ajax请求
  3. 文档: github.com/axios/axios

10.2、axios特点

  1. 基本promise的异步ajax请求库
  2. 浏览器端/node端都可以使用
  3. 支持请求/响应拦截器
  4. 支持请求取消
  5. 请求/响应数据转换
  6. 批量发送多个请求

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)

  1. 根据指定配置创建一个新的axios, 也就就每个新axios都有自己的配置

  2. 新axios只是没有取消请求和批量发请求的方法, 其它所有语法都是一致的

  3. 为什么要设计这个语法?

    • 需求: 项目中有部分接口需要的配置与另一部分接口需要的配置不太一样, 如何处理
    • 解决: 创建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请求拦截器
  1. 是什么?

    • 在真正发请求前执行的一个回调函数
  2. 作用:

    • 对所有的请求做统一的处理:追加请求头、追加参数、界面loading提示等等
    axios.interceptors.request.use((config)=>{
        console.log('请求拦截器1执行了');
        if(Date.now() % 2 === 0){
            config.headers.token = 'atguigu'
        }
        return config
    })
    
10.6.2、axios响应拦截器
  1. 是什么?

    • 得到响应之后执行的一组回调函数
  2. 作用:

    • 若请求成功,对成功的数据进行处理
    • 若请求失败,对失败进行统一的操作
    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、基本流程
  1. 配置cancelToken对象
  2. 缓存用于取消请求的cancel函数
  3. 在后面特定时机调用cancel函数取消请求
  4. 在错误回调中判断如果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、理解什么是模块
  1. 将一个复杂的程序依据一定的规则分成单个文件, 并最终组在一起
  2. 这些拆分的文件就是模块,模块内部数据是私有的, 只是向外部暴露一些方法与外部其它模块通信
11.1.2、为什么要模块化?
  • 降低复杂度,提高解耦性
  • 避免命名冲突
  • 更好的分离, 按需加载
  • 更高复用性,高可维护性
11.1.3、模块化概念带来的问题
  1. 请求过多
  2. 依赖模糊
  3. 难以维护

11.2、模块化规范

前言:一个大的项目必定会使用模块化技术,使用模块化就会使用相应的模块化规范,现在比较流行的模块化规范有以下2种:CommonJS、ES6

11.3、 CommonJS

11.3.1、规范
  1. 官网:wiki.commonjs.org/wiki/Module…
  2. 每个文件都是一个模块。
  3. CommonJS模块化的代码既可在服务端运行,也可在浏览器端运行
  4. 服务器端: 模块化的代码可直接运行。
  5. 浏览器端: 模块化的代码要经过Browserify(browserify.org)编译。
11.3.2、基本语法
  1. 暴露模块:

    module.exports = value   //第一种方式
    exports.xxx = value   //第二种方式
    
  2. 引入模块

    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
  1. 【在Node环境下运行】,直接使用命令:

    node app.js
    
  2. 【在浏览器环境下运行】,需要如下操作:

    • 全局安装Browserify

      npm install browserify -g
      
    • 编译指定文件

      browserify a.js -o build.js 
      
    • html页面中引入build.js

11.4、ES6模块化规范

11.4.1、规范
  1. 每个文件都是一个模块。
  2. 要借助Babel和Browserify依次编译代码,才能在浏览器端运行。
  3. Babel中文网:www.babeljs.cn/
11.4.2、基本语法
  1. 暴露模块

    1. 分别暴露: export 暴露内容

      export const data = 'atguigu'
      export const msg = 'hello,0826'
      export function showData(){
      	console.log(data);
      }
      
    2. 统一暴露: export { 暴露内容1, 暴露内容2 }

      const school = '尚硅谷'
      function getLaoliu(){
      	console.log(person);
      }
      export {school,getLaoliu}
      
    3. 默认暴露: export defalut 暴露内容(只能暴露一次!!!!)

      export default {
      	name:"wc",
      	age:5,
      }
      
  2. 引入模块

    1. 方法1 import{xxx,yyy} from './module1'

    2. 方法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'
      
  3. 使用规则

    • 若使用分别暴露统一暴露的方式暴露内容,那么就要用方法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/...)都作为模块处理。
    • 它将根据模块的依赖关系进行分析,生成对应的资源
  • 五个核心概念:

    1. 【入口(entry)】 :指示 webpack 应该使用哪个模块,来作为构建其内部依赖图的开始。
    2. 【输出(output)】 :在哪里输出文件,以及如何命名这些文件。
    3. 【Loader】 :处理那些非 JavaScript 文件(webpack 自身只能解析 JavaScript和json)。
    4. 【插件(plugins)】 执行范围更广的任务,从打包到优化都可以实现。
    5. 【模式(mode)】 ,有生产模式production和开发模式development
  • 对loader的理解

    • webpack 本身只能处理JS、JSON模块,如果要加载其他类型的文件(模块),就需要使用对应的loader 。
    • 它本身是一个函数,接受源文件作为参数,返回转换的结果。
    • loader 一般以 xxx-loader 的方式命名,xxx 代表了这个 loader 要做的转换功能,比如 css-loader。
  • 对plugins的理解

    • 插件可以完成一些loader不能完成的功能。
  • 配置文件

    • webpack.config.js : 用于存储webpack配置信息。

12.2、开启项目

  • 初始化项目:

    • 使用npm inityarn 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

  1. 概述:有些时候,我们一个模块向外暴露了n个函数、对象、或其他一些数据,但是我们只是用到了其中的一个或几个,那在最终打包的时候,我们只希望把我们所用的打包进去,这时候就要tree-shaking,即:去除无用代码。

  2. 配置:同时满足两个条件webpack会自动开启tree-shaking

    1. 使用ES6模块化
    2. 开启production环境