本文主要针对webpack打包流程,注意事项,es6新特性变化,以及es6 开发react的入门教程:
React开发和模块管理的主流工具webpack也被大家所熟知。
web开发中常用到的静态资源主要有JavaScript、CSS、图片、Jade等文件,webpack中将静态资源文件称之为模块。webpack是一个module bundler(模块打包工具),其可以兼容多种js书写规范,且可以处理模块间的依赖关系,具有更强大的js模块化的功能。
webpack具有requireJs和browserify的功能,但仍有很多自己的新特性:
序号 | 特点 | 备注 |
---|---|---|
1 | 对 CommonJS 、 AMD 、ES6的语法做了兼容 | 兼容性强 |
2 | 对js、css、图片等资源文件都支持打包 | 打包范围广 |
3 | 串联式模块加载器以及插件机制,让其具有更好的灵活性和扩展性,例如提供对CoffeeScript、ES6的支持 | 灵活易扩展 |
4 | 有独立的配置文件webpack.config.js | 独立配置,高内聚 |
5 | 可以将代码切割成不同的chunk,实现按需加载,降低了初始化时间 | 按需,高效,低耦合 |
6 | 支持 SourceUrls 和 SourceMaps,易于调试 | [不很懂]就是即使代码压缩,也可以转化为非混淆压缩形式调试 |
7 | 具有强大的Plugin接口,大多是内部插件,使用起来比较灵活 | 灵活 |
8 | webpack 使用异步 IO 并具有多级缓存。这使得 webpack 很快且在增量编译上更加快 | 异步,缓存,性能优 |
1.相比Grunt,WebPack除了具备丰富的插件外,同时带有一套加载(Loader)系统。使它支持多种规范的加载方式,包括ES6、CommonJS、AMD等方式,这是Grunt、Gulp所不具备的。
2.代码混淆的角度来看,WebPack更加的极致
3.代码分片为处理单元(而不是文件),使得文件的分片更为灵活。
npm install -g webpack //全局安装
npm install --save-dev webpack //产品模式用dependencies,开发模式用devDep; save就是存到依赖列表里package.json
全局安装的webpack,直接执行webpack命令,会默认使用当前目录的webpack.config.js作为配置文件。如果要指定另外的配置文件,可以执行:
webpack -config webpack.chauncey.config.js //指定新文件
环境变量
通过NODE_ENV可以来设置环境变量(默认值为development)。区分开发和生产环境,对于文件的不同处理。
linux & mac: export NODE_ENV=production
windows:set NODE_ENV=production
运行前可以设置当前环境,SET NODE_ENV=production(开发环境)
set PORT=1234设置接口
entry
entry参数: 打包的输入文件,参数值代表输出后文件形式,字符串(单个文件)、数组(一大个文件)、对象(不同属性为不同文件)
{
entry: {
page1: "./page1",
//支持数组形式,将加载数组中的所有模块,但以最后一个模块作为输出
page2: ["./entry1", "./entry2"],
page3: {
subp1: "./sp1",
subp2: "./sp2"
}
},
output: {
path: "dist/js/page",//目标目录【文件夹】
publicPath: "/output/",
filename: "[name].bundle.js"
}
}
output
output参数为对象,定义了输出文件的位置及名字
output: {
path: "dist/js/page",//打包文件存放的绝对路径
publicPath: "/output/",//网站运行时的访问路径
filename: "[name].bundle.js"//打包后的文件名
}
当我们在entry中定义构建多个文件时,filename可以对应的更改为[name].js用于定义不同文件构建后的名字.
module
在webpack中JavaScript,CSS,LESS,TypeScript,JSX,CoffeeScript,图片等静态文件都是模块,不同模块的加载是通过模块加载器(webpack-loader)来统一管理的。loaders之间是可以串联的,一个加载器的输出可以作为下一个加载器的输入,最终返回到JavaScript上:
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'}
]
}
其中, test:表示匹配的资源类型;
loader或loaders:表示用来加载这种类型资源的loader(加载器);[可参考using loaders]
! 用来定义loader的串联关系,”-loader”是可以省略不写的,多个loader之间用“!”连接起来,但所有的加载器都需要通过 npm 来加载。
此外,还可以添加用来定义png、jpg这样的图片资源在小于10k时自动处理为base64图片的加载器:
{ test: /\.(png|jpg)$/,loader: 'url-loader?limit=10000'}
给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()还支持在资源path前面指定loader,即require(![loaders list]![source path])形式:
require("!style!css!less!bootstrap/less/bootstrap.less");
// “bootstrap.less”这个资源会先被"less-loader"处理,
// 其结果又会被"css-loader"处理,接着是"style-loader"
// 可类比pipe操作
require()时指定的loader会覆盖配置文件里对应的loader配置项。
resolve
webpack在构建包的时候会按目录的进行文件的查找,resolve属性中的extensions数组中用于配置程序可以自行补全文件后缀:
resolve: {
//查找module的话从这里开始查找
root: '/pomy/github/flux-example/src', //绝对路径
//自动扩展文件后缀名,意味着我们require模块可以省略不写后缀名
extensions: ['', '.js', '.json', '.scss'],
//模块别名定义,方便后续直接引用别名,无须多写长长的地址
alias: {
AppStore : 'js/stores/AppStores.js',//后续直接 require('AppStore') 即可
ActionType : 'js/actions/ActionType.js',
AppAction : 'js/actions/AppAction.js'
}
}
然后我们想要加载一个js文件时,只要require(‘common’)就可以加载common.js文件了。
注意一下, extensions 第一个是空字符串! 对应不需要后缀的情况.
plugin
webpack提供了[丰富的组件]用来满足不同的需求,当然我们也可以自行实现一个组件来满足自己的需求:
plugins: [
//your plugins list
]
在webpack中编写js文件时,可以通过require的方式引入其他的静态资源,可通过loader对文件自动解析并打包文件。通常会将js文件打包合并,css文件会在页面的header中嵌入style的方式载入页面。但开发过程中我们并不想将样式打在脚本中,最好可以独立生成css文件,以外链的形式加载。这时extract-text-webpack-plugin插件可以帮我们达到想要的效果。需要使用npm的方式加载插件,然后参见下面的配置,就可以将js中的css文件提取,并以指定的文件名来进行加载。
npm install extract-text-webpack-plugin --save-dev
plugins: [
new ExtractTextPlugin('styles.css')
]
externals
当我们想在项目中require一些其他的外部类库或者API,而又不想让这些类库的源码被构建到运行时文件中,这在实际开发中很有必要。此时我们就可以通过配置externals参数来解决这个问题:
externals: {
"jquery": "jQuery"
}
这样我们就可以放心的在项目中使用这些API了:var jQuery = require(“jquery”);
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);
关于 webpack.config.js 更详尽的配置可以参考这里
webpack的使用通常有三种方式:
1、命令行使用:webpack
var webpack = require('webpack');
webpack({
//configuration
}, function(err, stats){});
3、默认使用当前目录的webpack.config.js作为配置文件。如果要指定另外的配置文件,可以执行:webpack –config webpack.custom.config.js
webpack 的执行也很简单,直接执行
$ webpack --display-error-details
即可,后面的参数“–display-error-details”是推荐加上的,方便出错时能查阅更详尽的信息(比如 webpack 寻找模块的过程),从而更好定位到问题。
常用命令
webpack的使用和browserify有些类似,下面列举几个常用命令:
即可,后面的参数“--display-error-details”是推荐加上的,方便出错时能查阅更详尽的信息(比如 webpack 寻找模块的过程),从而更好定位到问题。
常用命令
webpack的使用和browserify有些类似,下面列举几个常用命令:
前面的四个命令比较基础,使用频率会比较大,后面的命令主要是用来定位打包时间较长的原因,方便改进配置文件,提高打包效率。
图片打包
webpack中对于图片的处理,可以通过url-loader来实现图片的压缩。
div.img{
background: url(../image/xxx.jpg)
}
//或者
var img = document.createElement("img");
img.src = require("../image/xxx.jpg");
document.body.appendChild(img);
针对上面的两种使用方式,loader可以自动识别并处理。根据loader中的设置,webpack会将小于指点大小的文件转化成 base64 格式的 dataUrl,其他图片会做适当的压缩并存放在指定目录中。
module: {
{
test: /\.(png|jpg)$/,
loader: 'url-loader?limit=10000&name=build/[name].[ext]'
}]
}
对于上面的配置,如果图片资源小于10kb就会转化成 base64 格式的 dataUrl,其他的图片会存放在build/文件夹下。
静态资源服务器
除了提供模块打包功能,Webpack还提供了一个基于Node.js Express框架的开发服务器,它是一个静态资源Web服务器,对于简单静态页面或者仅依赖于独立服务的前端页面,都可以直接使用这个开发服务器进行开发。在开发过程中,开发服务器会监听每一个文件的变化,进行实时打包,并且可以推送通知前端页面代码发生了变化,从而可以实现页面的自动刷新。
Webpack开发服务器需要单独安装,同样是通过npm进行:
npm install -g webpack-dev-server
可以使用webpack-dev-server直接启动,也可以增加参数来获取更多的功能,具体配置可以参见官方文档。默认启动端口8080,通过localhost:8080/webpack-dev-server/可以访问页面,文件修改后保存时会在页面头部看到sever的状态变化,并且会进行热替换,实现页面的自动刷新。
1.建议,主页尽量做到最精简,毕竟决定用户存留时间。按需。
2.【推荐很好】http://www.cnblogs.com/giveiris/p/5237080.html
【前端构建】WebPack实例与前端性能优化
3.【有雪碧图,gulp和webpack】【基于webpack搭建纯静态页面型前端工程解决方案模板】https://github.com/chemdemo/webpack-seed
4.所有资源都是模块。
webpack安装
npm install webpack -g
webpack配置
// webpack.config.js
var path = require("path");
module.exports = {
entry: './public/js/entry.js', //演示单入口文件
output: {
path: path.join(__dirname, './public/pro'), //打包输出的路径
filename: 'bundle.js', //打包后的名字
publicPath: "./pro" //html引用路径,在这里是本地地址。
}
};
编写入口文件
接下来就编写我们的入口文件 entry.js 和第一个模块文件 module1.js 。我们一切从简,里面只用来加载一个Js模块。
// entry.js
require("./module1"); // 使用CommonJs来加载模块
// module1.js
console.log("hello,world! module1");
document.write("超哥威武");
启动webpack
一切准备好后,我们仅需要在项目根目录下,用命令行 webpack 执行一下即可。
// webpack 命令行的几种基本命令
$ webpack // 最基本的启动webpack方法
$ webpack -w // 提供watch方法,实时进行打包更新
$ webpack -p // 对打包后的文件进行压缩,提供production
$ webpack -d // 提供source map,方便调试。
多模块依赖
跑通webpack打包entry,现在尝试commonjs和amd,两种模块机制。
require(["./module3"], function(){
console.log("hello,world! module1&&require(module3)");
document.write("超哥威武");
});
// module2.js,使用的是CommonJs机制导出包
module.exports = function(a, b){
return a + b;
}
// module3.js,使用AMD模块机制
define(['./module2.js'], function(sum){
return console.log("1 + 2 = " + sum(1, 2));
})
混用两种不同机制非常不好,这里仅仅是展示用的;
在开发新项目时还是推荐CommonJs或ES2015的Module。ES2015的模块机制,是趋势。【这一块我会报个小错误,没有完全实现】
【loader加载器】
预处理器做一些CoffeeScript 和 Sass 的编译。我们以前要编译这些预处理器,用 gulp相对复杂,webpack可以一次性解决!
在这里我们用Sass和babel编译ES2015为例子,看一下loader是如何使用的。
安装loader
npm install style-loader css-loader url-loader babel-loader sass-loader file-loader --save-dev
配置loader
// webpack.config.js
module.exports = {
entry: path.join(__dirname, 'src/entry.js'),
output: {
path: path.join(__dirname, 'out'),
publicPath: "./out/",
filename: 'bundle.js'
},
// 新添加的module属性
module: {
loaders: [
{test: /\.js$/, loader: "babel"},
{test: /\.css$/, loader: "style!css"},
{test: /\.(jpg|png)$/, loader: "url?limit=8192"},
{test: /\.scss$/, loader: "style!css!sass"}
]
}
};
1.uglify
webpack提供插件UglifyJsPlugin,可以优化(支持压缩、混淆)代码。
配置以下列表,在混淆代码时,以下配置的变量,不会被混淆
new webpack.optimize.UglifyJsPlugin({
mangle: {
except: ['$super', '$', 'exports', 'require']
}
})
以上变量‘ super′,‘ ’, ‘exports’ or ‘require’,不会被混淆
example
var UglifyJsPlugin = require("../../node_modules/webpack/lib/optimize/UglifyJsPlugin");
module.exports = {
entry: "./entry.js",
output: {
path: __dirname,
filename: "bundle.js",
},
plugins: [
//使用丑化js插件
new UglifyJsPlugin({
compress: {
warnings: false
},
mangle: {
except: ['$scope', '$']
}
})
]
};
//entry.js
define("entry", function () {
//变量 iabcdef 已引用,混淆
var iabcdef = 11;
//变量 $scope 已引用,但不混淆
var $scope = "scope";
document.write("entry module" + iabcdef);
document.write($scope);
//变量 ixzy 未被引用,剔除
var ixzy = 3241;
});
2.版本控制
对于静态资源的版本控制,目前微信项目采取办法是版本号作为请求参数,版本号为发布日期,但有两个问题:
1、更新版本时,CDN不能及时更新;
2、没有发生变更的文件也被赋上新版本
Webpack的做法是,生成hash,区分文件。
配置方法
//所有代码块添加hash
module.exports = {
entry: "./entry.js",
output: {
path: "assets/[hash]/",
publicPath: "assets/[hash]/",
filename: "bundle.js"
}
};
生成单个代码块文件的hash
配置方法
//单个代码块添加hash
module.exports = {
entry: "./entry.js",
output: {
path: "build/",
publicPath: "build/",
chunkFilename: "[id].[hash].bundle.js",
filename: "output.[hash].bundle.js",
}
};
【关于:chunkFilename意义】
http://react-china.org/t/webpack-output-filename-output-chunkfilename/2256
Http头对文件设置很大的max-age,例如1年。同时,给每个文件命名上带上该文件的版本号,例如把文件的hash值做为版本号,topic. ef8bed6c.js。即是让文件很长时间不过期。
当文件没有更新时,使用缓存的文件自然不会出错;
从上面的截图可以看出来,通过WebPack是可以很轻松做到第二点的——只需要给文件名配置上[chunkhash:8]即可,其中8是指hash长度为8,默认是16。
output{
path: _dirname + '/release/',
filename: '[chunkhash:8].[name].js',
chunkFilename: '[name].[chunkhash:8].js'
}
P.S.这样的处理效果已经很好了,但同样有劣处,即浏览器给这种缓存方式的缓存容量太少了,只有12Mb,且不分Host。所以更极致的做法是以文件名为Key,文件内容为value,缓存在localStorage里,命中则从缓存中取,不命中则去服务器取,虽然缓存容量也只有5Mb,但是每个Host是独享这5Mb的。
4.自动生成页面
文件名带上版本号后,每一次文件变化,都需要Html文件里手动修改引用的文件名,这种重复工作很琐碎且容易出错。html内部文件的名字如何动态改变。
使用 HtmlWebpackPlugin 和 ExtractTextPlugin 插件可以解决此问题。
生成带js的页面
【参考】http://www.cnblogs.com/haogj/p/5160821.html
new HtmlWebpackPlugin({
filename: 'index.html',
template: __dirname + '/public/index.html',
inject: true,//是否injection
chunks: ['page1'],//所要控制的模块组
//排序
chunksSortMode: function(a, b){
var index = {'page1': 1},
aI = index[a.origins[0].name],
bI = index[b.origins[0].name];
return aI&&bI ? bI - aI : -1;
}
}),
生成带css的页面
new ExtractTextPlugin(“comm.[contenthash:9].css”)
插件介绍到此为止,然而,还有一个关于同步加载和异步加载的问题,否则入口文件还是会很臃肿。
var TopicItem = require('../topic/topicitem');
异步写法
require.ensure([], function(){
//to do
});
3.待学习的:
1)webpack在PC项目中的应用
http://web.jobbole.com/85396/
2)基于 Webpack 和 ES6 打造 JavaScript 类库
http://web.jobbole.com/84858/
3)webpack使用优化
http://web.jobbole.com/84847/
4)彻底解决 webpack 打包文件体积过大
http://www.jianshu.com/p/a64735eb0e2b
5)大公司里怎样开发和部署前端代码
https://github.com/fouber/blog/issues/6