🌟给webpack一个认识吧!

1. webpack是什么

webpack是一个现代Javascript应用程序的静态模块打包器,指的是webpack在不进行特殊配置时,就只能处理JavaScript这一种语言,打包指的就是多个js文件不须要人为的去理清其的依赖关系,并且还能将其合成为一个文件。css

为何要打包呢?由于前端项目中的逻辑多了,文件多了,复杂度提升了(好比一个项目依赖多个模块),因而提出多种模块化的标准,webpack就是这么一种优秀的模块化方案。
webpack不只强大,并且灵活(每一个功能可插拔)。html

webpack除了打包还能干什么?前端

  • 翻译!加入loader,把代码翻译成浏览器看的懂的代码。
  • 作点别的操做,能力扩展类,在webpack的plugin里。

下面会解释loader和pluginnode

2. webpack中的关键人物

  • loader:指文件加载器,执行一个文件的编译转译功能,例若有,es6转es5,就有babel-loader,文件中引入css文件就须要css-loaderstyle-loader
  • plugin:用于加强一个项目里面webpack的能力,其机制就是强调一个事件监听的能力,在项目里监听一些事件,而且改变一些文件打包输出的结果。好比使用一个叫UglifyJSPlugin的插件来对js文件进行压缩,从而减少js文件的大小,加速load速度。

Loader负责文件转换,那么Plugin即是负责功能扩展。Loader和Plugin做为Webpack的两个重要组成部分,承担着两部分不一样的职责。react

3. 前端模块化

什么是模块呢? 举个例子:一个公司须要正常运转,就有市场部,技术部,人事部等等,这每一个部门就至关于一个模块,在前端项目中也就有好比专门网络请求的模块,错误处理的模块,专门渲染的模块。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
复制代码

这样大概就是早期的模块化的形式了。

如今的模块化方案有

  • AMD(Asynchronous Module Definition 异步模块定义)
//大概形式以下
//定义
define("mymodule", ["dep1", "dep2"], function(d1, d2) {
});

// 加载
require(["module", "../file"], function(module, file) {
});
复制代码
  • CommonJs:Node.js 专用, 该方案的核心思想就是容许模块经过require方案同步加载依赖的其余模块,经过exports或module.exports来暴露出须要的接口。
// 经过require函数来引用
const math = require("./math");

// 经过exports将其导出
exports.getSum = function(a,b){
    return a + b;
}
复制代码
  • ES6 Module:该方案最大的特色就是静态化,静态化的优点在于能够在编译的时候肯定模块的依赖关系以及输入输出的变量。上面提到的CommonJs和AMD都只能在运行时肯定这些东西。
// 经过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__来实现模块的引入和导出,从而实现模块缓存机制,以及抹平不一样模块规范之间的一些差别性。

4. webpack打包的核心思路

  • 初始化:启动构建,读取与合并配置参数,加载 Plugin,实例化 Compiler
  • 编译:从 Entry入口文件出发,针对每一个 Module 串行调用对应的 Loader 去翻译文件的内容,再找到该 Module 依赖的 Module,递归地进行编译处理
  • 输出:将编译后的 Module 组合成 Chunk,将 Chunk 转换成文件,输出到文件系统中

5. 一个简单的webpack配置示例

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文件中引入的外部资源如scriptlink动态添加每次compile后的hash,防止引用缓存的外部文件问题

  • 能够生成建立html入口文件,好比单页面能够生成一个html文件入口,配置N个html-webpack-plugin能够生成N个页面入口

webpack基于发布订阅模式,在运行的生命周期中会广播出许多事件,plugin插件经过监听这些事件,就能够在特定的阶段执行本身的插件任务,从而实现本身想要的功能。

一些常见的loader和plugin能够看看这里,最好对这些有个大概了解和记忆。。

6.总结

到这里就为理解webpack下一个简单定义:webpack做用于模块打包,将不一样模块的文件打包整合在一块儿,而且保证它们之间的引用正确,执行有序。而且经过其的loader机制,让webpack可以去处理其余类型的文件,并将它们转换为有效模块,以供应用程序使用,以及被添加到依赖图中。且再经过webpack的Plugin机制,咱们还能够进一步实现诸如按需加载,代码压缩等一系列扩展功能。

参考文章:

当面试官问Webpack的时候他想知道什么

再来一打webpack面试考题

相关文章
相关标签/搜索