html文件如何也同步到dist目录?bundle.js文件修改了,万一被浏览器缓存了怎么办?如何为导出的文件加md5?如何把js引用自动添加到html?非业务代码和业务代码如何分开打包?如何搭建开发环境?如何实现开发环境的热更新?

在上一节我们已经搭建了一个最基本的webpack环境, 这一节我们带着上节的一些疑问,继续优化我们的react工程。
 
1.整合html-webpack-plugin
 
public的index.html应该自动编译到dist目录,并且所有的js引用是自动添加的。你可以使用html-webpack-plugin插件来处理这个优化。
(1)安装html-webpack-plugin:
npm install --save-dev html-webpack-plugin
(2)在webpack.prod.conf.js中配置plugins属性。
    const merge = require('webpack-merge');
const baseWebpackConfig = require('./webpack.base.conf.js');
const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = merge(baseWebpackConfig, {
mode: 'production',
plugins: [
new HtmlWebpackPlugin({
template: 'public/index.html',
inject: 'body',
minify: {
removeComments: true,
collapseWhitespace: true,
removeAttributeQuotes: true
},
})
]
});
(3)删除index.html中手动引入的script标签

index.html的代码应该是这样的:

    <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>从零开始搭建react工程</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
(4)重新执行编译
npm run build

查看dist文件夹,index.html也被加载进来了,并且已经自动加上了script标签。

2.为导出的js文件添加内容hash
 
当我们的业务有修改,bundle被重新打包,很可能在客户的电脑上并没有奏效,你告诉客户,应该是被缓存了,需要刷新浏览器,清理下浏览器缓存。这也许能解决问题,但是太糟糕了,我们有更好的方式,让导出的js文件加上文件hash,从而每次修改,转译出的js文件名称都不相同,那么js文件当然不会被缓存了。
添加文件hash的方法很简单,只要修改 output.filename 属性即可,这里我们做一个小小的优化,把导出的文件存放在js目录下,并且直接使用name+chunkhash的方式来命名。
filename: "js/[name].[chunkhash:16].js"
其中,name就是模块名称,我们在entry中进行过配置,chunkhash是文件内容的hash,webpack默认采用md5的方式对文件进行hash。16是hash的长度,如果不设置,webpack会设置默认值为20。
现在,你的webpack.prod.conf.js文件看起来应该是这样:
    const merge = require('webpack-merge');
const baseWebpackConfig = require('./webpack.base.conf.js');
const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = merge(baseWebpackConfig, {
mode: 'production',
output: {
filename: "js/[name].[chunkhash:16].js",
},
plugins: [
new HtmlWebpackPlugin({
template: 'public/index.html',
inject: 'body',
minify: {
removeComments: true,
collapseWhitespace: true,
removeAttributeQuotes: true
},
})
]
});
3.编译前清理dist目录
 
现在,如果你修改了你的业务代码,然后重新编译,你会发现在dist/js文件夹出现多个js文件。因为导出的js文件hash已经不相同,每次编译都会增加新的js文件,原来的文件没有被删除。所以,我们需要在编译前进行清理dist文件夹。
(1)安装clean-webpack-plugin
npm install --save-dev clean-webpack-plugin
(2)修改webpack.prod.conf.js,使用clean-webpack-plugin
    const merge = require('webpack-merge');
const baseWebpackConfig = require('./webpack.base.js');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin'); module.exports = merge(baseWebpackConfig, {
mode: 'production',
output: {
filename: "js/[name].[chunkhash:16].js",
},
plugins: [
new HtmlWebpackPlugin({
template: 'public/index.html',
inject: 'body',
minify: {
removeComments: true,
collapseWhitespace: true,
removeAttributeQuotes: true
},
}),
new CleanWebpackPlugin(['../dist'], { allowExternal: true })
]
});
(3)执行试试看
npm run build

编译过程,注意查看控制台输出,你会发现webpack删除了dist目录。

4.非业务代码单独打包
 
在build结束,webpack会在终端显示打包文件的大小,我们可以看到这个app.js包大概在96.9KB

随着我们业务代码的增加,这个包将会越来越大。
你每次发布,这个文件都会被重新下载。你的代码有修改,用户需要重新下载无可厚非。可是,你别忘了这个app.js内还包含了很多不变的代码,比如react,react-dom。我们需要把这些不变的代码分开打包。
在webpack.base.conf.js,我们添加一个入口配置。entry有2个入口。

    entry: {
app: './app/index.js',
framework:['react','react-dom'],
},

重新执行npm run build,再看看。

的确,react和react-dom 被编译成framework.js。可是,你会发现,app.js并没有减少,还是96.9KB。因为我们还缺少一步,就是抽离app.js中公共的代码。
webpack3版本是通过配置CommonsChunkPlugin插件来抽离公共的模块。webpack4版本,官方废弃了CommonsChunkPlugin,而是改用配置optimization.splitChunks的方式,更加方便。
在webpack.prod.conf.js增加如下代码:

    optimization: {
splitChunks: {
chunks: "all",
minChunks: ,
minSize: ,
cacheGroups: {
framework: {
test: "framework",
name: "framework",
enforce: true
}
}
}
}

cacheGroups对象,定义了需要被抽离的模块,其中test属性是比较关键的一个值,他可以是一个字符串,也可以是正则表达式,还可以是函数。如果定义的是字符串,会匹配入口模块名称,会从其他模块中把包含这个模块的抽离出来。name是抽离后生成的名字,和入口文件模块名称相同,这样抽离出来的新生成的framework模块会覆盖被抽离的framework模块,虽然他们都叫framework。
重新执行npm run build你看到app.js的体积变小了 才1kb。

注意查看生成的文件的hash,接下去我们随意修改app/index.js的代码。重新执行npm run build编译。看看编译后的结果:

看到了吗,app的hash发生了改变(它不能被浏览器缓存),而framework没有改变(它会被浏览器缓存),这达到了我们预期的结果。

5.压缩js文件
 
打开build后的js文件看看,js文件没有被压缩。在prod环境,我们希望js已经被压缩了,这样做的好处是减少文件体积,更快地被用户加载。js压缩,用到了uglifyjs-webpack-plugin,在optimization内进行配置。
(1)安装uglifyjs-webpack-plugin
npm install --save-dev uglifyjs-webpack-plugin
(2)在webpack.prod.conf.js页面上引入
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
(3)optimization内配置minimizer参数
    minimizer: [
new UglifyJSPlugin()
],

你的optimization参数现在应该是这样:

     optimization: {
minimizer: [
new UglifyJSPlugin()
],
splitChunks: {
chunks: "all",
minChunks: ,
cacheGroups: {
framework: {
priority: ,
test: "framework",
name: "framework",
enforce: true,
reuseExistingChunk: true
},
vendor: {
priority: ,
test: /node_modules/,
name: "vendor",
enforce: true,
reuseExistingChunk: true
}
}
}
}
(4)重新执行npm run build
npm run build
6.整合dev环境
 
我们不可能每次修改代码都去手动编译,等编译出来再去打开文件查看效果。webpack提供了开发环境服务,并且支持热更新,这在开发环境是非常有必要的。
webpack-dev-server这个模块提供了开发服务的支持,通过在webpack.dev.conf.js文件配置devServer可以方便地整合webpack-dev-server。
(1)安装webpack-dev-server
npm install --save-dev webpack-dev-server
(2)在build中添加webpack.dev.conf.js文件
    const path = require('path');
const merge = require('webpack-merge');
const baseWebpackConfig = require('./webpack.base.conf.js');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack'); module.exports = merge(baseWebpackConfig, {
mode: 'development',
output: {
filename: "js/[name].[hash:16].js",
},
plugins: [
new HtmlWebpackPlugin({
template: 'public/index.html',
inject: 'body',
minify: {
html5: true
},
hash: false
}),
new webpack.HotModuleReplacementPlugin()
],
devServer: {
port: '',
contentBase: path.join(__dirname, '../public'),
compress: true,
historyApiFallback: true,
hot: true,
https: false,
noInfo: true,
open: true,
proxy: {}
}
});

HotModuleReplacementPlugin是webpack热更新的插件,设置devServer.hot为true,并且在plugins中引入HotModuleReplacementPlugin插件即可。
还需要注意的是我们开启了hot,那么导出不能使用chunkhash,需要替换为hash。

(3)在package.json增加一个npm scripts
"dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
(4)执行dev命令
npm run dev

打开 http://localhost:8080 查看,你可以尝试改动index.js的代码,浏览器自动更新了,说明整合webpack-dev-server成功。
你可能注意到,对于css相关的技术栈,我只字未提,别急,下一节我们会详细针对css相关的技术栈进行整合。

【webpack系列】从零搭建 webpack4+react 脚手架(二)的更多相关文章

  1. 【webpack系列】从零搭建 webpack4+react 脚手架(一)

    搭建一个React工程的方式有很多,官方也有自己的脚手架,如果你和我一样,喜欢刨根究底,从零开始自己一行一行代码创建一个React脚手架项目,那你就来对地方了.本教程是针对React新手,以及对web ...

  2. 【webpack系列】从零搭建 webpack4+react 脚手架(四)

    经过三个章节的学习,你已经学会搭建了一个基于webpack4的react脚手架.如果要更改配置,比如,你希望把编译后的js文件和css文件等单独放dist下的static目录下,你想想,是不是有点麻烦 ...

  3. 【webpack系列】从零搭建 webpack4+react 脚手架(三)

    本章节,我们对如何在脚手架中引入CSS,如何压缩CSS,如何使用CSS Modules,如何使用less,如何使用postcss等问题进行展开学习. 1 支持css (1)在app目录,新建一个css ...

  4. 【webpack系列】从零搭建 webpack4+react 脚手架(五)

    本章节,我们一起来探讨以下问题:如何对编译后的文件进行gzip压缩,如何让开发环境的控制台输出更加高逼格,如何更好的对编译后的文件进行bundle分析等. 1 gzip压缩 如果你想节省带宽提高网站速 ...

  5. 从零搭建Spring Boot脚手架(1):开篇以及技术选型

    1. 前言 目前Spring Boot已经成为主流的Java Web开发框架,熟练掌握Spring Boot并能够根据业务来定制Spring Boot成为一个Java开发者的必备技巧,但是总是零零碎碎 ...

  6. 初探webpack之从零搭建Vue开发环境

    初探webpack之搭建Vue开发环境 平时我们可以用vue-cli很方便地搭建Vue的开发环境,vue-cli确实是个好东西,让我们不需要关心webpack等一些繁杂的配置,然后直接开始写业务代码, ...

  7. 从零搭建Spring Boot脚手架(2):增加通用的功能

    1. 前言 今天开始搭建我们的kono Spring Boot脚手架,首先会集成Spring MVC并进行定制化以满足日常开发的需要,我们先做一些刚性的需求定制,后续再补充细节.如果你看了本文有什么问 ...

  8. 从零搭建Spring Boot脚手架(3):集成mybatis

    1. 前言 今天继续搭建我们的kono Spring Boot脚手架,上一文集成了一些基础的功能,比如统一返回体.统一异常处理.快速类型转换.参数校验等常用必备功能,并编写了一些单元测试进行验证,今天 ...

  9. 从零搭建Spring Boot脚手架(4):手写Mybatis通用Mapper

    1. 前言 今天继续搭建我们的kono Spring Boot脚手架,上一文把国内最流行的ORM框架Mybatis也集成了进去.但是很多时候我们希望有一些开箱即用的通用Mapper来简化我们的开发.我 ...

随机推荐

  1. java 数字左补齐0

    NumberFormat nf = NumberFormat.getInstance();        //设置是否使用分组        nf.setGroupingUsed(false);    ...

  2. Intellij IDEA项目包分层结构显示设置

    问题:(这种方式的查看包名,确实十分难看) 解决方法:

  3. beego学习1

    下载 go get -u -v github.com/astaxie/beego   beego源码 go get -u -v  github.com/beego/bee   bee开发工具 bee ...

  4. ClassLoader那事儿

    ClassLoader是什么 ClassLoader中文类加载器,java编写出来的是.java文件,然后编译成.class文件,而ClassLoader就是把class文件加载到jvm内存中:但jv ...

  5. mysql常用操作(一)

    [数据库设计的三大范式]1.第一范式(1NF):数据表中的每一列,必须是不可拆分的最小单元.也就是确保每一列的原子性. 例如:userInfo:'山东省烟台市 18865518189' 应拆分成 us ...

  6. Python:正则表达式(一):search()、match()、findall() 的区别

    以前一直觉得正则很难,不会用,今天试验了几个方法,整理总结了一下,简洁明了. 简单来讲,正则就是 提取一段特征数据,用(.*?)代替. 自己总结的特点&区别: (.*) 贪婪匹配,会尽可能的往 ...

  7. JAVA进阶19

    1.冒泡排序 package cn.zh.abstrac; import java.util.Arrays; //冒泡排序 public class Demo019 { public static v ...

  8. L2-004 这是二叉搜索树吗? (25 分) (树)

    链接:https://pintia.cn/problem-sets/994805046380707840/problems/994805070971912192 题目: 一棵二叉搜索树可被递归地定义为 ...

  9. 使用extjs的页面弹出窗口宽度不能自适应如何解决?

    1.资源趋势详情下钻页面宽度不能自适应,无法点击关闭按钮 var detailWindow = Ext.create("App.view.com.huawei.drp.qoe.vivid.C ...

  10. Android Wear 2.0 AlarmManager 后台定时任务

    以前在Android 4.0时,alarmManager 没什么问题.后来android为了优化系统耗电情况,引入了doze模式,参见此页 https://developer.android.com/ ...