Webpack从零配置React的运行环境下篇——优化打包策略
项目的地址
https://github.com/jianjiache…
内部配置大部分做了详细说明
执行方式:
cnpm install
cnpm run start
cnpm run build
完成的功能
- [x] 分离生产环境与开发环境
- [x] 添加source-map方便调试
- [x] 分析了babel-polyfill不同导入方式的影响
- [x] 代码分割 定义了被抽离的模块如何分成组
- [x] 识别React语法以及ES6
- [x] 处理了不兼容ES7修饰器(@)Decorator的问题
- [x] 小于8k的图片以base64压入url减少请求
- [x] 添加了组件按需加载而不是直接导入整个模块
- [x] 加入了less以及sass的处理
- [x] 抽离了所有的css样式文件
- [x] 压缩了JS代码以及CSS代码
- [x] 添加了CSS自动补全浏览器前缀,增强了适配性
- [x] 完成对字体、图片、媒体文件的按大小分类打包策略
- [x] 生成一个HTML文件,并主动加入JS文件
- [x] 配置删除生产环境的console.log
- [x] 每次打包先清除dist目录
- [x] 加入了代码的热更新,修改代码自动编译
- [x] 加入了webpack多入口的配置
- [x] 加入了对包大小的可视化分析工具
还想实现的功能
- babel缓存
我们每次执行构建都会把所有的文件都重复编译一遍,这样的重复工作是否可以被缓存下来呢,答案是可以的,目前大部分 loader 都提供了cache 配置项。比如在 babel-loader 中,可以通过设置cacheDirectory 来开启缓存,babel-loader?cacheDirectory=true 就会将每次的编译结果写进硬盘文件(默认是在项目根目录下的node_modules/.cache/babel-loader目录内,当然你也可以自定义) - 使用动态链接库文件DLL
如果不使用使用 DLLPlugin 插件,当引入第三方模块时,每一次打包都要进行分析,是消耗打包的性能的。使用 DLLPlugin 提高打包速度,在第一次打包时,把第三方模块单独打包生成一个文件 vendors.dll.js ,之后在打包时就可以直接从 vendors.dll.js 中引入之前打包好的第三方模块,速度就会变快。
要想实现,就得做一些配置:先配置 webpack.dll.js 文件,在 package.json中添加一个脚本,在配置 webpack.common.js 文件 - Tree Shaking:只支持 ES Module 例如 import 和 export 的静态结构特性的引入。当引入一个模块时,不引入所有的代码,只引入需要的代码
- happypack并发执行任务(代码中加入未开启)
- CDN(已添加方法到笔记)
- 按需加载babel-plugin-import(已加入未开启)
- 首屏渲染loading(方法已添加到笔记)
需要用到的Webpack内置的配置
给打包出的js文件换个不确定名字
使用webpack内置的[hash]或[chunkhash]
const merge = require('webpack-merge'); const common = require('./webpack.common.config.js'); module.exports = merge(common, { mode: 'production', output: { filename: 'js/[name].[chunkhash:8].bundle.js', }, ······ });
代码分割
Webpack内置了optimization(优化字段)的splitChunks来实现代码的分割,大多数情况下不需要配置就够用了,但你也可以个性化的配置,
比如你想指定哪些代码该分到哪一组可以使用cacheGroups来配置
module.exports = { //... optimization: { splitChunks: {// 抽离公共代码 具体配置看官网 chunks: 'all',// 效值是all、async和initial。提供all可能特别强大,因为这意味着即使在异步和非异步块之间也可以共享块 minSize: 30000, maxSize: 0, minChunks: 1, cacheGroups: {// 定义了被抽离的模块如何分成组,不然公共代码全打包到一个JS文件里面 vendors: {// 第三方库抽离 priority: 1,// 权重 先进行第三方库抽离 test: /[\\/]node_modules[\\/]/,// 选从node_modules文件夹下引入的模块,所以所有第三方模块才会被拆分出来 递归的 name: "vendor", enforce: true, }, } } //... };
自动编译打包
安装webpack-dev-server
npm install webpack-dev-server --save-dev
增加代码至webpack.dev.config.js
:
const path = require('path'); const merge = require('webpack-merge'); const common = require('./webpack.common.config.js'); const webpack = require('webpack'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = merge(common, { mode: 'development', output: { filename: 'js/[name].[hash:8].bundle.js', }, devServer: { contentBase: path.resolve(__dirname, '../dist'), open: true, port: 9000, compress: true, hot: true }, plugins: [ new HtmlWebpackPlugin({ template: 'public/index.html', inject: 'body', hash: false }), new webpack.HotModuleReplacementPlugin() ] });
HotModuleReplacementPlugin
是webpack热更新的插件,设置devServer.hot
为true,并且在plugins中引入HotModuleReplacementPlugin插件即可。
注意的是我们开启了hot,那么导出不能使用chunkhash,需要替换为hash。
修改我们的package.json
"scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "webpack --config ./config/webpack.prod.config.js", + "start": "webpack-dev-server --inline --config ./config/webpack.dev.config.js" },
修改代码自动运行
执行代码
npm run start
自动开了一个端口为9000的网页,上面是我们写的页面内容,这和我们的配置都是一一对应的。
现在你随意修改app.js中的代码,再回到页面看下是不是也跟着变了,那我们就整合webpack-dev-server成功!
其他需要用到的插件简介
terser-webpack-plugin
可配置删除console.log
npm install --save-dev terser-webpack-plugin
clean-webpack-plugin
只想要最新打包编译的文件,就需要先清除dist目录
npm install --save-dev clean-webpack-plugin
uglifyjs-webpack-plugin
压缩代码
npm install --save-dev uglifyjs-webpack-plugin
mini-css-extract-plugin
将css提出去,而不是直接在页面来内嵌
npm install --save-dev mini-css-extract-plugin
webpack-bundle-analyzer
可视化的包大小分析工具
npm install --save-dev webpack-bundle-analyzer
其他优化打包的插件使用方式
直接在plugins字段里面new一个实例,参数就是插件的配置项
我就不一一介绍了直接给一个完整的webpack.prod.config配置
const merge = require('webpack-merge'); // 区分生产环境和开发环境 const common = require('./webpack.common.config.js'); // 导入基础配置 const HtmlWebpackPlugin = require('html-webpack-plugin'); // 插件为你生成一个HTML文件,并主动加入JS文件 const TerserPlugin = require('terser-webpack-plugin');// 可配置删除console.log const {CleanWebpackPlugin} = require('clean-webpack-plugin');// 只想要最新打包编译的文件,就需要先清除dist目录 const UglifyJsPlugin = require('uglifyjs-webpack-plugin');// 压缩代码 const MiniCssExtractPlugin = require('mini-css-extract-plugin');// 将css提出去,而不是直接在页面来内嵌 const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');// 将提出去的css压缩 const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; // 包大小分析工具 module.exports = merge(common, { mode: 'production', output: { filename: 'js/[name].[chunkhash:8].bundle.js', }, module: { rules: [ { test: /\.css$/, use: [ MiniCssExtractPlugin.loader,// 单独提出CSS 'css-loader', 'postcss-loader'// 对css编译的工具 可以:1.使用下一代css语法 2 . 自动补全浏览器前缀 3 . 自动把px代为转换成rem ] }, { test: /\.less$/, use: [ MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader', 'less-loader' ] }, { test: /\.(scss|sass)$/, use: [ MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader', 'sass-loader' ] }, ] }, plugins: [ new HtmlWebpackPlugin({ filename: 'index.html', template: 'public/index.html',// 定义的html为模板生成 从根路径开始查找 inject: 'body', minify: {// 压缩HTML文件 removeComments: true,//去除注释 collapseWhitespace: true,//去除空格 }, }), new CleanWebpackPlugin(), new UglifyJsPlugin(), new MiniCssExtractPlugin({ filename: 'css/[name].[hash].css', chunkFilename: 'css/[id].[hash].css', }), new BundleAnalyzerPlugin({ analyzerPort: 9001, }) ], optimization: {// 优化 minimizer: [ // 最小化器 new TerserPlugin({// 缩减代码的插件 terserOptions: { compress: { drop_console: true, // 删除console.log }, } }), new OptimizeCssAssetsPlugin({// 压缩CSS assetNameRegExp:/\.css$/g, cssProcessor:require("cssnano"),// 用于压缩和优化CSS 的处理器 cssProcessorPluginOptions:{ preset:['default', { discardComments: { removeAll:true } }] }, canPrint:true }) ], splitChunks: {// 抽离公共代码 具体配置看官网 chunks: 'all',// 效值是all、async和initial。提供all可能特别强大,因为这意味着即使在异步和非异步块之间也可以共享块 minSize: 30000, maxSize: 0, minChunks: 1, cacheGroups: {// 定义了被抽离的模块如何分成组,不然公共代码全打包到一个JS文件里面 vendors: {// 第三方库抽离 priority: 1,// 权重 先进行第三方库抽离 test: /[\\/]node_modules[\\/]/,// 选从node_modules文件夹下引入的模块,所以所有第三方模块才会被拆分出来 递归的 name: "vendor", enforce: true, }, } } } });
打包图片字体媒体文件,以及调试优化
下载文件打包的依赖
npm install file-loader url-loader --save-dev
调试devtool里填个cheap-module-eval-source-map,当你保存时能直接找到源文件的多少在报错
下面直接给个一个给个webpack.common.config的配置
const path = require("path"); module.exports = { devtool: "cheap-module-eval-source-map",// 方便调试 entry: { // 定义入口文件 index: "./src/index.js" //index: ['babel-polyfill','./src/index.js'], // 添加polyfill垫片(挂载ES6+的方法和API如Promise等)这是很老的写法被坑了,会污染全局,现在preset-env内置了按需加载 //framework: ['react','react-dom'],// 代码分割 定义分组 }, output: { // 编译打包之后的文件名以及所在路径 filename: "js/bundle.js", path: path.resolve(__dirname, "../dist") }, module: { // 编译器 //noPars:/jquery/,// 比如我引入了jquery 它不依赖其他的包,就不需要解析 直接打包 rules: [ { test: /\.(js|jsx)$/, // 代码 use: "babel-loader", exclude: /node_modules/ // 不需要去转译 }, { test: /\.(jpg|png|gif)$/, //图片 use: { loader: "url-loader", options: { name: "[name].[ext]", outputPath: "images/", limit: 8192 // 大于8Kb走file-loader(好像是自动的不用添加fallback),小的ICON什么的直接打包插入到bundle.js中减少Http请求 /* fallback: { loader: 'file-loader', options: { name: 'img/[name].[hash:8].[ext]' } */ } } }, { test: /\.(eot|ttf|svg|woff|woff2)$/, // 字体 use: { loader: "file-loader", options: { name: "[name]_[hash].[ext]", outputPath: "font/" } } }, { test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, //媒体文件 use: { loader: "file-loader", options: { name: "[name].[hash:8].[ext]", outputPath: "media/" } } }, /* { test: /\.css$/, use: [ 'style-loader',// 最后计算完的css,将会使用style-loader生成一个内容为最终解析完的css代码的style标签,放到head标签里 'css-loader' // css-loader加载器去解析这个文件,遇到“@import”等语句就将相应样式文件引入 ] } */ ] } };
完整的目录结构
WebpackReact ├── config │ ├── webpack.common.config.js │ ├── webpack.dev.config.js │ ├── webpack.multipleEntry.config.js │ └── webpack.prod.config.js ├── dist ├── dist备份 │ └── 第一次打包.js ├── font │ ├── demo.css │ ├── demo_index.html │ ├── iconfont.css │ ├── iconfont.eot │ ├── iconfont.js │ ├── iconfont.json │ ├── iconfont.svg │ ├── iconfont.ttf │ ├── iconfont.woff │ └── iconfont.woff2 ├── image │ ├── vscode.png │ ├── 可视化.png │ ├── 快餐.png │ ├── 美食.png │ └── 面包.png ├── package.json ├── postcss.config.js ├── public │ └── index.html ├── readme.md ├── src │ ├── app.js │ ├── app.less │ └── index.js ├── video │ └── 110.mp4 └── 笔记.md
参考文章
- https://webpack.js.org/guides…
- https://www.babeljs.cn/
- https://segmentfault.com/n/13…
- https://juejin.im/post/5aa3d2…
- https://juejin.im/post/5bf610…
原文地址:https://segmentfault.com/a/1190000021492350
相关推荐
-
微信小程序网络组件 weapp.request javascript/jquery
2019-3-4
-
javascript 代码校验器 eslint: 在 sublime text 编辑器中安装和配置 eslint javascript/jquery
2019-5-9
-
高级 Vue 技巧:控制父类的 slot javascript/jquery
2020-5-26
-
对于字符串中连续出现的字母进行递增,出现的数字进行递减 javascript/jquery
2019-6-1
-
第一次使用VS Code时你应该知道的一切配置 javascript/jquery
2019-5-14
-
书本:来吧,证明你爱我的时候到了! javascript/jquery
2020-5-27
-
美团扫码付小程序的优化实践 javascript/jquery
2020-6-9
-
纯JS实现在一个字符串b中查找另一个字符串a出现的所有位置,并且不使用字符串的方法(递归) javascript/jquery
2019-9-13
-
JS 稀疏数组 javascript/jquery
2019-8-21
-
“云”端的语雀:用 JavaScript 全栈打造商业级应用 javascript/jquery
2020-5-28