深刻浅出Webpack

核心概念

  • Entry:入口,Webpack执行构建的第一步将从Entry开始,可抽象成输入。
  • Module:模块,在Webpack里一切皆模块,一个模块对应一个文件。Webpack会从配置的Entry开始递归找出全部依赖的模块。
  • Chunk:代码块,一个Chunk由多个模块组合而成,用于代码合并与分割。
  • Loader:模块转换器,用于将模块的原内容按照需求转换成新内容。
  • Plugin:拓展插件,在wp构建流程中的特定时机注入拓展逻辑。
  • Output:输出结果,在Webpack通过一系列处理并得出最终想要的代码后输出结果.

Entry

入口,Webpack执行构建的第一步将从Entry开始,可抽象成输入。css

单页面单入口文件配置

module.exports = {
	entry: './path/to/my/entry/file.js'
}
复制代码

多页面多入口文件配置

module.exports = {
	entry: {
		one: './path/one.js', 
		two: './path/two.js'
	},
	plugins:[
		new HtmlWebpackPlugin({
			title:'第一个页面',
			template: './pages/one.html', // 指定第一个页面的模板
			filename: './pages/one.html', // 指定第一个页面打包完成后的文件名
			chunks: ['one','two'] // 指定第一个页面要打包进入的js
		}),	
		new HtmlWebpackPlugin({
			title:'第二个页面',
			template: './pages/two.html', // 指定第二个页面的模板
			filename: './pages/two.html', // 指定第二个页面打包完成后的文件名
			chunks: ['one','two'] // 指定第二个页面要打包进入的js
		}),	
	]
}
复制代码

Webpack构建单页面和多页面的实例

网上关于单页面和多页面的优势和缺点都有比较详细的描述,具体须要应用单页面仍是多页面得根据项目的需求来选择。html

通常单页面的配置都有相应的脚手架,好比vue-cli,集成了wp,减小了配置webpack的不少繁琐的工做vue

多页面应用如今没有脚手架,能够进行配置,具体的实例可参考这篇:node

juejin.im/post/5a2257…react

多页面想向单页面实现组件共用和封装,能够参考这篇进行配置,须要引入ejs模板,参考这篇:jquery

juejin.im/post/5a35f6…webpack

Module

模块,在Webpack里一切皆模块,一个模块对应一个文件。Webpack会从配置的Entry开始递归找出全部依赖的模块。 ####配置Loaderweb

  • test:匹配要进行转换的文件,使用正则表达式来匹配。正则表达式

  • include: 只包含指定目录的文件进行转换,加快webpack的编译速度。vue-cli

  • exclude: 排除某个文件的转换,加快编译和搜索速度。

  • use:对use后面加参数,好比进行缓存和压缩,也能够加快编译的速度。

    module:{
      	rules: [
      		{
      			//解析js文件
      			test: /\.js$/,
      			// 用babel-loader转换js文件
      			 // ?cacheDirectory表示传给babel-loader的参数,用于缓存babel的编译结果,加快从新编译的速度
      			use: ['babel-loader?cacheDirectory']
      			 // 只命中src目录里的Js文件,加快webpack的编译速度
      			 include: path.resolve(_dirname,'src')
      		},
      		{
      			//解析Scss文件
      			test: /\.scss$/,
      			// 使用一组loader去处理scss文件
      			// 处理顺序为从后到前,即先交给scss-loader处理,再将结果交给css-loader,最后交给style-loader
      			use: ['style-loader','css-loader','sass-loader'],
      			// 排除node_modules目录下的文件
      			exclude: path.resolve(__dirname,'node_modules')
      		},
      		{
      			// 对非文本文件采用file-loader加载
      			test: /\.(gif|png|jpe?g|eot|woff|ttf|svg|pdf)$/,
      			use: ['file-loader']
      		}
      	]
      }
    复制代码

在上面的例子中,test、include、exclude只传入了一个字符串或正则,其实他们也支持数组类型

{
	test:[
		/\.jsx?$/,
		/\.tsx?$/
	],
	include:[
		path.resolve(__dirname,'src'),
		path.resolve(__dirname,'tests')
	],
	exclude:[
		path.resolve(__dirname, 'node_modiles'),
		path.resolve(__dirname, 'bower_modules')
	]
}
复制代码

noParse

noPaese配置项可让Webpack忽略对部分没采用模块化的文件的递归解析和处理,这样作的好处是能提升构建性能。 缘由是一些库如jQuery,ChartJS庞大又没有采用模块化的标准,让Webpack去解析这些文件既耗时又没有意义。

noParse: /jquery|chartjs/
复制代码
使用函数,从Webpack3.0.0开始支持
noParse: (content) => {
	//content表明一个模块的文件路径
	//返回true或false
	return /jquery|chartjs/.test(content)
}
复制代码

parse

由于Webpack是以模块化的js文件为入口的,因此内置了对模块化js的解析功能,支持AMD,CommonJS,SystemJS,ES6 parse属性能够更细粒度地配置哪些模块语法被解析,哪些不被解析。

同noParse配置项的区别在于.parser能够精确到语法层面,而noParse只能控制哪些文件不被解析。

parse的使用方法以下:

modele:{
	rules:[
		test: /\.js$/,
		use: ['babel-loader'],
		parser: {
			amd: false, //禁用AMD
			commonjs: false, // 禁用CommonJS
			system: false, // 禁用 SystemJS
			harmony: false, // 禁用ES6 import/export
			requireInclude: false, // 禁用requireInclude
			requireEnsure: false, // 禁用requireEnsure
			requireContext: false, // 禁用requireContext
			browserify: false,  // 禁用browserify
			requireJs: false // 禁用requirejs
		}
	]
}
复制代码

Loader

模块转换器,用于将模块的原内容按照需求转换成新内容。

  • Loader的执行顺序是由后到前的。

  • 每Loader均可以经过URL querystring的方式传入参数,例如 css-loader?minimize中的minimize告诉css-loader要开启css压缩。

  • 向loader中传入属性的方式除了能够经过querystring实现,还能够经过object实现。

    user:[
      	'style-loader',{
      		loader:'css-loader',
      		options:{
      			minimize:true
      		}
      	}
      ]
    复制代码

在Loader须要传入不少参数时,咱们还能够经过一个Object来描述,例如在上面的babel-loader配置中有以下代码

use:[
	{
		loader:'babel-loader',
		options:{
			cacheDirectory:true
		},
		// enforce: 'post'的含义是将该loader的执行顺序放到最后
		// enforce: 'pre'的含义是将loader的执行顺序放到最前面
	}
]
复制代码

Plugin

拓展插件,在Webpack构建流程中的特定时机注入拓展逻辑。

Plugin的配置很简单,plugins的配置项接受一个数组,数组里的每一项都是一个要使用的Plugin的实例,Plugin须要的参数经过构造函数传入。

const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin')
module.exports = {
	plugins: [
		// 全部页面都会用到的公共代码被提取到common代码块中
		new CommonsChunkPlugin({
			name: 'common',
			chunks: ['a','b']
		})
	]
}
复制代码

将css文件单独提取出来

const ExtractTextPluginn = require('extract-text-webpack-plugin')

	module:{
		reules:[
			{
				textL /\.css$/,
				loaders: ExtractTextPlugin.extract({
					use:[
						'css-loader'
					]
				}),
			}
		]
	},
	plugins: [
		new ExtractTextPlugin({
			//从.js文件中提取出来的.css文件的名称
			filename: `[name]_[contenthash:8].css`
		})	
	]
复制代码

Resolve

webpack在启动后会从配置的入口模块出发找出全部依赖的模块,resolve配置webpack如何查找模块所对应的文件

alias

resolve.alias配置项经过别名来将原导入路径映射成一个新的导入路径。例如使用一下配置:

resolve:{
	alias:{
		components: './src/components'
	}
}
复制代码

当经过**import Button from 'components/button'**导入时

实际上被alias等价替换成了import Button from './src/components/button'

extensions

在导入语句没带文件后缀时,Webpack会自动带上后缀后去尝试访问文件是否存在。

resolve.extensions用于配置在尝试过程当中用到的后缀列表,默认是:

extensionsL ['.js','.json']
复制代码

也就是说,当遇到require('./data')这样的导入语句时,Webpack会先寻找./data.js文件,若是该文件不存在,就去找./data.json文件,若是仍是找不到就报错。

escriptionFiles

若是resolve.enforceExtension被配置为true,则全部导入语句都必须带文件后缀,例如开启前import './foo'能正常工做,开启后就必须写成**import './foo.js' **

Devtool

devltool配置Webpack如何生成source map,默认值是false,即不生成source map,若想构建出的代码生成source map以方便调试,则能够这样配置:

module.export = {
	devtool: 'source-map'
}
复制代码

开启source-map会方便咱们开发中的调试,方便咱们定位到具体的代码问题,当也会影响一下相关的构建性能问题,全部要作出多配置文件,开发环境配置和生产环境配置

source-map模式下会输出质量最高且最详细的Source Map,这会形成构建速度缓慢,特别是在开发过程当中须要频繁修改时会增长等待时间

在Source-Map模式下会将Source Map暴露,若构建发布到线上的代码的source map暴力,就等同于源码被泄露

为了解决以上两个问题,能够这样作,以下所述

  • 在开发环境下devtool设置成cheap-module-eval-source-map,由于生成这种source map的速度最快,能加速构建。因为在开发环境下不会作代码压缩,因此在source map的即便没有列信息,也不会影响断电调试.

  • 在生产环境下将devtool设置成hidden-source-map,意思是生成最详细的source map,但不会将source map暴露出去。因为生产环境下会作代码压缩,一个js文件只有一行,因此须要列信息。

在生产环境下一般不会将Source Map上传到http服务器让用户获取,而是上传到JavaScript错误收集系统,在错误收集系统上根据Source Map和收集到的JavaScript运行错误队栈,计算出错误所在源码的位置。

不要在生产环境下使用inline模式的Source Map,由于这会使JavaSctipt文件变的很大,并且会泄露源码。

Externals

External用来告诉Webpack要构建的代码中使用了哪些不用被打包的模块,也就是说这些模板是外部环境提供的,Webpack在打包时能够忽略它们

经过externals能够告诉Webpack在js运行环境中已经内置了哪些全局变量,不用将这些全局变量打包到代码中而是直接使用它们。

moudle.export = {
	externals: {
		//将导入语句里的jquery替换成运行环境里的全局变量jQuery
		jquery: 'jQuery'
	}
}
复制代码

Webpack优化

  • 优化开发体验

    优化开发体验的目的是提高开发效率,减小每次构建的耗时
      1. 优化构建速度
      2. 优化使用体验,经过自动化手段完成一些重复的工资哦,让咱们专一于解决问题自己。
    复制代码
  • 优化输出质量

    呈现用户体验更好的网页,减小首屏加载时间,提高性能流畅度。
    
      1. 缩小文件的搜索范围
      2. 优化Loader的配置,经过include去命中 只有哪些文件去处理,经过exclude去去除哪些文件不须要处理,好比node_module
      3. 优化resolve.modules配置
      resolve.modules用于配置Webpack去哪些目录下寻找第三方模块。
      
      resolve.modelus的默认值是['node_modules']含义是先去当前目录的./node_modules目录下去找咱们想找的模块,若是没找到,就去上一级目录../node_modules中找,再没有就去../../node_modules中找,以此类推
      
      当安装的第三方模块都放在项目根目录的./node_modules目录下时,就没有必要按照默认的方式去一层层的寻找,能够指明存放第三方模块的绝对路径,以减小寻找,配置以下:
      
      	module.exports = {
      		resolve: {
      			// 使用绝对路径指明第三方模块存放的位置,以减小搜索步骤
      			// 其中,__dirname表示当前工做目录,也就是项目根目录
      			modules: [path.resolve(__dirname,'node_modules')]
      		}
      	}
      4.优化resolve.alias配置,跳过递归解析操做
      
      5.优化resolve.extensions配置减小后缀,后缀要尽量少,提高速度
      
      6.优化noParse配置
    复制代码

使用DllPlugin

包含大量复用模块的动态连接库只需被编译一次,在以后的构建过程当中被动态连接库包含的模块将不会被从新编译,而是直接使用动态连接库中的代码。因为动态连接库中大多数包含的是经常使用的第三方模块,例如react,react-dom,因此只要不升级这些模块的版本,动态连接库就不用从新编译。

Webpack已经内置了对动态连接库的支持,须要经过如下两个内置的额插件接入。

  • DllPlugin插件:用于打包粗一个个单独的鼎泰连接库文件.
  • DllReferecePlugin插件:用于在主要的配置文件中引入DllPlugin插件打包好的动态连接库文件

HappyPack

运行在Node.js之上的Webpack是单线程模型,Happy Pack将任务分解给多个子进程去并发执行,子进程处理完后再讲结果发送给主进程,因为js是单线程模型,因此想要发挥多核cpu的功能,就只能经过多进程实现,而没法经过多线程实现。

整个Webpack构建流程中,最耗时的流程可能就是loader对文件的转换操做了,由于要转换的文件数据量巨大,并且这些转换操做都只能一个一个地处理。HappyPack的核心原理就是将这部分任务分解到多个进程中去并行处理,从而减小总的构事件。

ParallelUglifyPlugin

本来会使用Uglifyjs去一个一个压缩再输出

Paralleuglifyplugin会开启多个子线程,将对多个文件的压缩工做分配给多个子进程完成,每一个子进程其实仍是经过uglify去压缩代码,可是变成了并行执行,因此Paralleuglifyplugin能更快地完成对多个文件的压缩工做

文件监听

文件监听是发现源码发生变化时,自动从新构建出新的输出文件.

让Webpack开启监听模式,有以下两种方式。

  • 在配置文件webpack.config.js中设置watch:true
  • 在执行启动webpack的命令时带上--watch参数,完成的命令是webpack--watch

优化文件的性能

忽略node_modules

module.export = {
	watchOptions:{
		ignored: /node_modules/
	}
}
复制代码

自动刷新浏览器

开启模块热替换

##区分环境 区分开发环境和生产环境,指定对用的不一样调试模式Source Map,是否开启压缩,是否提取公共代码等

提取公共代码

相同的资源被重复加载,浪费用户的流量和服务器成本

页面须要加载的资源太大,会致使网页首屏加载缓慢,影响用户体验。

webpackchunkplugin
复制代码

代码分割,按需加载。

如何按需加载

  1. 在为单页应用作按需加载优化时,通常采用如下原则

  2. 将整个网站划分红一个个小功能,再按照每一个功能的相关程度将它们分红几类

  3. 将每一类合并为一个chunk,按需加载对应的chunk.

  4. 不要按需加载用户首次打开网站是须要看到的画面所对应的功能,将其放到执行入口所在的Chunk中,以减小用户能感知的网页加载时间。

相关文章
相关标签/搜索