打包优化

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 ,在运行就可以了。接着又打开clean-webpack-plugin ,不报错了。搞不懂!!!

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

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

    //  抽离第三方的代码,也就是生产依赖里的代码,现在是单文件的入口:
           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让这两个文件引用,这样减少代码的重复。

// 抽离第三方的代码,也就是生产依赖里的代码,现在是单文件的入口:
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

new copyWebpackPlugin([{
                from:__dirname+'/src/assert',//打包的静态资源目录地址
                to:'./assert' //打包到dist下面的public
}]),//拷贝静态资源到指定的位置,也就是不用编译这个目录下的文件
  • 按需加载 在webpack 的文件输出配置output中加上按需加载生成js 文件的配置 chunkname ,关于filename 与 chunkname 的理解可以看 https://www.jianshu.com/p/d9ebab57bca1。按需加载是需要搭配js文件里的代码使用的,光设置webpack是什么效果都没有的。拿react的路由做例子,在每一个路由点击的时候加载对应的组件。

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/

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>


)

Last updated