一、Webpack 是什么?
webpack是一款模块加载器兼打包工具,它能把各种资源,例如JS(含JSX)、coffee、样式(含less/sass)、图片等都作为模块来使用和处理。 简单说就是模块加载器,通过使用Webpack,能够像Node.js一样处理依赖关系,然后解析出模块之间的依赖,将代码打包。
二、为什么需要打包?
-
像sass,JSX等代码虽然极大的提高了开发效率,但是本身并不被浏览器所识别,需要我们对其进行编译和打包,变成浏览器识别的代码
-
模块化(让我们可以把复杂的代码细化为小的文件)
-
优化加载速度(压缩和合并代码来提高加载速度,压缩可以减少文件体积,代码合并可以减少http请求)
-
使用新的开发模式
三、webpack主要特性
-
同时支持CommonJS和AMD模块(对于新项目,推荐直接使用CommonJS);
-
串联式模块加载器以及插件机制,让其具有更好的灵活性和扩展性,例如提供对CoffeeScript、ES6的支持;
-
可以基于配置或者智能分析打包成多个文件,实现公共模块或者按需加载;
-
支持对CSS,图片等资源进行打包,从而无需借助Grunt或Gulp(browserify只能打包JS文件);
-
开发时在内存中完成打包,性能更快,完全可以支持开发过程的实时打包需求;
-
对source map有很好的支持。
四、配置(webpack.config.js文件)
var webpack = require('webpack');
var path = require('path');
var SpritesmithPlugin = require('webpack-spritesmith');
module.exports = {
// context: __dirname + '\app', //上下文
entry: path.resolve(__dirname, 'app/index.js'), //入口文件
output: { //输出文件
path: path.resolve(__dirname, 'app/build/script'),
filename: 'bundle.js'
},
module: {
loaders: [ //加载器
{test: /.html$/, loader: 'raw'},
// {test: /.css$/, loader: 'style!css!postcss'},
{test: /.css$/, loader: 'style!css'},
// {test: /.(png|jpg|ttf)$/, loader: 'url?limit=8192'}
{test: /.(png|jpg|ttf)$/, loader: 'file!url?limit=8192'}
]
},
// postcss: function () {
// return [autoprefixer];
// },
plugins: [ //插件
new webpack.BannerPlugin('This file is created by shiddong.'),
new SpritesmithPlugin({
src: {
cwd: path.resolve(__dirname, 'app/images/'),
glob: '*.png'
},
target: {
image: path.resolve(__dirname, 'app/build/images/sprite.png'),
css: path.resolve(__dirname, 'app/build/css/sprite.css') //产生的样式文件,图片的样式类名是 icon-图片名
},
apiOptions: {
cssImageRef: '../images/sprite.png'
},
spritesmithOptions: {
algorithm: 'top-down'
}
})
]
};
五、webpack.config.js 配置详解
(1)entry
入口文件,可以传字符串,那说明入口文件只有一个;也可以传数组或对象,指定多个入口文件。
有两种语法:
<1> 先require path,再利用reslove方法对路径进行解析。
var path = require('path');
entry: path.resolve(__dirname, 'app/index.js'), //入口文件
<2> 先定义好上下文路径,那么entry中的路径直接在这个路径下开始
context: __dirname + '\app', //上下文
entry: './index.js', //入口文件
注:webpack中引入的path是nodejs内置的package,用来处理路径 —— 参考菜鸟教程node.js工具模块。
当entry是数组的时候,里面同样包含入口js文件,另外一个参数可以是用来配置webpack提供的一个静态资源服务器,webpack-dev-server。webpack-dev-server会监控项目中每一个文件的变化,实时的进行构建,并且自动刷新页面:
entry: [
'webpack/hot/only-dev-server',
'./js/app.js'
]
当entry是个对象的时候,我们可以将不同的文件构建成不同的文件,按需使用。每一个键值对,就是一个入口文件。
备注:当然也有其他的办法,如express框架,在index.html同路径建立一个server.js,然后运行 node server.js即可。
// express 是一个基于Node.js 平台的web应用开发框架 —— 快速、极简、开放
var port = 8000,
express = require('express'),
app = express();
app.use('/', express.static(__dirname));
app.listen(port);
console.log('Now serving http://localhost:' + port + '/index.html');
(2)output
生成打包文件的配置,可以指定path(路径),当有多个入口文件时,还可以使用[name]、[hash]、[chunkhash]等值,来对应替换为入口的文件的配置。
output: { //输出构建后的文件
path: path.resolve(__dirname, 'app/build/script'),
filename: 'bundle.js'
}
当entry中定义构建多个文件时,filename可以对应的更改为[name].js用于定义不同文件构建后的名字。
entry: {
index: 'index.js'
},
output: {
path: './js',
filename: '[name].bundle.js' // [name] 会由entry中的键(这里是index)替换
}
(3)module
定义了对模块的处理逻辑,在webpack中,所有的资源都被当做模块。对于不同文件类型的资源,都有对应的loader。
模块的加载相关配置定义在module.loaders中。
loaders: 加载器配置,通过正则表达式去匹配不同后缀的文件名,然后给它们定义不同的加载器。它可以转换项目中的资源文件,比如说给css文件定义串联的两个加载器(!用来定义级联关系),顺序是从右向左使用:
module: {
loaders: [
{ test: /.js?$/, loaders: ['react-hot', 'babel'], exclude: /node_modules/ },
{ test: /.js$/, exclude: /node_modules/, loader: 'babel-loader'},
{ test: /.css$/, loader: "style!css" },
{ test: /.less/, loader: 'style-loader!css-loader!less-loader'}
]
}
其中,exclude: /node_modules/,
exclude,include手动添加屏蔽不需要处理的文件(文件夹)或必须处理的文件(文件夹)
比较常用的loader:
css-loader: 将样式打包成字符串。
style-loader:将样式字符串添加到页面的style标签中
还可以添加用来定义png、jpg这样的图片资源在大于8k时正常打包,小于8k时自动处理为base64图片的加载器:
{ test: /.(png|jpg)$/,loader: 'url-loader?limit=8192'}
其中,? 表示加载器支持通过查询字符串的方式接受参数。
给css和less还有图片添加了loader之后,我们不仅可以像在node中那样require js文件了,我们还可以require css、less甚至图片文件:
require('./bootstrap.css');
require('./myapp.less');
var img = document.createElement('img');
img.src = require('./glyph.png');
这样require来的文件会内联到 js bundle中。如果我们需要保留require的写法又想把css文件单独拿出来,可以使用[extract-text-webpack-plugin]插件。
(4)plugins
插件,它可以干很多很多的事情,非常强大,官方提供了很多插件,第三方也可以写插件。对于用到的插件,只需要将插件new出来放到数组中即可。
<1> BannerPlugin 内置插件来实践插件的配置和运行,这个插件的作用是给输出的文件头部添加注释信息。
<2> ProvidePlugin 插件的作用是自动加载jquery模块,也就是说将jquery变成了全局的模块,当然我们需要在index.html中使用script标签导入。
[原因:由于jquery没有模块化的概念,也没有适配webpack,所以我们使用jquery时,需要在index.html中导入,然后使用 ProvidePlugin 插件使其自动加载。]
plugins: [
new webpack.BannerPlugin('This file is created by shiddong.'),
// 此插件会自动加载jquery,解决jquery无法引用的问题
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery",
window.jQuery: "jquery"
})
]
(5)导入使用
只需要在index.html中导入bundle.js,<script src="bundle.js">script>,不需要在index.html中导入js文件了,只需要使用 require 导入模块就行,webpack会自己解决它们之间的依赖。
<1> 依赖第三方库可以将其下载到node-modules中,然后导入相应的模块:
//require('angular')导入的是 node_modules 中的angular模块
require("angular"); //var angular = require('angular');
require("bootstrap");
var demoApp = angular.module('demoApp', []);
<2> 或者类似于导入本地的js文件,可以建立一个 common.js 文件,然后导入 angular 和 bootstrap。然后在使用的时候直接require('./common')
(6)处理图片
loaders: [
{
test: /.(png|jpg)$/,
loader: 'url-loader?limit=8192'
}
]
其中,
<1> test 属性代表可以匹配的图片类型,除了 png、jpg 之外也可以添加 gif 等,以竖线隔开即开。
<2> loader 后面 limit 字段代表图片打包限制,这个限制并不是说超过了就不能打包,而是指当图片大小小于限制时会自动转成 base64 码引用。上例中大于8192字节的图片正常打包,小于8192字节的图片以 base64 的方式引用。
//Option 1,使用 ?
{ test: /.png$/,
loader: 'url-loader?limit=1024'
}
//Option 2,使用query属性
{ test: /.png$/,
loader: 'url-loader',
query: {limit=1024}
}
<3> url-loader 后面除了 limit 字段,还可以通过 name 字段来指定图片打包的目录与文件名:
loaders: [
{
test: /.(png|jpg)$/,
loader: 'url-loader?limit=8192&name=images/[hash:8].[name].[ext]'
}
]
name 字段指定了在打包根目录(output.path)下生成名为 images 的文件夹,并在原图片名前加上8位 hash 值。
(7)生成雪碧图
var path = require('path');
var SpritesmithPlugin = require('webpack-spritesmith'); //安装雪碧图依赖模块:webpack-spritesmith
module.exports = {
entry: path.resolve(__dirname, 'app/main.js'),
output: {
path: path.resolve(__dirname, 'build'),
filename: 'bundle.js'
},
plugins: [
new SpritesmithPlugin({
src: {
cwd: path.resolve(__dirname, 'app/images/'),
glob: '*.png'
},
target: {
image: path.resolve(__dirname, 'build/images/sprite.png'),
css: path.resolve(__dirname, 'build/css/sprite.css') //产生的样式文件,图片的样式类名是 icon-图片名
},
apiOptions: {
cssImageRef: '../images/sprite.png'
},
spritesmithOptions: {
algorithm: 'top-down'
}
})
]
};
链接:https://zhuanlan.zhihu.com/p/23873229
其他:
(8)resolve
定义解析模块路径时的配置,最常用的就是extensions,可以用来指定模块的后缀,这样在引入模块是就不需要写后缀了,会自动补全。
webpack在构建包的时候会按目录的进行文件的查找,resolve属性中的extensions数组中用于配置程序可以自行补全哪些文件后缀:
resolve:{
extensions:['', '.js', '.json']
}
然后我们想要加载一个js文件时,只要require('common')就可以加载common.js文件了。
(9)externals
当我们想在项目中require一些其他的类库或者API,而又不想让这些类库的源码被构建到运行时文件中,这在实际开发中很有必要。此时我们就可以通过配置externals参数来解决这个问题:
externals: {
"jquery": "jQuery"
}
这样我们就可以放心的在项目中使用这些API了:var jQuery = require(“jquery”);
(10)context
当我们在require一个模块的时候,如果在require中包含变量,像这样:
require("./mods/" + name + ".js");
那么在编译的时候我们是不能知道具体的模块的。但这个时候,webpack也会为我们做些分析工作:
1.分析目录:'./mods';
2.提取正则表达式:'/^.*.js$/';
于是这个时候为了更好地配合webpack进行编译,我们可以给它指明路径,像在cake-webpack-config中所做的那样(我们在这里先忽略abcoption的作用):
var currentBase = process.cwd();
var context = abcOptions.options.context ? abcOptions.options.context :
path.isAbsolute(entryDir) ? entryDir : path.join(currentBase, entryDir);
六、按需加载
传统的模块打包工具(module bundlers)最终将所有的模块编译生成一个庞大的bundle.js文件。但是在真实的app里边,“bundle.js”文件可能有10M到15M之大可能会导致应用一直处于加载中状态。因此Webpack使用许多特性来分割代码然后生成多个“bundle”文件,而且异步加载部分代码以实现按需加载。
七、执行打包
如果通过npm install -g webpack方式安装webpack的话,可以通过命令行直接执行打包命令,比如:
$ webpack –config webpack.config.js
这样就会读取当前目录下的webpack.config.js作为配置文件执行打包操作。默认查找webpack.config.js,所以我们在这里只需要执行webpack 命令即可。
八、常用webpack命令
在开发环境构建一次
webpack
构建并生成源代码映射文件
webpack -d
在生成环境构建,压缩、混淆代码,并移除无用代码
webpack -p
快速增量构建,可以和其他选项一起使用
webpack –watch
progress 显示打包过程中的进度,colors打包信息带有颜色显示
webpack –progress –colors
九、理解文件路径
require(‘lodash’) // 从模块目录查找
require(‘./file’) // 按相对路径查找
CSS 及图片的引用
require(‘./bootstrap.css’);
require(‘./myapp.less’);
var img = document.createElement(‘img’);
img.src = require(‘./weibo.png’);
十、其他插件功能
OccurenceOrderPlugin:给经常使用的模块分配最小长度的id,这样可以减少文件大小。
HotModuleReplacementPlugin:是热替换,热替换和dev-server的hot有什么区别?不用刷新页面,可用于生产环境。
NoErrorsPlugin:在打包时不会因为错误而中断
ProvidePlugin: 定义一些在import时能自动引入的变量,如定义了 $: 'jquery' 后,可以在文件中直接使用$,webpack可以自动帮你加上 var $ = require('jquery')。
DllPlugin: 将一些模块预编译,类似windows里的dll,可以在项目中直接使用,无需再构建。注意要在output中指定 library ,并在DllPlugin中指定与其一致的 name ,在有多个入口时可以使用 [name] 和 [hash] 来区分,因为这个参数是要赋值到global上的,所以这里使用 [hash] 不容易出现变量名冲突的情况。
DllReferencePlugin: 引用之前打包好的dll文件,注意下context参数,这个应该根据manifest.json文件中的引用情况来赋值,如果引用的都是npm安装的库,这里就填项目根目录就好了。
DefinePlugin: 可以定义编译时的全局变量,有很多库(React, Vue等)会根据 NODE_ENV 这个变量来判断当前环境。为了尽可能减少包大小,在生产环境中要定义其为 JSON.stringify(“production”)
optimize.UglifyJsPlugin: 配置压缩代码。
optimize.DedupePlugin: 可以减少重复文件数。
ExtractTextPlugin: 可以将所有css文件打包到一个css文件中。
十一、参考资料
1、webpack参考链接
http://webpackdoc.com
2、Webpack入门(其文章底部有些链接可以参考)———— 基本概念、webpack.config.js配置详解
http://blog.csdn.net/liujie19901217/article/details/51026943
3、入门 Webpack,看这篇就够了(很好,工作方式对比清晰、参数的表格清晰)******
https://segmentfault.com/a/1190000006178770#articleHeader8
4、[译] 通过 Webpack 实现 AngularJS 的延迟加载 ******
https://toutiao.io/posts/46gvgm/preview
5、30分钟手把手教你学webpack实战(内容比较全面、详细)******
http://www.cnblogs.com/tugenhua0707/p/4793265.html
6、Webpack + Angular的组件化实践
https://segmentfault.com/a/1190000003915443
7、为何webpack风靡全球?三大主流模块打包工具对比
http://www.techug.com/webpack-requirejs-browserify
webpack: code spliting / loader/plugin
8、前端构建工具漫谈,fis3、webpack、rollup.js
https://zhuanlan.zhihu.com/p/20933749
9、轻松入门webpack——官方文档解读
http://shuaihua.cc/article/webpack/webpack.php
10、关于雪碧图的详细介绍
https://zhuanlan.zhihu.com/p/23873229?utm_source=tuicool&utm_medium=referral
11、boi剖析 - 基于webpack的css sprites实现方案
http://www.cnblogs.com/ihardcoder/p/6053231.html