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

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

内容简介:在此对

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

webpack ,打包所有的资源

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

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

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

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

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

  • 开发模式热更新
  • 识别 JSX 文件
  • 识别 class 组件
  • 代码混淆压缩,防止反编译代码,加密代码
  • 配置 alias 别名,简化 import 的长字段
  • 同构直出, SSR 的热调试(基于 Node 做中间件)
  • 实现 javaScripttree shaking 摇树优化 删除掉无用代码
  • 实现 CSStree shaking
  • 识别 async / await 和 箭头函数
  • react-hot-loader 记录 react 页面留存状态 state
  • PWA 功能,热刷新,安装后立即接管浏览器 离线后仍让可以访问网站 还可以在手机上添加网站到桌面使用
  • preload 预加载资源 prefetch 按需请求资源
  • CSS 模块化,不怕命名冲突
  • 小图片的 base64 处理
  • 文件后缀省掉 jsx js json
  • 实现React懒加载,按需加载 , 代码分割 并且支持服务端渲染
  • 支持 less sass stylus 等预处理
  • code spliting 优化首屏加载时间 不让一个文件体积过大
  • 加入 dns-prefetchpreload 预请求必要的资源,加快首屏渲染(京东策略)
  • 加入 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-loadableimport 按需加载,附带 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'
        }),

CSStree 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 大结局,谢谢
  • 以后会出一些偏向跨平台技术,原生 javascriptTSGolang 等内容的文章

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

查看所有标签

猜你喜欢:

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

Perl语言编程

Perl语言编程

克里斯蒂安森 (Tom Christiansen) (作者)、Brian D Foy (作者)、Larry Wall (作者)、Jon Orwant (作者) / 苏金国 (译者)、吴爽 (译者) / 中国电力出版社 / 2014-9-1 / 148

从1991年第一版问世以来,《Perl语言编程》很快成为无可争议的Perl宝典,如今仍是这种高实用性语言的权威指南。Perl最初只是作为一个功能强大的文本处理工具,不过很快发展成为一种通用的编程语言,可以帮助成千上万的程序员、系统管理员,以及像你一样的技术爱好者轻松完成工作。 人们早已经翘首以待这本“大骆驼书”的更新,如今终于得偿所愿。在这一版中,三位颇有声望的Perl作者讲述了这种语言当前......一起来看看 《Perl语言编程》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

MD5 加密
MD5 加密

MD5 加密工具

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

html转js在线工具