您的位置:首页 > Web前端 > Webpack

webpack:从入门到真实项目配置(二)

2017-09-19 14:08 513 查看

如何在项目中使用 webpack

项目中已经配置了很简单的 babel 和 webpack,直接运行
npm
run start
即可



这时候你会发现这个 bundle.js 居然有这么大,这肯定是不能接受的,所以接下来章节的主要目的就是将单个文件拆分
为多个文件,优化项目。

分离代码

先让我们考虑下缓存机制。对于代码中依赖的库很少会去主动升级版本,但是我们自己的代码却每时每刻都在变更,所
以我们可以考虑将依赖的库和自己的代码分割开来,这样用户在下一次使用应用时就可以尽量避免重复下载没有变更的
代码,那么既然要将依赖代码提取出来,我们需要变更下入口和出口的部分代码。
// 这是 packet.json 中 dependencies 下的
const VENOR = ["faker",
"lodash",
"react",
"react-dom",
"react-input-range",
"react-redux",
"redux",
"redux-form",
"redux-thunk"
]

module.exports = {
// 之前我们都是使用了单文件入口
// entry 同时也支持多文件入口,现在我们有两个入口
// 一个是我们自己的代码,一个是依赖库的代码
entry: {
// bundle 和 vendor 都是自己随便取名的,会映射到 [name] 中
bundle: './src/index.js',
vendor: VENOR
},
output: {
path: path.join(__dirname, 'dist'),
filename: '[name].js'
},
// ...
}

现在我们 build 一下,看看是否有惊喜出现





真的有惊喜。。为什么 bundle 文件大小压根没变。这是因为 bundle 中也引入了依赖库的代码,刚才的步骤并没有抽取
bundle 中引入的代码,接下来让我们学习如何将共同的代码抽取出来。

抽取共同代码

在这小节我们使用 webpack 自带的插件
CommonsChunkPlugin

module.exports = {
//...
output: {
path: path.join(__dirname, 'dist'),
// 既然我们希望缓存生效,就应该每次在更改代码以后修改文件名
// [chunkhash]会自动根据文件是否更改而更换哈希
filename: '[name].[chunkhash].js'
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
// vendor 的意义和之前相同
// manifest文件是将每次打包都会更改的东西单独提取出来,保证没有更改的代码无需重新打包,
这样可以加快打包速度
names: ['vendor', 'manifest'],
// 配合 manifest 文件使用
minChunks: Infinity
})
]
};

当我们重新 build 以后,会发现 bundle 文件很明显的减小了体积



但是我们使用哈希来保证缓存的同时会发现每次 build 都会生成不一样的文件,这时候我们引入另一个插件来帮助我们
删除不需要的文件。
npm install --save-dev clean-webpack-plugin

然后修改配置文件
module.exports = {
//...
plugins: [
// 只删除 dist 文件夹下的 bundle 和 manifest 文件
new CleanWebpackPlugin(['dist/bundle.*.js','dist/manifest.*.js'], {
// 打印 log
verbose: true,
// 删除文件
dry: false
}),
]
};

然后 build 的时候会发现以上文件被删除了。
因为我们现在将文件已经打包成三个 JS 了,以后也许会更多,每次新增 JS 文件我们都需要手动在 HTML 中新增标签,
现在我们可以通过一个插件来自动完成这个功能。
npm install html-webpack-plugin --save-dev

然后修改配置文件
module.exports = {
//...
plugins: [
// 我们这里将之前的 HTML 文件当做模板
// 注意在之前 HTML 文件中请务必删除之前引入的 JS 文件
new HtmlWebpackPlugin({
template: 'index.html'
})
]
};

执行 build 操作会发现同时生成了 HTML 文件,并且已经自动引入了 JS 文件



按需加载代码

在这一小节我们将学习如何按需加载代码,在这之前的 vendor 入口我发现忘记加入 router 这个库了,大家可以加入这
个库并且重新 build 下,会发现 bundle 只有不到 300KB 了。
现在我们的 bundle 文件包含了我们全部的自己代码。但是当用户访问我们的首页时,其实我们根本无需让用户加载除了
首页以外的代码,这个优化我们可以通过路由的异步加载来完成。
现在修改
src/router.js

// 注意在最新版的 V4路由版本中,更改了按需加载的方式,如果安装了 V4版,可以自行前往官网学习
import React from 'react';
import { Router, Route, IndexRoute, hashHistory } from 'react-router';

import Home from './components/Home';
import ArtistMain from './components/artists/ArtistMain';

const rootRoute = {
component: Home,
path: '/',
indexRoute: { component: ArtistMain },
childRoutes: [
{
path: 'artists/new',
getComponent(location, cb) {
System.import('./components/artists/ArtistCreate')
.then(module => cb(null, module.default))
}
},
{
path: 'artists/:id/edit',
getComponent(location, cb) {
System.import('./components/artists/ArtistEdit')
.then(module => cb(null, module.default))
}
},
{
path: 'artists/:id',
getComponent(location, cb) {
System.import('./components/artists/ArtistDetail')
.then(module => cb(null, module.default))
}
}
]
}

const Routes = () => {
return (
<Router history={hashHistory} routes={rootRoute} />
);
};

export default Routes;

然后执行 build 命令,可以发现我们的 bundle 文件又瘦身了,并且新增了几个文件



将 HTML 文件在浏览器中打开,当点击路由跳转时,可以在开发者工具中的 Network 一栏中看到加载了一个 JS 文件。
首页



点击右上角 Random Artist 以后



自动刷新

每次更新代码都需要执行依次 build,并且还要等上一会很麻烦,这一小节介绍如何使用自动刷新的功能。
首先安装插件
npm i --save-dev webpack-dev-server

然后修改 packet.json 文件
"scripts": {
"build": "webpack",
"dev": "webpack-dev-server --open"
},

现在直接执行
npm
run dev
可以发现浏览器自动打开了一个空的页面,并且在命令行中也多了新的输出



等待编译完成以后,修改 JS 或者 CSS 文件,可以发现 webpack 自动帮我们完成了编译,并且只更新了需要更新的代码



但是每次重新刷新页面对于 debug 来说很不友好,这时候就需要用到模块热替换了。但是因为项目中使用了 React,
并且 Vue 或者其他框架都有自己的一套 hot-loader,所以这里就略过了,有兴趣的可以自己学习下。

生成生产环境代码

现在我们可以将之前所学和一些新加的插件整合在一起,build 生产环境代码。
npm i --save-dev url-loader optimize-css-assets-webpack-plugin file-loader
extract-text-webpack-plugin

修改 webpack 配置
var webpack = require('webpack');
var path = require('path');
var HtmlWebpackPlugin = require('html-webpack-plugin')
var CleanWebpackPlugin = require('clean-webpack-plugin')
var ExtractTextPlugin = require('extract-text-webpack-plugin')
var OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')

const VENOR = ["faker",
"lodash",
"react",
"react-dom",
"react-input-range",
"react-redux",
"redux",
"redux-form",
"redux-thunk",
"react-router"
]

module.exports = {
entry: {
bundle: './src/index.js',
vendor: VENOR
},
// 如果想修改 webpack-dev-server 配置,在这个对象里面修改
devServer: {
port: 8081
},
output: {
path: path.join(__dirname, 'dist'),
filename: '[name].[chunkhash].js'
},
module: {
rules: [{
test: /\.js$/,
use: 'babel-loader'
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
use: [{
loader: 'url-loader',
options: {
limit: 10000,
name: 'images/[name].[hash:7].[ext]'
}
}]
},
{
test: /\.css$/,
loader: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [{
// 这边其实还可以使用 postcss 先处理下 CSS 代码
loader: 'css-loader'
}]
})
},
]
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: ['vendor', 'manifest'],
minChunks: Infinity
}),
new CleanWebpackPlugin(['dist/*.js'], {
verbose: true,
dry: false
}),
new HtmlWebpackPlugin({
template: 'index.html'
}),
// 生成全局变量
new webpack.DefinePlugin({
"process.env.NODE_ENV": JSON.stringify("process.env.NODE_ENV")
}),
// 分离 CSS 代码
new ExtractTextPlugin("css/[name].[contenthash].css"),
// 压缩提取出的 CSS,并解决ExtractTextPlugin分离出的 JS 重复问题
new OptimizeCSSPlugin({
cssProcessorOptions: {
safe: true
}
}),
// 压缩 JS 代码
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
}
})
]
};

修改 packet.json 文件
"scripts": {
"build": "NODE_ENV=production webpack -p",
"dev": "webpack-dev-server --open"
}

执行
npm
run build




可以看到我们在经历了这么多步以后,将 bundle 缩小到了只有 27.1KB,像 vendor 这种常用的库我们一般可以使用
CDN 的方式外链进来。

补充

webpack 配置上有些实用的小点在上文没有提到,统一在这里提一下。
module.exports = {
resolve: {
// 文件扩展名,写明以后就不需要每个文件写后缀
extensions: ['.js', '.css', '.json'],
// 路径别名,比如这里可以使用 css 指向 static/css 路径
alias: {
'@': resolve('src'),
'css': resolve('static/css')
}
},
// 生成 source-map,用于打断点,这里有好几个选项
devtool: '#cheap-module-eval-source-map',
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: