module: {
rules: [
{
test: /\.js$/,
use: ['babel-loader?cacheDirectory'],
include: path.resolve(__dirname, 'src'),
exclude: /node_modules/
}
]
}
复制代码
'babel-loader?cacheDirectory'javascript
You can also speed up babel-loader by as much as 2x by using the cacheDirectory option. This will cache transformations to the filesystem.css
[BABEL] Note: The code generator has deoptimised the styling of "/Users/xxx/Documents/xxx/webpack_test/test3/node_modules/lodash/lodash.js" as it exceeds the max of "500KB".html
加上exclude限制范围就不会报错了java
resolve: {
modules: [path.resolve('node_modules'), path.resolve('lib')]
}
复制代码
Absolute and relative paths can both be used, but be aware that they will behave a bit differently.node
A relative path will be scanned similarly to how Node scans for node_modules, by looking through the current directory as well as it's ancestors (i.e. ./node_modules, ../node_modules, and on).react
With an absolute path, it will only search in the given directory.jquery
If you want to add a directory to search in that takes precedence over node_modules/:(便是有前后顺序的)webpack
modules: [path.resolve(__dirname, "src"), "node_modules"]
复制代码
Module not found: Error: Can't resolve 'ajax' in '/Users/xxx/Documents/xxx/webpack_test/test3/src'git
当你须要指定除node_modules以外的其它模块目录的时候能够在数组中添加属性es6
安装的第三方模块中都会有一个 package.json文件,用于描述这个模块的属性,其中有些字段用于描述入口文件在哪里,resolve.mainFields 用于配置采用哪一个字段做为入口文件的描述。
能够存在多个字段描述入口文件的缘由是由于有些模块能够同时用在多个环境中,针对不一样的运行环境须要使用不一样的代码。 以 isomorphic-fetch API 为例,它是 Promise的一个实现,但可同时用于浏览器和 Node.js 环境。
为了减小搜索步骤,在你明确第三方模块的入口文件描述字段时,你能够把它设置的尽可能少。 因为大多数第三方模块都采用 main字段去描述入口文件的位置,能够这样配置 Webpack:
module.exports = {
resolve: {
// 只采用 main 字段做为入口文件描述字段,以减小搜索步骤
mainFields: ['main'],
},
};
复制代码
alias: {
"bootstrap": "bootstrap/dist/css/bootstrap.css"
}
复制代码
module: {
noParse: [/react\.min\.js/]
}
复制代码
被忽略掉的文件里不该该包含 import 、 require 、 define 等模块化语句
dll 为后缀的文件称为动态连接库,在一个动态连接库中能够包含给其余模块调用的函数和数据
定义插件(DLLPlugin) ---> 引用插件(DllReferencePlugin)
本次例子用jquery举例
webpack.jquery.config.js
module.exports = {
entry: ["jquery"],
output: {
filename: "vendor.js",
path: path.resolve(__dirname, "dist"),
libraryTarget: 'var',// 打包的方式,hou
library: "vendor_lib_vendor"// DLL的名字
},
plugins: [
new webpack.DllPlugin({
name: "vendor_lib_vendor",// 定义DLL
path: path.resolve(__dirname, "dist/vendor-manifest.json")
})
]
};
复制代码
package.json 的scripts添加
"dll": "webpack --config webpack.jquery.config.js --mode development"
复制代码
配置好上述的文件后,在终端运行 npm run dll
,时候会在dist目录下生成两个文件,分别是vendor.js
和 vendor-manifest.json
。vendor.js
包含的就是打包后的jquery
文件代码,vendor-manifest.json
是用来作关联的。DLL定义好了,接下来就是应用打包好的DLL了。
webpack.config.js 配置文件中引入DllPlugin插件打包好的动态链接库
plugins: [
new webpack.DllReferencePlugin({
context: __dirname,
manifest: require("./dist/vendor-manifest.json")
})
],
复制代码
app.html 在app.html的底部添加
<script src="./vendor.js"></script>
复制代码
libraryTarget
和library
当用 Webpack 去构建一个能够被其余模块导入使用的库时须要用到它们。
output.libraryTarget 是字符串的枚举类型,支持如下配置。
编写的库将经过 var 被赋值给经过 library 指定名称的变量。
假如配置了 output.library='LibraryName',则输出和使用的代码以下:
// Webpack 输出的代码
var LibraryName = lib_code; //其中 lib_code 代指导出库的代码内容,是有返回值的一个自执行函数。
// 使用库的方法
LibraryName.doSomething();
复制代码
编写的库将经过 CommonJS 规范导出。
假如配置了 output.library='LibraryName',则输出和使用的代码以下:
// Webpack 输出的代码
exports['LibraryName'] = lib_code;
// 使用库的方法
require('library-name-in-npm')['LibraryName'].doSomething();
// 其中 library-name-in-npm 是指模块发布到 Npm 代码仓库时的名称。
复制代码
编写的库将经过 CommonJS2 规范导出,输出和使用的代码以下:
// Webpack 输出的代码
module.exports = lib_code;
// 使用库的方法
require('library-name-in-npm').doSomething();
复制代码
CommonJS2 和 CommonJS 规范很类似,差异在于 CommonJS 只能用 exports 导出,而 CommonJS2 在 CommonJS 的基础上增长了 module.exports 的导出方式。 在 output.libraryTarget 为 commonjs2 时,配置 output.library 将没有意义。
编写的库将经过 this 被赋值给经过 library 指定的名称,输出和使用的代码以下:
// Webpack 输出的代码
this['LibraryName'] = lib_code;
// 使用库的方法
this.LibraryName.doSomething();
复制代码
编写的库将经过 window 被赋值给经过 library 指定的名称,即把库挂载到 window 上,输出和使用的代码以下:
// Webpack 输出的代码
window['LibraryName'] = lib_code;
// 使用库的方法
window.LibraryName.doSomething();
复制代码
编写的库将经过 global 被赋值给经过 library 指定的名称,即把库挂载到 global 上,输出和使用的代码以下:
// Webpack 输出的代码
global['LibraryName'] = lib_code;
// 使用库的方法
global.LibraryName.doSomething();
复制代码
HappyPack就能让Webpack把任务分解给多个子进程去并发的执行,子进程处理完后再把结果发送给主进程。
install
因为webpack 4.0 刚刚发布,响应的插件尚未更新完,不过能够在后面加一个@next
来安装即将发布的版本
npm i happypack@next -D
复制代码
webpack.config.js
module: {
rules: [
{
test: /\.css$/,
use: 'happypack/loader?id=css',
//把对.js文件的处理转交给id为babel的HappyPack实例
//用惟一的标识符id来表明当前的HappyPack是用来处理一类特定文件
include: path.resolve('./src'),
exclude: /node_modules/
},
{
test: /\.js/,
use: 'happypack/loader?id=babel',
include: path.resolve('./src'),
exclude: /node_modules/
}
]
},
plugins: [
new HtmlWebPackPlugin({
template: './src/index.html'
}),
new HappyPack({
id: 'babel',
loaders: ['babel-loader']// 和rules里的配置相同
}),
new HappyPack({
id: 'css',
loaders: ['style-loader', 'css-loader']// 和rules里的配置相同
}),
]
复制代码
insatll
npm install webpack-parallel-uglify-plugin -D
复制代码
webpackage.config.js
new ParallelUglifyPlugin({
workerCount: os.cpus().length - 1,//开启几个子进程去并发的执行压缩。默认是当前运行电脑的 CPU 核数减去1
uglifyJS: {
output: {
beautify: false, //不须要格式化
comments: true, //不保留注释
},
compress: {
warnings: false, // 在UglifyJs删除没有用到的代码时不输出警告
drop_console: true, // 删除全部的 `console` 语句,能够兼容ie浏览器
collapse_vars: true, // 内嵌定义了可是只用到一次的变量
reduce_vars: true, // 提取出出现屡次可是没有定义成变量去引用的静态值
}
}
})
复制代码
watch: true,
watchOptions: {
ignored: /node_modules/,
aggregateTimeout: 300,
poll: 1
}
复制代码
watch
只有在开启监听模式时(watch为true),watchOptions才有意义
aggregateTimeout
监听到变化发生后等300(ms)再去执行动做,防止文件更新太快致使编译频率过高
poll
经过不停的询问文件是否改变来判断文件是否发生变化,默认每秒询问1000次
文件监听流程
webpack定时获取文件的更新时间,并跟上次保存的时间进行比对,不一致就表示发生了变化,poll就用来配置每秒问多少次。
当检测文件再也不发生变化,会先缓存起来,等等待一段时间后以后再通知监听者,这个等待时间经过aggregateTimeout配置。
webpack只会监听entry依赖的文件 咱们须要尽量减小须要监听的文件数量和检查频率,固然频率的下降会致使灵敏度降低。
devServer: {
inline: true
},
复制代码
webpack负责监听文件变化,webpack-dev-server负责刷新浏览器。这些文件会被打包到chunk中,它们会代理客户端向服务器发起WebSocket链接
webpack.config.js
devServer: {
hot:true//将hot设置为true
},
// 须要的插件
plugins: [
new webpack.NamedModulesPlugin(),//显示模块的相对路径
new webpack.HotModuleReplacementPlugin()// 启动热加载功能
]
复制代码
code
if (module.hot) {
module.hot.accept('./hot.js', () => {
let hot = require('./hot');
document.getElementById('app2').innerHTML = hot + '1';
})
}
复制代码
须要热加载的模块须要在初始化的时候引入到模块中,不然不会触发HMR。
在开发网页的时候,通常都会有多套运行环境,例如,在开发过程当中方便开发调试的环境。发布到线上给用户使用的运行环境。
线上的环境和开发环境区别主要有如下不一样:
package.json
cross-env
跨平台设置环境变量(后面没有&&)"scripts": {
"build-dev": "cross-env NODE_ENV=development webpack --mode development",
"build-prod": "cross-env NODE_ENV=production webpack --mode production"
}
复制代码
webpack.config.js
webpack.base.config.js
合并,生产环境(或者开发环境)的优先级高于webpack.base.config.js
的配置。let merge = require('webpack-merge');
let base = require('./webpack.base.config');
let other = null;
if (process.env.NODE_ENV === 'development') {
other = require('./webpack.dev.config');
} else {
other = require('./webapack.prod.config');
}
module.exports = merge(base, other);
复制代码
webpack.base.config.js
webpack.DefinePlugin
定义环境变量基本配置...
plugins: [
new webpack.DefinePlugin({
__isDevelopment__: JSON.stringify(process.env.NODE_ENV == 'development')
})
]
复制代码
webpack.dev.config.js
output
举例,若是开发和生产环境的参数不一样,就会覆盖webpack.base.config.js
里面的配置const path = require('path');
module.exports = {
output: {
path: path.resolve('./dist'),
filename: "[name].dev.[hash:2].js"
}
};
复制代码
webpack.prod.config.js
output
举例)const path = require('path');
module.exports = {
output: {
path: path.resolve('./dist'),
filename: "[name].prod.[hash:8].js"
}
};
复制代码
base.js
webpack.DefinePlugin
定义的变量(__isDevelopment__
),在入口文件和入口文件引用的其余文件中均可以获取到__isDevelopment__
的值let env = null;
if (__isDevelopment__) {
env = 'dev';
} else {
env = 'prod';
}
module.exports = env;
复制代码
index.js
let env = require('./base.js');
if (__isDevelopment__) {
console.log('dev');
} else {
console.log('prod');
}
console.log('env', env);
/* prod env prod */
复制代码
webpack.DefinePlugin
定义环境变量的值时用 JSON.stringify 包裹字符串的缘由是环境变量的值须要是一个由双引号包裹的字符串,而 JSON.stringify('production')的值正好等于'"production"'
CDN 又叫内容分发网络,经过把资源部署到世界各地,用户在访问时按照就近原则从离用户最近的服务器获取资源,从而加速资源的获取速度。
tree Shaking 能够用来剔除JavaScript中用不上的死代码。
use: {
loader: 'babel-loader',
query: {
presets: [
[
"env", {
modules: false //含义是关闭 Babel 的模块转换功能,保留本来的 ES6 模块化语法
}
],
"react"
]
}
},
复制代码
须要注意的是它依赖静态的ES6模块化语法,例如经过import和export导入导出。也就是说若是项目代码运行在不支持es6语法的环境上,Tree Shaking也就没有意义了。
大网站有多个页面,每一个页面因为采用相同技术栈和样式代码,会包含不少公共代码,若是都包含进来会有问题
相同的资源被重复的加载,浪费用户的流量和服务器的成本; 每一个页面须要加载的资源太大,致使网页首屏加载缓慢,影响用户体验。 若是能把公共代码抽离成单独文件进行加载能进行优化,能够减小网络传输流量,下降服务器成本
不一样类型的文件,打包后的代码块也不一样:
webpack.config.js
optimization: {
splitChunks: {
cacheGroups: {
commons: {// 页面之间的公用代码
chunks: 'initial',
minChunks: 2,
maxInitialRequests: 5, // The default limit is too small to showcase the effect
minSize: 0 // This is example is too small to create commons chunks
},
vendor: {// 基础类库
chunks: 'initial',
test: /node_modules/,
name: "vendor",
priority: 10,
enforce: true
}
}
}
},
复制代码
./src/pageA.js
require('./utils/utility1.js');
require('./utils/utility2.js');
require('react');
复制代码
./src/pageB.js
require('./utils/utility2.js');
require('./utils/utility3.js');
复制代码
./src/pageC.js
require('./utils/utility2.js');
require('./utils/utility3.js');
复制代码
utils/utility1.js
module.exports = 1;
复制代码
utils/utility2.js
module.exports = 2;
复制代码
utils/utility3.js
module.exports = 3;
复制代码
打包后的结果
上述三种代码的生成的结果,以下图:
Scope Hoisting 可让 Webpack 打包出来的代码文件更小、运行的更快, 它又译做 "做用域提高",是在 Webpack3 中新推出的功能。
package.json
"build": "webpack --display-optimization-bailout --mode development",
复制代码
webpack.config.js
plugins: [
new ModuleConcatenationPlugin()
],
复制代码
./h.js
export default 'scope hoist'
复制代码
./index.js
import str from './h.js'
console.log(str);
复制代码
必须使用ES6语法,不然不起做用(
--display-optimization-bailout
参数会提示)
代码分离是 webpack 中最引人注目的特性之一。此特性可以把代码分离到不一样的 bundle 中,而后能够按需加载或并行加载这些文件。 有三种经常使用的代码分离方法:
入口起点和防止重复上面已经提到了,下面咱们重点讲一下动态导入
用户当前须要用什么功能就只加载这个功能对应的代码,也就是所谓的按需加载 在给单页应用作按需加载优化时,通常采用如下原则:
import(module)
的语法document.getElementById('play').addEventListener('click',function(){
import('./vedio.js').then(function(video){
let name = video.getName();
console.log(name);
});
});
复制代码