(三)webpack入门——webpack功能集合的demo
import stylesheet from 'styles/my-styles.scss'; import logo from 'img/my-logo.svg'; import someTemplate from 'html/some-template.html'; console.log(stylesheet); // "body{font-size:12px}" console.log(logo); // "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5[...]" console.log(someTemplate) // "<html><body><h1>Hello</h1></body></html>"
{ // When you import a .ts file, parse it with Typescript test: /\.ts/, loader: 'typescript', }, { // When you encounter images, compress them with image-webpack (wrapper around imagemin) // and then inline them as data64 URLs test: /\.(png|jpg|svg)/, loaders: ['url', 'image-webpack'], }, { // When you encounter SCSS files, parse them with node-sass, then pass autoprefixer on them // then return the results as a string of CSS test: /\.scss/, loaders: ['css', 'autoprefixer', 'sass'], }
export default 'body{font-size:12px}';
$ node --version
v5.7.1
$ npm --version
3.6.0
$ npm init -y
$ npm install jquery --save
$ npm install webpack --save-dev
var $ = require('jquery'); $('body').html('Hello');
module.exports = { entry: './src', output: { path: 'builds', filename: 'bundle.js', }, };
<!DOCTYPE html> <html> <head></head> <body> <h1>My title</h1> <a>Click me</a> <script src="builds/bundle.js"></script> </body> </html>
$ webpack
Hash: d41fc61f5b9d72c13744
Version: webpack 1.12.14
Time: 301ms
Asset Size Chunks Chunk Names
bundle.js 268 kB 0 [emitted] main
[0] ./src/index.js 53 bytes {0} [built]
+ 1 hidden modules
$ webpack --display-modules
bundle.js 268 kB 0 [emitted] main
[0] ./src/index.js 53 bytes {0} [built]
[1] ./~/jquery/dist/jquery.js 259 kB {0} [built]
$ npm install babel-loader --save-dev
$ npm install babel-core babel-preset-es2015 --save-dev
{ "presets": ["es2015"] }
module.exports = {
entry: './src',
output: {
path: 'builds',
filename: 'bundle.js',
},
module: {
loaders: [{
test: /\.js/,
loader: 'babel',
include: /src/,
}],
}
};
import $ from 'jquery';
$('body').html('Hello');
$ npm install mustache --save
$ npm install css-loader style-loader html-loader sass-loader node-sass --save-dev
{
test: /\.js/,
loader: 'babel',
include: /src/,
},
{
test: /\.scss/,
//loader: 'style!css!sass',
// Or
loaders: ['style', 'css', 'sass'],
},
{
test: /\.html/,
loader: 'html',
}
.button{ background:tomato; color:white; }
<a class="button"href="{{link}}">{{text}}</a>
import $ from 'jquery'; import template from './Button.html'; import Mustache from 'mustache'; import './Button.scss'; export default class Button { constructor(link) { this.link = link; } onClick(event) { event.preventDefault(); alert(this.link); } render(node) { const text = $(node).text(); // Render our button $(node).html(Mustache.render(template, { text })); // Attach our listeners $('.button').click(this.onClick.bind(this)); } }
import Button from './Components/Button';
const button = new Button('google.com');
button.render('a');
import $ from 'jquery'; // This is a split point require.ensure([], () = >{ // All the code in here, and everything that is imported // will be in a separate file const library = require('some-big-library'); $('foo').click(() = >library.doSomething()); });
bundle.js
|- jquery.js
|- index.js // our main file
chunk1.js
|- some-big-libray.js
|- index-chunk.js // the code in the callback
if (document.querySelectorAll('a').length) { require.ensure([], () = >{ const Button = require('./Components/Button').default; const button = new Button('google.com'); button.render('a'); }); }
$ webpack --display-modules --display-chunks
Hash: 43b51e6cec5eb6572608
Version: webpack 1.12.14
Time: 1185ms
Asset Size Chunks Chunk Names
bundle.js 3.82 kB 0 [emitted] main
1.bundle.js 300 kB 1 [emitted]
chunk {0} bundle.js (main) 235 bytes [rendered]
[0] ./src/index.js 235 bytes {0} [built]
chunk {1} 1.bundle.js 290 kB {0} [rendered]
[1] ./src/Components/Button.js 1.94 kB {1} [built]
[2] ./~/jquery/dist/jquery.js 259 kB {1} [built]
[3] ./src/Components/Button.html 72 bytes {1} [built]
[4] ./~/mustache/mustache.js 19.4 kB {1} [built]
[5] ./src/Components/Button.scss 1.05 kB {1} [built]
[6] ./~/css-loader!./~/sass-loader!./src/Components/Button.scss 212 bytes {1} [built]
[7] ./~/css-loader/lib/css-base.js 1.51 kB {1} [built]
[8] ./~/style-loader/addStyles.js 7.21 kB {1} [built]
path: 'builds', filename: 'bundle.js', publicPath: 'builds/',
require.ensure([], () = >{ const Button = require('./Components/Button').default; const button = new Button('google.com'); button.render('a'); }, 'button'); webpack.config.js output: { path: 'builds', filename: 'bundle.js', chunkFilename: '[name].bundle.js', publicPath: 'builds/', },
.header { font-size: 3rem; }
<headerclass="header">{{text}}</header>
import $ from 'jquery'; import Mustache from 'mustache'; import template from './Header.html'; import './Header.scss'; export default class Header { render(node) { const text = $(node).text(); $(node).html(Mustache.render(template, { text })); } }
然后将它渲染在我们的页面
// If we have an anchor, render the Button component on it if (document.querySelectorAll('a').length) { require.ensure([], () = >{ const Button = require('./Components/Button').default; const button = new Button('google.com'); button.render('a'); }); } // If we have a title, render the Header component on it if (document.querySelectorAll('h1').length) { require.ensure([], () = >{ const Header = require('./Components/Header').default; new Header().render('h1'); }); }
$ webpack --display-modules --display-chunks
Hash: 178b46d1d1570ff8bceb
Version: webpack 1.12.14
Time: 1548ms
Asset Size Chunks Chunk Names
bundle.js 4.16 kB 0 [emitted] main
1.bundle.js 300 kB 1 [emitted]
2.bundle.js 299 kB 2 [emitted]
chunk {0} bundle.js (main) 550 bytes [rendered]
[0] ./src/index.js 550 bytes {0} [built]
chunk {1} 1.bundle.js 290 kB {0} [rendered]
[1] ./src/Components/Button.js 1.94 kB {1} [built]
[2] ./~/jquery/dist/jquery.js 259 kB {1} {2} [built]
[3] ./src/Components/Button.html 72 bytes {1} [built]
[4] ./~/mustache/mustache.js 19.4 kB {1} {2} [built]
[5] ./src/Components/Button.scss 1.05 kB {1} [built]
[6] ./~/css-loader!./~/sass-loader!./src/Components/Button.scss 212 bytes {1} [built]
[7] ./~/css-loader/lib/css-base.js 1.51 kB {1} {2} [built]
[8] ./~/style-loader/addStyles.js 7.21 kB {1} {2} [built]
chunk {2} 2.bundle.js 290 kB {0} [rendered]
[2] ./~/jquery/dist/jquery.js 259 kB {1} {2} [built]
[4] ./~/mustache/mustache.js 19.4 kB {1} {2} [built]
[7] ./~/css-loader/lib/css-base.js 1.51 kB {1} {2} [built]
[8] ./~/style-loader/addStyles.js 7.21 kB {1} {2} [built]
[9] ./src/Components/Header.js 1.62 kB {2} [built]
[10] ./src/Components/Header.html 64 bytes {2} [built]
[11] ./src/Components/Header.scss 1.05 kB {2} [built]
[12] ./~/css-loader!./~/sass-loader!./src/Components/Header.scss 192 bytes {2} [built]
var webpack = require('webpack'); module.exports = { entry: './src', output: { // ... }, plugins: [new webpack.optimize.CommonsChunkPlugin({ name: 'main', // Move dependencies to our main file children: true, // Look for common dependencies in all children, minChunks: 2, // How many times a dependency must come up before being extracted }), ], module: { // ... } };
chunk {0} bundle.js (main) 287 kB [rendered]
[0] ./src/index.js 550 bytes {0} [built]
[2] ./~/jquery/dist/jquery.js 259 kB {0} [built]
[4] ./~/mustache/mustache.js 19.4 kB {0} [built]
[7] ./~/css-loader/lib/css-base.js 1.51 kB {0} [built]
[8] ./~/style-loader/addStyles.js 7.21 kB {0} [built]
chunk {1} 1.bundle.js 3.28 kB {0} [rendered]
[1] ./src/Components/Button.js 1.94 kB {1} [built]
[3] ./src/Components/Button.html 72 bytes {1} [built]
[5] ./src/Components/Button.scss 1.05 kB {1} [built]
[6] ./~/css-loader!./~/sass-loader!./src/Components/Button.scss 212 bytes {1} [built]
chunk {2} 2.bundle.js 2.92 kB {0} [rendered]
[9] ./src/Components/Header.js 1.62 kB {2} [built]
[10] ./src/Components/Header.html 64 bytes {2} [built]
[11] ./src/Components/Header.scss 1.05 kB {2} [built]
[12] ./~/css-loader!./~/sass-loader!./src/Components/Header.scss 192 bytes {2} [built]
new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', children: true, minChunks: 2, }),
<script src="builds/vendor.js"></script>
首先,我们将会加入一些plugins到我们的应用程序中,但是我们只希望在NODE_ENV 等于 production的时候加载它们,所以让我们在配置文件中增加一些逻辑。它只是一个JS文件,这将很容易做到:
var webpack = require('webpack'); var production = process.env.NODE_ENV === 'production'; var plugins = [new webpack.optimize.CommonsChunkPlugin({ name: 'main', // Move dependencies to our main file children: true, // Look for common dependencies in all children, minChunks: 2, // How many times a dependency must come up before being extracted }), ]; if (production) { plugins = plugins.concat([ // Production plugins go here ]); } module.exports = { entry: './src', output: { path: 'builds', filename: 'bundle.js', publicPath: 'builds/', }, plugins: plugins, // ... };
module.exports = { debug: !production, devtool: production ? false: 'eval',
if (production) { plugins = plugins.concat([ // This plugin looks for similar chunks and files // and merges them for better caching by the user new webpack.optimize.DedupePlugin(), // This plugins optimizes chunks and modules by // how much they are used in your app new webpack.optimize.OccurenceOrderPlugin(), // This plugin prevents Webpack from creating chunks // that would be too small to be worth loading separately new webpack.optimize.MinChunkSizePlugin({ minChunkSize: 51200, // ~50kb }), // This plugin minifies all the Javascript code of the final bundle new webpack.optimize.UglifyJsPlugin({ mangle: true, compress: { warnings: false, // Suppress uglification warnings }, }), // This plugins defines various variables that we can set to false // in production to avoid code related to them from being compiled // in our final bundle new webpack.DefinePlugin({ __SERVER__: !production, __DEVELOPMENT__: !production, __DEVTOOLS__: !production, 'process.env': { BABEL_ENV: JSON.stringify(process.env.NODE_ENV), }, }), ]); }
output: { path: 'builds', filename: production ? '[name]-[hash].js': 'bundle.js', chunkFilename: '[name]-[chunkhash].js', publicPath: 'builds/', },
$ npm install clean-webpack-plugin --save-dev
var webpack = require('webpack'); var CleanPlugin = require('clean-webpack-plugin'); // ... if (production) { plugins = plugins.concat([ // Cleanup the builds/ folder before // compiling our final assets new CleanPlugin('builds'),
$ webpack
bundle.js 314 kB 0 [emitted] main
1-21660ec268fe9de7776c.js 4.46 kB 1 [emitted]
2-fcc95abf34773e79afda.js 4.15 kB 2 [emitted]
$ NODE_ENV=production webpack
main-937cc23ccbf192c9edd6.js 97.2 kB 0 [emitted] main
$ npm install extract-text-webpack-plugin --save-dev
var webpack = require('webpack'); var CleanPlugin = require('clean-webpack-plugin'); var ExtractPlugin = require('extract-text-webpack-plugin'); var production = process.env.NODE_ENV === 'production'; var plugins = [ new ExtractPlugin('bundle.css'), // <=== where should content be piped new webpack.optimize.CommonsChunkPlugin({ name: 'main', // Move dependencies to our main file children: true, // Look for common dependencies in all children, minChunks: 2, // How many times a dependency must come up before being extracted }), ]; // ... module.exports = { // ... plugins: plugins, module: { loaders: [{ test: /\.scss/, loader: ExtractPlugin.extract('style', 'css!sass'), }, // ... ], } };
body { font-family: sans-serif; background: darken(white, 0.2); }
import'./styles.scss'; // Rest of our file
现在运行wbpack命令,现在非常确定我们有一个bundle.css文件,可以把它引入到html中:
$ webpack
bundle.js 318 kB 0 [emitted] main
1-a110b2d7814eb963b0b5.js 4.43 kB 1 [emitted]
2-03eb25b4d6b52a50eb89.js 4.1 kB 2 [emitted]
bundle.css 59 bytes 0 [emitted] main
你也可以提取chunks的styles,可以通过ExtractTextPlugin('bundle.css', {allChunks: true})选项来设置。注意一下,你也可以额给你的文件名设置成变量,所以如果你需要讲你的样式表版本化的话你仅需要做的是这么配置一下就行
ExtractTextPlugin('[name]-[hash].css')
在Webpack中使用images
在Webpack环境中是怎么工作的及如何最好的优化他们?让我们在网络上找一张图片,我们将会用这张图片作为页面背景图,因为我已经看到有人在Geocities有人这么做了,并且他看起来非常酷:
body { font-family: sans-serif; background: darken(white, 0.2); background-image: url('../img/puppy.jpg'); background-size: cover; }
$ npm install url-loader file-loader --save-dev
{ test: /\.(png|gif|jpe?g|svg)$/i, loader: 'url?limit=10000', },
{ test: /\.(png|gif|jpe?g|svg)$/i, loader: 'url', query: { limit: 10000, } }
bundle.js 15 kB 0 [emitted] main
1-b8256867498f4be01fd7.js 317 kB 1 [emitted]
2-e1bc215a6b91d55a09aa.js 317 kB 2 [emitted]
bundle.css 2.9 kB 0 [emitted] main
这是很强大的因为它意味着Webpack现在通过判断大小或者请求来优化任何固定资源。这里有一个很好的loader你可以通过管道传输进一步推动一些可优化的地方,一个非常常用的loader是image-loader,它通过imagemin在打包之前压缩所有的图片文件。它甚至有个?bypassOnDebug 查询参数允许你仅在生产环境来压缩图片。这里有很多像这样的插件,我鼓励你去看看文章最后列表里面的内容。
为了让HMR被使用,我们需要一个服务器,热资源将从它提供。我们可以使用Webpack提供的 dev-server, 让我们来加载它:
$ npm install webpack-dev-server --save-dev
$ webpack-dev-server --inline --hot
你可以将webpack-dev-server当做本地服务器使用。如果你打算为HMR总是使用它,你可以在你的配置文件中这么配置:
output: { path: 'builds', filename: production ? '[name]-[hash].js': 'bundle.js', chunkFilename: '[name]-[chunkhash].js', publicPath: 'builds/', }, devServer: { hot: true, },
现在无论何时运行 webpack-dev-server 它已经是HMR模式了. 注意我们使用webpack-dev-server来提供热资源但是你可以使用其他的选择,比如Express server。Webpack提供了一个中间件来供你在其他服务器上使用 HMR.
彻底弄明白或者糊里糊涂的?
$ npm install eslint eslint-loader babel-eslint --save-dev
然后创建以 .eslintrc 命名的文件:
parser: 'babel-eslint'
rules:
quotes: 2
现在加载我们的pre-loader,跟之前的语法类似,只不过是在module.preLoaders里面:
module: { preLoaders: [{ test: /\.js/, loader: 'eslint', }],
现在运行Webpack,和确定的它会失败:
$ webpack
Hash: 33cc307122f0a9608812
Version: webpack 1.12.2
Time: 1307ms
Asset Size Chunks Chunk Names
bundle.js 305 kB 0 [emitted] main
1-551ae2634fda70fd8502.js 4.5 kB 1 [emitted]
2-999713ac2cd9c7cf079b.js 4.17 kB 2 [emitted]
bundle.css 59 bytes 0 [emitted] main
+ 15 hidden modules
ERROR in ./src/index.js
/Users/anahkiasen/Sites/webpack/src/index.js
1:8 error Strings must use doublequote quotes
4:31 error Strings must use doublequote quotes
6:32 error Strings must use doublequote quotes
7:35 error Strings must use doublequote quotes
9:23 error Strings must use doublequote quotes
14:31 error Strings must use doublequote quotes
16:32 error Strings must use doublequote quotes
18:29 error Strings must use doublequote quotes
现在举另外一个pre-loader的例子:每一个组件我们现在引用的样式列表都是同一个名字,模板名也都是同一个。现在让我们使用一个pre-loader来自动加载和模块名相同的任何文件:
$ npm install baggage-loader --save-dev
{ test: /\.js/, loader: 'baggage?[file].html=template&[file].scss', }
import $ from 'jquery'; import template from './Button.html'; import Mustache from 'mustache'; import './Button.scss';
import $ from 'jquery';
import Mustache from 'mustache';
现在我们的应用程序还是相当小的,但是随着他开始变得越来越大它也许对依赖树到底是什么的深刻理解非常有帮助。我们可能做的对还是错,什么是我们的应用程序的瓶颈等等。现在在内部,Webpack知道各种事情,但是你的礼貌的问它它知道的将会向你展示。你可以像下面这样做来生产一个profile文件:
webpack --profile --json > stats.json
{ "scripts": { "build": "webpack", "build:api": "aglio -i docs/api/index.apib -o docs/api/index.html" } }
var gulp = require('gulp'); var gutil = require('gutil'); var webpack = require('webpack'); var config = require('./webpack.config'); gulp.task('default', function(callback) { webpack(config, function(error, stats) { if (error) throw new gutil.PluginError('webpack', error); gutil.log('[webpack]', stats.toString()); callback(); }); });