我的webpack,持续更新......
压缩js
使用自带的webpack.optimize.UglifyJsPlugin
const webpack = require("webpack");
module.exports = function(env) {
return {
entry: {
main: "./index.js",
},
output: {
filename: "[chunkhash].[name].js",
path: path.resolve(__dirname, "dist")
},
plugins: [
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false,
drop_console: false,
}
}),
]
}
}
分离css
1、安装extract-text-webpack-plugin插件
npm i --save-dev extract-text-webpack-plugin@beta
2、在webpack.config.js中使用这个插件
var ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = function () {
return {
entry: "./index.js",
output: {
path: "./dist",
filename: "bundle.js"
},
module: {
rules: [{
test: /.css$/,
exclude: /node_modules/,
use: ExtractTextPlugin.extract({
loader: "css-loader"
})
}]
},
// devtool: "source-map",
plugins: [
new ExtractTextPlugin({ filename: "bundle.css", disable: false, allChunks: true })
]
}
}
提取公共文件
使用CommonsChunkPlugin的几种方式:
https://webpack.js.org/plugins/commons-chunk-plugin/#components/sidebar/sidebar.jsx
以下内容参考自:https://doc.webpack-china.org/guides/code-splitting-libraries/#-vendor-chunk
但是,如果我们改变应用的代码并且再次运行 webpack,可以看到 vendor 文件的 hash 改变了。即使我们把 vendor 和 main 的 bundle 分开了,也会发现 vendor bundle 会随着应用代码改变。
这意味着我们任然无法从浏览器缓存机制中受益,因为 vendor 的 hash 在每次构建中都会改变,浏览器也必须重新加载文件。这里的问题在于,每次构建时,webpack 生成了一些 webpack runtime 代码,用来帮助 webpack完成其工作。当只有一个 bundle 的时候,runtime 代码驻留在其中。但是当生成多个 bundle的时候,运行时代码被提取到了公共模块中,在这里就是 vendor 文件。
为了防止这种情况,我们需要将运行时代码提取到一个单独的 manifest 文件中。尽管我们又创建了另一个 bundle,其开销也被我们在 vendor 文件的长期缓存中获得的好处所抵消。
// index.js
var moment = require("moment");
var path = require("path");
console.log(moment().format(), 11122334455);
console.log(path.resolve(__dirname, "/dist"));
console.log(path.join(__dirname, "/dist"));
// webpack.config.js
var path = require("path");
var webpack = require("webpack");
var HtmlWebpackPlugin = require("html-webpack-plugin")
module.exports = function(env) {
return {
entry: {
main: "./index.js",
vendor: ["moment", "path"] // 提取moment,path模块到wendor文件
},
output: {
filename: "[name].js", // 原来是[chunkhash].[name].js
path: path.resolve(__dirname, "dist")
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
// name: "vendor_modules",
// minChunks: function (module) {
// // 该配置假定你引入的 vendor 存在于 node_modules 目录中
// return module.context && module.context.indexOf("node_modules") !== -1;
// }
names: ["vendor", "manifest"]
}),
new HtmlWebpackPlugin({
template: "./index.html" //使用当前目录(根目录)下的index.html
})
]
}
}
使用webpack打包后,dist目录命令有(xxxxx表示hash值)
index.html
xxxxx.main.js
xxxxx.manifest.js //webpack runtime 代码
xxxxx.vendor.js
修改index.js代码,再次打包后,只会生更新manifest.js和main.js。而最大的共同包vendor.js是不会变得。通过hash值可以看到。这样vendor.js就可以缓存到浏览器了。
提取公共文件+
https://doc.webpack-china.org/guides/caching/
为了最小化生成的文件大小,webpack 使用标识符而不是模块名称。在编译期间,生成标识符并映射到块文件名,然后放入一个名为 chunk manifest 的 JavaScript 对象中。
为了生成保存在构建中的标识符,webpack 提供了 NamedModulesPlugin
(推荐用于开发模式)和HashedModuleIdsPlugin
(推荐用于生产模式)这两个插件。
然后将 chunk manifest(与引导/运行时代码一起)放入 entry chunk,这对 webpack 打包的代码工作是至关重要的。
改变其他文件时mainfest.js改变
这个问题和以前一样:每当我们改变代码的任何部分时,即使它的内容的其余部分没有改变,都会更新我们的入口块以便包含新的映射(manifest)。 这反过来,将产生一个新的哈希值并且使长效缓存失效。
使用ChunkManifestWebpackPlugin
要解决这个问题,我们应该使用 ChunkManifestWebpackPlugin,它会将 manifest 提取到一个单独的 JSON 文件中。 这将用一个 webpack runtime 的变量替换掉chunk manifest。 但我们可以做得更好;我们可以使用 CommonsChunkPlugin 将运行时提取到一个单独的入口起点(entry)中去。这里是一个更新后的 webpack.config.js,将生成我们的构建目录中的 manifest 和 runtime 文件:
// webpack.config.js
var ChunkManifestPlugin = require("chunk-manifest-webpack-plugin"); module.exports = {
/*...*/
plugins: [
/*...*/
new webpack.optimize.CommonsChunkPlugin({
name: ["vendor", "manifest"], // vendor libs + extracted manifest
minChunks: Infinity,
}),
/*...*/
new ChunkManifestPlugin({ filename: "chunk-manifest.json", manifestVariable: "webpackManifest" })
]
};
因为我们从入口块(entry chunk)中移除了 manifest,所以我们现在有责任为 webpack 提供它。上面示例中的 manifestVariable 选项是全局变量的名称,webpack 将利用它查找 manifest JSON 对象。这个变量应该在我们引入 bundle 到 HTML 之前就定义好。这是通过在 HTML 中内联 JSON 的内容来实现的。我们的 HTML 头部应该像这样:
<!-- 这里我不明白webpackManifest的作用 -->
<html>
<head>
<script>
//<![CDATA[ window.webpackManifest = {"0":"main.5f020f80c23aa50ebedf.js","1":"vendor.81adc64d405c8b218485.js"} //]]>
</script>
</head>
<body>
</body>
</html>
在结束时,文件的哈希值应该基于文件的内容。对此,我们可以使用 webpack-chunk-hash 或者 webpack-md5-hash。
所以最终的 webpack.config.js 看起来像这样:
var path = require("path");var webpack = require("webpack");
var ChunkManifestPlugin = require("chunk-manifest-webpack-plugin");
var WebpackChunkHash = require("webpack-chunk-hash");
module.exports = {
entry: {
vendor: "./src/vendor.js", // vendor reference file(s)
main: "./src/index.js" // application code
},
output: {
path: path.join(__dirname, "build"),
filename: "[name].[chunkhash].js",
chunkFilename: "[name].[chunkhash].js"
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: ["vendor", "manifest"], // vendor libs + extracted manifest
minChunks: Infinity,
}),
new webpack.HashedModuleIdsPlugin(),
new WebpackChunkHash(),
new ChunkManifestPlugin({
filename: "chunk-manifest.json",
manifestVariable: "webpackManifest"
})
]};
使用webpack-dev-server
关于API:https://doc.webpack-china.org/configuration/dev-server/
// package.json
// 其中scripts这样设置
"scripts": {
"build": "webpack",
"dev": "webpack-dev-server --devtool eval-source-map --progress --colors"
},
- 关于devtool:https://doc.webpack-china.org/configuration/devtool/
- –progress 打印打包日志
- –colors带颜色的日志
// webpack.config.js
var path = require("path");
var webpack = require("webpack");
var HtmlWebpackPlugin = require("html-webpack-plugin")
module.exports = function(env) {
return {
entry: {
main: "./index.js",
vendor: ["moment", "path"]
},
output: {
// filename: "[chunkhash].[name].js",
filename: "[name].js",
publicPath: "/assets/",
path: path.resolve(__dirname, "build")
},
devServer: {
inline: true,
port: 8099,
contentBase: path.join(__dirname, "build")
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
names: ["vendor", "manifest"]
}),
new HtmlWebpackPlugin({
template: "./index.html"
}),
// etc.
]
}
}
使用npm run dev的时候,可以看到输出有以下:
不会实际生成文件夹,这一部分,推荐看这里:https://github.com/liangklfangl/webpack-dev-server/commit/9b9b86beffedee9c15c7ba3a807b30eb78d477e9
> webpack-dev-server --devtool eval-source-map --progress --colors
10% building modules 2/2 modules 0 active
Project is running at http://localhost:8099/
webpack output is served from /assets/
Content not from webpack is served from C:Users
eal...uild
10%Hash: d968bc9b6ab05170f44f
Version: webpack 2.2.1
在 dev-server 的两种不同模式之间切换。默认情况下,应用程序启用内联模式(inline
mode)。这意味着一段处理实时重载的脚本被插入到你的包(bundle)中,并且构建消息将会出现在浏览器控制台。 也可以使用 iframe模式,它在通知栏下面使用<iframe>
标签,包含了关于构建的消息。
切换到 iframe 模式:inline:false
当使用模块热替换时,建议使用内联模式(inline mode)。output.path:指定编译目录而已(/build/),不能用于html中的js引用。
output.publicPath/devServer.publicPath:publicPath:虚拟目录,自动指向path编译目录(/assets/ => /build/)。html中引用js文件时,必须引用此虚拟路径(但实际上引用的是内存中的文件,既不是/build/也不是/assets/)。
devServer.contentBase:指明应用根目录,与上面两个配置项毫无关联。
所以在我打开localhost:8099的时候,如果设置了contentBase,那么就会跳到contentBase指明的目录,这里是:C:Users
eal...uild
,这个目录是不会自动生成的,如果手动建立了这个目录,就会去找到这个目录下的index.html文件。否则,不能找到。
实际上可以访问的是:localhost:8099/assets/index.html,访问这个文件,会看到我们写的index.html,其中index.html自动引用了生成的main.js、vendor.js、manifest.js。并且当我们修改js文件的时候,页面会自动刷新。
热更新
webpack中文网的一个例子:实测可用。
https://doc.webpack-china.org/guides/hmr-react/
我的在这里:http://blog.csdn.net/real_bird/article/details/62927644
require.ensure
参考自:
http://blog.csdn.net/zhbhun/article/details/46826129
https://doc.webpack-china.org/guides/code-splitting-require/
说明: require.ensure在需要的时候才下载依赖的模块,当参数指定的模块都下载下来了(下载下来的模块还没执行),便执行参数指定的回调函数。require.ensure会创建一个chunk,且可以指定该chunk的名称,如果这个chunk名已经存在了,则将本次依赖的模块合并到已经存在的chunk中,最后这个chunk在webpack构建的时候会单独生成一个文件。
考虑下面的文件结构:
.
├── dist
├── js
│ ├── a.js
│ ├── b.js
│ ├── c.js
│ └── entry.js
└── webpack.config.js
entry.js
require("./a");
require.ensure(["./b"], function(require){
require("./c");
console.log("done!");
});
a.js
console.log("***** I AM a *****");
b.js
console.log("***** I AM b *****");
c.js
console.log("***** I AM c *****");
webpack.config.js
var path = require("path");
module.exports = function(env) {
return {
entry: "./js/entry.js",
output: {
filename: "bundle.js",
path: path.resolve(__dirname, "dist"),
// publicPath: "https://cdn.example.com/assets/",
// tell webpack where to load the on-demand bundles.
publicPath: __dirname + "/dist/",
pathinfo: true,
// show comments in bundles, just to beautify the output of this example.
// should not be used for production.
}
}
}
通过执行这个项目的 webpack 构建,我们发现 webpack 创建了 2 个新的 bundle,bundle.js 和 0.bundle.js。
entry.js 和 a.js 被打包进 bundle.js。b.js和c.js被打包进0.bundle.js。0.bundle.js是按需加载的(通过output.publicPath,这里我修改了,以便能正确加载到)。
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="dist/bundle.js" charset="utf-8"></script>
</head>
<body>
</body>
</html>
打开这个文件后可以看到,它自动加载了0.bundle.js。
而在控制台输出的是:
***** I AM a *****
***** I AM c *****
done!
这就说明了虽然require.ensure中虽然依赖了b.js,但是没有require("./b");
,所以就没有打印出来。模块虽然下载了,但必须手动require才会执行。就算没有使用require("./c");
,0.bundle.js还是会被加载的。注意到,这里是async
。