极限特工--webpack与React

极限特工–webpack与React

在使用webpack的时候,我们是可以优化包大小的。下面记录下优化的过程。

拆分js

webpack的配置是All in。All in的意思是把所有的Javascript打包成一个js文件,注意是所有的。也就是说,无论你引入了React、React-Router还是lodash,都要全部打包在一起!那这时候就会产生问题,比如,我只改一个业务逻辑,没有涉及到js包,这样的话webpack也会重新编译,生成的js的hash也就发生变化了,用户在使用的时候还要重新加载js。所以这时候要把那些引入的包以及库拆分。

要点

  • entry
    在配置入口的时候,把药拆分的包写上
    entry: {
    'vendors': [
    'react',
    'react-dom',
    'redux',
    'whatwg-fetch',
    'react-virtualized',
    'localforage',
    'react-motion',
    'core-js',
    'immutable',
    'react-tappable',
    'history',
    'react-redux',
    'react-router',
    'react-router-dom'
    ],
    'app': ['babel-polyfill', `${rootPath}/src/modules/main.jsx`]
    }

这样webpack就会把一些依赖包写入vendors里面

  • output
    output是输出格式定义,如果在生产环境,那么希望产出的文件名为 入口文件名+min+五位hash值。path是产出的路径,publicPath是域名(可配置cdn)

    output: {
    path: path.resolve(`${public_path.root}${public_path.static}`),
    filename: 'js/[name].min.[chunkhash:5].js',
    chunkFilename: "js/[name].min.[chunkhash:5].js",
    publicPath: public_path.domain
    }
  • CommonsChunkPlugin
    这个插件是把公共代码提出来,以减少js的体积,配置很简单,我把公共代码也放到vendors.js里面了。

    plugins: [
    somethingPlugins,
    new CommonsChunkPlugin({
    name: ["vendors", "manifest"],
    minChunks: Infinity
    })
    ]
  • HashedModuleIdsPlugin 与webpack-chunk-hash
    但这样还不能完全解决我们所期望的效果—-打包只更新响应模块的代码
    这时候测试出的结果是webpack每次编译,产生的hash都是不同的。
    在webpack2里面,webpack提供了一个插件HashedModuleIdsPlugin,我们还需要一个WebpackChunkHash,这样就可以解决上述说的问题啦

    import WebpackChunkHash from 'webpack-chunk-hash'
    plugins: [
    somethingPlugins,
    new webpack.HashedModuleIdsPlugin(),
    new WebpackChunkHash()
    ]

manifest缓存

上面说到不修改的js文件在编译的时候不产生新的hash。现在希望,浏览器能将js文件缓存起来,然后在用户访问的时候可以直接读取浏览器的缓存。这时需要用到以下插件
*inline-manifest-webpack-plugin
这个直接写在plugins里面就好了,然后在webpack编译的过程中,会生成一段js代码,要做的把这段代码内联到html里面。

import InlineManifestWebpackPlugin from 'inline-manifest-webpack-plugin'
plugins: [
somethingPlugins,
new CommonsChunkPlugin({
name: ["vendors", "manifest"],
minChunks: Infinity
})
new InlineManifestWebpackPlugin()
]

有一点要注意的是CommonsChunkPlugin这个插件里面写上manifest这是为了把InlineManifestWebpackPlugin插件生成的js代码给拆分出来。
*html-webpack-plugin
利用html-webpack-plugin这个插件,可以生成html,所以可以把js内联进去。

import InlineManifestWebpackPlugin from 'inline-manifest-webpack-plugin'
plugins: [
somethingPlugins,
new HtmlWebpackPlugin({
template: `${rootPath}/src/index.ejs`,
filename: `${public_path.root}${public_path.view}/index.tpl`,
inject: 'body',
flexible: fs.readFileSync(`${rootPath}/src/resources/scripts/flexible.js`, 'utf-8')
})
]

在ejs里面写把js注入进去

<!DOCTYPE html>
<html>
<head>
<title>test</title>
<script type="text/javascript"><%=htmlWebpackPlugin.options.flexible%></script>
<meta content="" name="description"/>
<meta content="" name="keywords"/>
<meta charset="UTF-8">
<meta content="yes" name="apple-mobile-web-app-capable"/>
<meta content="yes" name="apple-touch-fullscreen"/>
<meta content="telephone=no,email=no" name="format-detection"/>
<meta content="fullscreen=yes,preventMove=no" name="ML-Config" />
<link rel="apple-touch-icon-precomposed" href="/common/static/favicon.ico">
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<link href="http://cdn.56hello.com/favicon.ico" rel="Shortcut Icon" type="image/x-icon">
</head>
<body>
<div id="app"></div>
<%=htmlWebpackPlugin.files.webpackManifest%>
</body>
</html>

以上就完成了manifest缓存。

路由拆分代码

在react-router4文档里面有个例子,对于大多数工程来说,这个例子可以胜任了。原理大概是,利用bundle-loader来拆分代码,然后利用Bundle Component来加载模块

import { PureComponent } from 'react'

class Bundle extends PureComponent {
static displayName = 'Bundle'
constructor () {
super()
this.state = {
mod: null
}
}
componentWillMount () {
this.load(this.props)
}
componentWillReceiveProps (nextProps) {
if (nextProps.load !== this.props.load) {
this.load(nextProps)
}
}
load (props) {
this.setState({
mod: null
})
props.load((mod) => {
this.setState({
mod: mod.default ? mod.default : mod
})
})
}
render () {
return this.props.children(this.state.mod)
}
}
const LazyLogin = () => (
<Bundle load={Login}>
{(Login) => Login ? <Login /> : <Loading type="page" color="blue"/>}
</Bundle>
)

const LoginRouter = () => (
<Route
path="/login"
render={ props => !isLogin()
? <LazyLogin /> : <Redirect to={{ pathname: '/m/orderTransfer', state: { from: props.location }}} />
}
/>
)

lodash

这个工具库实在是太大了。在使用它的时候,打包进来是500K+。并且许多js库都是和它有依赖的。比如说redux,在webpack打包的时候无意中就把所有的lodash打包进来。但实际上,只是用到lodash的少部分方法而已。webpack提供了一个去lodash的插件,babel也有一个。效果都不错。我使用的是babel的去lodash插件,只需要在.babelrc添加plugin就行了

“plugins”: [lodash]

Tree shaking

这个玩意是我不懂玩还是怎么的,怎么配都不成功。原理应该是根据babel的编译规则,把不用的export干掉呀。

文章作者: 韦宗圻
文章链接: https://www.weizongqi.com/2017/05/17/极限特工-webpack与React/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 wiki
支付宝打赏
微信打赏