webpack是一个JavaScript应用的静态模块打包工具-----模块 和 打包
1,将webpack中的各种资源模块进行打包合并成一个或多个包(Bundle)。
2,在打包的过程中,还可以对资源进行处理,比如压缩图片,将scss转成css,将ES6语法转成ES5语法,将TypeScript转成JavaScript等等操作。
grunt/gulp和webpack的不同:
1, grunt/gulp更加强调的是前端流程的自动化,模块化不是它的核心。
2, webpack更加强调模块化开发管理,而文件压缩合并、预处理等功能,是他附带的功能
webpack的安装
安装webpack首先需要安装Node.js,Node.js自带了软件包管理工具npm
1,全局安装webpack: npm install webpack@3.6.0 -g
2,局部安装webpack: npm install webpack@3.6.0 --save-dev
–save-dev`是开发时依赖,项目打包后不需要继续使用的。
为什么全局安装后,还需要局部安装呢?
在终端直接执行webpack命令,使用的全局安装的webpack
一个项目往往依赖特定的webpack版本,全局的版本可能很这个项目的webpack版本不一致,导出打包出现问题
当在package.json中定义了scripts时,其中包含了webpack命令,那么使用的是局部webpack
文件和文件夹解析:
1,dist文件夹:用于存放之后打包的文件
2,src文件夹:用于存放我们写的源文件
main.js:项目的入口文件。
mathUtils.js:定义了一些数学工具函数,可在其他地方引用并使用。
3,index.html:浏览器打开展示的首页html,
4,package.json:通过npm init生成的,npm包管理的文件
1,多个js文件打包:使用webpack的指令------------webpack scr/main.js dist/bundle.js
打包后会在dist文件下,生成一个bundle.js文件
2,引用
bundle.js文件,只需要将这个js文件在index.html中引入----
webpack.config.js文件 : webpack的命令的入口参数和出口参数
启动局部webpack打包: node_modules/.bin/webpack
package.json中的scripts的脚本在执行时,会按照一定的顺序寻找命令对应的位置。
首先,会寻找本地的node_modules/.bin路径中对应的命令。如果没有找到,会去全局的环境变量中寻找。
执行build指令:npm run build
用webpack来处理我们写的js代码,并且webpack会自动处理js之间相关的依赖。
webpack扩展对应的loader: 加载css、图片,也包括一些高级的将ES6转成ES5代码,将TypeScript转成ES5代码,将scss、less转成css,将.jsx、.vue文件转成js文件等等。
loader使用过程:
步骤一:通过npm安装需要使用的loader
步骤二:在webpack.config.js中的modules关键字下进行配置
在src目录中,创建一个css文件,其中创建一个normal.css文件。
npm install css-loader --save-dev // css-loader只负责加载css文件
npm install style-loader --save-dev // style-loader 将css具体样式添加到DOM中
按照逻辑,在处理css文件过程中,应该是css-loader先加载css文件,再由style-loader来进行进一步的处理
为什么会将style-loader放在前面呢?: 因为webpack在读取使用的loader的过程中,是按照从右向左的顺序读取的。
npm install --save-dev less-loader less
url-loader 像 file loader 一样工作,但如果文件小于限制,可以返回 data URL
file-loader 将文件发送到输出文件夹,并返回(相对)URL
npm install --save-dev url-loader
limit属性的作用,当图片小于8kb时,对图片进行base64编码,
大于8kb的图片,会通过file-loader进行处理
npm install --save-dev file-loader
webpack自动生成一个非常长的名字:一个32位hash值,目的是防止名字重复
name: './img/[name].[hash:8].[ext]' // 修改文件名称
一些对ES6还不支持的浏览器没有办法很好的运行代码
npm install --save-dev babel-loader@7 babel-core babel-preset-es2015
webpack环境中集成Vuejs,安装vue: npm install vue --save
浏览器中有报错,因为使用的是runtime-only版本的Vue
resolve:{
'vue$': 'vue/dist/vue.esm.js' // 找到开发时可以用的vue文件
}
},
el和template模板的关系是什么呢?
el用于指定Vue要管理的DOM,可以帮助解析其中的指令、事件监听等等。
如果Vue实例中同时指定了template,那么template模板的内容会替换掉挂载的对应el的模板。
1,安装vue-loader和vue-template-compiler:
npm install vue-loader vue-template-compiler --save-dev
2,修改webpack.config.js的配置文件:{ test: /\.vue$/, use: ['vue-loader'] }
loader和plugin区别
plugin的使用过程:
a,添加版权的Plugin:BannerPlugin
b,打包html的plugin: html-webpack-plugin
index.html文件打包到dist文件夹中
npm install html-webpack-plugin --save-dev
c,js压缩的Plugin:uglifyjs-webpack-plugin
uglifyjs-webpack-plugin,并且版本号指定1.1.1,和CLI2保持一致
npm install uglifyjs-webpack-plugin@1.1.1 --save-dev
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const UglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin');
plugins: [
new webpack.BannerPlugin('最终版权归 Cookie_fzx所有'),
new HtmlWebpackPlugin({ template: 'index.html' }),
// new UglifyjsWebpackPlugin(), // 压缩,提交时使用
],
webpack提供了一个可选的本地开发服务器,这个本地服务器基于node.js搭建,内部使用express框架,让浏览器自动刷新显示修改后的结果。
1,安装: npm install --save-dev webpack-dev-server@2.9.1
2,webpack.config.js文件配置修改如下:
devServer: { contentBase: './dist', inline: true } // 本地服务器,开发时使用
3,配置scripts:"dev" : "webpack-dev-server --open
–open参数表示直接打开浏览器
配置webpack.config.js文件
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const UglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin');
module.exports= {
entry: './src/main.js',
output: {
path: path.resolve(__dirname, 'dist'), //
filename: 'bundle.js',
// publicPath: './dist/'
},
module: {
rules: [
{
// \. 对 .进行转义, $结尾 ---- 目的是找到css文件
test: /\.css$/,
// 使用多个loader时,是从左到右
// style-loader 负责将样式加载到DOM中
// css-loader 只负责将css加载
use: ['style-loader', 'css-loader']
},
{
test: /\.less$/,
use: [{
loader: "style-loader" // creates style nodes from JS strings
}, {
loader: "css-loader" // translates CSS into CommonJS
}, {
loader: "less-loader" // compiles Less to CSS
}
]
},
{
test: /\.(png|jpg|gif)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 11000,
name: './img/[name].[hash:8].[ext]'
}
}
]
},
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['es2015']
}
}
},
{
test: /\.vue$/,
use: ['vue-loader']
}
]
},
resolve:{
// import导入时,不需要加文件后缀
extensions : ['.vue', '.js', '.css'],
alias: { // 起别名
// 找到开发时可以用的vue文件
'vue$': 'vue/dist/vue.esm.js'
}
},
plugins: [
new webpack.BannerPlugin('最终版权归 Cookie_fzx所有'),
new HtmlWebpackPlugin({
template: 'index.html'
}),
// new UglifyjsWebpackPlugin(), // 压缩,提交时使用
],
devServer: { // 本地服务器,开发时使用
contentBase: './dist',
inline: true
}
};
三者都是前端构建工具,grunt和gulp在早期比较流行,现在webpack相对来说比较主流,不过一些轻量化的任务还是会用gulp来处理,比如单独打包CSS文件等。
grunt和gulp是基于任务和流(Task、Stream)的。
类似jQuery,找到一个(或一类)文件,对其做一系列链式操作,更新流上的数据, 整条链式操作构成了一个任务,多个任务就构成了整个web的构建流程。
webpack是基于入口的。webpack会自动地递归解析入口所需要加载的所有资源文件,然后用不同的Loader来处理不同的文件,用Plugin来扩展webpack功能。
1,从构建思路来说
gulp和grunt需要开发者将整个前端构建过程拆分成多个Task
,并合理控制所有Task
的调用关系 webpack需要开发者找到入口,并需要清楚对于不同的资源应该使用什么Loader做何种解析和加工
2,对于知识背景来说
gulp更像后端开发者的思路,需要对于整个流程了如指掌, webpack更倾向于前端开发者的思路
同样是基于入口的打包工具还有以下几个主流的:
webpack rollup parcel
从应用场景上来看:
webpack适用于大型复杂的前端站点构建
rollup适用于基础库的打包,如vue、react
parcel适用于简单的实验性项目,他可以满足低门槛的快速看到效果
由于parcel在打包过程中给出的调试信息十分有限,所以一旦打包出错难以调试,所以不建议复杂的项目使用parcel
webpack的打包流程:
初始化配置对象,创建compiler对象
实例化插件,调用插件的apply方法,挂载插件的监听
从入口文件执行编译,按照文件类型调用相应的loader,在合适的时间调用plugin执行,并查找各个模块的依赖
将编译后的代码组装成一个个代码块(chunk),并安依赖和配置确定输出内容
根据output把文件输出到对象的目录下
9、webpack构建过程
从entry里配置的module开始递归解析entry依赖的所有module
每找到一个module,就会根据配置的loader去找对应的转换规则
对module进行转换后,再解析出当前module依赖的module
这些模块会以entry为单位分组,一个entry和其所有依赖的module被分到一个组Chunk
最后webpack会把所有Chunk转换成文件输出
在整个流程中webpack会在恰当的时机执行plugin里定义的逻辑
Entry:入口,Webpack 执行构建的第一步将从 Entry 开始,可抽象成输入。
Module:模块,在 Webpack 里一切皆模块,一个模块对应着一个文件。Webpack 会从配置的 Entry 开始递归找出所有依赖的模块。
Chunk:代码块,一个 Chunk 由多个模块组合而成,用于代码合并与分割。
Loader:模块转换器,用于把模块原内容按照需求转换成新内容。
Plugin:扩展插件,在 Webpack 构建流程中的特定时机会广播出对应的事件,插件可以监听这些事件的发生,在特定时机做对应的事情。```
file-loader:把文件输出到一个文件夹中,在代码中通过相对 URL 去引用输出的文件
url-loader:和 file-loader 类似,但是能在文件很小的情况下以 base64 的方式把文件内容注入到代码中去
source-map-loader:加载额外的 Source Map 文件,以方便断点调试
image-loader:加载并且压缩图片文件
babel-loader:把 ES6 转换成 ES5
css-loader:加载 CSS,支持模块化、压缩、文件导入等特性
style-loader:把 CSS 代码注入到 JavaScript 中,通过 DOM 操作去加载 CSS。
eslint-loader:通过 ESLint 检查 JavaScript 代码
4.有哪些常见的Plugin?他们是解决什么问题的?
define-plugin:定义环境变量
commons-chunk-plugin:提取公共代码
uglifyjs-webpack-plugin:通过UglifyES压缩ES6代码
不同的作用
Loader直译为"加载器"。Webpack将一切文件视为模块,但是webpack原生是只能解析js文件,如果想将其他文件也打包的话,就会用到loader。 所以Loader的作用是让webpack拥有了加载和解析非JavaScript文件的能力。
Plugin直译为"插件"。Plugin可以扩展webpack的功能,让webpack具有更多的灵活性。 在 Webpack 运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。不同的用法
Loader在module.rules中配置,也就是说他作为模块的解析规则而存在。 类型为数组,每一项都是一个Object,里面描述了对于什么类型的文件(test),使用什么加载(loader)和使用的参数(options)
Plugin在plugins中单独配置。 类型为数组,每一项是一个plugin的实例,参数都通过构造函数传入。
Loader像一个"翻译官"把读到的源文件内容转义成新的文件内容,并且每个Loader通过链式操作,将源文件一步步翻译成想要的样子。
编写Loader时要遵循单一原则,每个Loader只做一种"转义"工作。 每个Loader的拿到的是源文件内容(source),可以通过返回值的方式将处理后的内容输出,也可以调用this.callback()方法,将内容返回给webpack。 还可以通过 this.async()生成一个callback函数,再用这个callback将处理后的内容输出出去。 此外webpack还为开发者准备了开发loader的工具函数集——loader-utils。
相对于Loader而言,Plugin的编写就灵活了许多。 webpack在运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。
webpack的热更新又称热替换(Hot Module Replacement),缩写为HMR。 这个机制可以做到不用刷新浏览器而将新变更的模块替换掉旧的模块。
1,Webpack编译期,为需要热更新的 entry 注入热更新代码(EventSource通信)
2,页面首次打开后,服务端与客户端通过 EventSource 建立通信渠道,把下一次的 hash 返回前端
3,客户端获取到hash,这个hash将作为下一次请求服务端 hot-update.js 和 hot-update.json的hash,
4,修改页面代码后,Webpack 监听到文件修改后,开始编译,编译完成后,发送 build 消息给客户端
5,客户端获取到hash,成功后客户端构造hot-update.js script链接,然后插入主文档,
6,hot-update.js 插入成功后,执行hotAPI 的 createRecord 和 reload方法,获取到 Vue 组件的 render方法,重新 render 组件, 继而实现 UI 无刷新更新。```
用webpack优化前端性能是指优化webpack的输出结果,让打包的最终结果在浏览器运行快速高效。
压缩代码。删除多余的代码、注释、简化代码的写法等等方式。可以利用webpack的UglifyJsPlugin和ParallelUglifyPlugin来压缩JS文件, 利用cssnano(css-loader?minimize)来压缩css
利用CDN加速。在构建过程中,将引用的静态资源路径修改为CDN上对应的路径。可以利用webpack对于output参数和各loader的publicPath参数来修改资源路径
删除死代码(Tree Shaking)。将代码中永远不会走到的片段删除掉。可以通过在启动webpack时追加参数–optimize-minimize来实现
提取公共代码。
多入口情况下,使用CommonsChunkPlugin来提取公共代码
通过externals配置来提取常用库
利用DllPlugin和DllReferencePlugin预编译资源模块 通过DllPlugin来对那些我们引用但是绝对不会修改的npm包来进行预编译,再通过DllReferencePlugin将预编译的模块加载进来。
使用Happypack 实现多线程加速编译
使用webpack-uglify-parallel来提升uglifyPlugin的压缩速度。 原理上webpack-uglify-parallel采用了多核并行压缩来提升压缩速度
使用Tree-shaking和Scope Hoisting来剔除多余代码: 在打包中去除那些引入了,但是在代码中没有被用到的那些死代码。