Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

webpack优化 #34

Open
lznbuild opened this issue Jul 7, 2020 · 0 comments
Open

webpack优化 #34

lznbuild opened this issue Jul 7, 2020 · 0 comments

Comments

@lznbuild
Copy link
Owner

lznbuild commented Jul 7, 2020

webpack优化相关

不需要babel把ES Module 转换成曾经的commonjs模块 ,用tree shaking的话,要关闭babel默认的模块转义,tree shaking需要对代码进行静态分析,commonjs模块化做不到。

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

抽离代码

这部分太重要了。
webpack4默认会对一些公共内容做抽离,这部分不看了,只看需要开发者处理的。

第一种。
commonsChunkPlugin 将chunks相同的模块代码提取成公共JS (去重和分离)。通过将公共模块拆出来,最终合成的文件能够在最开始的时候加载一次,便存到缓存中供后续使用。这个带来速度上的提升,因为浏览器会迅速将公共的代码从缓存中取出来,而不是每次访问一个新页面时,再去加载一个更大的文件。

这个在webpack4已经不推荐使用了,了解一下就好。

当满足 minChunks 的引用次数时,commonsChunkPlugin都会将对应的模块抽离如一个新的 chunk 文件中,这个文件为所有的业务文件的父级。而这种设计思路带来了会造成模块打包冗余。详细

使用方式如下

entry: {
  vendor: ["jquery", "other-lib"],
  app: "./entry"
},
plugins: [
  new webpack.optimize.CommonsChunkPlugin({
    names: ['vendor','manifest'], // 指定公共bundle的名称,manifest是单独指定的,非常好用
    minChunks: Infinity
  }),
]

第二种。
在webpack4中,splitChunksPlugin 代替commonsChunkPlugin插件。

拆分文件是为了更好地利用缓存,分离公共类库很大程度上是为了让多页面利用缓存,从而减少下载的代码量,同时,也有代码变更时可以利用缓存减少下载代码量。

使用方式如下

module.exports = {
  optimization: {
    splitChunks: {
    // 自定义chunk
      cacheGroups: { 
        vendor: { // vendor 是我们第三方类库的公共代码的名称
          test: /react|angluar|lodash/, // 直接使用 test 来做路径匹配
          chunks: "initial",
          name: "vendor",
          enforce: true,
        },
      },
    },
  },
}

// 或者
module.exports = {
  optimization: {
    splitChunks: {
      cacheGroups: {
        vendor: {
          chunks: "initial",
          test: path.resolve(__dirname, "node_modules") // 路径在 node_modules 目录下的都作为公共部分
          name: "vendor", // 使用 vendor 入口作为公共部分
          enforce: true,
        },
      },
    },
  },
}

上述第一种做法利用了 test 来做模块路径的匹配,对应路径的都作为第三方类库,第二种做法则是把所有在 node_modules 下的模块,即作为依赖安装的,都作为公共部分。你可以针对项目情况,选择最合适的做法。

第三种。

预编译资源模块

vender包的hash每次都改变,没有完美的实现缓存(React等框架的代码每次发布都不会改变,但还是重新加载了)。
这部分和splitChunksPlugin不要一起使用,可能会有冲突。预编译是预先打包好公共部分,后面再打包不需要对公共部分做处理,直接引用就好。而splitChunksPlugin是每次都要去对公共部分做抽离。

webpack.DLLPlugin将react这种第三方包做抽离,但这种方式有缺点
有些库你并不需要使用到全部功能,比如组件库,函数库,你可以只是需要其它一小部分内容,而 DLLPlugin 插件才不管你这些,它会通通地全部打包进去,这样你就无法去使用打包体积减小的策略了(如:tree shaking)。

像 React、Vue 这样整体性偏强的库,可以生成 vendor 第三方库来去做缓存,因为你一般技术体系是固定的,一个站点里面基本上都会用到统一技术体系,所以生成 vendor 库用于缓存,这部分可以通过 DLLPlugin 去做。
像 antd、lodash 这种功能性组件库,可以通过 tree shaking 来进行消除,只保留有用的代码,千万不要直接打到 vendor 第三方库里,不然你将大量加载执行无用的代码。

思路:将react,react-dom,redux,react-redux等基本不会变动的基础包打包成一个文件

方法:使用DLLPlugin进行分包,DllReferencePlugin 对manifest.json引用

首先,进行分包,最好新建脚本命令,方便

"script": {
  "dll": 'webpack --config webpack.dll.js'
}
// webpack.ddl.js

entry: {
  library: [
    'react',
    'react-dom',
    ...
  ]
},
output: {
  filename: '[name]_[hash].dll.js',
  path:path.join(__dirname,'build/library'),
  library: '[name]'
},
plugins: [
  new webpack.DllPlugin({
    name: '[name]_[hash]'
    path: path.join(__dirname,'build/library/[name].json')
  })
]

然后,在生产环境引入对应的包

// webpack.prod.js
plugins: [
  new webpack.DllReferencePlugin({
    manifest: require('./build/library/library.json')
  })
]

最后,需要在html 模板里面引入 [name]_[hash].dll.js,webpack 不会自动引入

第四种。

静态代码分割

import(...).then()

可能需要babel-plugin-syntax-dynamic-import做处理

其实就是异步加载资源,这种写法不如React.lazy,做的事都是一样的,建议直接用后者。 bundle-loader做的也是同样的事。

scope hoisting

每个模块都是一个函数,模块多了,函数,闭包也就多了。这部分需要明白webpack打包之后的内容是什么样子。

原理:scope hoisting 将所有模块的代码按照引用顺序放在一个函数作用域里,然后适当的重命名一些变量以防止变量名冲突

对比:通过scope hoisting 可以减少函数声明代码和内存开销,文件体积也小了。

mode为production默认开启 ,必须ES6语法,commonJS不支持

可以不指定mode,手动添加

plugins: [
    new webpack.optimize.ModuleConcatenationPlugin()
  ]

thread-loader 多进程打包js和css

每次webpack解析一个模块,thread-loader会将它及它的以来分配给worker线程中

webpack.optimization.sideEffects ???

减少构建时间

缩小 Babel 的编译范围,并使用 webpack cache 缓存模块。

module.exports = {
  // 减小模块的查找范围
  resolve: {
    modules: [path.resolve(__dirname, 'node_modules')],
  },
  module: {
    rules: [
      {
        test: /\.js?$/,
        use: [{
          loader: 'babel-loader',
          query: {
            // 将 babel 编译过的模块缓存在 webpack_cache 目录下,下次优先复用
            cacheDirectory: './webpack_cache/',
          },
        }],
        // 减少 babel 编译范围,忘记设置会让 webpack 编译慢上好几倍
        include: path.resolve(__dirname, 'src'),
      },
    ]
  },
}

webpack提升构建速度

  1. 减少resolve的解析,过滤node_modules,后缀名减少

  2. loader应用的文件范围缩小,include字段指定需要转换的文件范围,modules.rules.noParse

  3. 一处没有必要的plugin,比如生产环境自动压缩代码,去掉无用代码

  4. 选择合适的devtool(sourcemap)

  5. cache-loader 缓存转换的内容

postcss的使用

autoprefixer
postcss.config.js 文件,把 postcss-loader 的参数抽离出来

postcss-preset-env 集成了很多 postcss的插件包括 autoprefixer,使用 browserslist 来配置目标环境

https://juejin.im/post/5d897e3cf265da03c721e230

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant