webpack4大结局:加入腾讯IM配置策略,实现前端工程化环境极致优化

栏目: 编程工具 · 发布时间: 7年前

内容简介:在此对

webpack4大结局:加入腾讯IM配置策略,实现前端工程化环境极致优化

webpack ,打包所有的资源

不知道不觉, webpack 已经偷偷更新到 4.34 版本了,本人决定,这是今年最后一篇写 webpack 的文章,除非它更新到版本5,本人今年剩下的时间都会放在 Golang 和二进制数据操作以及后端的生态上

在看本文前,假设你对 webpack 有一定了解,如果不了解,可以看看我之前的手写 React 和 Vue 脚手架的文章

在此对 webpack 的性能优化进行几点声明:

  • 在部分极度复杂的环境下,需要双 package.json 文件,即实行三次打包
  • 在代码分割时,低于 18K 的文件没必要单独打包成一个 chunk , http 请求次数过多反而影响性能
  • prerender 和 PWA 互斥,这个问题暂时没有解决
  • babel 缓存编译缓存的是索引,即 hash 值,非常吃内存,每次开发完记得清理内存
  • babel-polyfill 按需加载在某些非常复杂的场景下比较适合
  • prefetch,preload 对首屏优化提升是明显
  • 代码分割不管什么技术栈,一定要做,不然就是垃圾项目
  • 多线程编译对构建速度提升也很明显
  • 代码分割配合 PWA +预渲染+ preload 是首屏优化的巅峰,但是 pwa 无法缓存预渲染的 html 文件

本文的 webpack 主要针对 React 技术栈,实现功能如下:

  • 开发模式热更新
  • 识别 JSX 文件
  • 识别 class 组件
  • 代码混淆压缩,防止反编译代码,加密代码
  • 配置 alias 别名,简化 import 的长字段
  • 同构直出, SSR 的热调试(基于 Node 做中间件)
  • 实现 javaScript 的 tree shaking 摇树优化 删除掉无用代码
  • 实现 CSS 的 tree shaking
  • 识别 async / await 和 箭头函数
  • react-hot-loader 记录 react 页面留存状态 state
  • PWA 功能,热刷新,安装后立即接管浏览器 离线后仍让可以访问网站 还可以在手机上添加网站到桌面使用
  • preload 预加载资源 prefetch 按需请求资源
  • CSS 模块化,不怕命名冲突
  • 小图片的 base64 处理
  • 文件后缀省掉 jsx js json 等
  • 实现React懒加载,按需加载 , 代码分割 并且支持服务端渲染
  • 支持 less sass stylus 等预处理
  • code spliting 优化首屏加载时间 不让一个文件体积过大
  • 加入 dns-prefetch 和 preload 预请求必要的资源,加快首屏渲染(京东策略)
  • 加入 prerender ,极大加快首屏渲染速度
  • 提取公共代码,打包成一个 chunk
  • 每个 chunk 有对应的 chunkhash ,每个文件有对应的 contenthash ,方便浏览器区别缓存
  • 图片压缩
  • CSS 压缩
  • 增加 CSS 前缀 兼容各种浏览器
  • 对于各种不同文件打包输出指定文件夹下
  • 缓存 babel 的编译结果,加快编译速度
  • 每个入口文件,对应一个 chunk ,打包出来后对应一个文件 也是 code spliting
  • 删除 HTML 文件的注释等无用内容
  • 每次编译删除旧的打包代码
  • 将 CSS 文件单独抽取出来
  • 让babel不仅缓存编译结果,还在第一次编译后开启多线程编译,极大加快构建速度
  • 等等....

本质上, webpack 是一个现代 JavaScript 应用程序的静态模块打包器( module bundler )。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图( dependency graph ),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle

webpack 打包原理

  • 识别入口文件
  • 通过逐层识别模块依赖。( Commonjs、amd 或者es6的 import,webpack 都会对其进行分析。来获取代码的依赖)
  • webpack 做的就是分析代码。转换代码,编译代码,输出代码
  • 最终形成打包后的代码
  • 这些都是 webpack 的一些基础知识,对于理解 webpack 的工作机制很有帮助。

舒适的开发体验,有助于提高我们的开发效率,优化开发体验也至关重要

  • 组件热刷新、CSS热刷新
  • 自从webpack推出热刷新后,前端开发者在开环境下体验大幅提高。
  • 没有热刷新能力,我们修改一个组件后

webpack4大结局:加入腾讯IM配置策略,实现前端工程化环境极致优化

  • 加入热刷新后

webpack4大结局:加入腾讯IM配置策略,实现前端工程化环境极致优化

主要看一下 React 技术栈,如何在构建中接入热刷新

  • 无论什么技术栈,都需要在 dev 模式下加上 webpack.HotModuleReplacementPlugin 插件
devServer: {
        contentBase: '../build',
        open: true,
        port: 5000,
        hot: true
    },

注:也可以使用react-hot-loader来实现,具体参考官方文档

在开发模式下也要代码分割,加快打开页面速度

optimization: {
        runtimeChunk: true,
        splitChunks: {
            chunks: 'all',
            minSize: 10000, // 提高缓存利用率,这需要在http2/spdy
            maxSize: 0,//没有限制
            minChunks: 3,// 共享最少的chunk数,使用次数超过这个值才会被提取
            maxAsyncRequests: 5,//最多的异步chunk数
            maxInitialRequests: 5,// 最多的同步chunks数
            automaticNameDelimiter: '~',// 多页面共用chunk命名分隔符
            name: true,
            cacheGroups: {// 声明的公共chunk
            vendor: {
            // 过滤需要打入的模块
            test: module => {
            if (module.resource) {
            const include = [/[\\/]node_modules[\\/]/].every(reg => {
            return reg.test(module.resource);
            });
            const exclude = [/[\\/]node_modules[\\/](react|redux|antd)/].some(reg => {
            return reg.test(module.resource);
            });
            return include && !exclude;
            }
            return false;
            },
            name: 'vendor',
            priority: 50,// 确定模块打入的优先级
            reuseExistingChunk: true,// 使用复用已经存在的模块
            },
            react: {
            test({ resource }) {
            return /[\\/]node_modules[\\/](react|redux)/.test(resource);
            },
            name: 'react',
            priority: 20,
            reuseExistingChunk: true,
            },
            antd: {
            test: /[\\/]node_modules[\\/]antd/,
            name: 'antd',
            priority: 15,
            reuseExistingChunk: true,
            },
            },
        }
    }

简要解释上面这段配置

  • 将node_modules共用部分打入 vendor.js bundle 中;
  • 将react全家桶打入 react.js bundle 中;
  • 如果项目依赖了 antd ,那么将 antd 打入单独的 bundle 中;(其实不用这样,可以看我下面的 babel 配置,性能更高)
  • 最后剩下的业务模块超过3次引用的公共模块,将自动提取公共块

注意 上面的配置只是为了给大家看,其实这样配置代码分割,性能更高

optimization: {
        runtimeChunk: true,
        splitChunks: {
            chunks: 'all',
                     }
}

react-hot-loader 记录 react 页面留存状态 state

yarn add react-hot-loader

// 在入口文件里这样写
 
import React from "react";
import ReactDOM from "react-dom";
import { AppContainer } from "react-hot-loader";-------------------1、首先引入AppContainre
import { BrowserRouter } from "react-router-dom";
import Router from "./router";
 
/*初始化*/
renderWithHotReload(Router);-------------------2、初始化
 
/*热更新*/
if (module.hot) {-------------------3、热更新操作
  module.hot.accept("./router/index.js", () => {
    const Router = require("./router/index.js").default;
    renderWithHotReload(Router);
  });
}
 
 
function renderWithHotReload(Router) {-------------------4、定义渲染函数
  ReactDOM.render(
    <AppContainer>
      <BrowserRouter>
        <Router />
      </BrowserRouter>
    </AppContainer>,
    document.getElementById("app")
  );
}

然后你再刷新试试

React 的按需加载,附带代码分割功能 ,每个按需加载的组件打包后都会被单独分割成一个文件

import React from 'react'
        import loadable from 'react-loadable'
        import Loading from '../loading' 
        const LoadableComponent = loadable({
            loader: () => import('../Test/index.jsx'),
            loading: Loading,
        });
        class Assets extends React.Component {
            render() {
                return (
                    <div>
                        <div>这即将按需加载</div>
                        <LoadableComponent />
                    </div>
                )
            }
        }
        
        export default Assets

* 加入 html-loader 识别 html 文件

{
    test: /\.(html)$/,
    loader: 'html-loader'
    }

配置别名

resolve: {
    modules: [
    path.resolve(__dirname, 'src'), 
    path.resolve(__dirname,'node_modules'),
    ],
    alias: {
    components: path.resolve(__dirname, '/src/components'),
    },
    }

加入 eslint-loader

{
    enforce:'pre',
    test:/\.js$/,
    exclude:/node_modules/,
    include:resolve(__dirname,'/src/js'),
    loader:'eslint-loader'
    }

resolve 解析配置,为了为了给所有文件后缀省掉 js jsx json ,加入配置

resolve: {
    extensions: [".js", ".json", ".jsx"]
}

加入 HTML 文件压缩,自动将入门的 js 文件注入 html 中,优化 HTML 文件

new HtmlWebpackPlugin({
            template: './public/index.html',
            minify: {
                removeComments: true,
                collapseWhitespace: true,
                removeRedundantAttributes: true,
                useShortDoctype: true,
                removeEmptyAttributes: true,
                removeStyleLinkTypeAttributes: true,
                keepClosingSlash: true,
                minifyJS: true,
                minifyCSS: true,
                minifyURLs: true,
            }
        }),

SSR 同构直出热调试

  • , 采用 webpack watch+nodemon 结合的模式实现对 SSR 热调试的支持。 node 服务需要的 html/js 通过 webpack 插件动态输出,当 nodemon 检测到变化后将自动重启, html 文件中的静态资源全部替换为 dev 模式下的资源,并保持 socket 连接自动更新页面。
  • 实现热调试后,调试流程大幅缩短,和普通非直出模式调试体验保持一致。下面是SSR热调试的流程图:

webpack4大结局:加入腾讯IM配置策略,实现前端工程化环境极致优化

加入 babel-loader 还有 解析 JSX ES6 语法的 babel preset

  • @babel/preset-react 解析 jsx语法
  • @babel/preset-env 解析 es6 语法
  • @babel/plugin-syntax-dynamic-import 解析 react-loadable 的 import 按需加载,附带 code spliting 功能
  • ["import", { libraryName: "antd-mobile", style: true }], Antd-mobile的按需加载
{
                            loader: 'babel-loader',
                            options: {   //jsx语法
                                presets: ["@babel/preset-react",
                                    //tree shaking 按需加载babel-polifill
                                    ["@babel/preset-env", { "modules": false, "useBuiltIns": "false", "corejs": 2 }]],
                                plugins: [
                                    //支持import 懒加载 
                                    "@babel/plugin-syntax-dynamic-import",
                                    //andt-mobile按需加载  true是less,如果不用less style的值可以写'css' 
                                    ["import", { libraryName: "antd-mobile", style: true }],
                                    //识别class组件
                                    ["@babel/plugin-proposal-class-properties", { "loose": true }],
                                ],
                                cacheDirectory: true
                            },
                        }

特别提示,如果电脑性能不高,不建议开启 babel 缓存索引,非常吃内存,记得每次开发完了清理内存

加入 thread-loader ,在 babel 首次编译后开启多线程

const os = require('os')
    {
            loader: 'thread-loader',
            options: {
                workers: os.cpus().length   
                     }
    }

加入单独抽取 CSS 文件的 loader 和插件

const MiniCssExtractPlugin = require('mini-css-extract-plugin')

    {
        test: /\.(less)$/,
        use: [
            MiniCssExtractPlugin.loader,
            {
                loader: 'css-loader', options: {
                    modules: true,
                    localIdentName: '[local]--[hash:base64:5]'
                }
            },
            {loader:'postcss-loader'},
            { loader: 'less-loader' }
        ]
    }
    
     new MiniCssExtractPlugin({
            filename:'[name].[contenthash:8].css'
        }),

CSS 的 tree shaking

const PurifyCSS = require('purifycss-webpack')
const glob = require('glob-all')
plugins:[
    // 清除无用 css
    new PurifyCSS({
      paths: glob.sync([
        // 要做 CSS Tree Shaking 的路径文件
        path.resolve(__dirname, './src/*.html'), // 请注意,我们同样需要对 html 文件进行 tree shaking
        path.resolve(__dirname, './src/*.js')
      ])
    })
]

对小图片进行 base64 处理,减少 http 请求数量,并对输出的文件统一打包处理

{
                    test: /\.(jpg|jpeg|bmp|svg|png|webp|gif)$/,
                    loader: 'url-loader',
                    options: {
                        limit: 8 * 1024,
                        name: '[name].[hash:8].[ext]',

                    }
                }, {
                    exclude: /\.(js|json|less|css|jsx)$/,
                    loader: 'file-loader',
                    options: {
                        outputPath: 'media/',
                        name: '[name].[hash].[ext]'
                    }
                }
                ]
            }]
    },

加入单独抽取 CSS 文件的 loader 和插件

const MiniCssExtractPlugin = require('mini-css-extract-plugin')

    {
        test: /\.(less)$/,
        use: [
            MiniCssExtractPlugin.loader,
            {
                loader: 'css-loader', options: {
                    modules: true,
                    localIdentName: '[local]--[hash:base64:5]'
                }
            },
            {loader:'postcss-loader'},
            { loader: 'less-loader' }
        ]
    }
    
     new MiniCssExtractPlugin({
            filename:'[name].[contenthash:8].css'
        }),

加入压缩 css 的插件

const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')
    new OptimizeCssAssetsWebpackPlugin({
                cssProcessPluginOptions:{
                    preset:['default',{discardComments: {removeAll:true} }]
                }
            }),

加入每次打包输出文件清空上次打包文件的插件

const CleanWebpackPlugin = require('clean-webpack-plugin')
    
    new CleanWebpackPlugin()

加入图片压缩

{
                test: /\.(jpg|jpeg|bmp|svg|png|webp|gif)$/,
                
                use:[
                    {loader: 'url-loader',
                    options: {
                        limit: 8 * 1024,
                        name: '[name].[hash:8].[ext]',
                        outputPath:'/img'
                    }},
                    {
                        loader: 'img-loader',
                        options: {
                          plugins: [
                            require('imagemin-gifsicle')({
                              interlaced: false
                            }),
                            require('imagemin-mozjpeg')({
                              progressive: true,
                              arithmetic: false
                            }),
                            require('imagemin-pngquant')({
                              floyd: 0.5,
                              speed: 2
                            }),
                            require('imagemin-svgo')({
                              plugins: [
                                { removeTitle: true },
                                { convertPathData: false }
                              ]
                            })
                          ]
                        }
                      }
                ]
                
                

            }

加入代码混淆,反编译

var JavaScriptObfuscator = require('webpack-obfuscator');

// ...

// webpack plugins array
plugins: [
    new JavaScriptObfuscator ({
      rotateUnicodeArray: true
  }, ['excluded_bundle_name.js'])
],

加入 PWA 的插件 , WorkboxPlugin

  • pwa 这个技术其实要想真正用好,还是需要下点功夫,它有它的生命周期,以及它在浏览器中热更新带来的副作用等,需要认真研究。可以参考百度的 lavas 框架发展历史~
const WorkboxPlugin = require('workbox-webpack-plugin')


    new WorkboxPlugin.GenerateSW({ 
                clientsClaim: true, //让浏览器立即servece worker被接管
                skipWaiting: true,  // 更新sw文件后,立即插队到最前面 
                importWorkboxFrom: 'local',
                include: [/\.js$/, /\.css$/, /\.html$/,/\.jpg/,/\.jpeg/,/\.svg/,/\.webp/,/\.png/],
            }),

加入预渲染 prerener

new PreloadWebpackPlugin({
            rel: 'preload',
            as(entry) {
                if (/\.css$/.test(entry)) return 'style';
                if (/\.woff$/.test(entry)) return 'font';
                if (/\.png$/.test(entry)) return 'image';
                return 'script';
            },
            include: 'allChunks'
            //include: ['app']
        }),

我这套 webpack 配置,无论多复杂的环境,都是可以搞定的

  • webpack 真的非常非常重要,如果用不好,就永远是个初级前端
  • 只要 webpack 不更新到5,以后就不出 webpack 的文章了
  • webpack4 大结局,谢谢
  • 以后会出一些偏向跨平台技术,原生 javascript , TS , Golang 等内容的文章

以上所述就是小编给大家介绍的《webpack4大结局:加入腾讯IM配置策略,实现前端工程化环境极致优化》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。

Mastering Regular Expressions, Second Edition

Mastering Regular Expressions, Second Edition

Jeffrey E F Friedl / O'Reilly Media / 2002-07-15 / USD 39.95

Regular expressions are an extremely powerful tool for manipulating text and data. They have spread like wildfire in recent years, now offered as standard features in Perl, Java, VB.NET and C# (and an......一起来看看 《Mastering Regular Expressions, Second Edition》 这本书的介绍吧!

URL 编码/解码
URL 编码/解码

URL 编码/解码

html转js在线工具
html转js在线工具

html转js在线工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具