webpack学习笔记--按需加载

摘要:
将每个类合并到一个Chunk中,并根据需要加载相应的Chunk。使用Webpack按需加载Webpack具有强大的内置代码拆分功能,可按需加载,实现起来非常简单/*WebpackChunkName:“show”*/表示为动态生成的Chunk指定一个名称,以便我们可以跟踪和调试代码。为了正确输出/*webpackChunkName:“show”*/中配置的ChunkName,还需要按如下方式配置Webpack:模块。exports={//JS执行条目文件条目:{main:‘./main.JS‘,},output:{//为从条目文件文件名‘[name].JS‘生成的Chunk配置配置配置配置输出文件的名称,//为动态加载的Chunk配置文件chunkFilename:‘[name]JS‘,}}配置输出文件名;最关键的一行是chunkFilename:'[name]。js',它指定输出时动态生成的Chunk的文件名。
为什么需要按需加载

随着互联网的发展,一个网页需要承载的功能越来越多。 对于采用单页应用作为前端架构的网站来说,会面临着一个网页需要加载的代码量很大的问题,因为许多功能都集中的做到了一个 HTML 里。 这会导致网页加载缓慢、交互卡顿,用户体验将非常糟糕。

导致这个问题的根本原因在于一次性的加载所有功能对应的代码,但其实用户每一阶段只可能使用其中一部分功能。 所以解决以上问题的方法就是用户当前需要用什么功能就只加载这个功能对应的代码,也就是所谓的按需加载。

如何使用按需加载

在给单页应用做按需加载优化时,一般采用以下原则:

  • 把整个网站划分成一个个小功能,再按照每个功能的相关程度把它们分成几类。
  • 把每一类合并为一个 Chunk,按需加载对应的 Chunk。
  • 对于用户首次打开你的网站时需要看到的画面所对应的功能,不要对它们做按需加载,而是放到执行入口所在的 Chunk 中,以降低用户能感知的网页加载时间。
  • 对于个别依赖大量代码的功能点,例如依赖 Chart.js 去画图表、依赖 flv.js 去播放视频的功能点,可再对其进行按需加载。

被分割出去的代码的加载需要一定的时机去触发,也就是当用户操作到了或者即将操作到对应的功能时再去加载对应的代码。 被分割出去的代码的加载时机需要开发者自己去根据网页的需求去衡量和确定。

由于被分割出去进行按需加载的代码在加载的过程中也需要耗时,你可以预言用户接下来可能会进行的操作,并提前加载好对应的代码,从而让用户感知不到网络加载时间。

用 Webpack 实现按需加载

Webpack 内置了强大的分割代码的功能去实现按需加载,实现起来非常简单。

举个例子,现在需要做这样一个进行了按需加载优化的网页:

  • 网页首次加载时只加载  main.js  文件,网页会展示一个按钮, main.js  文件中只包含监听按钮事件和加载按需加载的代码。
  • 当按钮被点击时才去加载被分割出去的  show.js  文件,加载成功后再执行  show.js  里的函数。

其中  main.js  文件内容如下:

window.document.getElementById('btn').addEventListener('click', function () {
  // 当按钮被点击后才去加载 show.js 文件,文件加载成功后执行文件导出的函数
  import(/* webpackChunkName: "show" */ './show').then((show) => {
    show('Webpack');
  })
});
show.js 文件内容如下:
module.exports = function (content) {
  window.alert('Hello ' + content);
};

代码中最关键的一句是  import(/* webpackChunkName: "show" */ './show') ,Webpack 内置了对  import(*)  语句的支持,当 Webpack 遇到了类似的语句时会这样处理:

  • 以  ./show.js  为入口新生成一个 Chunk;
  • 当代码执行到  import  所在语句时才会去加载由 Chunk 对应生成的文件。
  • import 返回一个 Promise,当文件加载成功时可以在 Promise 的  then  方法中获取到  show.js  导出的内容。
  • 在使用 import() 分割代码后,你的浏览器并且要支持 Promise API 才能让代码正常运行, 因为 import() 返回一个 Promise,它依赖 Promise。对于不原生支持 Promise 的浏览器,你可以注入 Promise polyfill。

 /* webpackChunkName: "show" */ 的含义是为动态生成的 Chunk 赋予一个名称,以方便我们追踪和调试代码。 如果不指定动态生成的 Chunk 的名称,默认名称将会是  [id].js。 /* webpackChunkName: "show" */  是在 Webpack3 中引入的新特性,在 Webpack3 之前是无法为动态生成的 Chunk 赋予名称的。

为了正确的输出在  /* webpackChunkName: "show" */ 中配置的 ChunkName,还需要配置下 Webpack,配置如下:

module.exports = {
  // JS 执行入口文件
  entry: {
    main: './main.js',
  },
  output: {
    // 为从 entry 中配置生成的 Chunk 配置输出文件的名称
    filename: '[name].js',
    // 为动态加载的 Chunk 配置输出文件的名称
    chunkFilename: '[name].js',
  }
};

其中最关键的一行是  chunkFilename: '[name].js ',,它专门指定动态生成的 Chunk 在输出时的文件名称。 如果没有这行,分割出的代码的文件名称将会是  [id].js 。 chunkFilename 具体含义见2-2 配置-Output。本实例提供项目完整代码

按需加载与 ReactRouter

在实战中,不可能会有上面那么简单的场景,接下来举一个实战中的例子:对采用了 ReactRouter 的应用进行按需加载优化。 这个例子由一个单页应用构成,这个单页应用由两个子页面构成,通过 ReactRouter 在两个子页面之间切换和管理路由。

这个单页应用的入口文件  main.js  如下:

import React, {PureComponent, createElement} from 'react';
import {render} from 'react-dom';
import {HashRouter, Route, Link} from 'react-router-dom';
import PageHome from './pages/home';

/**
 * 异步加载组件
 * @param load 组件加载函数,load 函数会返回一个 Promise,在文件加载完成时 resolve
 * @returns {AsyncComponent} 返回一个高阶组件用于封装需要异步加载的组件
 */
function getAsyncComponent(load) {
  return class AsyncComponent extends PureComponent {

    componentDidMount() {
      // 在高阶组件 DidMount 时才去执行网络加载步骤
      load().then(({default: component}) => {
        // 代码加载成功,获取到了代码导出的值,调用 setState 通知高阶组件重新渲染子组件
        this.setState({
          component,
        })
      });
    }

    render() {
      const {component} = this.state || {};
      // component 是 React.Component 类型,需要通过 React.createElement 生产一个组件实例
      return component ? createElement(component) : null;
    }
  }
}

// 根组件
function App() {
  return (
    <HashRouter>
      <div>
        <nav>
          <Link to='/'>Home</Link> | <Link to='/about'>About</Link> | <Link to='/login'>Login</Link>
        </nav>
        <hr/>
        <Route exact path='/' component={PageHome}/>
        <Route path='/about' component={getAsyncComponent(
          // 异步加载函数,异步地加载 PageAbout 组件
          () => import(/* webpackChunkName: 'page-about' */'./pages/about')
        )}
        />
        <Route path='/login' component={getAsyncComponent(
          // 异步加载函数,异步地加载 PageAbout 组件
          () => import(/* webpackChunkName: 'page-login' */'./pages/login')
        )}
        />
      </div>
    </HashRouter>
  )
}

// 渲染根组件
render(<App/>, window.document.getElementById('app'));

以上代码中最关键的部分是  getAsyncComponent  函数,它的作用是配合 ReactRouter 去按需加载组件,具体含义请看代码中的注释。

由于以上源码需要通过 Babel 去转换后才能在浏览器中正常运行,需要在 Webpack 中配置好对应的 babel-loader,源码先交给 babel-loader 处理后再交给 Webpack 去处理其中的 import(*) 语句。 但这样做后你很快会发现一个问题:Babel 报出错误说不认识 import(*) 语法。 导致这个问题的原因是 import(*) 语法还没有被加入到在 3-1使用ES6语言中提到的 ECMAScript 标准中去, 为此我们需要安装一个 Babel 插件  babel-plugin-syntax-dynamic-import ,并且将其加入到 babelrc  中去:

{
  "presets": [
    "env",
    "react"
  ],
  "plugins": [
    "syntax-dynamic-import"
  ]
}

执行 Webpack 构建后,你会发现输出了三个文件:

main.js:执行入口所在的代码块,同时还包括 PageHome 所需的代码,因为用户首次打开网页时就需要看到 PageHome 的内容,所以不对其进行按需加载,以降低用户能感知到的加载时间;
page-about.js:当用户访问 /about 时才会加载的代码块;
page-login.js:当用户访问 /login 时才会加载的代码块。

同时你还会发现  page-about.js  和  page-login.js  这两个文件在首页是不会加载的,而是会当你切换到了对应的子页面后文件才会开始加载。本实例提供项目完整代码

免责声明:文章转载自《webpack学习笔记--按需加载》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇pandas中的空值处理YY 数据库平台化建设实践下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

webpack踩坑之路 (2)——图片的路径与打包

刚开始用webpack的同学很容易掉进图片打包这个坑里,比如打包出来的图片地址不对或者有的图片并不能打包进我们的目标文件夹里(bundle)。下面我们就来分析下在webpack项目中图片的应用场景。 在实际生产中有以下几种图片的引用方式: 1. HTML文件中img标签的src属性引用或者内嵌样式引用 <img src="photo.jpg" /&g...

(转)react 项目构建

原文:https://segmentfault.com/a/1190000016342792 写在前面 每次构建react项目的时候都会配置一大堆东西,时间久了就会忘记怎么配置。为了方便自己记忆也为了其他开发者在构建react应用时能够快速开发,故作此记录。 本项目基于 create-react-app 脚手架进行配置。主要配置了一些项目开发中常用的方法,...

前端框架Vue自学之webpack(四)

终极目标:掌握和使用Vue(全家桶:Core+Vue-router+Vuex) 本博客目的:记录Vue学习的进度和心得(webpack) 内容:学习和使用webpack。为学习Vue CLI做准备。 正文: webpack 一、webpack 1、认识webpack 本质上来说,webpack是一个现代的JavaScript应用的静态模块打包工具。模块和打...

react 实现圆环进度条

import React, { useState, useEffect } from "react" import { css } from "emotion" //num是从父级传来的百分比数值 export default function({ num }) { let rightTrnas = css` transform: rotate(0deg)...

聊聊 webpack 打包如何压缩包文件大小

想必很多人都经历过做完一个项目后,再打包发现某些文件非常大,导致页面加载时很慢,这就很影响用户体验了,所以在我经历了一些打包后,讲讲如何有效地缩小包体积,加快页面的首屏渲染 动态 polyfill 相信很多项目都会用到polyfill 那么一整个polyfill 会占据很多的空间,这个时候需要使用动态polyfill来解决这个问题了: 在 index.ht...

React == 实现简易购物车

React == 实现简易版购物车 1、几个要点: 为了方便后面使用input type = "checkbox" 实现复选框的选中/不选中,给传递过来的属性要在遍历的时候,单独加上一个新属性 checked count 属性 默认值 都是1. state = { all : false, sumprice :0, one...