webpack(三)-性能优化

773 阅读5分钟

loader优化

test、include、exclude三个配置项来缩小loader的处理范围

include、exclude只能使其一

include: path.resolve(__dirname, "./src"),

优化resolve.modules配置

resolve.modules⽤于配置webpack去哪些⽬录下寻找第三⽅模块,默认是 ['node_modules']

寻找第三⽅模块,默认是在当前项⽬录下的node_modules⾥面去找,如果没有找 到,就会去上⼀级目录../node_modules找,再没有会去../../node_modules中找,以 此类推,和Node.js的模块寻找机制很类似。

如果我们的第三⽅模块都安装在了项⽬根目录下,就可以直接指明这个路径

module.exports={
  resolve:{
    modules: [path.resolve(__dirname, "./node_modules")]
  }
}

优化resolve.alias配置

resolve.alias配置通过别名来将原导⼊路径映射成一个新的导⼊路径

默认情况下,webpack会从⼊口文件./node_modules/bin/react/index开始递归解 析和处理依赖的⽂件。我们可以直接指定文件,避免这处的耗时

resolve: {
    alias: {
        "@": path.join(__dirname, "./pages"),
        react: path.resolve(
          __dirname,
          "./node_modules/react/umd/react.production.min.js"
        ),
      "react-dom": path.resolve(
          __dirname,
          "./node_modules/react-dom/umd/react-dom.production.min.js"
      )
    }
}

优化resolve.extensions配置

resolve.extensions在导⼊语句没带文件后缀时,webpack会⾃动带上后缀后,去尝试 查找文件是否存在。

默认值

extensions:['.js','.json','.jsx','.ts']

使⽤静态资源路径publicPath

就是在输出的静态资源打包前添加目录,目录可以是本地目录,也可以是服务目录(CDN)

CDN通过将资源部署到世界各地,使得⽤户可以就近访问资源,加快访问速度。要接 ⼊CDN,需要把⽹页的静态资源上传到CDN服务上,在访问这些资源时,使用CDN服 务提供的URL

//webpack.config.js
output:{
    publicPath: '//cdnURL.com', //指定存放JS⽂件的CDN地址
}

CSS处理

 
{
  test: /\.less$/,
  use: ["style-loader", "css-loader", "less-loader"]
}

使⽤postcss为样式自动补齐浏览器前缀

caniuse

// 新建postcss.config.js
module.exports = {
  plugins: [
    require("autoprefixer")({
      overrideBrowserslist: ["last 2 versions", ">1%"]
    })
 ] 
};

//webpack.config.js
{
  test: /\.less$/,
  include: path.resolve(__dirname, "./src"),
  use: [
    "style-loader",
    {
        loader: "css-loader",
        options: {}
    },
    "less-loader",
    "postcss-loader"
 ]
},
 

如果不做抽取配置,我们的 css 是直接打包进 js ⾥面的,我们希望能单独生成 css ⽂件。 因为单独⽣成css,css可以和js并行下载,提⾼页⾯加载效率

const MiniCssExtractPlugin = require("mini-css-extract-plugin");
 {
   test: /\.scss$/,
    use: [
        // "style-loader", // 不再需要style-loader,用MiniCssExtractPlugin.loader代替
        MiniCssExtractPlugin.loader,
        "css-loader", // 编译css
        "postcss-loader",
        "sass-loader" // 编译scss
    ]
 },
 plugins: [
    new MiniCssExtractPlugin({
      filename: "css/[name]_[contenthash:6].css",
      chunkFilename: "[id].css"
    })
]

压缩css

  • 借助 optimize-css-assets-webpack-plugin
  • 借助cssnano(压缩规范)
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");

new OptimizeCSSAssetsPlugin({
    cssProcessor: require("cssnano"), //引入cssnano配置压缩选项 cssProcessorOptions: {
        discardComments: { removeAll: true }
    }
})

压缩HTML

new htmlWebpackPlugin({
    title: "京东商城",
    template: "./index.html",
    filename: "index.html",
    minify: {
        // 压缩HTML文件
        removeComments: true, // 移除HTML中的注释
        collapseWhitespace: true, // 删除空白符与换⾏符
        minifyCSS: true // 压缩内联css
    }
}),

development vs Production模式区分打包

  1. 两份配置
  2. 通过传参做区分
// package.json
    "dev:build": "webpack --config ./test.config.js --env.production",

//env.production  默认为true
module.exports = env => {

  if (env && env.production) {
    return merge(baseConfig, proConfig);
  } else {
    return merge(baseConfig, devConfig);
  }
};
  1. 通过环境变量做区分
    //package.json
    "test": "cross-env NODE_ENV=test webpack --config//兼容
    
    "test": "NODE_ENV=test webpack --config // mac下无问题, win下有问题
    
    // 在webpack.config.js⾥拿到参数
    
    process.env.NODE_ENV

tree Shaking

Dead Code 一般具有以下几个特征

  • 代码不会被执⾏,不可到达 代码执行的结果不会被用到
  • 代码只会影响死变量(只写不读)
  • Js tree shaking只⽀持ES module的引⼊方式!!!!,

1. Css tree shaking

npm i glob-all purify-css purifycss-webpack --save-dev

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 js ⽂件进行 tree shaking
            path.resolve(__dirname, './src/*.js')
        ])
    })
]

2. JS tree shaking

只⽀持import⽅式引入,不支持commonjs的方式引入

//expo.js
export const add = (a, b) => {
  return a + b;
}

export const minus = (a, b) => {
  return a - b;
};


//index.js
import { add } from "./expo";
add(1, 2);


 
//webpack.config.js
optimization: {
    usedExports: true // 哪些导出的模块被使⽤了,再做打包
}

// 只要mode是production就会生效,develpoment的tree shaking是不生效的,因为 webpack为了方便你的调试
 

只要mode是production就会⽣效,develpoment的tree shaking是不生效的,因为 webpack为了⽅便调试 可以查看打包后的代码注释以辨别是否生效。 ⽣产模式不需要配置,默认开启

usedExports: true 副作⽤

开启tree shaking 代码中引用的css 和图片会不生效

 
//package.json
"sideEffects":false //正常对所有模块进行tree shaking , 仅⽣产模式有效,需要配合usedExports
或者 在数组⾥面排除不需要tree shaking的模块

"sideEffects":['*.css','@babel/polyfill']

代码分割 code Splitting

打包完后,所有⻚面只⽣成了⼀一个bundle.js

  • 代码体积变大,不利利于下载
  • 没有合理利用浏览器资源

其实code Splitting概念 与 webpack并没有直接的关系,只不过webpack中提供了一种 更加⽅便的⽅法供我们实现代码分割

optimization: {
    // 自动帮我们做代码分割,默认支持异步
    splitChunks: {
        chunks: "all", // 所有的 chunks 代码公共的部分离出来成为⼀个单独的 文件
    },
},
optimization: {
    splitChunks: {
        chunks: 'async',  //对同步 initial,异步 async,所有的模块有效 all minSize: 30000,  //最小尺寸,当模块大于30kb
        maxSize: 0,  //对模块进⾏二次分割时使用,不推荐使用
        minChunks: 1,  //打包⽣成的chunk⽂件最少有⼏个chunk引⽤了这个模块
        maxAsyncRequests: 5,  //最⼤异步请求数,默认5
        maxInitialRequests:3,//最⼤初始化请求书,⼊口文件同步请求,默认3 automaticNameDelimiter: '-',//打包分割符号
        name: true,//打包后的名称,除了了布尔值,还可以接收一个函数function cacheGroups: {//缓存组
        vendors: {
            test: /[\\/]node_modules[\\/]/,
            name:"vendor", // 要缓存的 分隔出来的 chunk 名称
            priority: -10//缓存组优先级 数字越⼤,优先级越高
        },
        other:{
            chunks: "initial", // 必须三选⼀一: "initial" | "all" |"async"(默认就是async)
            test: /react|lodash/, // 正则规则验证,如果符合就提取 chunk, name:"other",
            minSize: 30000,
            minChunks: 1,
        },
        default: {
            minChunks: 2,
            priority: -20,
            reuseExistingChunk: true//可设置是否重用该chunk
        }
    }
} }
 

打包后的chunk