zoukankan      html  css  js  c++  java
  • webpack系列3:各种优化

    webpack各种优化

    上一章节我们已经掌握了webpack常见的所有配置

    这一节我们来看看如何实现webpack中的优化,我们先来编写最基本的webpack配置,然后依次实现各种优化!

    const MiniCssExtractPlugin = require("mini-css-extract-plugin");
    const HtmlWebpackPlugin = require("html-webpack-plugin");
    const path = require("path");
    module.exports = mode => {
      return {
        mode: mode,
        entry: "./src/main.js",
        output: {
          filename: "bundle.js",
          path: path.resolve(__dirname, "dist")
        },
        module: {
          rules: [
            {
              test: /.(png|jpg|gif)$/,
              use: "file-loader"
            },
            {
              test: /.js$/,
              use: "babel-loader" // .babelrc已经配置支持react
            },
            {
              test: /.css$/,
              use: [
                mode !== "development"
                  ? MiniCssExtractPlugin.loader
                  : "style-loader",
                "css-loader"
              ]
            }
          ]
        },
        plugins: [
          new PurgecssPlugin({
            paths: glob.sync(`${path.join(__dirname, "src")}/**/*`, { nodir: true }) // 不匹配目录,只匹配文件
          }),
          mode !== "development" &&
            new MiniCssExtractPlugin({
              filename: "css/[name].css"
            }),
          new HtmlWebpackPlugin({
            template: "./src/template.html",
            filename: "index.html"
          })
        ].filter(Boolean)
      };
    };

    .babelrc配置文件

    {
        "presets": [
            "@babel/preset-env",
            "@babel/preset-react"
        ]
    }

    1.删除无用的Css样式

    先来看编写的代码

    import './style.css'
    import React from 'react';
    import ReactDOM from 'react-dom';
    ReactDOM.render(<div>hello</div>,document.getElementById('root'));
    body{
        background: red
    }
    .class1{
        background: red
    }

    这里的.class1显然是无用的,我们可以搜索src目录下的文件,删除无用的样式

    const glob = require('glob');
    const PurgecssPlugin = require('purgecss-webpack-plugin');
    
    // 需要配合mini-css-extract-plugin插件
    mode !== "development" && new PurgecssPlugin({
        paths: glob.sync(`${path.join(__dirname, "src")}/**/*`, { nodir: true }) // 不匹配目录,只匹配文件
    }),

    2.图片压缩插件

    将打包后的图片进行优化

    npm install image-webpack-loader --save-dev
    

    在file-loader之前使用压缩图片插件

    loader: "image-webpack-loader",
    options: {
      mozjpeg: {
        progressive: true,
        quality: 65
      },
      // optipng.enabled: false will disable optipng
      optipng: {
        enabled: false,
      },
      pngquant: {
        quality: [0.90, 0.95],
        speed: 4
      },
      gifsicle: {
        interlaced: false,
      },
      // the webp option will enable WEBP
      webp: {
        quality: 75
      }
    }

    可以发现图片大小是有了明显的变化

    3.CDN加载文件

    我们希望通过cdn的方式引入资源

    const AddAssetHtmlCdnPlugin = require('add-asset-html-cdn-webpack-plugin')
    new AddAssetHtmlCdnPlugin(true,{
        'jquery':'https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js'
    })

    但是在代码中还希望引入jquery来获得提示

    import $ from 'jquery'
    console.log('$',$)

    但是打包时依然会将jquery进行打包

    externals:{
      'jquery':'$'
    }

    在配置文件中标注jquery是外部的,这样打包时就不会将jquery进行打包了

    4.Tree-shaking && Scope-Hoisting

    4.1 Tree-shaking

    顾名思义就是将没用的内容摇晃掉,来看下面代码

    main.js

    import { minus } from "./calc";
    console.log(minus(1,1));

    calc.js

    import {test} from './test';
    export const sum = (a, b) => {
      return a + b + 'sum';
    };
    export const minus = (a, b) => {
      return a - b + 'minus';
    };

    test.js

    export const test = ()=>{
        console.log('hello')
    }
    console.log(test());

    观察上述代码其实我们主要使用minus方法,test.js代码是有副作用的!

    默认mode:production时,会自动tree-shaking,但是打包后'hello'依然会被打印出来,这时候我们需要配置不使用副作用

    package.json中配置

    "sideEffects":false,

    如果这样设置,默认就不会导入css文件啦,因为我们引入css也是通过import './style.css'

    这里重点就来了,tree-shaking主要针对es6模块,我们可以使用require语法导入css,但是这样用起来有点格格不入,所以我们可以配置css文件不是副作用

    "sideEffects":[
        "**/*.css"
    ]

    在开发环境下默认tree-shaking不会生效,可以配置标识提示

    optimization:{
      usedExports:true 
    }

    4.2 Scope Hoisting

    作用域提升,可以减少代码体积,节约内存

    let a = 1;
    let b = 2;
    let c = 3;
    let d = a+b+c
    export default d;
    // 引入d
    import d from './d';
    console.log(d)

    最终打包后的结果会变成 console.log(6)

    • 代码量明显减少
    • 减少多个函数后内存占用也将减少

    5.DllPlugin && DllReferencePlugin

    每次构建时第三方模块都需要重新构建,这个性能消耗比较大,我们可以先把第三方库打包成动态链接库,以后构建时只需要查找构建好的库就好了,这样可以大大节约构建时间

    import React from 'react';
    import ReactDOM from 'react-dom';
    
    ReactDOM.render(<h1>hello</h1>,document.getElementById('root'))

    5.1 DllPlugin

    这里我们可以先将reactreact-dom单独进行打包

    单独打包创建webpack.dll.js

    const path = require('path');
    const DllPlugin = require('webpack/lib/DllPlugin');
    module.exports = {
        entry:['react','react-dom'],
        mode:'production',
        output:{
            filename:'react.dll.js',
            path:path.resolve(__dirname,'dll'),
            library:'react'
        },
        plugins:[
            new DllPlugin({
                name:'react',
                path:path.resolve(__dirname,'dll/manifest.json')
            })
        ]
    }

    执行"webpack --config webpack.dll.js命令,可以看到dll目录下创建了两个文件分别是manifest.json,react.dll.js

    关系是这个酱紫的,到时候我们会通过manifest.json找到react.dll.js文件中的模块进行加载

    5.2 DllReferencePlugin

    在我们的项目中可以引用刚才打包好的动态链接库

    const DllReferencePlugin = require('webpack/lib/DllReferencePlugin');
    const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin');
    // 构建时会引用动态链接库的内容
    new DllReferencePlugin({
      manifest:path.resolve(__dirname,'dll/manifest.json')
    }),
    // 需要手动引入react.dll.js
    new AddAssetHtmlWebpackPlugin(
      { filepath: path.resolve(__dirname,'dll/react.dll.js') }
    )

    使用DllPlugin可以大幅度提高构建速度

    6.动态加载

    实现点击后动态加载文件

    let btn = document.createElement('button');
    btn.innerHTML = '点击加载视频';
    btn.addEventListener('click',()=>{
        import('./video').then(res=>{
            console.log(res.default);
        });
    });
    document.body.appendChild(btn);

    给动态引入的文件增加名字

    output:{
      chunkFilename:'[name].min.js'
    }
    import(/* webpackChunkName: "video" */ './video').then(res=>{
        console.log(res.default);
    })

    这样打包后的结果最终的文件就是 video.min.js

    7.打包文件分析工具

    安装webpack-bundle-analyzer插件

    npm install --save-dev webpack-bundle-analyzer
    

    使用插件

    const {BundleAnalyzerPlugin} = require('webpack-bundle-analyzer');
    mode !== "development" && new BundleAnalyzerPlugin()

    默认就会展现当前应用的分析图表

    8.SplitChunks

    我们在来看下SplitChunks这个配置,他可以在编译时抽离第三方模块、公共模块

    将项目配置成多入口文件

    entry:{
      a:'./src/a.js',
      b:'./src/b.js'
    }

    我们让a,b两个模块同时引用jquery,别忘了去掉之前的externals配置

    配置SplitChunks插件

    默认配置在此,我一个个描述下含义

    splitChunks: {
      chunks: 'async', // 分割异步模块
      minSize: 30000, // 分割的文件最小大小
      maxSize: 0, 
      minChunks: 1, // 引用次数
      maxAsyncRequests: 5, // 最大异步请求数
      maxInitialRequests: 3, // 最大初始化请求数
      automaticNameDelimiter: '~', // 抽离的命名分隔符
      automaticNameMaxLength: 30, // 名字最大长度
      name: true,
      cacheGroups: { // 缓存组
        vendors: { // 先抽离第三方
          test: /[\/]node_modules[\/]/,
          priority: -10
        },
        default: { 
          minChunks: 2,
          priority: -20, // 优先级
          reuseExistingChunk: true
        }
      }
    }

    我们将async改为initial

    我们在为每个文件动态导入lodash库,并且改成async

    import('lodash')

    为每个入口引入c.js,并且改造配置文件

    splitChunks: {
      chunks: 'all',
      name: true,
      cacheGroups: {
        vendors: {
          test: /[\/]node_modules[\/]/,
          priority: -10
        },
        default: {
          minSize:1, // 不是第三方模块,被引入两次也会被抽离
          minChunks: 2,
          priority: -20,
        }
      }
    }

    这样再反过来看chunks的参数是不是就了然于胸啦!

    9.热更新

    模块热替换(HMR - Hot Module Replacement)是 webpack 提供的最有用的功能之一。它允许在运行时替换,添加,删除各种模块,而无需进行完全刷新重新加载整个页面

    • 保留在完全重新加载页面时丢失的应用程序的状态
    • 只更新改变的内容,以节省开发时间
    • 调整样式更加快速,几乎等同于就在浏览器调试器中更改样式

    启用热更新,默认样式可以支持热更新,如果不支持热更新则采用强制刷新

    devServer:{
      hot:true
    }
    new webpack.NamedModulesPlugin(),

    js支持热更新

    import sum from './sum';
    console.log(sum(1,2));
    if(module.hot){ // 如果支持热更新
        module.hot.accept(); // 当入口文件变化后重新执行当前入口文件
    }

    10.IgnorePlugin

    忽略 importrequire语法

    new webpack.IgnorePlugin(/^./locale$/, /moment$/)

    11.费时分析

    可以计算每一步执行的运行速度

    const SpeedMeasureWebpackPlugin = require('speed-measure-webpack-plugin');
    const smw = new SpeedMeasureWebpackPlugin();
      module.exports =smw.wrap({
    });

    12.noParse

    module.noParse,对类似jq这类依赖库,内部不会引用其他库,我们在打包的时候就没有必要去解析,这样能够增加打包速率

    noParse:/jquery/

    13.resolve

    resolve: {
      extensions: [".js",".jsx",".json",".css"],
      alias:{},
      modules:['node_modules']
    },

    14.include/exclude

    在使用loader时,可以指定哪些文件不通过loader,或者指定哪些文件通过loader

    {
      test: /.js$/,
      use: "babel-loader",
      // include:path.resolve(__dirname,'src'),
      exclude:/node_modules/
    },

    15.happypack

    多线程打包,我们可以将不同的逻辑交给不同的线程来处理

    npm install --save-dev happypack

    使用插件

    const HappyPack = require('happypack');
    rules:[
      {
        test: /.js$/,
        use: 'happypack/loader?id=jsx'
      },
    
      {
        test: /.less$/,
        use: 'happypack/loader?id=styles'
      },
    ]
    new HappyPack({
      id: 'jsx',
      threads: 4,
      loaders: [ 'babel-loader' ]
    }),
    
    new HappyPack({
      id: 'styles',
      threads: 2,
      loaders: [ 'style-loader', 'css-loader', 'less-loader' ]
    })
  • 相关阅读:
    算法----(1)冒泡排序
    淘宝爬虫
    爬虫_豆瓣电影top250 (正则表达式)
    爬虫_猫眼电影top100(正则表达式)
    Android 简单调用摄像头
    Android 简单天气预报
    思维模型
    This view is not constrained, it only has designtime positions, so it will jump to (0,0) unless you
    Android studio preview界面无法预览,报错render problem
    Android studio 3.1.2报错,no target device found
  • 原文地址:https://www.cnblogs.com/jianxian/p/12760402.html
Copyright © 2011-2022 走看看