zoukankan      html  css  js  c++  java
  • [转] react-router4 + webpack Code Splitting

    项目升级为react-router4后,就尝试着根据官方文档进行代码分割。https://reacttraining.com/react-router/web/guides/code-splitting

    在实际项目中,js,css文件默认通过webpack打包的话会很大,动不动就好几兆。

    在实际场景中,我们需要缩短首屏的时间展现时间,需要将 首屏没有 涉及到 其他页面的 业务和组件 进行代码分离,按需加载。

    通过按需加载,如果只是修改了某个页面的逻辑,也不用整个项目文件加载,增加了浏览器缓存的利用

    下面就一步一步的介绍在我的项目中是怎么实现Code Splitting的。

    根据webpack文档 https://webpack.js.org/guides/code-splitting/ 推荐的代码分割的方式是  import(), 当然 require.ensure() 作为兼容,还是支持的。

    而react-router4 文档上说到的 bundle-loader 组件就是通过 require.ensure 实现的。

    实际项目中

    a.太小的文件打个包也没有太大的意义,最终考虑的是 每个一级菜单 作为分割点进行打包加载.

    b.能够通过参数配置 分割打包 还是 整体打包,尽可能的在webconfig中进行配置,可以参数化

    c.打包文件名字要有意义,每次打包只是某个module文件修改的话,不会影响其他js文件hash值,提高缓存利用

    下面介绍下最终的结果,其中有些坎坷的心路历程,只能简单的略过了

    1./router/moduleA.router.jsx,把需要打包在一起的文件整理到对用的文件下,方便统一管理

    2.webpack.config.js

    复制代码
    output: {
            //[chunkhash:8] 设置文件摘要,用于缓存
            filename: '[name].[chunkhash:8].bundle.js', //entry 对用生成文件的文件名规则
            chunkFilename : '[name].[chunkhash:8].js',  //不是entry定义的文件(分离打包的模块文件,提取的共同文件),对用生成文件的文件名规则
            path: TARGET,
            publicPath: '/public/'
        },
        plugins: [
            //...
            //...
            //** 设置打包id生成规则,以文件地址 + hash 形式,是生成的  webpack 模块id固定,参见参考文献
            new webpack.HashedModuleIdsPlugin(),
            //提取的共同文件插件配置
            new webpack.optimize.CommonsChunkPlugin({
                //在模块中如果存在的功用的话,也进行提取设置
                //如moduleA,moduleB 中都用了编辑器,entry中没有,则会抽出公用打包在一个  数字.hash.js 中
                async: true, 
                minChunks: 2 //有2处使用的js文件就提取
            }), 
            //vendor: entry文件中用到的共用文件打包在vendor文件
            //** manifest: 增加这个配置,则把一个加载的id信息统一到一个文件,这样就可以实现每次打包未改的文件生成的hash不变,参见参考文献
            new webpack.optimize.CommonsChunkPlugin({
                names: ['vendor', 'manifest']
            }),
            //对应的 chunks 加上 'manifest', 'vendor'
            new HtmlWebpackPlugin({
                filename: `${page.name}.html`,
                template: `${ROOT_PATH}/template.ejs`,
                chunks: ['manifest', 'vendor', page.name]
            }
        ],
        module: {
            rules: [{
                    test: /.router.jsx/,
                    loader: [
                        //根据文件后缀.router.jsx 设置规则,主要是name 和 regExp 的实现,这个可以查看bundle-loader源代码就能发现相关的支持
                        //现在的逻辑是取文件名,/router/moduleA.router.jsx 则打包成 moduleA.hash.js
                        'bundle-loader?lazy&name=[1]&regExp=([^\\\/]*)\.router\.jsx',
                        'babel-loader',
                    ],
                    exclude: /node_modules|assets/
                }, {
                    test: /.jsx?$/,
                    loader: 'babel-loader',
                    exclude: /node_modules|assets/
                }
            ]
        },
    复制代码
    bundle-loader name参数,regExp参数的应用是查看的源代码,一开始想看看是通过什么实现异步加载,就看见了相关插件的源代码
    require.ensure(),想要打包的文件打包在一起,只需要下面代码中的 chunkNameParam 设置成同一个值就可以了,
    实际情况考虑到更好的管理文件,就通过取.router.jsx前面的文件名 进行命名

      

     3.Bundle.jsx

    复制代码
     1 import React, { Component } from 'react'
     2 import { Route } from 'react-router-dom';
     3 const Loading = function () {
     4     return <div></div>
     5 }
     6 //注意props的传递,在组件与Switch,嵌套的时候会有涉及
     7 //增加lazyKey属性,对应moduleA.router.jsx对应的key值
     8 class Bundle extends Component {
     9     state = {
    10         mod: null
    11     }
    12     componentWillMount() {
    13         this.load(this.props)
    14     }
    15     componentWillReceiveProps(nextProps) {
    16         if (nextProps.load !== this.props.load) {
    17             this.load(nextProps)
    18         }
    19     }
    20     load(props) {
    21         var key = props.lazyKey || 'default';
    22         this.setState({
    23             mod: null
    24         })
    25         props.load(mod => {
    26             this.setState({
    27                 mod: mod[key] ? mod[key] : mod
    28             })
    29         })
    30     }
    31     render() {
    32         return this.state.mod?<this.state.mod {...this.props} />:<Loading/>
    33     }
    34 }
    35 //注意props的传递,在组件与Switch,嵌套的时候会有涉及
    36 //通过isLazy进行是否分离打包配置,LazyRoute组件不是必要的,各自需求各自处理
    37 class LazyRoute extends React.PureComponent{
    38     componentMap = {};
    39     render() {
    40         let {menu, ...props} = this.props;
    41         let {component, isLazy, componentKey, path} = menu;
    42         componentKey = componentKey || 'default';
    43         if (!isLazy) {
    44             return (<Route component={component[componentKey] || component} {...props}/>);
    45         } else {
    46             //通过this.componentMap进行缓存,防止不必要的组件重新加载
    47             if (!this.componentMap[path]) {
    48                 this.componentMap[path] = function(props)  {
    49                     return (<Bundle load={component} lazyKey={componentKey} {...props}></Bundle>)
    50                 }
    51             }
    52             return (<Route component={this.componentMap[path]} {...props}/>);
    53         } 
    54     }
    55 }
    56 export {Bundle as default, LazyRoute}
    复制代码

    实际的打包效果,还是可以的,代码得到了分离。有些地方还可以优化,比如 第3方lib包不怎么会变的,和 自己写的组件 进行不同的提取合并。文献中都有提及。

    极致的按需加载 和 异步加载,对代码 组件了解的要求比较高,现在主要还是通过webpack 公共提取 做基本的优化

    参考文件

    http://geek.csdn.net/news/detail/135599    使用 Webpack 打包单页应用的正确姿势

    https://sebastianblade.com/using-webpack-to-achieve-long-term-cache/   用 webpack 实现持久化缓存

  • 相关阅读:
    oracle中Blob和Clob类型的区别
    为什么要分库分表
    Enable file editing in Visual Studio's debug mode
    SQL Server Dead Lock Log
    Debug .NET Framework Source
    SQL Server text field里面有换行符的时候copy到excel数据会散乱
    诊断和修复Web测试记录器(Web Test Recorder)问题
    Can't load Microsoft.ReportViewer.ProcessingObjectModel.dll
    'telnet' is not recognized as an internal or external command
    Linq to XML
  • 原文地址:https://www.cnblogs.com/chris-oil/p/8457528.html
Copyright © 2011-2022 走看看