webpack总结

一、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