webpack常见面试点(持续更新中)

webpack打包原理

模块化机制

webpack并不强制你使用某种模块化方案,而是经过兼容全部模块化方案让你无痛接入项目。有了webpack,你能够随意选择你喜欢的模块化方案,至于怎么处理模块之间的依赖关系及如何按需打包,webpack会帮你处理好的。css

关于模块化的一些内容,能够看看我以前的文章:js的模块化进程html

核心思想:

  • 一切皆模块:

正如js文件能够是一个“模块(module)”同样,其余的(如css、image或html)文件也可视做模 块。所以,你能够require(‘myJSfile.js’)亦能够require(‘myCSSfile.css’)。这意味着咱们能够将事物(业务)分割成更小的易于管理的片断,从而达到重复利用等的目的。前端

  • 按需加载:

传统的模块打包工具(module bundlers)最终将全部的模块编译生成一个庞大的bundle.js文件。可是在真实的app里边,“bundle.js”文件可能有10M到15M之大可能会致使应用一直处于加载中状态。所以Webpack使用许多特性来分割代码而后生成多个“bundle”文件,并且异步加载部分代码以实现按需加载。node

文件管理

每一个文件都是一个资源,能够用require/import导入js 每一个入口文件会把本身所依赖(即require)的资源所有打包在一块儿,一个资源屡次引用的话,只会打包一份 对于多个入口的状况,其实就是分别独立的执行单个入口状况,每一个入口文件不相干(可用CommonsChunkPlugin优化)webpack

打包原理

webpack只是一个打包模块的机制,只是把依赖的模块转化成能够表明这些包的静态文件。并非什么commonjs或者amd之类的模块化规范。webpack就是识别你的 入口文件。识别你的模块依赖,来打包你的代码。至于你的代码使用的是commonjs仍是amd或者es6的import。webpack都会对其进行分析。来获取代码的依赖。webpack作的就是分析代码,转换代码,编译代码,输出代码。webpack自己是一个node的模块,因此webpack.config.js是以commonjs形式书写的(node中的模块化是commonjs规范的)es6

webpack中每一个模块有一个惟一的id,是从0开始递增的。整个打包后的bundle.js是一个匿名函数自执行。参数则为一个数组。数组的每一项都为个function。function的内容则为每一个模块的内容,并按照require的顺序排列。web

// webpack.config.js
module.exports = {
  entry:'./a.js',
  output:{
    filename:'bundle.js'
  }
};
复制代码
// a.js
var b = require('./b.js');

console.log('a');

b.b1();
复制代码
// b.js
exports.b1 = function () {
  console.log('b1')
};

exports.b2 = function () {
  console.log('b2')
};
复制代码

以上代码咱们打包处理的js为express

// bundle.js
/******/ (function(modules) { // webpackBootstrap
/******/    // The module cache
/******/    var installedModules = {};

/******/    // The require function
/******/    function __webpack_require__(moduleId) {

/******/        // Check if module is in cache
/******/        if(installedModules[moduleId])
/******/            return installedModules[moduleId].exports;

/******/        // Create a new module (and put it into the cache)
/******/        var module = installedModules[moduleId] = {
/******/            exports: {},
/******/            id: moduleId,
/******/            loaded: false
/******/        };

/******/        // Execute the module function
/******/        modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);

/******/        // Flag the module as loaded
/******/        module.loaded = true;

/******/        // Return the exports of the module
/******/        return module.exports;
/******/    }
/******/    // expose the modules object (__webpack_modules__)
/******/    __webpack_require__.m = modules;

/******/    // expose the module cache
/******/    __webpack_require__.c = installedModules;

/******/    // __webpack_public_path__
/******/    __webpack_require__.p = "";

/******/    // Load entry module and return exports
/******/    return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ function(module, exports, __webpack_require__) {

    var b = __webpack_require__(1);

    console.log('a');

    b.b1();


/***/ },
/* 1 */
/***/ function(module, exports) {

    exports.b1 = function () {
      console.log('b1')
    };

    exports.b2 = function () {
      console.log('b2')
    };

/***/ }
/******/ ]);
复制代码

咱们看到_webpack_require是模块加载函数,接收模块id(对,webpack中每一个模块都会有一个独一无二的id,其实也就是在IIFE传参数组中的索引值(0,1,2.....) a依赖b,因此在a中调用webpack加载模块的函数npm

// 1是模块b的id
var b = __webpack_require__(1);
复制代码

上面是使用的commonjs规范书写的json

不管什么模块规范书写。咱们的webpack进行识别后打包的内容不会相差不少,webpack有优秀的语法分析能力,支持 CommonJs AMD 等规范。

webpack为何能把任何形式的资源都视做模块呢?

由于loader机制。不一样的资源采用不一样的loader进行转换。CMD、AMD 、import、css 、等都有相应的loader去进行转换。那为何咱们平时写的es6的模块机制,不用增长import的loader呢。由于咱们使用了babel把import转换成了require。而且Webpack 2 将增长对 ES6 模块的原生支持而且混用 ES六、AMD 和 CommonJS 模块。这意味着 Webpack 如今能够识别 import 和 export 了,不须要先把它们转换成 CommonJS 模块的格式:

webpack对于es模块的实现,也是基于本身实现的webpack_require 和webpack_exports ,装换成相似于commonjs的形式。es6 module是静态的依赖,因此在运行前进行代码转换,这里的实现是将全部导出项做为一个对象的属性,在入口文件执行时,去递归的加载模块。

loader原理

在解析对于文件,会自动去调用响应的loader,loader 本质上是一个函数,输入参数是一个字符串,输出参数也是一个字符串。固然,输出的参数会被当成是 JS 代码,从而被 esprima 解析成 AST,触发进一步的依赖解析。

webpack会按照从右到左的顺序执行loader。

利用webpack解决跨域问题

假设前端在3000端口,服务端在4000端口,咱们经过 webpack 配置的方式去实现跨域。 首先,咱们在本地建立一个 server.js:

let express = require('express');

let app = express();

app.get('/api/user', (req, res) => {
    res.json({name: '刘小夕'});
});

app.listen(4000);
复制代码

执行代码(run code),如今咱们能够在浏览器中访问到此接口: http://localhost:4000/api/user。

在 index.js 中请求 /api/user,修改 index.js 以下:

//须要将 localhost:3000 转发到 localhost:4000(服务端) 端口
fetch("/api/user")
    .then(response => response.json())
    .then(data => console.log(data))
    .catch(err => console.log(err));
复制代码

咱们但愿经过配置代理的方式,去访问 4000 的接口。 配置代理 修改 webpack 配置:

//webpack.config.js
module.exports = {
    //...
    devServer: {
        proxy: {
            "/api": "http://localhost:4000"
        }
    }
}
复制代码

从新执行 npm run dev,能够看到控制台打印出来了 {name: "刘小夕"},实现了跨域。

大多状况,后端提供的接口并不包含 /api,即:/user,/info、/list 等,配置代理时,咱们不可能罗列出每个api。

修改咱们的服务端代码,并从新执行。

//server.js
let express = require('express');

let app = express();

app.get('/user', (req, res) => {
    res.json({name: '刘小夕'});
});

app.listen(4000);
复制代码

尽管后端的接口并不包含 /api,咱们在请求后端接口时,仍然以 /api 开头,在配置代理时,去掉 /api,修改配置:

//webpack.config.js
module.exports = {
    //...
    devServer: {
        proxy: {
            '/api': {
                target: 'http://localhost:4000',
                pathRewrite: {
                    '/api': ''
                }
            }
        }
    }
}
复制代码

从新执行 npm run dev,在浏览器中访问: http://localhost:3000/,控制台中也打印出了{name: "刘小夕"},跨域成功,

webpack 如何区分不一样环境

目前为止咱们 webpack 的配置,都定义在了 webpack.config.js 中,对于须要区分是开发环境仍是生产环境的状况,咱们根据 process.env.NODE_ENV 去进行了区分配置,可是配置文件中若是有多处须要区分环境的配置,这种显然不是一个好办法。

更好的作法是建立多个配置文件,如: webpack.base.js、webpack.dev.js、webpack.prod.js。

  • webpack.base.js 定义公共的配置
  • webpack.dev.js:定义开发环境的配置
  • webpack.prod.js:定义生产环境的配置

webpack-merge 专为 webpack 设计,提供了一个 merge 函数,用于链接数组,合并对象。

npm install webpack-merge -D
复制代码
const merge = require('webpack-merge');
merge({
    devtool: 'cheap-module-eval-source-map',
    module: {
        rules: [
            {a: 1}
        ]
    },
    plugins: [1,2,3]
}, {
    devtool: 'none',
    mode: "production",
    module: {
        rules: [
            {a: 2},
            {b: 1}
        ]
    },
    plugins: [4,5,6],
});
//合并后的结果为
{
    devtool: 'none',
    mode: "production",
    module: {
        rules: [
            {a: 1},
            {a: 2},
            {b: 1}
        ]
    },
    plugins: [1,2,3,4,5,6]
}
复制代码

webpack.config.base.js 中是通用的 webpack 配置,以 webpack.config.dev.js 为例,以下:

//webpack.config.dev.js
const merge = require('webpack-merge');
const baseWebpackConfig = require('./webpack.config.base');

module.exports = merge(baseWebpackConfig, {
    mode: 'development'
    //...其它的一些配置
});
复制代码

而后修改咱们的 package.json,指定对应的 config 文件:

//package.json
{
    "scripts": {
        "dev": "cross-env NODE_ENV=development webpack-dev-server --config=webpack.config.dev.js",
        "build": "cross-env NODE_ENV=production webpack --config=webpack.config.prod.js"
    },
}
你可使用 merge 合并,也可使用 merge.smart 合并,merge.smart 在合并loader时,会将同一匹配规则的进行合并,webpack-merge 的说明文档中给出了详细的示例。
复制代码

按需加载

webpack打包图片原理

todo

webpack怎样在首页输出项目版本,分支信息

todo

参考 blog.csdn.net/weixin_3378…

webpack优化

参考【前端必知】Webpack性能优化

相关文章
相关标签/搜索