ES6
第一章 ECMASript 相关介绍
1.1 什么是 ECMA
ECMA(European Computer Manufacturers Association)中文名称为欧洲计算机制造商协会,这个组织的目标是评估、开发和认可电信和计算机标准。1994 年后该组织改名为 Ecma 国际。
1.2 什么是 ECMAScript
ECMAScript 是由 Ecma 国际通过 ECMA-262 标准化的脚本程序设计语言。
1.3 为什么要学习 ES6
- ES6 的版本变动内容最多,具有里程碑意义
- ES6 加入许多新的语法特性,编程实现更简单、高效
- ES6 是前端发展趋势,就业必备技能
1.4 ES6 兼容性
http://kangax.github.io/compat-table/es6/ 可查看兼容性
第二章 ECMAScript 6 新特性
2.1 let 关键字
特性
- 变量不能重复声明
- 块儿级作用域
- 不存在变量提升(不允许在变量声明之前使用变量)
- 不影响作用域链
应用场景
以后声明变量使用 let 就对了
2.2 const 关键字
特性
- 声明必须赋初始值
- 标识符一般为大写
- 不允许重复声明
- 值不允许修改
- 块儿级作用域
注意
对象属性修改和数组元素变化不会出发 const 错误
应用场景
声明对象类型使用 const,非对象类型声明选择 let
2.3 变量的解构赋值
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构赋值。
//数组的解构赋值
const arr = ['张学友', '刘德华', '黎明', '郭富城'];
let [zhang, liu, li, guo] = arr;
console.log(zhang); // 张学友//对象的解构赋值
const lin = {name: '林志颖',tags: ['车手', '歌手', '小旋风', '演员'],changge: function () {console.log('我可以唱歌');}
};
let {name, tags} = lin;
console.log(name); // 林志颖
let {cahngge} = lin;
changge(); // 我可以唱歌
2.4 模板字符串
模板字符串(template string)是增强版的字符串,用一对反引号(`)标识
特性
- 字符串中可以出现换行符
- 可以使用 ${xxx} 的输出变量
2.5 简化对象写法
ES6 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁。
let name = '尚硅谷';
let slogon = '永远追求行业更高标准';
let improve = function () {console.log('可以提高你的技能');
}
//属性和方法简写
let atguigu = {name,slogon,improve,change() {console.log('可以改变你')}
};
2.6 箭头函数
ES6 允许使用「箭头」 =>
定义函数。
ES6 允许给函数参数赋初始值
- 具有默认值的参数,一般靠后
- 可以与解构赋值结合
注意点
- 如果形参只有一个,则小括号可以省略
- 函数体如果只有一条语句,则花括号可以省略,此时 return 必须省略,而且函数的返回值为该条语句的执行结果
- 箭头函数 this 是静态的,始终指向声明时所在作用域下 this 的值
- 箭头函数不能作为构造函数实例化
- 不能使用 arguments
// 通用写法
let fn = (arg1, arg2, arg3) => {return arg1 + arg2 + arg3;
}
// 省略花括号的情况
let fn3 = score => score * 20;
应用场景
适合与 this 无关的回调 定时器、数组的方法回调
不适合与 this 有关的回调 事件回调、对象的方法
2.7 rest 参数
ES6 引入 rest 参数,用于获取函数的实参,用来代替 arguments
...args
- rest参数必须要放到最后
/**
* 作用与 arguments 类似
*/
function add(...args){console.log(args); // [1,2,3,4,5]
}
add(1,2,3,4,5);
/**
* rest 参数必须是最后一个形参
*/
function minus(a,b,...args){console.log(a); // 100console.log(b); // 1console.log(args); // [2,3,4,5,19]
}
minus(100,1,2,3,4,5,19);
应用场景
rest 参数非常适合不定个数参数函数的场景
2.8 spread 运算符
扩展运算符(spread)也是三个点...
。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列,对数组进行解包。
应用
-
数组的合并
const arr1 = [1, 2]; const arr2 = [3, 4]; // const hebing = arr1.concat(arr2); 原先api函数 const hebing = [...arr1, ...arr2]
-
数组的克隆
const arr1 = [1, 2]; // 注意是 浅克隆 const hebing = [...arr1,]
-
将伪数组转为真正的数组
const divs = document.querySelectAll('div'); // 伪数组 const divArr = [...divs]
2.9 Symbol
ES6 引入了一种新的原始数据类型 Symbol,表示独一无二的值。它是JavaScript 语言的第七种数据类型,是一种类似于字符串的数据类型。
特点
- Symbol 的值是唯一的,用来解决命名冲突的问题
- Symbol 值不能与其他数据进行运算
- Symbol 定义的 对象属性 不能使用 for…in 循环遍历 ,但是可以 使用 Reflect.ownKeys 来获取对象的所有键名
// 创建 Symbol
let s = Symbol();
console.log(s, typeof s); // Symbol() "symbol"
let s2 = Symbol('张艺兴'); // 注释作用
let s3 = Symbol('张艺兴');
console.log(s2 === s3); // false
// Symbol.for 创建
let s4 = Symbol.for('张艺兴');
let s5 = Symbol.for('张艺兴');
console.log(s4, typeof s4); // Symbol(张艺兴) "symbol"
console.log(s4 === s5); // true
Symbol['say']: function () {}
内置值
JS 数据类型总结
USONB (you are so niubility)
- u : undefined
- s : string Symbol
- o : object
- n : null number
- b : boolean
2.10 迭代器
遍历器(Iterator)就是一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作。
-
ES6 创造了一种新的遍历命令 for…of 循环,Iterator 接口主要供 for…of 消费
// 声明一个数组 const xiyou = ['1', '2', '3', '4'];// 使用 for...of 遍历数组 for (const v of xiyou) {console.log(v); }
-
原生具备 iterator 接口的数据(可用 for of 遍历)
-
Array
-
Arguments
-
Set
-
Map
-
String
-
TypedArray
-
NodeList
-
工作原理
- 创建一个指针对象,指向当前数据结构的起始位置
- 第一次调用对象的 next 方法,指针自动指向数据结构的第一个成员
- 接下来不断调用 next 方法,指针一直往后移动,直到指向最后一个成员
- 每调用 next 方法返回一个包含 value 和 done 属性的对象
let iterator = xiyou[Symbol.iterator]();// 调用对象的 next 方法 console.log(iterator.next()); console.log(iterator.next()); console.log(iterator.next()); console.log(iterator.next()); console.log(iterator.next());
运行结果:
应用场景
需要自定义遍历数据的时候,要想到迭代器。
自定义遍历对象
// 声明一个对象
const banji = {name: '终极一班',stus: ['xiaoming','xiaoning','xiaotian','knight'],[Symbol.iterator]() {// 索引变量let index = 0;let _this = this;return {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 (const v of banji) {console.log(v);
}
2.11 生成器
生成器函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同,起其实就是一个特殊的函数。
// yield 函数代码的分隔符
function * gen() {// console.log(111);yield '一只没有耳朵';// console.log(222);yield '一只没有尾巴';// console.log(333);yield '真奇怪';// console.log(444);
}let iterator = gen();
// 调用 next() 方法才会执行方法
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());for (const v of gen()) {console.log(v);
}
运行截图:
生成器函数参数
function * gen(arg) {console.log(arg);let one = yield '一只没有耳朵';console.log(one);let two = yield '一只没有尾巴';console.log(two);let three = yield '真奇怪';console.log(three);
}let iterator = gen('AAA');
// 调用 next() 方法才会执行方法
console.log(iterator.next());
console.log(iterator.next('BBB'));
console.log(iterator.next('CCC'));
console.log(iterator.next('DDD'));
运行截图:
实例
…
2.12 Promise
Promise 是 ES6 引入的异步编程的新解决方案。语法上 Promise 是一个构造函数,用来封装异步操作并可以获取其成功或失败的结果。
Promise 构造方法
const promise = new Promise(function(resolve, reject) {}
// 实例化 Promise 对象
const p = new Promise(function(resolve, reject) {setTimeout(function () {// let data = '数据库中的用户数据'// resolve(data); // 调用 then 中第一个方法let err = '数据读取失败';reject(err); // 调用 then 中第二个方法}, 1000);
});// 调用 Promise 对象的 then 方法
p.then(function (value) {console.log(value); // 数据库中的用户数据
}, function (reason) {console.log(reason); // 数据读取失败
})
Promise 读取文件
// 引入 fs 模块
const fs = require('fs');const p = new Promise(function (resolve, reject) {fs.readFile('./resources/为学.md', 'utf8', (err, data) => {if (err) reject(err);// 如果成功resolve(data);})
})p.then(function (value) {console.log(value);
}, function (reason) {console.log('读取失败');
})
Promise 封装 AJAX
// 接口地址 http://api.apiopen.top/getJokeconst p = new Promise(function (resolve, reject) {// 1. 创建对象const xhr = new XMLHttpRequest();// 2. 初始化xhr.open('GET', 'https://api.apiopen.top/getJoke');// 3. 发送xhr.send();// 4. 绑定事件,处理响应结果xhr.onreadystatechange = function () {if (xhr.readyState === 4) {if (xhr.status >= 200 && xhr.status < 300) {resolve(xhr.response)} else {reject()}}}
})// 指定回调
p.then(function (value) {console.log(value);
}, function (reason) {console.log('获取失败');
})
Promise.prototype.then
// 创建 promise 对象
const p = new Promise((resolve, reject) => {setTimeout(() => {resolve('用户数据');// reject('出错啦');}, 1000);
});// 调用 then 方法 then方法的返回结果是 Promise 对象,对象状态又回调函数的执行结果决定
// 1. 如果回调函数中返回的结果是一个 非Promise 类型的数据,状态为成功,返回值为对象那个的成功值
const result = p.then(value => {console.log(value);// 1. 非 Promise 类型的数据// return 123;// 2. 是 Promise 对象// return new Promise((resolve, reject) => {// // resolve('ok');// reject('出错')// })// 3. 抛出错误// throw new Error('出错啦')
}, reason => {console.warn(reason);
});
console.log(result);
链式调用
// 链式调用 可以避免回调地狱的形成
p.then(value=>{},reason=>{}).then(value=>{})
另外,then 方法中的两个方法 不一定需要全有
案例-读取多个文件
// 引入 fs 模块
const fs = require('fs');// 之前的方法 - 回调地狱
// fs.readFile('./resources/为学.md', 'utf8', (err, data1) => {
// fs.readFile('./resources/插秧诗.md', 'utf8', (err, data2) => {
// fs.readFile('./resources/观书有感.md', 'utf8', (err, data3) => {
// let result = data1 + '\r\n' + data2 + '\r\n' + data3;
// console.log(result);
// })
// })
// })// 使用 Promise 实现
const p = new Promise((resolve, reject) => {fs.readFile('./resources/为学.md', 'utf8', (err, data) => {resolve(data);})
})p.then(value => {return new Promise((resolve, reject) => {fs.readFile('./resources/插秧诗.md', 'utf8', (err, data) => {resolve([value, data]);})})
}).then(value => {return new Promise((resolve, reject) => {fs.readFile('./resources/观书有感.md', 'utf8', (err, data) => {value.push(data);resolve(value);})})
}).then(value => {console.log(value.join('\r\n'));
})
Promise catch 方法
不用不影响开发,catch 的作用只是简化,可以只写后面错误的函数,不需要写前面的。
const p = new Promise((resolve, reject) => {setTimeout(() => {// 设置 p 对象的状态为失败,并设置失败的值reject('出错啦!')}, 1000);
})p.then(value => {}, reason => {console.error(reason);
})p.catch(reason => {console.warn(reason);
})
2.13 Set
ES6 提供了新的数据结构 Set(集合)。
它类似于数组,但成员的值都是唯一的,集合实现了 iterator 接口,所以可以使用『扩展运算符』和『for…of…』进行遍历。
属性&方法
- size 返回集合的元素个数
- add 增加一个新元素,返回当前集合
- delete 删除元素,返回 boolean 值
- has 检测集合中是否包含某个元素,返回 boolean 值
- clear 清空集合,返回 undefined
// 声明一个 Set
let s = new Set();
let s2 = new Set(['大事儿', '小事儿', '好事儿', '坏事儿', '小事儿']);
console.log(s, typeof s);
console.log(s2); // 自动去重 {"大事儿", "小事儿", "好事儿", "坏事儿"}// 元素的个数
console.log(s2.size); // 4
// 添加新的元素
s2.add('喜事儿');
console.log(s2); // {"大事儿", "小事儿", "好事儿", "坏事儿", "喜事儿"}
// 删除元素
s2.delete('坏事儿');
console.log(s2); // {"大事儿", "小事儿", "好事儿", "喜事儿"}
// 检测
console.log(s2.has('好事儿')); // true
// 清空
s2.clear();
console.log(s2); // {}// 也可实现 for...of 遍历
for (const v of s2) {console.log(v);
}
Set 集合实践
var arr = [1,2,3,4,5,4,3,2,1];
// 1. 数组去重
// let result = [...new Set(arr)];
// console.log(result);
// 2. 交集
let arr2 = [4,5,6,5,6];
// let result = [...new Set(arr)].filter(item => {
// let s2 = new Set(arr2);
// if (s2.has(item)) {
// return true;
// } else {
// return false;
// }
// })
// 简化
let result = [...new Set(arr)].filter(item => new Set(arr2).has(item))
console.log(result); // [4, 5]// 3. 并集
let union = [...new Set([...arr, ...arr2])]
console.log(union); // [1, 2, 3, 4, 5, 6]// 4. 差集
let diff = [...new Set(arr)].filter(item => !(new Set(arr2).has(item)))
console.log(diff); // [1, 2, 3]
2.14 Map
ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合。但是“键”
的范围不限于字符串,各种类型的值(包括对象)都可以当作键。Map 也实现了iterator 接口,所以可以使用『扩展运算符』和『for…of…』进行遍历。
属性&方法
- size 返回 Map 的元素个数
- add 增加一个新元素,返回当前 Map
- get 返回键名对象的键值
- has 检测 Map 中是否包含某个元素,返回 boolean 值
- clear 清空集合,返回 undefined
// 声明 Map
let m = new Map();// 添加元素
m.set('name', '张艺兴')
m.set('change', function () {console.log('张艺兴是最帅的');
})
let key = {school : 'AgoniLay'
}
m.set(key, ['北京', '上海', '深圳'])// size
console.log(m.size); // 3// 删除
m.delete('name')// 获取
// console.log(m.get('change'));
console.log(m.get(key)); // ["北京", "上海", "深圳"]// 清空
// m.clear();// 遍历
for (const v of m) {console.log(v);
}console.log(m);
2.15 Class 类
ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对
象的模板。通过 class 关键字,可以定义类。基本上,ES6 的 class 可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的 class 写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。
知识点
- class 声明类
- constructor 定义构造函数初始化
- extends 继承父类
- super 调用父级构造方法
- static 定义静态方法和属性
- 父类方法可以重写
ES5 方法
// ES5
// 手机
function Phone(brand, price) {this.brand = brand;this.price = price;
}// 添加方法
Phone.prototype.call = function () {console.log('我可以打电话');
}// 实例化对象
let huawei = new Phone('华为', 5999);console.log(huawei);
// huawei.call();
ES6 class
class Shouji {// 构造方法 名字不能修改constructor (brand, price) {this.brand = brand;this.price = price;}// 方法必须使用该语法,不能使用 ES5 的对象完整形式call () {console.log('我可以打电话');}
}let onePlus = new Shouji('1+', 1999);
console.log(onePlus);
静态成员
class Phone {// 静态属性static name = '手机';
static change = function () {console.log('我可以改变世界');
};
}let nokia = new Phone();
console.log(nokia.name); // undefined
console.log(Phone.name); // 手机
类继承
class Phone {// 构造方法constructor (brand, price) {this.brand = brand;this.price = price;}// 父类的成员属性call () {console.log('我可以打电话');}
}class SmartPhone extends Phone {// 构造方法constructor (brand, price, color, size) {super(brand, price);this.color = color;this.size = size;}photo () {console.log('拍照');}playGame () {console.log('打游戏');}
}const xiaomi = new SmartPhone('小米', 799, '黑色', '4.7inch');
console.log(xiaomi);
xiaomi.call();
xiaomi.photo();
xiaomi.playGame();
子类可以进行对父类方法的重写
注:但是不能通过super()
调用父类重名方法
set 和 get
获取时,触发 get 方法
修改赋值时,触发 set 方法
注:set 方法必须有一个参数
class Phone {get price () {console.log('价格属性被读取了');return 111}set price (newV) {console.log('价格属性被修改了');}
}// 实例化对象
let s = new Phone();
console.log(s.price); // 111 // 触发 get
s.price = 'free'; // 触发 set
2.16 数值扩展
Number.EPSILON
Number.EPSILON 是 JavaScript 表示的最小精度 ε
EPSILON属性的值接近于:2.2204460492503130808472633361816E-16
console.log(0.1 + 0.2 == 0.3); // false
function equal(a, b) {if (Math.abs(a - b) < Number.EPSILON) {return true} else {return false}
}
console.log(equal(0.1 + 0.2, 0.3)); // true
二进制和八进制
ES6 提供了二进制和八进制数值的新的写法,分别用前缀 0b 和 0o 表示。
let b = 0b1010; // 二进制
let b = 0o777; // 八进制let d = 100; // 十进制
let x = 0xff; // 十六进制
Number.isFinite()
用来检查一个数值是否为有限的,返回 boolean
Number.isNaN()
用来检查一个值是否为 NaN,返回 boolean
Number.parseInt() & Number.parseFloat()
移植到了 Number 对象下,使用不变,(截取整数,截取浮点数)
Number.isInteger()
用来判断一个数值是否为整数, 返回 boolean
Math.trunc()
将数字的小数部分抹掉
Math.sign()
判断一个数是 正数(1) 负数(-1) 还是 0(0) 括号内为相应的返回值
2.17 对象方法
Object.is(a,b)
判断两个值是否完全相等
与全等号 ===
区别:
console.log(Object.is(NaN, NaN)); // true
console.log(NaN === NaN); // false
Object.assign(a,b)
对象的合并,后面的将前面的覆盖掉
可以用于更新对象
const config1 = {host: 'localhost',prot: 3306,name: 'root',pass: 'root',test: 'test'
}
const config2 = {host: 'http://www.baidu.com',prot: 33060,name: 'AgoniLay',pass: '020316'
}console.log(Object.assign(config1, config2));
/*host: "http://www.baidu.com"name: "AgoniLay"pass: "020316"prot: 33060test: "test"
*/
Object.setPrototypeof & Object.getPrototypeof
设置原型对象,一般不使用
console.log(Object.assign(config1, config2));
// Object.setPrototypeof Object.getPrototypeof
const school = {name: '张艺兴'
}
const cities = {xiaoqu: ['北京', '上海', '深圳']
}Object.setPrototypeOf(school, cities)
console.log(Object.getPrototypeOf(school));
console.log(school);
2.18 模块化
概述
模块化是指将一个大的程序文件,拆分成许多小的文件,然后将小文件组合起来。
好处
- 防止命名冲突
- 代码复用
- 高维护性
语法
模块功能主要由两个命令构成:export 和 import
- export 命令用于规定模块的对外接口
- import 命令用于输入其他模块提供的功能
模块化规范
浏览器端
- AMD:Request.js (http://www.requirejs.cn/)
- CMD:Sea.js (https://seajs.github.io/seajs/docs/)
服务器端
- CommonJS:NodeJS
- 模块分为 单文件模块 与 包
- 模块成员导出:module.exports 和 exports
- 模块成员导入:require(‘模块标识符’)
2.19 大一统的模块化规范 - ES6模块化
是 浏览器端 与 服务器端 通用的模块化开发规范
- 每一个 js 文件都是一个独立的模块
- 导入模块成员使用 import 关键字
- 暴露模块成员使用 export 关键字
Node.js 中 babel 体验 ES6 模块化
步骤
-
安装工具
npm install --save-dev @babel/core @babel/cli @babel/preset-env @babel/node
npm install --save @babel/polyfill
-
根目录下新建 babel.config.js 文件 写入:
const presets = [["@babel/env", {targets: {edge: "17",firefox: "60",chrome: "67",safari: "11.1"}}] ]module.exports = { presets }
-
执行代码
npx babel-node index.js
基本语法
默认导出 与 默认导入
- 默认导出:
export default 默认导出的成员
- 默认导入:
import 接收名称 from '模块标识符/路径'
注意点:
- 在每个模块中,只允许使用唯一的一次 export default,否则会报错
- 在模块中如果没有默认导出,那么将会 是一个空对象
{}
按需导出 与 按需导入
- 按需导出:
export let s1 = 10
- 按需导入:
import { s1 } from '模块标识符/路径'
注意点:
- 在一个模块中,可以使用多次按需导出
直接导入并执行模块代码
import '模块标识符/路径'
直接执行代码,没有导出。
2.20 webpack
当前 Web开发 面临的困境:
- 文件以来关系错综复杂
- 静态资源请求效率低
- 模块化支持不友好
- 浏览器对高级 JavaScript 特性兼容程度较低
webpack 概述
webpack 是一个流行的前端项目构建工具(打包工具),可以解决当前 web 开发中所面临的困境。
提供了友好的模块化支持,以及代码压缩混淆、处理 js 兼容问题、性能优化等强大的功能,提高了开发效率和项目的可维护性。
基本使用
1.创建列表隔行变色项目
- 创建项目空白目录,并运行
npm init -y
命令,进行初始化包管理配置文件 package.json - 新建 src 源代码目录
- 新建 src -> index.html 首页
- 初始化首页基本结构
- 运行
npm install jquery -S
命令, 安装 jQuery - 通过模块化的形式,实现列表隔行变色效果
2. 在项目中安装和配置 webpack
-
运行
npm insatll webpack webpack-cli -D
命令,安装 webpack 相关的包 -
在项目根目录中,创建名为 webpack.config.js 的 webpack 配置文件
-
在 webpack 的配置文件中,初始化如下基本配置:
module.exports = {mode: 'development' // mode 用来指定构建模式 }
注意:开发阶段 mode 值一般设置为 development ,上线时才设置成 production (会进行压缩与混淆,转换时间会更长)
-
在 package.json 配置文件中的 scripts 节点下,新增 dev 脚本如下:
"scripts": {"dev": "webpack" // script 节点下的脚本,可以通过 npm run 执行 }
-
在终端中运行
npm run dev
命令,启动 webpack 进行项目打包。
注意:主页面中导入的应该为打包生成后的 dist 文件夹下的 js 文件
3. 配置打包的入口和出口
webpack 的 4.x 版本中默认约定:
- 打包的 入口文件 为 src -> index.js
- 打包的 输出文件 为 dist -> main.js
修改打包的入口(entry)和出口(output),可以在 webpack.config.js 中增加如下配置:
const path = require('path')module.exports = {entry: path.join(__dirname, './src/index.js'), // 打包入口文件的路径output: {path: path.join(__dirname, './dist'), // 输出文件的存放路径filename: 'bundle.js' // 输出文件的名称}
}
4. 配置 webpack 的自动打包功能
-
运行
npm install webpack-dev-server -D
命令,安装支持项目自动打包的工具 -
修改 package.json -> scripts 中的 dev 命令如下:
"scripts": {"dev": "webpack server" },
-
将 src -> index.html 中,script 脚本的引用路径,修改为 “/bundle.js”
-
运行
npm run dev
命令,重新进行打包 -
在浏览器中访问 http://localhost:8080 地址,查看自动打包效果。
5. 配置 html-webpack-plugin 生成预览页面
-
运行
npm install html-webpack-plugin -D
命令,安装生成预览页面的插件 -
修改 webpack.config.js 文件头部区域,添加如下配置信息:
const HtmlWebpackPlugin = require('html-webpack-plugin')const htmlPlugin = new HtmlWebpackPlugin({template: './src/index.html',filename: 'index.html' })
-
修改 webpack.config.js 文件中向外暴露的配置对象,新增如下配置节点:
module.exports = {plugins: [ htmlPlugin ] // plugins 数组时 webpack 打包期间会用到的一些插件列表 }
6. 配置自动打包相关的参数
- –open 打包完成自动打开浏览器页面
- –host 配置 IP 地址
- –port 配置端口
"scripts": {"dev": "webpack server --open --host 127.0.0.1 --port 8080"
},
webpack 中的加载器
报错解决
控制台报错:
DevTools failed to load SourceMap: Could not load content for webpack:///node_modules/sockjs-client/
解决:
webpack.config.js 中配置一项
module.exports = {devtool: 'inline-source-map'
}
通过 loader 打包非 js 模块
- less-loader 可以打包处理 .less 相关的文件
- sass-loader 可以打包处理 .scss 相关的文件
- url-loader 可以打包处理 css 中与 url 路径相关的文件
基本使用
1. 打包处理 css 文件
-
运行
npm install style-loader css-loader -D
命令,安装处理 css 文件的 loader -
在 webpack.config.js 的 module -> rules 数组中,添加 loader 规则如下:
module: {rules: [{test: /\.css$/, use: ['style-loader', 'css-loader']}] }
其中,test 表示匹配的文件类型,use 表示对应要调用的 loader
注意:
- use 数组中指定的 loader 顺序是固定的
- 多个 loader 的调用顺序是:从后往前调用
2. 打包处理 less 文件
-
运行
npm i less-loader less -D
命令, -
在 webpack.config.js 的 modeule -> rules 数组中,添加 loader 规则如下:
module: {rules: [{test: /\.less$/, use: ['style-loader', 'css-loader', 'less-loader']}] }
3. 打包处理 scss 文件
-
运行
npm i sass-loader node-sass -D
命令,运行不通使用:
-
首先删除 node_modules 中的 sass-loader 和 node-sass 目录
-
配置淘宝镜像
npm install -g cnpm --registry=https://registry.npm.taobao.org
-
用 cnpm 重新安装一次
cnpm install node-sass -D
cnpm install sass-loader -D
-
-
在 webpack.config.js 的 modeule -> rules 数组中,添加 loader 规则如下:
module: {rules: [{test: /\.scss$/, use: ['style-loader', 'css-loader', 'sass-loader']}] }
4. 配置 postCSS 自动添加 css 的兼容前缀
-
运行
npm i postcss-loader autoprefixer -D
命令, -
在项目根目录中创建 postcss 的配置文件 postcss.config.js,并初始化如下配置:
// 导入自动添加前缀的插件 const autoprefixer = require('autoprefixer')module.exports = {plugins: [ autoprefixer ] // 挂载插件 }
-
在 webpack.config.js 的 modeule -> rules 数组中,修改 css 的 loader 规则如下:
module: {rules: [{test: /\.css$/, use: ['style-loader', 'css-loader', 'postcss-loader']}] }
5. 打包样式表中的图片和字体文件
-
运行
npm i url-loader file-loader -D
命令, -
在 webpack.config.js 的 modeule -> rules 数组中,添加 loader 规则如下:
module: {rules: [{ test: /\.jpg|png|gif|bmp|ttf|eot|svg|woff|woff2$/, use: 'url-loader?limit=32417' },] }
注:? 后 limit参数是文件的大小(单位是字节),小于此大小 才会编译为 base64 格式
6. 打包处理 js 文件中的高级语法
-
运行
npm i babel-loader @babel/core @babel/runtime -D
命令,安装 babel 转换器相关的包
-
运行
npm i @babel/preset-env @babel/plugin-transform-runtime @babel/plugin-proposal-class-properties -D
命令,安装babel语法插件相关的包
-
在项目根目录下,创建 babel 配置文件 babel.config.js 并初始化基本配置如下:
module.exports = {presets: ['@babel/env'],plugins: ['@babel/plugin-transform-rutime', '@babel/plugin-proposal-class-properties'] }
-
在 webpack.config.js 的 modeule -> rules 数组中,添加 loader 规则如下:
// exclude 为排除项,表示 babel-loader 不需要处理 node_modules 中的 js 文件 module: {rules: [{ test: /\.js$/, use: 'babel-loader', exclude: /node_modules/ }] }
npm 安装不了,就用 cnpm 安装
7. 配置 vue 组件的加载器
-
运行
npm i vue-loader vue-template-compiler -D
命令, -
在 webpack.config.js 的 modeule -> rules 数组中,添加 loader 规则如下:
const VueLoaderPlugin = require('vue-loader/lib/plugin')module.exports = {module: {rules: [{ test: /\.vue$/, use: 'vue-loader' }]},plugin: [new VueLoaderPlugin()] }
webpack 中使用 Vue
- 运行
npm i vue -S
安装 vue - 在 src -> index.js 入口文件中,通过
ipmort Vue from 'vue'
来导入 vue 构造函数 - 创建 vue 的实例对象,并且要控制的 el 区域
- 通过 render 函数渲染 App 根组件
index.js
import Vue from 'vue'
// 导入 单文件组件
import App from './components/App.vue'const vm = new Vue({el: '#app',render: h => h(App)
})
报错处理:
Error: Cannot find module ‘@babel/preset-preset.env’
解决:
将 babel.config.js 配置文件中的 preset-env
,改为 env
webpack 打包发布
上线之前需要通过 webpack 将应用进行整体打包,可以通过 package.json 文件配置打包命令:
{"scripts": {"build": "webpack"},
}
第三章 ES7 新特性
3.1 Array.prototype.includes
Includes 方法用来检测数组中是否包含某个元素,返回 boolean 类型值
3.2 指数运算符
在 ES7 中引入指数运算符「**」,用来实现幂运算,功能与 Math.pow 结果相同
console.log(2 ** 10); // 1024
console.log(Math.pow(2, 10)); // 1024