深入浅出Webpack1——快速上手认识Webpack
写在前面
前段时间在网上看到了webpack的教学视频,觉得还是不够系统,官方文档对初学者也不太友好,所以我决定花一周的时间,认真系统的学一遍《深入浅出Webpack》这本书,以下的笔记也是基于这本书的。
1. 前端的发展
模块化
我们知道前端代码经历了模块化的变化过程,主要包括以下几种常见的规范:
- CommonJS
这种规范运行在node.js环境下,但是无法在浏览器环境中直接运行,必须通过工具转换成标准的ES5;//导入 const moduleA=require('./moduleA') //导出 module.exports=moduleA.someFunc;
- AMD
该代码能在不转换代码的情况下直接在浏览器中运行,AMD用的比较少,这里就不做具体展开; - ES6模块化
浏览器厂商和node.js都宣布要原生支持该规划,它也逐渐取代了AMD和CommonJS规范,成为浏览器和服务器通用的模块解决方案//导入 import {readFile} from 'fs' import React from 'react' //导出 export function hello(){}; export default{};
- 样式文件中的模块化
除了js开始进行模块化改造,前端开发中的样式文件也支持模块化,比如SCSS中,将一些常用的样式片段放进一个通用的文件中,再在另一个文件中通过@import语句导入和使用这些样式片段;
新框架
当web应用变得越来越来复杂,直接操作DOM会让代码变得复杂和难以维护,许多新思想被引入网页开发中以减少开发难度和提升开发效率
- React
- Vue
- Angular2
新语言
js刚开始设计出来是用来完成一些简单的工作,但是当开发大型应用的时候,有一些语言缺陷会暴露出来,为了解决这些问题,许多新语言诞生了:
-
ES6:这是js语言的下一代标准,它在语言层面引入了很多新语法和API,如:
- 规范模块化;
- Class语法;
- 用let声明代码块中有效的变量,用const声明常量;
- 箭头函数;
- async函数;
- Set和Map数据结构
虽然这些新特性能更高效的编写代码,但是不同浏览器对这些特性的支持不一致,使用这些特性的代码可能在部分浏览器无法运行,为了解决兼容性问题,需要将ES6转换成ES5,Babel
是目前解决这个问题的优秀工具;
-
TypeScript
这是js的一个超集,除了支持ES6的全部功能之外,还提供了静态类型检查,采用TypeScript编写的代码可以编译成符合ES5、ES6标准的js,只有在大型项目中使用TypeScript优点才会体现出来,因为大型项目由很多模块,不同模块由不同的人编写,在对接不同模块的时候,静态类型检查会在编译阶段找出可能存在的问题;TypeScript的缺点是在于语法相对js更啰嗦,并且无法在浏览器和node环境下直接运行
-
Flow
Flow和ts相似但更灵活 -
SCSS
一种CSS预处理器,这里不做阐述
2.常见的构建工具及对比
Npm Script
Npm Script是一个任务执行者,是node.js安装的时候附带的包管理器,是npm内置的一个功能,允许在package.json文件中使用scripts字段定义任务:
{
"scripts":{
"dev":"node dev.js",
"pub":"node build.js"
}
}
里面的scripts字段是一个对象,每个属性对应一段Shell脚本,以上代码定义了两个任务:dev和pub。其底层实现的原理就是调用Shell去运行脚本命令,例如npm run pub
命令等同于执行node build.js
Grunt、Gulp、Fis3和Rollup
这些用的不多,就不展开阐述了
Webpack
Webpack是一个打包模块化JavaScript的工具,在Webpack里一切文件皆模块,通过Loader转换文件,通过Plugin注入钩子,最后输出由多个模块组合成的文件。 Webpack专注于构建模块化项目。
Webpack的缺点是只能用于模块化开发的项目
3. 安装Webpack
- 新建一个目录
- 进入根目录执行
npm init
来初始化 npm i -D webpack
- 安装好webpack后,我们通过设置Npm Script中定义的任务,让其自动运行安装到本项目的Webpack
{ "scripts":{ "start":"webpack --config webpack.config.js" } }
4.使用webpack
下面通过一个简单的例子,使用webpack构建一个采用CommonJS模块化编写的项目,该项目中的某个网页会通过JavaScript显示Hello,Webpack
运行构建前,先要将完成该功能的最基础的js和html文件建立好,需要以下文件:
页面入口文件index.html:
<html>
<head>
<meta charset=”UTF-8 ”>
</head>
<body>
<div id="app" ></div>
〈!一导入 Webpack 输出的 JavaScript 文件一〉
<script src=”./dist/bundle.js ”></script>
</body>
</html>
存放工具函数的show.js文件内容如下:
//操作DOM元素,将content显示到网页上
function show(content) {
window.document.getElementByid('app').innerText = 'Hello'+content;}
//通过 CommonJS 规范导出 show 函数
module.exports=show;
包含CommonJS规范导入show函数
//通过CommonJS规范导入show函数
const show=require('./show.js')
//执行show函数
show('Webpack')
webpack在执行构建的时候默认会从项目根目录下的webpack.config.js文件中读取配置,所以我们还需要新建它,其内容如下:
const path= require ('path');
module.exports = {
//JavaScript 执行入口文件
entry:'./main.js',
output: {
//将所有依赖的模块合并输出到一个bundle.js文件
filename:'bundle.js',
//将输出文件都放到 dist 目录下
path: path.resolve( __dirname,'./dist')
}
}
此时,项目目录为:
|--index.html
|--main.js
|--show.js
|--webpack.config.js
执行webpack命令进行构建后,我们发现目录下多出一个dist目录,里面有一个bundle.js文件,这个时候用浏览器打开index.html文件,会看到Hello,Webpack
5.使用Loader
在引入css文件的时候,webpack不支持解析原生css文件,所以需要Loader机制,这个时候需要将webpack配置修改成:
const path= require ('path');
module.exports = {
//JavaScript 执行入口文件
entry:'./main.js',
output: {
//将所有依赖的模块合并输出到一个bundle.js文件
filename:'bundle.js',
//将输出文件都放到 dist 目录下
path: path.resolve( __dirname,'./dist')
},
module:{
rules:[
{
//用正则表达式去匹配要用该Loader转换的CSS文件
test:/\.css$/,
use:['style-loader','css-loader?minimize'],
}
]
}
}
再重新执行webpack构建之前,要先安装新引入的Loader:
npm i -D style-loader css-loader
在这种情况下,可以直接在main.js中引入css文件:
require('./main.css')
6.使用Plugin
Plugin是用来扩展webpack功能的,通过在构建流程中注入钩子实现,它为webpack带来了很大的灵活性,下面举个例子,通过plugin将注入bundle.js文件中的css提取到单独的文件中,配置修改如下:
const path= require ('path');
const ExtractTextPlugin=require('extract-text-webpack-plugin');
module.exports = {
//JavaScript 执行入口文件
entry:'./main.js',
output: {
//将所有依赖的模块合并输出到一个bundle.js文件
filename:'bundle.js',
//将输出文件都放到 dist 目录下
path: path.resolve( __dirname,'./dist')
},
module:{
rules:[
{
//用正则表达式去匹配要用该Loader转换的CSS文件
test:/\.css$/,
use:['style-loader','css-loader?minimize'],
}
]
},
plugins:[
//从.js文件中提取出来的.css文件的名称
new ExtractTextPlugin({
filename:`[name]_[contenthash:8].css`
}),
]
}
要让以上代码运行起来,需要先安装新引入的插件:
npm i -D extract-text-webpack-plugin
安装成功后执行,我们会发现dist目录下多了一个css文件,bundle.js文件中没有css代码了
7.使用DevServer
DevServer会启动一个HTTP服务器用于服务网络请求,同时会帮助启动Webpack,并接收Webpack发出的文件更变信号,通过WebSocket协议自动刷新网页做到实时预览。
依旧以上面的例子为基础,首先安装DevServer:
npm i -D webpack-dev-server
安装成功后,执行webpack-dev-server
,DevServer就启动了,这个时候HTTP服务器监听在8080端口,DevServer启动后会一直驻留在后台保持运行
我们也可以开启监听模式,修改任何文件后浏览器会自动刷新,我们只需要在启动webpack的时候通过webpack --watch
开启监听模式
模块热替换:能做到不重新加载整个网页的情况下,通过将已更新的模块替换老模块,再重新执行一次来实现实时预览。要开启模块热替换,我们只需要在启动DevServer的时候带上–hot参数;
webpack支持source map,在浏览器运行的js都是编译输出的代码,这些代码可读性很差,我们可以同过source map让我们遇到bug的时候映射代码,让我们在源代码上断点调试,只需要在启动的·时候带上--devtool source-map
8.核心概念
webpack有几个核心概念,在后续的文章中会不断出现:
Entry:
入口Module:
模块,webpack会从配置的Entry开始递归找出递归的模块;Chunk:
代码块Loader:
模块转换器,用于将模块的原内容按照需求转换成新内容Plugin:
扩展插件Output:
输出结果
Webpack 在启动后会从Entry里配置的 Module 开始,递归解析 Entry 依赖的所有 Module 。每找到一个 Module,就会根据配置的 Loader 去找出对应的转换规则,对 Module 进行转换后,再解析出当前 Module 依赖的 Module.这些模块会以 Entry为单位进行分组, 一个Entry及其所有依赖的 Module 被分到一个组也就是一个 Chunk。最后Webpack 会将所有 Chunk 转换成文件输出。在整个流程中, Webpack 会在恰当的时机执行 Plugin 里定义的逻辑。
这篇文章只是粗略讲解了webpack的基础核心功能,下一篇文章会列举webpack常用功能所提供的配置选项,可供速查表使用。