先附上项目效果:
项目地址: http://47.105.144.204/index
github: github.com/dsying/reac…
未优化之前 webpack配置文件
const path = require('path')
const webpack = require('webpack')
const HTMLPlugin = require('html-webpack-plugin')
//判断是否是开发环境
const isDev = process.env.NODE_ENV === 'development'
const config = {
//入口
entry: {
app: path.join(__dirname, '../client/app.js')
},
//出口
output: {
filename: '[name].[hash].js', // 文件更改后重新打包,hash值变化,从而刷新缓存
path: path.join(__dirname, '../dist'),
//很重要
publicPath: '/public/',
},
//解析
resolve: {
extensions: ['.js', '.jsx'], // 自动解析确定的扩展
},
module: {
rules: [
{
//前置(在执行编译之前去执行eslint-loader检查代码规范,有报错就不执行编译)
enforce: 'pre',
test: /.(js|jsx)$/,
loader: 'eslint-loader',
exclude: [
path.join(__dirname,'../node_modules')
]
},
{ //将jsx转换成 js
test: /.jsx$/,
loader: 'babel-loader'
},
{ //将ES6语法转成 低版本语法
test: /.js$/,
loader: 'babel-loader',
exclude: [//排除node_modules 下的js
path.join(__dirname,'../node_modules')
]
},
{ // 解析 图片
test: /\.(png|jpg|gif|svg)$/,
loader: 'file-loader',
options: {
name: '[name].[ext]?[hash]'
}
}
]
},
plugins: [
// 生成一个html页面,同时把所有 entry打包后的 output 文件全部注入到这个html页面
new HTMLPlugin({
template: path.join(__dirname, '../client/template.html')
})
],
//开发模式
mode: 'development'
}
if(isDev){
config.entry = [
'react-hot-loader/patch', //设置这里
path.join(__dirname, '../client/app.js')
]
config.devtool = '#cheap-module-eval-source-map'
config.devServer = {
host: '0.0.0.0',
port: '8887',
contentBase: path.join(__dirname, '../dist'), //告诉服务器从哪个目录中提供内容
hot: true,//启用 webpack 的模块热替换特性
overlay: {//当出现编译器错误或警告时,就在网页上显示一层黑色的背景层和错误信息
errors: true
},
publicPath: '/public/',//webpack-dev-server打包的内容是放在内存中的,这些打包后的资源对外的的根目录就是publicPath,换句话说,这里我们设置的是打包后资源存放的位置
historyApiFallback: {
index: '/public/index.html'
},
proxy: { // client端 port为 8887, server端接口为 3333, 所以我们这里要设置 proxy代理
'/api' : 'http://localhost:3333'
}
}
config.plugins = [...config.plugins, new webpack.HotModuleReplacementPlugin() ]
}
module.exports = config
复制代码
运行 npm run build(package.json 内配置)之后
压缩js文件
// 第一种方式
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
config.plugins.push(
new UglifyJsPlugin(),
)
// 第二种方式
config.optimization ={
minimize: true
}
复制代码
分包
将第三方库分离到单独vendor chunk中
config.entry = {
app: path.join(__dirname, '../client/app.js'),
vendor: [
'react',
'react-dom',
'react-router-dom',
'mobx',
'mobx-react',
'axios',
'query-string',
'dateformat',
'marked'
]
}
复制代码
如何解决重复打包问题(app中不打包vendor中的内容)
- splitChunks: webpack4 移除了 commonChunkPlugin, splitChunks用于替代
- runtimeChunk: 将webpack生成的 runtime 作为独立 chunk ,runtime包含在模块交互时,模块所需的加载和解析逻辑(manifest)
config.optimization = {
runtimeChunk: {
name: "manifest"
},
splitChunks: {
cacheGroups: {
commons: {
test: /[\\/]node_modules[\\/]/,
name: "vendor",
chunks: "all"
}
}
}
}
复制代码
将第三方库(library)(例如 lodash 或 react)提取到单独的 vendor chunk 文件中,是比较推荐的做法,这是因为,它们很少像本地的源代码那样频繁修改。因此通过实现以上步骤,利用客户端的长效缓存机制,可以通过命中缓存来消除请求,并减少向服务器获取资源,同时还能保证客户端代码和服务器端代码版本一致
使用chunkhash
之前的配置中output是这样配置的
output: {
filename: '[name].[hash],js'
}
复制代码
打包后生成的所有文件的hash值都是一样的,当我们修改了业务代码,导致app.js被重新打包,则 app,vendor,mainfest 三个chunk的hash全部发生改变,此时浏览器访问页面时就不能使用 vendor和mainfest的缓存了(因为文件名发生了改变),而是得重新发送http请求资源。 通过 chunkhash可以使每个chunk拥有自己单独的 hash值
config.output.filename = '[name].[chunkhash].js'
复制代码
走到这步,突然发现我的配置中 mode没有改为 production, 这也是为什么 我的代码有点大。 重新打包后,多出了很多警告
这是因为限制了文件大小为250kb,如果超过就会提示 避免提示设置如下:const config = {
performance: {
// false | "error" | "warning" // 不显示性能提示 | 以错误形式提示 | 以警告...
hints: "warning",
// 开发环境设置较大防止警告
// 根据入口起点的最大体积,控制webpack何时生成性能提示,整数类型,以字节为单位
maxEntrypointSize: 5000000,
// 最大单个资源体积,默认250000 (bytes)
maxAssetSize: 3000000
}
}
复制代码
npm start 之后