Webpack
更优秀的前端模块依赖管理工具
材主 2016.2
- Webpack简介
- Webpack常用功能
- Webpack安装使用
- Webpack模块编写
- 项目迁移方案
- 附录
What is Webpack?
网上介绍
webpack是近期最火的一款模块加载器兼打包工具,它能把各种资源,例如JS(含JSX)、coffee、样式(含less/sass)、图片等都作为模块来使用和处理。
require("./lib.js");
require("./style.css");
require("./style.less");
require("./template.jade");
require("./image.png");
require
模块依赖,一招搞定
在 Webpack 当中, 所有的资源都被当作是模块
加载器
对应各种不同文件类型的资源,
Webpack 有对应的模块 loader
module: {
//加载器配置
loaders: [
//.css 文件使用 style-loader 和 css-loader 来处理
{ test: /\.css$/, loader: 'style-loader!css-loader' },
//.js 文件使用 jsx-loader 来编译处理
{ test: /\.js$/, loader: 'jsx-loader?harmony' },
//.scss 文件使用 style-loader、css-loader 和 sass-loader 来编译处理
{ test: /\.scss$/, loader: 'style!css!sass?sourceMap'},
//图片文件使用 url-loader 来处理,小于8kb的直接转为base64
{ test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192'}
]
}
Webpack常用功能
JS里:CSS及图片引用
require('./bootstrap.css');
require('./myapp.less');
var img = document.createElement('img');
img.src = require('./glyph.png');
- Synchronous
- CSS和LESS会被打包到JS
- 图片可能被转化成 base64 格式的 dataUrl
module: {
loaders: [
//图片文件使用 url-loader 来处理,小于8kb的直接转为base64
{ test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192'}
]
}
LESS/CSS里:图片引用
根据配置“url-loader?limit=xxx”来决定把图片转换成base64还是图片链接形式引用。
background-image: url("./logo.png");
module: {
loaders: [
//图片文件使用 url-loader 来处理,小于8kb的直接转为base64
{ test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192'}
]
}
CSS能单独打包吗?
"extract-text-webpack-plugin"
插件
只需两步
1. 插件安装
npm install extract-text-webpack-plugin --save
2. 配置文件webpack.config.js
var ExtractTextPlugin = require("extract-text-webpack-plugin");
...
plugins: [
// 目标文件名规则[name].css
new ExtractTextPlugin('[name].css', {allChunks: true})
],
module: {
loaders: [
{ test: /\.css$/, loader: ExtractTextPlugin.extract("style-loader", "css-loader") },
{ test: /\.less$/, loader: ExtractTextPlugin.extract('style-loader', 'css-loader!less-loader') },
]
},
公共代码自动抽离
// 分析以下模块的共用代码, 单独打一个包到common.js
var commonsPlugin =
new webpack.optimize.CommonsChunkPlugin(/*chunkName=*/'common', /*filename=*/'common.js');
plugins: [
commonsPlugin
],
<script src="build/common.js"></script>
记得要在HTML手动引入common.js
A.js,B.js
a.js,b.js, common.js
HTML自动引用JS/CSS
插件"html-webpack-plugin"
$: npm install html-webpack-plugin --save
参考:https://www.npmjs.com/package/html-webpack-plugin-extra-files
var HtmlWebpackPlugin = require('html-webpack-plugin'); // Html文件处理
module.exports = {
……
plugins: [
/**
* HTML文件编译,自动引用JS/CSS
*
* filename - 输出文件名,相对路径output.path
* template - HTML模板,相对配置文件目录
* chunks - 只包含指定的文件(打包后输出的JS/CSS),不指定的话,它会包含生成的所有js和css文件
* excludeChunks - 排除指定的文件(打包后输出的JS/CSS),比如:excludeChunks: ['dev-helper']
* hash
*/
new HtmlWebpackPlugin({filename: 'views/list.html', template: 'src/modules/app/list/index.html', chunks: ['common', 'List'], hash: true}),
new HtmlWebpackPlugin({filename: 'views/detail.html', template: 'src/modules/app/detail/index.html', chunks: ['common', 'Detail'], hash: true})
],
};
功能开关
有些代码我们只想在开发环境使用(比如log)
全局变量插件:webpack.DefinePlugin
if(__DEV__) {
console.log('run in dev.');
}
JS中使用全局变量:
module.exports = {
plugins: [
// 全局变量
new webpack.DefinePlugin({
// __DEV__: JSON.stringify(JSON.parse(process.env.DEBUG || 'false')), //通过环境变量设置
__DEV__: JSON.stringify(JSON.parse('true')), // 开发调试时把它改为true
__HELLO__: JSON.stringify('hello world')
})
]
};
配置文件:
注意:webpack -p 会执行 uglify dead-code elimination, 任何这种代码都会被剔除, 所以你不用担心秘密功能泄漏.
异步加载
require.ensure
语法:
require.ensure(dependencies: String[],
callback: function([require]),
[chunkName: String])
与require AMD类似,也是在需要的时候才会加载相应的模块。但不同的是,require.ensure在模块被下载下来后(模块还没被执行)便立即执行回调函数.
另外require.ensure可以指定构建后chunk名,如果之前已有require.ensure指定了该名称,webpack会将这些模块统一合并到一个模块集里。
// 异步加载
if(i < 0) {
require.ensure([], function() {
require('a.js');
});
}
简单例子
定义异步加载文件名字
output: {
chunkFilename: "[id].chunk.[hash].js"
},
生成的异步文件引用逻辑自动包含在源目标JS中,不用手动引用,所以以上文件名随便怎么定义都不影响。
file-loader
图片加载器url-loader其实是对file-loader的一个封装
loaders: [
{
test: /\.(png|jpg|gif)$/,
loader: 'url-loader',
query: {
name: '[path][name].[ext]?[hash:8]',
limit: 8192
}
}
]
如果文件超出体积, 就给一个这样规则的文件名
ES6支持
module: {
loaders: [
{
test: /\.js$/, loader: 'babel-loader', // ES6
exclude: /(node_modules|bower_components|ppaweb\\libs\\webpack)/
},
]
},
babel-loader
Alias
项目迁移更方便
resolve: {
alias: {
// 从module调用webpack上的公共lib库路径简写
'lib0': '../../../ppaweb/libs/webpack',
// 从module的子文件夹调用webpack上的公共lib库路径简写
'lib1': '../../../../ppaweb/libs/webpack',
// 从module的两层子文件夹调用webpack上的公共lib库路径简写
'lib2': '../../../../../ppaweb/libs/webpack'
},
// 现在可以写 require('file') 代替 require('file.coffee')
extensions: ['', '.js', '.json', '.coffee']
}
安装使用
安装
#首先确保机子上已安装node.js,然后通过npm安装webpack
$: npm install webpack -g
启动
# 切换到有 webpack.config.js 的目录然后运行
$: webpack // 执行一次开发的编译
$: webpack -p // 针对发布环境编译(压缩代码)
$: webpack -w // 进行开发过程持续的增量编译(飞快地!)
$: webpack -d // 生成map映射文件,告知哪些模块被最终打包到哪里了
$: webpack --config XXX.js //使用另一份配置文件(比如webpack.config2.js)来打包
配置文件
webpack.config.js
通用配置文件例子,能满足日常开发需要
// webpack.config.js
var webpack = require('webpack');
var commonsPlugin = new webpack.optimize.CommonsChunkPlugin(/* chunkName= */'common', /* filename= */'common.js'); // 分析以下模块的共用代码, 单独打一个包到common.js
var ExtractTextPlugin = require("extract-text-webpack-plugin"); // 单独打包CSS
var HtmlWebpackPlugin = require('html-webpack-plugin'); // Html文件处理
module.exports = {
entry: {
Detail: './modules/app/detail.js',
Home: './modules/app/home.js'
},
output: {
path: './build', // This is where images & js will go
//publicPath: 'http://m.pp.cn/ppaweb/test/build/', // This is used to generate URLs to e.g. images
publicPath: '/ppaweb/example/build/', // This is used to generate URLs to e.g. images
filename: '[name].js',
chunkFilename: "[id].chunk.js?[hash:8]"
},
plugins: [
commonsPlugin,
new ExtractTextPlugin('[name].css', {allChunks: true}), // 单独打包CSS
// 全局变量
new webpack.DefinePlugin({
//__DEV__: JSON.stringify(JSON.parse(process.env.BUILD_DEV||'false')) //通过环境变量设置
__DEV__: 'false' // 开发调试时把它改为true
}),
/**
* HTML文件编译,自动引用JS/CSS
*
* filename - 输出文件名,相对路径output.path
* template - HTML模板,相对配置文件目录
* chunks - 只包含指定的文件(打包后输出的JS/CSS),不指定的话,它会包含生成的所有js和css文件
* excludeChunks - 排除指定的文件(打包后输出的JS/CSS),比如:excludeChunks: ['dev-helper']
* hash
*/
new HtmlWebpackPlugin({filename: 'views/home.html', template: 'views/home.html', chunks: ['common', 'Home'], hash: true}),
new HtmlWebpackPlugin({filename: 'views/detail.html', template: 'views/detail.html', chunks: ['common', 'Detail'], hash: true})
],
module: {
loaders: [
{
test: /\.js$/, loader: 'babel-loader', // ES6
exclude: /(node_modules|bower_components|ppaweb\\libs\\webpack)/
},
// CSS,LESS打包进JS
{ test: /\.css$/, loader: 'style-loader!css-loader' },
{ test: /\.less$/, loader: 'style-loader!css-loader!less-loader' }, // use ! to chain loaders
// CSS,LESS单独打包
//{ test: /\.css$/, loader: ExtractTextPlugin.extract("style-loader", "css-loader") },
//{ test: /\.less$/, loader: ExtractTextPlugin.extract('style-loader', 'css-loader!less-loader') },
{ test: /\.tpl$/, loader: 'ejs'}, // artTemplate/ejs 's tpl
{
test: /\.(png|jpg|gif)$/,
loader: 'url-loader',
query: {
name: '[path][name].[ext]?[hash:8]',
limit: 8192 // inline base64 URLs for <=8k images, direct URLs for the rest
}
}
]
},
resolve: {
alias: {
'lib0': '../../../ppaweb/libs/webpack', // 从module调用webpack上的公共lib库路径简写
'lib1': '../../../../ppaweb/libs/webpack', // 从module的子文件夹调用webpack上的公共lib库路径简写
'lib2': '../../../../../ppaweb/libs/webpack' // 从module的两层子文件夹调用webpack上的公共lib库路径简写
},
// 现在可以写 require('file') 代替 require('file.coffee')
extensions: ['', '.js', '.json', '.coffee']
}
};
Webpack模块编写
Webpack模块框架
参考:[androidppweb]ppaweb/libs/webpack/*
// var $ = require('zepto');
// require('./index.less');
!(function () {
var module1 = (function () {
var _e = {};
_e.test = function () {
// do something here
};
return _e;
})();
window.module1 = module1;
try {
module.exports = module1;
} catch (e) {}
})();
目录规范
project-name
├─ debug (调试目录,如有需要)
├─ release (发布目录)
├─ src (开发目录)
├─ modules (模块化资源目录,JS/LESS/图片等资源分散到各模块中)
├─ views (非模块化资源目录,HTML/公共图片等)
├─ server (nodejs服务器,如有需要)
├─ package.json (nodejs包描述文件)
├─ webpack.config.js (webpack配置文件,记得配置output目录为"./release")
├─ LICENSE (协议说明文件,如有需要)
└─ README.md (项目说明文件)
以下目录规范,公供参考
例子原型
https://github.com/diamont1001/webpack-demo
项目迁移方案
一般一个页面(HTML)对应一个入口文件
1.入口文件
entry: {
Detail: './modules/app/detail.js',
Soft: './modules/app/soft/soft.js',
Search: './modules/app/search/search.js',
Activity: './modules/app/activity/activity.js',
Category: './modules/app/category.js',
Update: './modules/app/update/update.js',
IgnoreUpdate: './modules/app/update/ignore-update.js',
Home: './modules/app/home.js'
},
配置文件
2.文件引用
<script src="lib1.js"></script>
<script src="lib2.js"></script>
<script src="index.js"></script>
require('lib1.js');
require('lib2.js');
require('index.js');
简单粗暴型
温文儒雅型
<script src="lib1.js"></script>
<script src="lib2.js"></script>
<script src="index.js"></script>
#loading.js
require('loading.less');
#index.js
var loading = require('loading.js'),
header = require('header.js');
#header.js
require('header.less');
模块抽离
3.优化
- common.js
- CSS单独打包
- 异步加载
- ……
成功栗子
PP浏览器插件
FIS PURE => Webpack
专题模板化
Grunt => Webpack
Webpack+React
Git官网:
入门指引(含例子webpack+react):
https://github.com/petehunt/webpack-howto
还是看原作者的分享PPT吧^_^:
http://sokra.github.io/slides/webpack#1
官方文档
网上文章教程
Q&A
Q. HTML里引用JS能自动生成访问后缀吗?比如a.js?2016
A. 插件html-webpack-plugin
Thanks
Webpack
By 材主
Webpack
更优秀的前端模块依赖管理工具
- 4,078