1 Webpack起步
1.1 什么是Webpack?
At its core, webpack is a static module bundler for modern JavaScript applications.
从本质上来讲,webpack 是一个现代的 JavaScript 应用的静态模块打包工具。也就是说 webpack 用于 js 的模块化和打包,接下来通过这两块来介绍 webpack。
模块化
webpack 其中一个核心就是让我们可能进行模块化开发,并且会帮助我们处理模块间的依赖关系。不仅仅是 JavaScript 文件,CSS、图片、json 文件等等在 webpack 中都可以被当做模块来使用,这就是 webpack 中模块化的概念。
打包
就是将 webpack 中的各种资源模块进行打包合并成一个或多个包(Bundle),并且在打包的过程中,还可以对资源进行处理,比如压缩图片,将 scss 转成 css,将 ES6 语法转成 ES5 语法,将 TypeScript 转成JavaScript 等等操作。
打包的操作也可以通过 grunt/gulp 完成,那么它和 webpack 有什么不同呢?
webpack 和 grunt/gulp 的对比
*grunt/gulp 的核心是 Task *
我们可以配置一系列的 task ,并且定义 task 要处理的事务(例如ES6、ts转化,图片压缩,scss转成css),之后让 grunt/gulp 来依次执行这些 task,而且让整个流程自动化。所以grunt/gulp也被称为前端自动化任务管理工具。
这是一个 task 例子,就是将src下面的所有js文件转成ES5的语法,并且最终输出到dist文件夹中。

什么时候用 grunt/gulp 呢?
如果你的工程模块依赖非常简单,甚至是没有用到模块化的概念。只需要进行简单的合并、压缩,就使用grunt/gulp 即可。
什么时候用 webpack 呢?
但是如果整个项目使用了模块化管理,而且相互依赖非常强,我们就可以使用更加强大的webpack了。
grunt/gulp 更加强调的是前端流程的自动化,模块化不是它的核心。
webpack 更加强调模块化开发管理,而文件压缩合并、预处理等功能,是他附带的功能。
1.2 webpack 的安装
webpack依赖node环境。node环境依赖众多包,所以需要npm,npm(node packages manager)node包管理工具。

首先检查是否安装 node 版本
1 | node -v |
全局安装webpack
1 | npm install webpack@3.6.0 -g |
局部安装webpack(后续才需要)
1 | cd 对应目录 |
在终端直接执行 webpack 命令,使用的全局安装的 webpack
当在 package.json 中定义了 scripts 时,其中包含了 webpack 命令,那么使用的是局部 webpack
1.3 webpack 初体验
1)目录结构
我们来模拟一个 应用的目录,文件的目录结构应该如下:

dist文件夹:用于存放之后打包的文件
src文件夹:用于存放我们写的源文件
- main.js:项目的入口文件。
- mathUtils.js:定义了一些数学工具函数,可以在其他地方引用,并且使用。
index.html:浏览器打开展示的首页html
package.json:通过 npm init 生成的,npm包管理的文件(暂时没有用上,后面才会用上)
2)编写模块化的 js 代码(这里使用 common Js 规范,其他规范也可以),比如 mathUtils.js 中封装一些数据方法
1 | function add(num1, num2) { |
3)在入口 js(main.js )中导入模块,使用 add 和 sub 方法
1 | // 使用 common JS导入模块 |
4)使用webpack命令打包js文件
浏览器不认识 main.js 和 mathUtils.js 里面的 common JS 语法,就不会处理这些方法,因此我就需要借助于 webpack 给我们解析成浏览器可以识别的语法,也就是打包。
webpack 会自动识别入口里面的依赖 ,所以这里打包入口 main.js 即可,这里需要在项目的根目录下进行:
这里是手动通过命令打包,后面会通过 webpack 配置自动打包 js文件
1 | webpack src/main.js dist/bundle.js |

4)编写 index.html 导入bundle.js
1 | <script src="./dist/bundle.js"></script> |
最后运行页面。
2 webpack 配置
2.1 初始化 node 包
在 webpack 中我们需要配置模块的出口和入口,但是模块的出入需要一个绝对路径,因此我们需要动态获取项目的根目录。
node 导包可以动态获取项目的 path 包,而 node 的包需要使用 npm 来进行管理,因此我们首先需要初始化node包。
1 | npm init |
初始化之后,项目根目录自动生成一个 package.json 文件。

package.json中定义启动
我们可以在package.json的 *scripts *中定义自己的执行脚本。package.json中 的 scripts 的脚本在执行时,会按照一定的顺序寻找命令对应的位置。
比如下面添加了一个叫 build的脚本,npm run build ==== webpack(这个指令启动webpack 配置)
1 | "scripts": { |
2.2 配置 入口 和 出口
webpack.config.js 文件
1 | const path = require('path') //导入node中的包 |
配置完成之后,我们使用 webpack 指令,就可以打包文件。
1 | npm run build |
2.3 局部安装webpack
一个项目往往依赖特定的 webpack 版本,全局的版本可能很这个项目的 webpack 版本不一致,导出打包出现问题。所以通常一个项目,都有自己局部的 webpack。
项目中需要安装自己局部的webpack
1 | npm install webpack@3.6.0 --save-dev //开发时依赖 |

当我们平时在终端使用 webpack 命令,绑定的 webpack 依赖就是全局的 webpack,只有我们进入当前文件夹下的 webpack 文件,使用指令绑定的才是局部的 webpack。
而通过在 script 指令创建的 webpack 命令,这个指令会默然先在局部查找webpack,也就是默认使用 局部的 webpack。为了避免上面所说的 webpack 全局和局部 版本不一致,使用 script 指令是极好的。
3 webpack loader
在开发中我们不仅仅有基本的js代码处理,我们也需要加载css、图片,也包括一些高级的将ES6转成ES5代码,将TypeScript转成ES5代码,将scss、less转成css,将.jsx、.vue文件转成js文件等等。对于webpack本身的能力来说,对于这些转化是不支持的。
通过给 webpack 扩展对应的 loader 就可以解决这一问题。大部分loader我们都可以在 webpack的官网 中找到,并且学习对应的用法。
loader使用过程:
步骤一:通过npm安装需要使用的loader
步骤二:在webpack.config.js中的modules关键字下进行配置
3.1 webpack 打包 css文件
webpack loader 打包 css文件,是在 1.3 webpack 初体验上进行的,前提需要编写入口 js,并且在 index .html 中导入 入口js。
1.编写css文件,常见一个 normal.css
2.在入口 main.js 导入依赖 normal.css,webpack 会通过入口,找到所有依赖的文件并打包处理。
1 | require("./css/normal.css") |
3.安装 css loader
按照官方说明,依次安装
css-loader :解析 CSS 文件后,使用 import 加载,并且返回 CSS 代码
style-loader :将模块的导出作为样式添加到 DOM 中
4.使用 css-loader 和 style-loader
webpack使用多个loader是从右往左解析的,所以需要将 css-loader 放在 style-loader 右边,先加载后解析。
1 | module: { |
因为 webpack 配置常常涉及版本问题,下面的网站可以找到css-loader 和 style-loader的其他版本(右侧 version 版本)
- css-loader :https://www.npmjs.com/package/css-loader
- style-loader:https://www.npmjs.com/package/style-loader
5.打包 npm run build。
此时样式成加载解析到DOM元素上。
总结:
webpack可以帮我们打包 js文件,所有模块化规范都适用。前提是需要指定入口文件(main.js)和输出的文件(bundle.js),输出文件的路径需要动态获取,通过 npm 导入 node 的 path 包。考虑开发中每个人使用的 webpack 版本不一致,需要在项目内安装局部 webpack。
3.2 less 文件处理
在刚才的 css 文件处理的基础上进行如下操作:
1)编写 less 文件
2)在入口(main.js)导入less 依赖
3)去 webpack 官网下载与less 相关的 loader
less-loader
加载和转译 LESS 文件
4)在 config.js 里面配置 less文件的 rules
5)启动webpack npm run build
结果发现对 less 文件的处理报错了,首先想到可能是版本问题( 在package.json 中查看),果然 less-loader 版本过高。
解决方法:卸载安装的高版本的less-loader,安装指定低版本的less-loade
1 | npm uninstall less-loader |
3.3 图片打包处理
1)添加图片因为一般图片都是在 css 或者 html 中引入,所以不需要在出口中注入依赖
2)安装 url-loader
url-loader
像 file loader 一样工作,但如果文件小于限制,可以返回 data URL
- 配置 url-loader
注意 url-loader 的 limit 属性(可以自己设置值)将决定你的图片是否可以通过 base64 编译。
拓展:jpg 的全名、正式扩展名是 jpeg
- name 属性:设置打包之后文件的名字格式,这是为了让我们更清楚知道文件的名字
1 | { |
安装之后背景图是通过base64显示出来的

Base64图片编码具有不可读性,在没有上传文件的条件下可以将图片插入其它的网页、编辑器中。在web网上一般用于小图片上,不仅可以减少图片的请求数量(集合到js、css代码中),还可以防止因为一些相对路径等问题导致图片404错误。
3) 安装并配置 file-loader
如果图片大小超过 limit ,就需要安装 file-loader
file-loader
将文件发送到输出文件夹,并返回(相对)URL
4)配置 publicPath 路径
运行 webpack 虽然没有报错,但是背景图片不显示,因为打包之后的图片是存储在 dist 文件夹内的,正确的 url 是dist/be1b684...jpg)
,现在的 url 如下图。

我们整个程序是打包在dist文件夹下的,所以这里我们需要在路径下再添加一个dist/ 相关配置在模块的出口 output
1 | publicPath: 'dist/' |
4)运行webpack 打开页面
3.4 ES6 转 ES5
将 ES6 的语法转成 ES5,需要使用 babel
babel-loader
加载 ES2015+ 代码,然后使用 Babel 转译为 ES5
1)在webpack中,我们直接使用 babel 对应的 loader 就可以
1 | npm install --save-dev babel-loader@7 babel-core babel-preset-es2015 |
2)配置webpack.config.js文件
1 | { |
3)运行 webpack,之后在 bundle.js 中搜索 const,发现全部转化为 var 关键字。

4 vue
4.1 webpack 配置 vue
我们的 webpack 环境中集成 Vuejs
1 安装 vue ,vue 是运行时依赖 不是开发时依赖(dev)
1 | npm install vue --save |
2 导入vue 模块,编写 vue 代码
1 | import Vue from 'vue'//这里使用的 export default,重命名是 Vue |
运行却发现报错,报错内容如下
bundle.js:1320 [Vue warn]: You are using the runtime-only build of Vue where the template compiler is not available. Either pre-compile the templates into render functions, or use the compiler-included build.
首先介绍两个概念,上面的报错指出我们的代码中有 template,却使用的是 runtime-only 版本的 Vue ,因此我们需要修改配置使得运行时指向 runtime-compiler 版本的 Vue。
- runtime-only:代码中不可以有 任何的 template
- runtime-compiler :代码中,可以有 template , 因为有 compiler 可以用于编译 template
3 修改webpack的配置
我们需要在 config.js 中进行如下配置。webpack 打包的时候,会去局部环境中区找 vue.esm.js,这个 js 版本包含着上面所说的 runtime-compiler。
1 | resolve: { |
4.2 el 和 template 区别
首先了解 SPA(simple page web application) ,vue 应用就是这种单页面应用,所以 index.html 就是唯一的,如果里面使用组件就需要频繁修改 index 页面,这是不友好的操作。如何解决?
我们尝试着在 vue实例的 template 属性中编写 index 页面的内容。
在Vue实例中,我们定义了 el 属性,用于和 index.html 中的 #app 进行绑定,让 Vue 实例之后可以管理它其中的内容。这里,我们可以将div元素中的 内容删掉,只保留一个基本的 id 为div的元素。将原来的内容放到 Vue实例中的 template 中
1 | const app = new Vue({ |
结果页面正常运行,通过调试可以发现 #app 完全被 template 内容替代。

el 和 template 模板的关系是什么呢?
el 用于指定 Vue 要管理的 DOM,可以帮助解析其中的指令、事件监听等等。而如果Vue实例中同时指定了template,那么 template 模板的内容会替换掉挂载的对应 el 的模板。
这样做有什么好处呢?
这样做之后我们就不需要在以后的开发中再次操作 index.html,只需要在 template 中写入对应的标签即可
但是,书写template 模块非常麻烦怎么办呢?
没有关系,稍后我们会将template模板中的内容进行抽离。
会分成三部分书写:template、script、style,结构变得非常清晰。
4.3 Vue组件化开发引入
1.新建一个 app.js,用来编写 App 组件,并导出
1 | // 下面这个对象相当于一个模板,导出模板 |
2.入口(main.js)导入 App 组件
组件标签我们以前是写在 index.html 的 #app div里面,现在写在 template ,因为template 会替代 #app div里的内容。
1 | // 导入 app.js |
3.app.js 替换为 vue component 文件
在 app.js 里面模板和 js 代码混在一起,不易查看。vue component 文件可以将模板和组件分离。
4.安装 vue loader 并配置
1 | npm install vue-loader vue-template-compiler --save-dev// vue-template-compiler是编译模板 |
1 | { |
5.运行 webpack
如果报错无法识别 vue 文件,可能是 vue-loader 版本问题,推荐一种简单做法。
- 修改 package.json 文件中 devDependencies 里的 vue-loader为 13.0.0
1 | "vue-loader": "^13.0.0",//安装13.0.0 - 14 版本之间某一版本 |
- 运行 npm install 安装刚才修改的 loader,就可以了
再次运行发现,项目完整地展示出来了。
5 webpack plugin
plugin 是插件的意思,通常是用于对某个现有的架构进行扩展。webpack 中的插件,就是对webpack现有功能的各种扩展,比如打包优化,文件压缩等等。
plugin 的使用过程:
步骤一:通过 npm 安装需要使用的 plugins (某些 webpack 已经内置的插件不需要安装)
步骤二:在 webpack.config.js 中的 plugins 中配置插件。
5.1 添加版权的Plugin
BannerPlugin,属于webpack自带的插件,可以为打包的文件添加版权声明。所以我们就不需要安装 plugin,只要配置 webpack.config.js 的文件即可。
1 | const webpack = require('webpack') |
重新打包程序:查看bundle.js文件的头部,就可看见我们添加的版权
5.2 打包html的plugin
在真实发布项目时,发布的是 dist 文件夹中的内容,但是目前 index.html 还没有打包到 dist 文件夹中去。所以,我们需要将 index.html 文件打包到 dist 文件夹中,这个时候就可以使用 HtmlWebpackPlugin 插件 。
HtmlWebpackPlugin 插件可以:
自动生成一个index.html文件(可以指定模板来生成)
将打包的js文件,自动通过script标签插入到body中
使用 HtmlWebpackPlugin 插件,
1)安装 HtmlWebpackPlugin 插件
1 | npm install html-webpack-plugin --save-dev |
2)修改 webpack.config.js 文件中 plugins 配置
这里的 template 表示根据什么模板来生成index.html 。index.html 中可以删除引入bundle.js 插件会自动引入,另外,我们需要删除之前在output中添加的 publicPath属性,否则插入的script标签中的src可能会有问题
1 | const HtmlWebpackPlugin = require('html-webpack-plugin') |
因为所有的文件都打包到 dist 目录下面去了 ,所以 output 里的 publicPath 也不需要设置了
3)运行 webpack 报错 可能是 html-webpack-plugin 的问题,根据上面介绍的简单方法修改版本,在运行,就会看到 dist 文件夹自动生成一个 index.html.
1 | "html-webpack-plugin": "^3.2.0", |
5.3 js 压缩的 Plugin
在项目发布之前,我们必然需要对js等文件进行压缩处理。
我们使用一个第三方的插件 uglifyjs-webpack-plugin ,并且版本号指定 1.1.1,和 CLI2 保持一致。
1) 安装插件
1 | npm install uglifyjs-webpack-plugin@1.1.1 --save-dev |
2)修改webpack.config.js文件,使用插件:
1 | const uglifyJsPlugin = require('uglifyjs-webpack-plugin') |
3)运行 webpack ,查看打包后的bunlde.js文件,是已经被压缩过了。
6 搭建本地服务器
webpack 提供了一个可选的本地开发服务器,这个本地服务器基于 node.js ,内部使用 express 框架,可以实现我们想要的让浏览器自动刷新显示我们修改后的结果。但是它是一个单独的模块,在 webpack 中使用之前需要先安装它:
6.1 搭建本地服务器
1)安装本地服务器
1 | npm install --save-dev webpack-dev-server@2.9.1 |
2)配置
1 | devServer: { |
devserver 也是作为 webpack 中的一个选项,选项本身可以设置如下属性:
contentBase:为哪一个文件夹提供本地服务,默认是根文件夹,我们这里要填写 ./dist
port:端口号
inline:页面实时刷新
historyApiFallback:在 SPA 页面中,依赖 HTML5 的 history 模式
3)启动服务器
因为刚才安装的服务器是开发时依赖,只在项目本地,如果去终端直接敲命令,全局就会找不到服务器。
我们可以再配置另外一个 scripts:
1 | "dev": "webpack-dev-server --open" //--open参数表示直接打开浏览器 |
6. 2 webpack 配置分离
在 webpack.config.js 中的配置有的是开发时需要的,有的是生产时需要的,因此我们可以把配置分为3类,分别在3个文件中设置
1) 配置文件分类
- base.config.js :开发时和生产时都需要的配置
- dev.config.js: development 开发时配置,比如构建本地服务器
- prod.config.js: production 生产时配置,比如压缩 bundle.js
2)安装webpack-merge 合并文件
1 | npm install webpack-merge --sav |
以 prod.config.js 为例,导入 webpackMerge 合并 baseConfig 和当前配置,dev.config.js 类似
1 | const uglifyJsPlugin = require('uglifyjs-webpack-plugin') |
修改 script 快捷操作
指定运行 webpack 时,查找 config.js 文件的路径
1 | "scripts": { |
3)测试,修改output 路径
因为 config.js 文件现在都存储在 build 文件夹下面,所以我们需要修改 output 的输出路径。 dist 文件位于 config.js 文件的父目录的同级目录下面。
1 | output: { |