Webpack按需加载秒开应用

做者 DBCdouble

1、前言

本文将基于上一篇文章《Webpack4+Babel7优化70%速度》所搭建的环境去作动态路由加载,同时完成 React16 和 React-Router4 的升级工做,使整个项目的技术栈以及性能体验尽量达到最佳状态。javascript

2、背景

咱们知道,Webpack主要从两个方面进行优化,一个是提高构建速度,另外一个则是减少文件体积,而在上一篇文章《Webpack4+Babel7优化70%速度》中,咱们已经完成了提高构建速度的部分,这一章咱们将经过实现动态加载路由的方式来将最终生成的打包文件拆分成多个子文件来减少bundle.js的体积,这样就能极大的减少首屏加载过慢的痛点,至此以后,也就不再用担忧随着应用愈来愈大,bundle.js的体积愈来愈大致使首屏加载的速度愈来愈慢的问题了。
css

3、升级模块

这里一个个模块安装升级是为了更准确地来把控更新以后有可能引发的报错html

一、React16

npm install react@16.8.4 --save复制代码

这里安装目前react的最新版本v16.8.4,安装完成以后开启项目,发现页面报错,如图:java



出现上面的报错的缘由是React v15.5及以上版本已经将PropTypes模块剔除,而后执行 react

npm install prop-types --save 
将代码中
 import { PropTypes } from 'react' 
代码修改成
import PropTypes from 'prop-types'
注意:除了入口文件下的代码须要替换PropTypes,你项目中使用到的第三方库内部也有可能使用到了 
import { PropTypes } from 'react'
,遇到这种状况,须要将该第三方库升级到最新版本,包括react-router以前的老版本就是依赖于react库中的PropTypes做数据类型判断,因此接下来升级react-router

二、react-router-dom(这里使用react-router-dom,它基于react-router,加入了在浏览器运行环境下的一些功能)

npm uninstall react-router && npm install react-router-dom --save复制代码

react-router-dom依赖react-router,因此咱们使用npm安装依赖的时候,只须要安装相应环境下的库便可,不用再显式安装react-router
webpack

  • 将代码中的
     import { Link } from 'react-router' 
    修改成
     import { Link } from 'react-router-dom'
  • 由于react-router4.x版本较以前版本改动较大,基本须要重写项目中的路由层

4、路由配置

一、入口文件git

// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';import App from './app';

ReactDOM.render(
  <BrowserRouter> <App /> </BrowserRouter>,
  document.getElementById('app')
);复制代码

BrowserRouter是一个高阶组件,内置history的api来保持 UI 和 URL 的同步github

二、路由配置web

// routes.js
import Home from './home';
import About from './about';
import Help from './help';

export default [{
  path: '/',
  exact: true,
  component: Home
}, {
  path: '/about',
  component: About
}, {
  path: '/help',
  component: Help
}];复制代码

// app.js
import React from 'react';
import { Switch, Route } from 'react-router';
import routes from './routes';

class App extends React.Component {
  render() {
    return (
      <Switch> {routes.map((route, i) => <Route key={i} exact={!!route.exact} path={route.path} component={route.component} />)} </Switch> ); } } export default App;复制代码

Switch用于渲染与路径匹配的第一个子 <Route><Redirect>npm

5、异步动态加载路由和Code Splitting

异步动态加载路由从狭义上理解就是页面上没有出现的页面不加载对应的js和css,只加载当前页面展现出来页面的js和css,经过动态导入(dynamic imports)文件的方式实现代码拆分

一、封装一个高阶函数来异步加载组件

//async_load.js
import React, { Component } from 'react'
export default (loadComponent, placeholder = '拼命加载中...') => {
  return class AsyncComponent extends Component {
    unmount = false
    constructor () {
      super()
      this.state = {
        Child: null
      }
    }
    componentWillUnmount () {
      this.unmount = true
    }
    async componentDidMount () {
      const { default: Child } = await loadComponent()
      if (this.unmount) return
      this.setState({
        Child
      })
    }
    render () {
      const { Child } = this.state
      return (
        Child ? <Child {...this.props} /> : placeholder ) } } }复制代码

二、修改路由配置文件

// routes.js
import React from 'react';
import Load from './async_load';

export default [{
  path: '/',
  exact: true,
  component(props) {
    // 这里的 component 函数也是一个高阶组件
    return <Load {...props} load={() => import('./home')} />;
  }
}, {
  path: '/about',
  component(props) {
    return <Load {...props} load={() => import('./about')} />;
  }
}, {
  path: '/help',
  component(props) {
    return <Load {...props} load={() => import('./help')} />;
  }
}];复制代码

当涉及到动态代码拆分时,webpack 提供了两个相似的技术。对于动态导入,第一种,也是优先选择的方式是,使用符合 ECMAScript 提案import() 语法。第二种,则是使用 webpack 特定的 require.ensure

完成以上配置以后开启打包,出现一下错误


由于import语法还处于提案阶段,因此须要经过安装Babel的插件@babel/plugin-syntax-dynamic-import才能使用,安装完成以后须要在配置babel-loader的options:

{
    plugins: ['@babel/plugin-syntax-dynamic-import']
}复制代码

完成以上的步骤以后,想必已经没问题了吧,因而启动项目,又报错了:


在这个报错上我停留了过久,因而把问题抛给了一个朋友(大佬),在webpack在github上的Issue找到了答案,原来是webpack的4.29.x版本有bug



因而我回退了版本webpack@4.28.2,最终启动成功,能够看到bundle.js被拆分红多个子js文件


在浏览器打开开发者工具点击network查看请求的js文件,能够看到每进入一个新的页面,会动态加载一个新的js

进入首屏路由页面


进入另一个路由页面



6、总结

不得不说升级老项目过程很痛苦,坑也不少。可是把坑一个个填完,最终完美升级也是一件颇有意思,颇有成就感的事。但愿这篇文章能对你有所帮助。


文章有任何不清楚或者不许确的地方,麻烦在评论区指出

相关文章
相关标签/搜索