webpack
是一个现代Javascript应用程序的静态模块打包器,指的是webpack
在不进行特殊配置时,就只能处理JavaScript
这一种语言,打包指的就是多个js文件不须要人为的去理清其的依赖关系,并且还能将其合成为一个文件。css
为何要打包呢?由于前端项目中的逻辑多了,文件多了,复杂度提升了(好比一个项目依赖多个模块),因而提出多种模块化的标准,webpack
就是这么一种优秀的模块化方案。
webpack
不只强大,并且灵活(每一个功能可插拔)。html
webpack
除了打包还能干什么?前端
loader
,把代码翻译成浏览器看的懂的代码。plugin
里。下面会解释loader和plugin
。node
babel-loader
,文件中引入css文件就须要css-loader
,style-loader
。webpack
的能力,其机制就是强调一个事件监听的能力,在项目里监听一些事件,而且改变一些文件打包输出的结果。好比使用一个叫UglifyJSPlugin
的插件来对js文件进行压缩,从而减少js
文件的大小,加速load速度。Loader负责文件转换,那么Plugin即是负责功能扩展。Loader和Plugin做为Webpack的两个重要组成部分,承担着两部分不一样的职责。react
什么是模块呢? 举个例子:一个公司须要正常运转,就有市场部,技术部,人事部等等,这每一个部门就至关于一个模块,在前端项目中也就有好比专门网络请求的模块,错误处理的模块,专门渲染的模块。webpack
了解模块化以前了解下做用域的概念先。es6
传统作法会引入多个js脚本,它们共处于全局做用域下,就容易致使全局做用域变量冲突(例如同名变量冲突),而发生一些不可预测的事情。 例如:web
/*moduleA.js里*/
var a=10;
/*moduleB.js里*/
var a=11;
/*index.html里*/
<body>
<script src="./moduleA.js"></script>
<script src="./moduleB.js"></script>
<script src="./moduleC.js"></script>
</body>
复制代码
当出现上面得冲突后,a的值还能肯定吗?——不能!面试
而后就有人想出,每一个js脚本里都使用一个对象包裹,造成一个局部做用域。api
// 定义模块内的局部做用域,以moduleA为例
var Susan = {
name: "susan",
sex: "female",
tell: function(){
console.log("im susan")
}
}
复制代码
可是这样又有各严重的问题,就是对象里值咱们能更改,没法保证模块属性内部安全性,对于好比说用户名密码等数据的情景就很严重了。
因而又改进到了当即执行函数和闭包的形式。
// 定义模块内的闭包做用域(模块做用域),以moduleA为例
var SusanModule = (function(){
var name = "susan"
var sex = "female"
functioon tell(){//这样就不能更改其中的数据了
console.log("I'm ", this.name)
}
})()
复制代码
咱们再改进下写法,为当即执行函数写入参数为window
。
// 定义模块内的闭包做用域(模块做用域),以moduleA为例
(function(window){
var name = "susan"
var sex = "female"
functioon tell(){
console.log("I'm ", this.name)
}
window.susanModule = {tell}
})(window)// window做为参数传给
//////////////////////
//测试
window.susanModule.tell(); //im susan
复制代码
这样大概就是早期的模块化的形式了。
如今的模块化方案有:
//大概形式以下
//定义
define("mymodule", ["dep1", "dep2"], function(d1, d2) {
});
// 加载
require(["module", "../file"], function(module, file) {
});
复制代码
// 经过require函数来引用
const math = require("./math");
// 经过exports将其导出
exports.getSum = function(a,b){
return a + b;
}
复制代码
// 经过import函数来引用
import math from "./math";
// 经过export将其导出
export function sum(a, b){
return a + b;
}
复制代码
此外说说ES6
模块化和CommonJs
的模块化的区别:
CommonJS
模块输出的是一个值的拷贝,ES6
模块输出的是值的引用。
注意:CommonJS
模块输出的是值的拷贝,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值
ES6
模块是动态引用,而且不会缓存值,模块里面的变量绑定其所在的模块。
CommonJS
模块是运行时加载,ES6
模块是编译时输出接口。
缘由:CommonJS
加载的是一个对象(即module.exports
属性),该对象只有在脚本运行完才会生成。而 ES6 模块
不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。
CommonJS
模块的require()
是同步加载模块,ES6
模块的import
命令是异步加载,有一个独立的模块依赖的解析阶段。
前端模块化主要解决了两个问题:“命名空间冲突”,“文件依赖管理”。
和介绍webpack又有什么关系呢?
在webpack中,一切皆模块。咱们在模块化开发的时候,一般会使用ES Module
或者CommonJS
规范导出或引入依赖模块,webpack打包编译的时候,会统一替换成本身的__webpack_require__
来实现模块的引入和导出,从而实现模块缓存机制,以及抹平不一样模块规范之间的一些差别性。
Entry入口文件
出发,针对每一个 Module 串行调用对应的 Loader 去翻译文件的内容,再找到该 Module 依赖的 Module,递归地进行编译处理const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: path.resolve(__dirname, '../src/index.js'), //指定入口文件,程序从这里开始编译,__dirname当前所在目录, ../表示上一级目录, ./同级目录
output: {
path: path.resolve(__dirname, '../dist'), // 输出的路径
filename: 'app/[name]_[hash:8].js' // 打包后文件
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
use: {
loader: 'babel-loader',
options: {
presets: ['es2015', 'react'],
}
},
exclude: /node_modules/
}
]
},
plugins: [//plugin下的插件
new HtmlWebpackPlugin({
template: path.resolve(__dirname, '../src/index.template.html'),
inject: true
})
]
复制代码
loader下:
babel-loader
: babel加载器
babel-preset-es2015
: 支持es2015
babel-preset-react
: jsx
转换成js
loader
是支持以数组的形式配置多个的,当Webpack在转换该文件类型的时候,会按顺序链式调用每个loader
,前一个loader
返回的内容会做为下一个loader
的入参。
plugin下:
html-webapck-plugin
插件的两个主要做用:
为html
文件中引入的外部资源如script
、link
动态添加每次compile
后的hash
,防止引用缓存的外部文件问题
能够生成建立html入口文件
,好比单页面能够生成一个html文件入口,配置N个html-webpack-plugin
能够生成N个页面入口
webpack基于发布订阅模式,在运行的生命周期中会广播出许多事件,plugin插件
经过监听这些事件,就能够在特定的阶段执行本身的插件任务,从而实现本身想要的功能。
一些常见的loader和plugin能够看看这里,最好对这些有个大概了解和记忆。。
到这里就为理解webpack
下一个简单定义:webpack做用于模块打包
,将不一样模块的文件打包整合在一块儿,而且保证它们之间的引用正确,执行有序。而且经过其的loader机制
,让webpack
可以去处理其余类型的文件,并将它们转换为有效模块,以供应用程序使用,以及被添加到依赖图中。且再经过webpack的Plugin机制
,咱们还能够进一步实现诸如按需加载,代码压缩等一系列扩展功能。
参考文章: