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 本质上是一个函数,输入参数是一个字符串,输出参数也是一个字符串。固然,输出的参数会被当成是 JS 代码,从而被 esprima 解析成 AST,触发进一步的依赖解析。
webpack会按照从右到左的顺序执行loader。
假设前端在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.config.js 中,对于须要区分是开发环境仍是生产环境的状况,咱们根据 process.env.NODE_ENV 去进行了区分配置,可是配置文件中若是有多处须要区分环境的配置,这种显然不是一个好办法。
更好的作法是建立多个配置文件,如: 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 的说明文档中给出了详细的示例。
复制代码
todo
todo