首页 > 移动平台 > 详细

记一次webpack优化 create-react-app 从单个文件变为多个文件打包将公共依赖,公共代码整理到特定js文件里面

时间:2020-07-09 19:09:23      阅读:58      评论:0      收藏:0      [点我收藏+]

版本:

  "webpack": "3.5.1",

  "react": "^15.6.2",

  "antd": "^3.26.7",
1.项目本来是一直跑在内网的,原先项目build之后是在一个js包里面,导致包基本上都在3M+  甚至到5M+,本来在内网上,倒也速度到也不太慢,不过最近有考虑可能要放到外网上展示,所以要搞一下js大小,不然加载慢的一批
2.核心要求:js文件要尽量小,加载速度要快 优先于 打包速度 ,最好能打包速度快的同时 能js文件能尽量小
3.开始
  1).不作任何改变的话,文件大小5M,时间110s
  2).先考虑将单个文件拆分:从router入手,将文件变为懒加载或者说按需加载
   
//router.js
import {asyncComponent} from ‘@utils/loadable‘;
const Login = asyncComponent(()=> import(/* webpackChunkName:‘Login‘ */"../pages/Login/index"))//中间的注释可以让webpack按照这个名字打包 便于分辨
//loadable.js

import React from ‘react‘
import Loadable from ‘react-loadable‘
import { Button, Result, Spin } from ‘antd‘
import store from ‘@store/store‘

const extra = ({ retry }) => (
  <Button type=‘primary‘ onClick={retry}>重新加载</Button>
)

const ErrorComponent = props => (
    <Result
        status=‘500‘
        title=‘504‘
        subTitle=‘似乎出了点问题‘
        extra={extra(props)}
    />
)

const LoadingComponent = (
    <div style={{ textAlign: ‘center‘, marginTop: 100 }}>
        <Spin size=‘large‘/>
    </div>
)

const Loading = props => {
    if (props.error || props.timedOut) {
            return <ErrorComponent {...props}/>
        } else {
            return LoadingComponent
    }
}

const DefaultProps = {
    loading: Loading,
    timeout: 20000, // 20 seconds
    delay: 300 // 0.3 seconds
}

/**
 * 异步加载页面
 * @param component
 * @param callback
 * @returns {*}
 */
export function asyncComponent (component, callback) {
    return Loadable({
        ...DefaultProps,
        loader: component,

        render (loaded, props) {
            callback && callback()
            const Component = loaded.default
            props = {...props, ...store.getState().Task}
            return <Component {...props}/>
        }
    })
}

/**
 * 异步加载页面 同时 添加其他文件
 * @param component
 * @param models
 * @returns {*}
 */
export function asyncComponentWithModels (component, models) {
    const name = ‘componentName‘
    return Loadable.Map({
        ...DefaultProps,
        loader: {
            [name]: component,
            ...models
        },
        render (loaded, props) {
            const Component = loaded[name].default
            return <Component {...props}/>
        }
    })
}

这样,开始直接打包,然后发现最新打包时间需要240s左右,而且每一个包都很大少则几百K,多则1,2M,,,继续,然后发现里面很多重复的包比如echarts,moment等等(可以用webpack-bundle-analyzer和speed-measure-webpack-plugin分析你的包,百度很简单)

按照这个思路,有两个方法dll和CommonsChunkPlugin两种

  ⑴dll

  之所以需要这个js文件是为了解决antd的按需加载,不过结果不尽人意

//package.json新增命令
"build:dll": "cross-env NODE_ENV=production node scripts/dll.js",//如果提示cross-env不是一个命令的话  npm i cross-env 就可以了
//根目录script新增dll.js
const webpack = require(‘webpack‘)
const chalk = require(‘chalk‘);
const { generator } = require(‘../config/webpack.dll.config‘)


// const compilerAntd = webpack(reactConfig);

new Promise((resolve, reject) => {
    const reactConfig = generator(‘react‘,[
        ‘react‘, ‘react-dom‘, ‘react-redux‘, ‘redux‘, ‘react-router‘, ‘react-loadable‘,
        ‘echarts‘, ‘moment‘, ‘jquery‘, ‘html2canvas‘, ‘jspdf‘
    ], false)
    const compilerReact = webpack(reactConfig);
    compilerReact.run((err, stats) => {
        if(err){
            console.log(err)
        }
        resolve(stats)
    })
}).then((stats) => {
    const antdConfig = generator(‘antdVendor‘, [
        ‘antd/lib/button‘,
        ‘antd/lib/modal‘,
        ‘antd/lib/page-header‘,
        ‘antd/lib/table‘,
        ‘antd/lib/card‘,
        ‘antd/lib/form‘,
        ‘antd/lib/input‘,
        ‘antd/lib/icon‘,
        ‘antd/lib/message‘,
        ‘antd/lib/select‘,
        ‘antd/lib/descriptions‘,
        ‘antd/lib/input-number‘,
        ‘antd/lib/tag‘,
        ‘antd/lib/date-picker‘,
        ‘antd/lib/inputNumber‘,
        ‘antd/lib/dropdown‘,
        ‘antd/lib/progress‘,
        ‘antd/lib/spin‘,
        ‘antd/lib/checkbox‘,
        ‘antd/lib/col‘,
        ‘antd/lib/row‘,
        ‘antd/lib/config-provider‘,
        ‘antd/lib/layout‘,
        ‘antd/lib/menu‘,
        ‘antd/lib/radio‘,
        ‘antd/lib/switch‘,
        ‘antd/lib/tree‘,
        ‘antd/lib/upload‘,
        ‘antd/lib/typography‘,

    ], true)
    const compilerAntd = webpack(antdConfig);
    compilerAntd.run((err, stats) => {
        if(err){
            console.log(err)
        }else{
            console.log(chalk.green(`success!!!`));
        }
    })
})
//config文件新增webpack.dll.config.js
// webpack.dll.config.js
const Webpack = require(‘webpack‘)
const fs = require(‘fs-extra‘);
const path = require(‘path‘);
const paths = require(‘./paths‘);
const HtmlWebpackPlugin = require(‘html-webpack-plugin‘);
const WebpackBar = require(‘webpackbar‘);
const BundleAnalyzerPlugin = require(‘webpack-bundle-analyzer‘).BundleAnalyzerPlugin;

function generator(name, arr, needDepend){
    !needDepend && fs.emptyDirSync(paths.vendorSrc);
    return {
        devtool: false,
        entry: {
            [name]: arr,
        },
        output: {
            filename: "[name].dll.[hash].js",
            path: paths.vendorSrc,
            libraryTarget: ‘var‘,
            library: ‘_dll_[name]_[hash]‘
        },
        plugins: [
            new WebpackBar(),
            new Webpack.DefinePlugin({
                "process.env": { 
                   NODE_ENV: JSON.stringify("production") 
                 }
            }),
            new Webpack.DllPlugin({
                path: paths.vendorSrc + ‘/[name].manifest.json‘,
                name: ‘_dll_[name]_[hash]‘
            }),
            new HtmlWebpackPlugin({
                filename: paths.appHtml,
                template: needDepend ? paths.appHtml : paths.appHtmlTemplate,
                inject: true,
                minify: {
                    removeComments: true,
                    collapseWhitespace: true,
                    removeRedundantAttributes: true,
                    useShortDoctype: true,
                    removeEmptyAttributes: true,
                    removeStyleLinkTypeAttributes: true,
                    keepClosingSlash: true,
                    minifyJS: true,
                    minifyCSS: true,
                    minifyURLs: true,
                }
            }),
            new Webpack.optimize.UglifyJsPlugin({
                compress: {
                  warnings: false,
                }
            }),
            needDepend && new Webpack.DllReferencePlugin({
                manifest: require(path.join(__dirname, ‘../public/vendor/react.manifest.json‘))
            }),
            new BundleAnalyzerPlugin({
                //  可以是`server`,`static`或`disabled`。
                //  在`server`模式下,分析器将启动HTTP服务器来显示软件包报告。
                //  在“静态”模式下,会生成带有报告的单个HTML文件。
                //  在`disabled`模式下,你可以使用这个插件来将`generateStatsFile`设置为`true`来生成Webpack Stats JSON文件。
                analyzerMode: ‘server‘,
                //  将在“服务器”模式下使用的主机启动HTTP服务器。
                analyzerHost: ‘127.0.0.1‘,
                //  将在“服务器”模式下使用的端口启动HTTP服务器。
                analyzerPort: name == "react" ? 8887 : 8886, 
                //  路径捆绑,将在`static`模式下生成的报告文件。
                //  相对于捆绑输出目录。
                reportFilename: ‘report.html‘,
                //  模块大小默认显示在报告中。
                //  应该是`stat`,`parsed`或者`gzip`中的一个。
                //  有关更多信息,请参见“定义”一节。
                defaultSizes: ‘parsed‘,
                //  在默认浏览器中自动打开报告
                openAnalyzer: true,
                //  如果为true,则Webpack Stats JSON文件将在bundle输出目录中生成
                generateStatsFile: false, 
                //  如果`generateStatsFile`为`true`,将会生成Webpack Stats JSON文件的名字。
                //  相对于捆绑输出目录。
                statsFilename: ‘stats.json‘,
                //  stats.toJson()方法的选项。
                //  例如,您可以使用`source:false`选项排除统计文件中模块的来源。
                //  在这里查看更多选项:https:  //github.com/webpack/webpack/blob/webpack-1/lib/Stats.js#L21
                statsOptions: null,
                logLevel: ‘info‘ //日志级别。可以是‘信息‘,‘警告‘,‘错误‘或‘沉默‘。
              })
        ].filter(Boolean),
    }
}

module.exports = {
    generator
}
//修改paths.js  在最后面新增
vendorSrc: resolveApp(‘public/vendor‘),
appHtmlTemplate: resolveApp(‘public/indexTemplate.html‘),
//在webpack.config.prod.js 修改或者webpack.config.js修改
module.exports = {
   plugins: [
       new webpack.DllReferencePlugin({
            manifest: require(path.join(__dirname, ‘../public/vendor/react.manifest.json‘))
      })
    ] 
}    

这样运行npm run build:dll 应该会成功打出两个js和两个json并且都挂在html里面了(省的自己手动写js文件路径了)

  ⑵CommonsChunkPlugin

//在webpack.config.prod.js 修改或者webpack.config.js修改
module.exports = {
   plugins: [
       new webpack.optimize.CommonsChunkPlugin({
         name: ‘vender‘,
         minChunks: function (module, count) {
           return (
              module.resource &&
              /\.js$/.test(module.resource) &&
              module.resource.indexOf(
                path.join(__dirname, ‘../node_modules‘)
            ) === 0
          )
       }
    }),
    new webpack.optimize.CommonsChunkPlugin({
      name: ‘components‘,
      minChunks: function (module, count) {
        return (
          module.resource &&
          /\.js$/.test(module.resource) &&
          module.resource.indexOf(
            path.join(__dirname, ‘../src‘)
          ) === 0
        )
      }
    }),
    new webpack.optimize.CommonsChunkPlugin({
      name: "manifest",
      chunks: [‘components‘, "vender"]
    })
} 

经过这一些系列的改动(antd的按需加载还是没有完全搞定,还是会有重复打包的地方),现在基本是除了dll包是在1M+,别的都是在500kb一下了,也可以讲究一下了

还有一个优化的地方就是使用多线程

const HappyPack = require(‘happypack‘);
  {
     test: /\.(js|jsx)$/,
     include: paths.appSrc,
     use: [‘happypack/loader?id=babel‘],//在这里修改使用happypack   id是跟后面的值对照
  }
 //plugin
  //开启多线程
    new HappyPack({
      // 用唯一的标识符 id 来代表当前的 HappyPack 是用来处理一类特定的文件
      id: ‘babel‘,
      // 如何处理 .js 文件,用法和 Loader 配置中一样
      // 注意:loaders 是 use 的别名
      use: [ ‘cache-loader‘, {
        loader: ‘babel-loader‘,
        options: {
          plugins: [
            [‘import‘, [{ libraryName: "antd", style: ‘less‘ }]],
          ],
          cacheDirectory: true,
        }
      }],
      threads: 10
      // ... 其它配置项
    })


也能减少一点构建时间

最后构建时间压缩到80s左右,开启gzip情况下单个js文件都在200KB一下,现在的问题基本上都是在antd的按需加载上,未完待续。。。

记一次webpack优化 create-react-app 从单个文件变为多个文件打包将公共依赖,公共代码整理到特定js文件里面

原文:https://www.cnblogs.com/web-chuan/p/13275243.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!