# 打包优化

* **js 压缩优化 ，使用多线程的 webpack-parallel-uglify-plugin ，具体的参数配置可以看**[**https://github.com/mishoo/UglifyJS2/tree/harmony#compress-options**](https://github.com/mishoo/UglifyJS2/tree/harmony#compress-options)

```javascript
new parallelUglifyPlugin({
    cacheDir: '.cache/',
    uglifyJS: {
        output: {
            comments: false, //false :关闭注释 

        },
        compress: {
            warnings: false,
            drop_console: true,
            collapse_vars: true,
            reduce_vars: true,
        },
    },
    sourceMap:true,// 开启sourcemap
})
```

注意：有的时候， 这个插件第一次运行的时候会报错，报不是空目录，可能是与删除dist目录的插件**clean-webpack-plugin** 有关，我现在也搞不清楚了，之前在配置的时候，总是报错，把 **webpack-parallel-uglify-plugin** 注释掉后就可以用了，今天我再运行**webpack-parallel-uglify-plugin** 时还是报错 ，我先关闭了**clean-webpack-plugin ，**&#x5728;运行就可以了。接着又打开**clean-webpack-plugin ，**&#x4E0D;报错了。搞不懂！！！

现在搞清楚了,第一次运行**webpack-parallel-uglify-plugin** 时不能让.cache(压缩缓存)有内容，必须是空的，或是没有这个目录

* **第三方库的抽离，在webpack4.x之前是使用的 webpack.optimize.CommonsChunkPlugin ,来抽取公共的代码和分离第三方的代码，具体可以看**[**https://blog.csdn.net/github\_26672553/article/details/52280655**](https://blog.csdn.net/github_26672553/article/details/52280655)**，不过webapck4.x，已经弃用这个插件了，改为 optimization.splitChunks 和 optimization.splitChunks ，其中 optimization.splitChunks 选项里webpack会添加一个只包含运行时(runtime)额外代码块到每一个入口。（这个需要看场景使用，会导致每个入口都加载多一份运行时代码）**[**https://segmentfault.com/a/1190000013476837**](https://segmentfault.com/a/1190000013476837)

```javascript
    //  抽离第三方的代码，也就是生产依赖里的代码,现在是单文件的入口：
           entry: {
                main: PATHS.app,
                //vender:["react","react-dom","antd"],//引用的第三方库
            },
            mode:"production",
            output: {
                path: PATHS.build,
                filename: filename,
                publicPath:PATHS.publicPath,
            },
            optimization: {
                runtimeChunk: {//包清单
                    name: "manifest"
                },
                splitChunks: { //拆分公共包
                    cacheGroups: {
                        vendor: {//第三方组件
                            test: /node_modules/,
                            chunks: "initial",
                            name: "vendor",
                            priority: 10,
                            enforce: true
                        }
                    }
                }

            },
```

* **有时需要多文件的入口，这对个文件里就肯定有相同额部分。比如 common.js 是个工具插件，a.js引用了common.js， b.js也引用了common.js。 当我们把a.js和b.js分别作为入口文件的时候，他们两个都加载了common.js，所以可以提供一个公共的js让这两个文件引用，这样减少代码的重复。**

```javascript
// 抽离第三方的代码，也就是生产依赖里的代码,现在是单文件的入口：
entry: {
    main: PATHS.main,
    home: PATHS.home,
    //vender:["react","react-dom","antd"], //引用的第三方库
},
mode:"production",
output: {
    path: PATHS.build,
    filename: filename,
    publicPath:PATHS.publicPath,
},
optimization: {
    runtimeChunk: {//包清单
        name: "manifest"
    },
    splitChunks: { //拆分公共包
        cacheGroups: {
            common: {//项目公共组件，多个入口文件可以用到
                chunks: "initial",//指定提取范围[],可以是所有"all"
                name: "common",//提取公共代码的js文件名
                minChunks: 2,//指定重复代码几次之后就提取出来 
                maxInitialRequests: 5,
                minSize: 0
            },
            vendor: {//第三方组件
                test: /node_modules/,
                chunks: "initial",
                name: "vendor",
                priority: 10,
                enforce: true
            }
        }
    }

},
```

* **静态资源拷贝插件 copy-webpack-plugin**&#x20;

```javascript
new copyWebpackPlugin([{
                from:__dirname+'/src/assert',//打包的静态资源目录地址
                to:'./assert' //打包到dist下面的public
}]),//拷贝静态资源到指定的位置，也就是不用编译这个目录下的文件
```

* **按需加载  在webpack 的文件输出配置output中加上按需加载生成js 文件的配置 &#x20;**_**chunkname ,关于filename 与 chunkname 的理解可以看**_ [_**https://www.jianshu.com/p/d9ebab57bca1**_](https://www.jianshu.com/p/d9ebab57bca1)**。按需加载是需要搭配js文件里的代码使用的，光设置webpack是什么效果都没有的。拿react的路由做例子，在每一个路由点击的时候加载对应的组件。**

```javascript
output: {
            path: PATHS.build,
            filename: "js/[name].min.js", // name 对应entry里的入口配置名
            publicPath:"./", 
            chunkFilename: 'js/[name].[chunkhash:5].chunk.js', // 生成对应额chunkhash加在文件名中 ，
            //name 对应 用 bundle-loader加载时设置额名字,看下面额例子
            //  import Apiopt from "bundle-laoder?lazy&name=apiopt!../containner/apiopt/apiopt.js"
},
```

**react** 配置 ： 在**react-router4**后不再通过 **getComponent**来按需加载路由组件，通过**bundle-loader**更加简化了按需加载。安装 **bundle-loader --save-dev 参考文件**[**https://www.vanadis.cn/2017/08/05/react-router-webpack-load-on-demand/**](https://www.vanadis.cn/2017/08/05/react-router-webpack-load-on-demand/)

```javascript
import React ,{Component} from  "react";
import { BrowserRouter ,HashRouter , Route ,Switch } from "react-router-dom";

import  {Home} from "../component/home.js";
import  ApiOpt from "bundle-loader?lazy&name=ApiOpt!../containner/apiopt/apiopt.js";
import  Application from "bundle-loader?lazy&name=Application!../containner/application/application.js";

class Bundle extends Componet {
    state={
        mod:null,
    }

    componentWillMound(){
        this.load(this.props);
    }

    componentWiillReciveProps(next,props){
         if (nextProps.load !== this.props.load) {
            this.load(nextProps)
        }
    }

    load(){
         this.setState({
            mod: null
        });

        // 这里的 props.load 是用  babel-loader 引入的异步加载函数，不是组件，是下面这样函数，参数是个回调函数
        //仔细看这个回调函数，发现它的参数才是真正的组件。使用 require()引入。
        // 所以这里又涉及到，我们在导出 模块时候是 怎么的方式。

         function(cb) {
            Promise.all(
                /*! require.ensure | Application */
                [__webpack_require__.e("vendors~ApiOpt~Application"),
                 __webpack_require__.e("vendors~Application"),
                 __webpack_require__.e("Application")]).then((function(require) {
                    cb(__webpack_require__(
                    /*! !../../../node_modules/_babel-loader@7.1.4@babel-loader/lib!./application.js */
                     "./node_modules/_babel-loader@7.1.4@babel-loader/lib/index.js!./src/containner/application/application.js"));
            }).bind(null, __webpack_require__)).catch(__webpack_require__.oe);
        }


        props.load((mod) => {
            this.setState({
                // handle both es imports and cjs
                mod: mod.default ? mod.default : mod 
                // 刚开始我不理解这个mod 是啥，现在很清楚了，是个用require 引入的模块
                // .default 是我们用的  export default 导出的。 所以 我们用require 时。 module 对象里的 key 值 default
                // 要是 我们用 export { } ，这里肯定就不能用default ,因为 module里的key 值就是我们当时定义的模块名
                // 然而我们最好使用 export default 导出  ，不然 不然在这个方法里不知道定义的模块名是什么。
            })
        })
    }

    render(){

        return this.props.children(this.state.mod); // 返回的是真正的组件

    }
}

const Loading=()=>{
    return (<div>Loading......</div>);
}


const createComponent = (component)=>{

        return (
            <Bundle load={component}>
                 (Component) => Component ? <Component {...props} /> : <Loading/>
            </Bundle>

        );
}

const Routes=(

    <Route path="./application"  component={createComponent(Appliction)} ></Route>
    <Route path="./apiopt" component={createComponent(ApiOpt)} ></Route>


)
```
