Webpack2.x 笔记

date
Mar 5, 2017
slug
webpack2.x-note
status
Published
tags
Webpack
summary
Webpack2.x 使用总结,包括与gulp、grunt对比以及常用loader配置
type
Post
webpack 2.x 用了小一个月了,一直都是照猫画虎。这俩天开始细读官网的部分文章结合一些讲解视频,深入学习一下。小结一发来汇总知识点。

为什么使用 Webpack

已存在的模块打包器(gulp、grunt)对于大型应用(大型的SPA)来说并不适合。发展另一个模块打包器最迫切的原因那就是代码分割(Code Splitting)、dll,并且可以让静态资源能够无缝的适用于模块化。

Webpack 能做的事情

  • 将项目间的依赖分割成代码块并按需加载
  • 将初始化载入时间保持在最低
  • 每一个静态资源都应被作为一个模块
  • 有将第三方的库整合为模块的能力
  • 有定制模块的能力
  • 适合大型的单页(多页)应用

Webpack 的特点

  • 代码分割:webapck 在它的依赖树中有俩种依赖关系,同步与异步!异步依赖可以分割成许多不同的点(部分)并且这些依赖来自于新的代码块。在块级树生效后,一个文件会被传递给每一个代码块。
  • 加载器:webpack 是唯一的与生俱来就具有处理 JS 代码的能力,但是加载器被用来将其他源文件转换为 JS 文件。这样来做我们就可以处理来自不同模块的源文件。
  • 智能解析:webpack 可以智能解析第三方库文件。它甚至允许在依赖中出现解释性的描述例如: require("./templates/" + name + ".jade")。具有 CMD&AMD&es6 三种处理模式。
  • 插件系统:webpack 最有特点的就是它生态丰富的插件系统。许多内部的特性都建立在这个插件系统之上。它允许你定制你自己的需求并且可以将其开源。
具体可参考:

Webpack 与 Gulp/Grunt 的区别

Gulp/Grunt 是流程工具,其工作方式是在一个配置文件中,指明对某些文件进行的操作(诸如编译,组合,压缩等任务)的具体步骤,之后可以自动完成这些任务。流程如下:
notion image
WebPack则是一个模块打包器,其工作方式通过主文件(index.js)找到全部依赖(自己设置的代码块、第三方库等)使用 loaders 处理,最终打包为可执行的 Javascript 文件。流程如下:
notion image

Webpack 简单示例

安装&使用

mkdir webpack-1
cd webpack-1
npm init
npm install -d webpack --save-dev
安装完成后,新建 js&html&css ,文件目录如下:
notion image
hello.js 代码如下:
require('./world.js');
require('./style.css');

function hello () {
  alert('test!');
}

hello();
style.css 中的代码如下:
html , body{
	padding: 0;
	margin: 0;
}
body{
	background-color: red;
}
在 webpack 能做的事情中我们了解到 webpack 具有识别 js 的能力,那css 呢?答案是不能!那怎么解决呢?利用 loaders 来处理。对于 css 文件我们使用 css-loader&style-loader 先利用 npm 分别安装。之后在当前目录下打开 cmd 键入以下命令:
webpack hello.js hello.bundle.js --module-bind css=style-loader!css-loader --watch
module-bind 命令用于启用 loader 处理主文件,watch 用来监视 hello.js 文件的变化,若变化则自动更新 hello.bundle.js 文件。
在项目中我们如果每次都要敲一长串命令的话很麻烦,我们只需要在 package.json 的 script 中配上 webpack 命令。我得配置如下:
"webpack": "webpack --config webpack.config.js --progress --display-modules --colors --display-reasons"
其中 progress&modules&reasons 分别代表过程百分比 / 各模块 / 打包原因。我们可以将webpack的配置单独放在一个配置文件(webpack.config.js)中会自动读取该文件,如果该文件不存在或者名称不一致则需要通过命令行来运行(不存在的话通过 webpack 命令,名称不一致通过 --config 来指定文件)。
 
对于 webpack 来说入口与出口肯定是必不可少的,那么在配置文件中我们这样写:
module.exports = {
	entry: './src/index.js',   // entry: ['./src/index-1.js' , './src/index-2.js'] 多入口
	output: {                        //配置文件输出位置
	  filename: 'bundle.js',
	  path: './dist/js'
	}
};
还有 entry 传入对象的形式入口:
module.exports = {
	entry:{
		main1: './src/index1.js',
		main2: './src/index2.js'
	},
	output: {                        //配置文件输出位置
		filename: '[name]-[chunkhash].js',  // 这里如果还这样写filename: 'bundle.js'的结果是index2会把index1覆盖,所以我们利用内置的占位符 name & chunkhash 来命名
		path: './dist/js'
	}
};
这样的话在 index.html中引入的 js 文件名我们不可能每次打包完都要手动修改,这样很麻烦。所以我们需要使用插件来自动帮我们设置。npm install html-webpack-plugin 安装后修改配置文件为:
var htmlWebpackPlugin = require('html-webpack-plugin');

	module.exports = {
		entry: {
			main: './src/scripts/main.js',
			mainA: './src/scripts/mainA.js'
		},
		output: {                        //配置文件输出位置
			filename: '[name]-[chunkhash]-bundle.js'        //打包的文件名称*/
			path : './dist',
			filename: 'js/[name]-[chunkhash].js',            //把html与js分开存放
			publicPath: 'http://bluespace/'                  //上线地址的绝对路径,自动替换当前项目中相对路径
		},
		plugins: [
			new htmlWebpackPlugin({
				//filename: '[hash].index.html',              //名称
				template: 'index.html',						//以什么为模板,一般以配置文件下的index.html为模板
				//inject: 'head'								//scripts存放的位置
				title: 'webpack is good!',
				inject: false,
				date: new Date(),
				minify: {                                    //压缩html文件,去除空格,去除注释
					collapseWhitespace: true,
					removeComments: true
				}
			})
		]

	};
而 index.html 模板的代码如下(html-webpack-plugin支持ejs写法):
<!DOCTYPE html>
	<html>
		<head>
			<meta charset="utf-8">
			<title><%= htmlWebpackPlugin.options.title%></title>
			<script src="<%= htmlWebpackPlugin.files.chunks.main.entry%>"></script>  <!-- 把脚本分开放 -->
			<!-- htmlWebpackPlugin.options.date -->
		</head>
		<body>
			<em><%= htmlWebpackPlugin.options.date%></em>
			<script src="<%= htmlWebpackPlugin.files.chunks.mainA.entry%>"></script><!--如果想更加优化的话为了减少一次post请求,这里可以直接把script代码放进来,方法如下:
			<script>
			<%=
			compilation.assets[htmlWebpackPlugin.files.chunks.mainA.entry.substr(htmlWebpackPlugin.files.publicPath.length)].source()%>
			</script>-->
		</body>
	</html>

常见 loader 配置

var htmlWebpackPlugin = require('html-webpack-plugin');
    var path = require('path');

		module.exports = {
			entry: './src/app.js',
			output: {
				path: './dist',
				filename: 'js/[name].bundle.js'
			},
			module: {
				rules: [
					{
						test: /\.js$/,
						use: [{
							loader: 'babel-loader',
							options: {
								presets: ['es2015']
							}
						}],
						include: path.resolve(__dirname , './src'),
						exclude: path.resolve(__dirname , './node_modules')                           //尽量使用绝对路径会提高打包速度在configruation里面通过exclude和include来提高打包速度

					},
					{
						test: /\.css$/,
						use: [
							'style-loader',
							{
					            loader: 'css-loader',
					            options: { importLoaders: 1 }
					        },
							{
								loader: 'postcss-loader',
								options: {
									plugins: function(){
										return [
											require('autoprefixer')
										];
									}
								}
							}
						]
					},
					{
						test: /.\.less$/,
						use: [
							'style-loader',
							'css-loader',
							{
								loader: 'postcss-loader',
								options: {
									plugins: function(){
										return [
											require('autoprefixer')         // postcss下的自动补全浏览器前缀的插件
										];
									}
								}
							},
							'less-loader'      //使用less或者sass时不用为@import的less/sass添加importLoaders:1因为自动添加。因为less/sass支持import
						]
					},
					{
						test: /\.html$/,
						use: 'html-loader'      // 将html当做字符串处理,对应innerhtml。坏处是不能使用for等模板语法
					},
					{
						test: /\.ejs$/,
						use: 'ejs-loader'       // 把html当做模板处理
					},
					{
						test: /\.(png|jpg|gif|svg)$/i,            //用包管理工具除了模板外的文件放置相对路径没有问题,但是在模板文件中应该![](${require('../../assets/George.jpg')})
						use: [
							{
								loader: 'url-loader',                       //url-loader可以设置limit若图片体积小于limit的话则通过url-loader转化为base64代码,若大于则调用file-loader
								options: {
									name: 'assets/[name]-[hash:5].[ext]',
									limit: 4000                                         //经常需要加载的图片利用http请求较好,因为可以享受到缓存,而base64则需每次读取
								}
							},
							'image-webpack-loader'                                      //压缩图片体积
						]
					}
				]
			},
			plugins: [
				new htmlWebpackPlugin({
					filename: 'index.html',
					template: 'index.html',
					inject: 'body'
				})
			]
		};

参考资料


© i7eo 2017 - 2022