webpack总结
原理
- 代码转换:
typeScript
编译成JavaScript
、scss,less
编译成css
- 文件优化:压缩
JavaScript
、html
、css
文件,压缩合并图片等 - 代码分割:提供多个页面的公共代码,抽离首屏不需要的代码使其异步加载
- 模块合并:模块化的项目里会有很多模块和文件,构建狗能把模块分类合并
- 自动刷新:监听本地源代码,自动重构刷新浏览器
- 扩展性好,插件机制完善
打包过程
- 利用babel完成代码转换,生成单个文件依赖
- 从入口开始分析,生成依赖图谱
- 将引用模块打包为立即执行函数
- 把最终的bundle文件写入bundle.js里
四大核心
- entry:入口,即js入口源文件
- output:生成文件
- loader:文件处理
- plugins:插件
Entry
webpack应该用哪个模块作为入口文件,作为构建内部依赖图的开始,进入入口后,webpack会找到哪些库和模块是入口依赖的,并随即被处理,最后输出到bundle.js的文件中
//单入口:entry是一个字符串
module.exports = {
entry: './src/index.js'
}
//多入口:entry是一个对象
module.exports = {
entry: './src/index.js'
}
Output
告诉webpack在哪输出bundles,以及怎么命名他,在配置文件中均可搞定
//单入口配置项
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js’,
path: __dirname + '/dist'
}
};
//多入口配置项
module.exports = {
entry: {
app: './src/app.js',
search: './src/search.js'
},
output: {
filename: '[name].js',//[name]占位符确保文件名唯一
path: __dirname + '/dist'
}
}
Loader
loader
让webpack能够处理非javaScript
文件,因为webpack自己只能看懂javaScript
。loader
能够把所有类型的文件转换成webpack能够处理的模块,然后利用打包能力对他进行处理
Loader的特点
- 每一个loader的职责都是单一的,只能完成一种转换
- loader本质上是node.js模块,这个模块需要导出一个函数
- loader总是从右到左被调用
常用Loader
处理样式
-
css-loader
加载css文件 -
style-loader
使用style标签把css-loader
内部样式注入到html中 -
less-loader,sass-loader
解析css预处理器
处理js
让我们能够使用最新的js代码(ES6,ES7等)及让我们能够使用基于js进行了拓展的语言,比如JSX等
处理文件
通常使用的两种loader是file-loader
和url-loader
,两者主要的区别是url-loader
能够设置图片的大小限制,如果大小超过限制,则行为等同于file-loader
,如果图片大小不超过限制,那么图片会以base64的形式打包
处理vue单文件
vue-loader
是webpack的加载器魔铠,它让我们的随心所欲的编写.vue
格式的单文件组件。vue-loader
模块允许 webpack
使用单独的加载器模块(例如 sass 或 scss 加载器
)提取和处理每个部分。该设置使我们可以使用 .vue
文件无缝编写程序。
在文章最后,我们会尝试开发一个简易的loader来加深我们的理解
Plugin
专注于处理webpack在编译过程里某个特定任务的功能模块
特点
-
是一个独立的模块
-
模块对外暴露一个js函数
-
函数的原型
(prototype)
上定义了一个注入compiler
对象的apply
方法apply
函数中需要有通过compiler
对象挂载的webpack
事件钩子,钩子的回调中能拿到当前编译的compilation
对象,如果是异步编译插件的话可以拿到回调callback
-
完成自定义自编程流程并处理
complition
对象的内部数据 -
如果异步编译插件的话,数据处理完成后执行
callback
回调
常用Plugin
-
HotModuleReplacementPlugin
代码热替换。因为Hot-Module-Replacement
的热更新是依赖于webpack-dev-server
,后者是在打包文件改变时更新打包文件或者 reload 刷新整个页面,HRM
是只更新修改的部分。 -
HtmlWebpackPlugin
, 生成 html 文件。将 webpack 中entry
配置的相关入口 chunk 和extract-text-webpack-plugin
抽取的 css 样式 插入到该插件提供的template
或者templateContent
配置项指定的内容基础上生成一个 html 文件,具体插入方式是将样式link
插入到head
元素中,script
插入到head
或者body
中。 -
ExtractTextPlugin
, 将 css 成生文件,而非内联 。该插件的主要是为了抽离 css 样式,防止将样式打包在 js 中引起页面样式加载错乱的现象。 -
NoErrorsPlugin
报错但不退出 webpack 进程 -
UglifyJsPlugin
,代码丑化,开发过程中不建议打开。uglifyJsPlugin
用来对 js 文件进行压缩,从而减小 js 文件的大小,加速 load 速度。uglifyJsPlugin
会拖慢 webpack 的编译速度,所有建议在开发简单将其关闭,部署的时候再将其打开。多个 html 共用一个 js 文件(chunk),可用CommonsChunkPlugin
-
purifycss-webpack
。打包编译时,可剔除页面和 js 中未被使用的 css,这样使用第三方的类库时,只加载被使用的类,大大减小 css 体积 -
optimize-css-assets-webpack-plugin
压缩 css,优化 css 结构,利于网页加载和渲染 -
webpack-parallel-uglify-plugin
可以并行运行 UglifyJS 插件,这可以有效减少构建时间
同样的,文章最后我们也会尝试开发一个Plugin
webpack配置
1、Webpack 项目初始化
1、新建 Web 项目
新建一个目录,再进入项目根目录执行 npm init 来初始化最简单的采用了模块化开发的项目;最终生成 package.json 文件;
$ npm init
复制代码
2、安装 Webpack 到本项目
(1)查看 Webpack 版本
运行以下命令可以查看 Webpack 的版本号
$ npm view webpack versions
复制代码
(2)安装 Webpack
可以选择(1)步骤罗列得到的 Webpack 版本号,也可以安装最新稳定版、最新体验版本,相关命令如下所示,我选择安装 4.28.2 版本(没有为什么,就想装个 4.x 的版本);
// 安装指定版本
npm i -D aaa@qq.com
// 安装最新稳定版
npm i -D webpack
// 安装最新体验版本
npm i -D aaa@qq.com
复制代码
(3)安装 Webpack 脚手架
需要安装 Webpack 脚手架,才能在命令窗口执行 Webpack 命令,运行以下命令安装 Webpack 脚手架;
$ npm i -D webpack-cli
复制代码
3、使用 Webpack
使用 Webpack 构建一个采用 CommonJS 模块化编写的项目;
(1)新建页面入口文件 index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Webpack</title>
</head>
<body>
<!--导入 Webpack 输出的 JavaScript 文件-->
<script src="./dist/bundle.js"></script>
</body>
</html>
复制代码
(2)新建需要用到的 JS 文件
show.js 文件
// 操作 DOM 元素,把 content 显示到网页上
function show(content) {
window.document.getElementById('app').innerText = 'Hello,' + content;
}
// 通过 CommonJS 规范导出 show 函数
module.exports = show;
复制代码
main.js 文件
// 通过 CommonJS 规范导入 show 函数
const show = require('./show.js');
// 执行 show 函数
show('Webpack');
复制代码
(3)新建 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'),
}
};
复制代码
(4)执行 webpack 命令进行构建
在 package.json 文件中配置编译命令,如下所示:
"scripts": {
"build": "webpack --config webpack.config.js",
},
复制代码
执行以下命令进行项目的 Webpack 编译,成功后会在项目根目录下生成编译目录 dist ;
$ npm run build
复制代码
(5)运行 index.html
编译成功后,我们用浏览器打开 index.html 文件,能看到页面成功显示 “Hello Webpack”;
2、Loader 配置
本节通过为之前的例子添加样式,来尝试使用 Loader;
(1)新建样式文件 main.css
#app{
text-align: center;
color:'#999';
}
复制代码
(2)将 main.css 文件引入入口文件 main.js 中,如下所示:
// 通过 CommonJS 规范导入 CSS 模块
require('./main.css');
// 通过 CommonJS 规范导入 show 函数
const show = require('./show.js');
// 执行 show 函数
show('Webpack');
复制代码
(3)Loader 配置
以上修改后去执行 Webpack 构建是会报错的,因为 Webpack 不原生支持解析 CSS 文件。要支持非 JavaScript 类型的文件,需要使用 Webpack 的 Loader 机制;
(3.1)运行以下命令,安装 style-loader 和 css-loader,其中:
- css-loader 用于读取 CSS 文件;
- style-loader 把 CSS 内容注入到 JavaScript 中;
$ npm i -D style-loader css-loader
复制代码
(3.2)进行以下配置
module:{
rules:[
{
// 用正则去匹配要用该 loader 转换的 CSS 文件
test:/\.css$/,
use:['style-loader','css-loader']
}
]
}
复制代码
(4)查看结果
编译后,刷新 index.html ,查看刚刚的样式 loader 已经起作用;
3、Plugin 配置
(1)安装样式提取插件 extract-text-webpack-plugin
$ npm i -D aaa@qq.com
复制代码
(2)plugin 文件配置如下
module:{
rules:[
{
// 用正则去匹配要用该 loader 转换的 CSS 文件
test:/\.css$/,
use:ExtractTextPlugin.extract({
use:['css-loader']
}),
}
]
},
plugins:[
new ExtractTextPlugin({
// 从 .js 文件中提取出来的 .css 文件的名称
filename:`[name]_[hash:8].css`
}),
]
复制代码
(3)查看结果
通过以上配置后,执行 Webapack 的执行命令,发现在 dist 目录下,生成对应的 css 文件;存在的坑点:
- 我们需要手动将生成的 css 文件引入到 index.html 中;
- 修改 css 文件后,会生成新的 css 文件,原先的不会删除;
4、使用 DevServer
(1)执行以下命令安装 webpack-dev-server
$ npm i -D webpack-dev-server
复制代码
在 package.json 中配置启动命令
"scripts": {
"build": "webpack --config webpack.config.js",
"dev": "webpack-dev-server",
},
复制代码
运行命令后,就可以启动 HTTP 服务
$ npm run dev
复制代码
启动结果如下所示,我们可以通过 http://localhost:8080/ 访问我们的 index.html 的demo
(2)实时预览
我们在运行命令后面添加参数 --watch 实现实时预览,配置如下所示:
"scripts": {
"dev": "webpack-dev-server --watch"
},
复制代码
然后我们修改 main.js 的传入参数,发现并不能实时预览,也没有报错!!! why?
踩坑:
在 index.html 中需要将 js 的路径修改为:
<script src="bundle.js"></script>
复制代码
而不能是之前的(因为这个是编译生成的,并不是通过 devServer 生成放在内存的)
<script src="./dist/bundle.js"></script>
复制代码
(3)模块热替换
可以通过配置 – hot 进行模块热替换;
番外
没错,文章最后了,现在我们开始开发一个loader
和plugin
开发一个loader
需求:开发一个loader
,将KobeBryant转换为
mamba forever`。
1.编写loader
新建目录my-loader
作为名字,执行npm init -y
快速创建一个模块化项目,然后新建index.js
module.exports = function(content) {
return content && content.replace(/KobeBryant/gi , 'mamba forever')
}
2.注册模块
一般情况下,我们需要的loader都是利用npm安装,但是我们可以使用npm link
命令,以求在不发布模块的情况下,将本地的源码链接到项目的node_modules
下。在项目的根目录下执行以下命令
λ npm link my-loader
3.在webpack中配置loader
在webpack.base.config.js
中加上以下配置
{
test:/\.js/,
loader:'my-loader'
}
这样我们js里所有的字符串KobeBryant
就会替换为mamba forever
了
4.配置参数
前面我们是写死的替换,如果我们想要通过配置项来进行改变怎么办
// index.js
var utils = require('loader-utils')
module.exports = function (content) {
const options = utils.getOptions(this)
return content && content.replace(/KobeBryant/gi , options.name)
}
// webpack.base.conf.js
{
test:/\.js/,
use: {
loader: 'my-loader',
options: {
name: 'KobeBryant',
}
}
}
开发一个Plugin
- webpack在编译中,会广播很多事件
- webpack应用了观察者模式,插件可以监听webpack事件来触发对应的处理逻辑
- 插件中可以使用webpack的api
1.编写插件
创建目录my-plugin
作为编写的插件,执行npm init -y
快速创建一个模块化项目,然后新建index.js
class MyPlugin {
constructor(doneCakkback , failCallback) {
//保存在创建实例时候传入的回调函数
this.doneCallback = doneCallback
this.failCallback = failCallback
}
apply(compiler) {
//成功完成一次完整的编译和输出时,会触发done
compile.plugin('done' , stats => {
this.doneCallback(stats)
})
//出现异常的时候,触发fail
compiler.plugin('fail' , err => {
this.failCallback(err)
})
}
}
module.exports = MyPlugin
2.注册模块
同样的,我们link一下,以便于我们能直接使用本地的模块
λ npm link my-plugin
3.配置插件
在webpack.base.conf.js
加上以下配置
plugins: [
new MyPlugin(
stats => {
console.log('编译成功')
},
err => {
console.error('编译失败')
}
)
]
上一篇: 搞定技术面试-图算法相关问题(上)
下一篇: word2vec_basic.py详解