美文网首页前端面试
webpack打包优化

webpack打包优化

作者: 艾晨星雨 | 来源:发表于2018-09-16 18:07 被阅读1920次

记一次react项目优化的过程
优化前,用uglifyjs-webpack-plugin插件压缩js后得到的大小,实际大小1.3M,如图:

image.png
以上图形化界面,可下载webpack-bundle-analyzer插件包查看。
可以发现打包出来的main.js内主要包含了两部分:node_modules包及js业务代码,所以第一步就是要拆分node_modules包及业务代码:
//业务代码与node_modules分离
new webpack.optimize.CommonsChunkPlugin({    
          name: 'vendor',
          minChunks: ({ resource }) => (
              resource &&
              resource.indexOf('node_modules') >= 0 &&
              resource.match(/\.js$/)
          ),
      }),

打包效果:


image2.png

第二步:分离node_modules包中比较大的包,并通过CDN或以静态文件的方式引入到项目中,在webpack中用externals指定即可,如:

externals: {
        'react': 'React',
        'react-dom': 'ReactDOM',
        'redux': 'Redux',
        'redux-thunk': 'ReduxThunk',
        'react-redux': 'ReactRedux',
        'redux-form': 'ReduxForm',
        'immutable': 'Immutable',
        'babel-polyfill': 'window', // polyfill 直接写 {} 也是可以的,
        'transit-js': 'transit',
    }

分离后在externals中所指定的包就不会打包的vendor.js中。
1.在index.html中通过CDN的方式引入在externals中指定过的包,如:

  //注:以下插件包需找项目中对应版本的包且是min.js压缩版的
  <script src="https://cdn.bootcss.com/babel-polyfill/6.2.0/polyfill.min.js"></script>
  <script src="https://cdn.bootcss.com/react/16.4.0/umd/react.production.min.js"></script>
  <script src="https://cdn.bootcss.com/react-dom/16.4.0/umd/react-dom.production.min.js"></script>
  <script src="http://cdn.cognitect.com/transit/transit-0.8.861-min.js"></script>
  <script src="https://cdn.bootcss.com/redux-thunk/2.2.0/redux-thunk.min.js"></script>
  <script src="https://cdn.bootcss.com/redux/3.7.2/redux.min.js"></script>
  <script src="https://cdn.bootcss.com/react-redux/5.0.7/react-redux.min.js"></script>
  <script src="https://cdn.bootcss.com/redux-form/7.0.1/redux-form.min.js"></script>
  <script src="https://cdn.bootcss.com/immutable/3.8.1/immutable.min.js"></script>

什么是CDN?
cdn(内容分发网络)的作用是加速网络传输,通过将资源部署到服务器,来加快资源到获取速度。目前用到的CDN主要是由Bootstrap 中文网支持并维护的前端开源项目免费 CDN 服务。

2.以静态文件的方式引入资源包
在当前项目的public目录下创建新的目录resource,将以上cdn引用包全都保存到resource目录(注:通过create-react-app创建的react项目中public目录下默认会有static目录,这里不能将js到静态文件放到static目录,在index.html中直接引入static目录下到文件会报错 Unexpected token <,所以要么将static目录名改成其他名字,或者新建一个目录)

image3.jpg
  <script src="/resource/polyfill.min.js"></script>
  <script src="/resource/react.production.min.js"></script>
  <script src="/resource/react-dom.production.min.js"></script>
  <script src="/resource/transit-0.8.861-min.js"></script>
  <script src="/resource/redux-thunk.min.js"></script>
  <script src="/resource/redux.min.js"></script>
  <script src="/resource/react-redux.min.js"></script>
  <script src="/resource/redux-form.min.js"></script>
  <script src="/resource/immutable.min.js"></script>

既然用CDN加速来加载引入的包,为什么还要用静态文件的方式引入呢?
答案当然是本地引入文件会更快,毕竟cdn也是服务环境,请求服务环境的资源也是耗时间的,可参考cdn加速与js引入文件的比较
分离后vendor.js的大小:从原来的1.06M减少到584.42kb,小了一半

image4@2x.png
注:细心的同学会发现,上图中的antd-mobile包为什么没有通过js引用?
原因是antd-mobile包在经过按需打包后所得大小是147kb,而通过cdn引入的antd-mobile竟然有373kb,远超出了打包所得大小,所以不建议通过js引入,
可参考cdn antd-mobile

3.第三步-拆分js业务代码,按需加载
可看上图image2,右侧蓝色部分,业务代码实际大小只有93kb,但在页面初次加载资源时会将整个js业务代码都加载进来,特别是在网络环境差时,会加载的比较慢
业务代码主要分三个模块:买家账户(放account目录),卖家(放seller目录),登录、产品等页面(放sys目录),再加上公共代码(common\component目录),总共分四个块,项目中用到react-router-dom路由,接下来就按路由的方式拆分代码,所用工具react-loadable

//目录结构
Projects
  --js
    --common
    --component
    --App.web.js
    --pages
      --index.web.js
      --account
      --sys
      --seller
index.web.js
//App.web.js
import {
    AsyncSys,
    AsyncSeller,
    AsyncAccount,
} from './pages/index.web'
import {Route, Redirect, Switch } from 'react-router-dom'
class Root extends Component {
    render() {
        return (
            <div>
                <Switch>
                    <Route exact path="/" render={() => (
                        <Redirect to="/Sys/Product"/>
                    )}/>
                    <Route path="/Sys" component={AsyncSys}/>
                    <Route path="/Account" component={AsyncAccount}/>
                    <Route path="/SellerList" component={AsyncSeller}/>
                </Switch>
            </div>
        )
    }
}
//./pages/index.web.js 这里按路由拆分
import Loadable from 'react-loadable';
import AsyncJSLoading from '../component/AsyncJSLoading'

const AsyncSys = Loadable({
    loader: () => import(/* webpackChunkName: 'sys' */'./sys/index.web'),
    loading: AsyncJSLoading
});

const AsyncSeller = Loadable({
    loader: () => import(/* webpackChunkName: 'seller' */'./seller/index.web'),
    loading: AsyncJSLoading
});

const AsyncAccount = Loadable({
    loader: () => import(/* webpackChunkName: 'account' */'./account/index.web'),
    loading: AsyncJSLoading
});
export {
    AsyncSys,
    AsyncAccount,
    AsyncSeller,
}
//./sys/index.web.js
import {Route, Switch} from 'react-router-dom';
import Product from './ProductDetails.web';
import Login from './Login.web';
import SupplyData from './SupplyData.web';

export default class extends React.Component {
    render() {
        return (
            <div>
                <Switch location={this.props.location}>
                    <Route path={`${this.props.match.path}/Login`} component={Login}/>
                    <Route path={`${this.props.match.path}/Product`} component={Product}/>
                    <Route path={`${this.props.match.path}/SupplyData`} component={SupplyData}/>
                </Switch>
            </div>
        )
    }
}
//./seller/index.web.js 、./account/index.web.js 与 sys/index.web.js写法一样,这里不列举了

3点注意:
1.在 pages/index.web.js中通过import分割相应模块,模块内的文件在当前分割js中不可再次引用,否则分割失效,如:

//./pages/index.web.js 
import Loadable from 'react-loadable';
import AsyncJSLoading from '../component/AsyncJSLoading'
import Login from './sys/Login.web'    //再次引入sys目录下的文件

const AsyncSys = Loadable({
    loader: () => import(/* webpackChunkName: 'sys' */'./sys/index.web'),
    loading: AsyncJSLoading
});

2.import() 会返回一个Promise,对于不支持Promise浏览器需要在页面上注入Promise polyfill,如:

<script src="https://cdn.bootcss.com/babel-polyfill/6.2.0/polyfill.min.js"></script>

另外import()语法还没有加入到ECMAScript标准里,所以项目中需要安装一个Babel插件 babel-plugin-syntax-dynamic-import,并且将其加入.babelrc中:

{
plugins: [
    "syntax-dynamic-import"
 ],
}

3./* webpackChunkName: 'sys' */ 的含义是为动态生成的Chunk赋予一个名称,以便我们追踪和调试代码

//filename中的name 就是 webpackChunkName所指定的名称
output: {
    path: paths.appBuild,
    filename: 'resource/js/[name].[chunkhash:8].js',
    chunkFilename: 'resource/js/[name].[chunkhash:8].chunk.js',
    publicPath: publicPath,
  },

最后看看分割的结果:按预期分成功了四大块:main.js 主要是公共代码、account.js 是买家模块、seller.js 是卖家模块、sys.js 是 登录、产品页部分


image5.png

第四步-gzip压缩
什么是gzip压缩?
参考
服务器开启GZIP
探索HTTP传输中gzip压缩的秘密

const CompressionWebpackPlugin = require('compression-webpack-plugin');

webpackConfig.plugins.push(
    new CompressionWebpackPlugin({
      asset: '[path].gz[query]',
      algorithm: 'gzip',
      test: new RegExp('\\.(js|css)$'),
      threshold: 10240,
      minRatio: 0.8
    })
)

压缩前:最大的文件vendor.js 589kb

image6.png
压缩后:vendor.js 154kb
image7.png

至此,大功告成!

相关文章

  • 基于webpack 3 打包性能优化

    基于webpack 3 打包性能优化 source Scope Hoisting. 过去 webpack 打包时的...

  • webpack打包优化

    实现webpack打包优化,有两个优化点: 如何减少打包时间 如何减少打包大小 减少打包时间 优化Loader对于...

  • React单页面应用项目 性能优化 实践

    react 单页面应用项目在加载优化这一块就得依赖webpack的打包方式。webpack的打包优化的本质就是将 ...

  • 浅谈webpack打包原理

    近来想要对旧项目进行优化,所以了解下webpack打包原理为优化做准备 webpack 4.x 打包文件 inde...

  • Webpack极限打包优化

    今天为了更好地了解一下Webpack打包优化的一些内容,看了一下NEXT公开课,Webpack打包极限优化,感兴趣...

  • Webpack 打包优化之速度篇

    在前文 Webpack 打包优化之体积篇中,对如何减小 Webpack 打包体积,做了些探讨;当然,那些法子对于打...

  • 前端打包部署优化之gzip

    使用webpack打包出来的文件过大,导致访问速度极其慢,搜索webpack打包优化,能够看到很多前辈的建议:gz...

  • webpack 性能优化

    webpack性能优化 开发环境性能优化 生产环境性能优化 开发环境性能优化 优化打包构建速度 优化调试功能 生产...

  • vue项目优化

    vue 项目优化 项目打包体积优化 通常vue项目通过webpack打包后,会出现vendor包的体积过大的情况,...

  • 2019-12-16 总结几个webpack打包优化的方法

    总结几个webpack打包优化的方法 为什么要优化打包? 项目越做越大,依赖包越来越多,打包文件太大 单页面应用首...

网友评论

    本文标题:webpack打包优化

    本文链接:https://www.haomeiwen.com/subject/kqztnftx.html