js重新加载div_学习笔记按需加载

51e49b83d12659b3210a46864d85e932.png

功能越来越多,资源越来越大,特别是如果引用了体积较大的第三方库的时候,是时候做一下代码分割了

按需加载是刚需

❀ react-loadable

import React, { useState } from 'react';import Loadable from 'react-loadable'const LoadableComponent = Loadable({  loader: () => import('./comp.js'),  loading: (props) => {  if (props.error || props.timedOut) {    return <button onClick={props.retry}>retrybutton>  }  return <div>loadingdiv>  }})export default function () {  const [show, setShow] = useState(false)  return (    <div>      <button onClick={() => setShow(!show)}>LazyLoadbutton>      {show ? (<LoadableComponent />) : null}    div>  );};

❀ 也可以自己写

这是个很小的库,简单翻译过来是这样子的

import React, { useState } from 'react';const loader = () => import('./comp.js')class LoadableComponent extends React.Component {  constructor(props) {    super(props)    this.state = {      comp: null,      err: null    }  }  componentDidMount() {    this.loadModule()  }  loadModule() {    loader().then((obj) => {      this.setState({ comp: obj && obj.__esModule ? obj.default : obj })    }).catch((err) => {      this.setState({ err })    })  }  render() {    if (this.state.err) {      return <div>errdiv>    } else if (this.state.comp) {      return React.createElement(this.state.comp, this.props)    }    return <div>Loading ...div>  }}export default function () {  const [show, setShow] = useState(false)  return (    <div>      <button onClick={() => setShow(!show)}>LazyLoadbutton>      {show ? (<LoadableComponent />) : null}    div>  );};

❀ Lazy + Suspense

从 React16.6 开始支持

import React, { useState } from 'react';const LazyComponent = React.lazy(() => import('./comp.js'))class ErrorBoundary extends React.Component {  constructor(props) {    super(props)    this.state = { error: null }  }  componentDidCatch(error) {    this.setState({ error })  }  render() {    return this.state.error ? (<div>errordiv>) : this.props.children  }}const SuspenseComponent = function () {  return (    <ErrorBoundary>      <React.Suspense fallback={(<div>loadingdiv>)}>        <LazyComponent />      React.Suspense>    ErrorBoundary>  )}export default function () {  const [show, setShow] = useState(false)  return (    <div>      <button onClick={() => setShow(!show)}>LazyLoadbutton>      {show ? (<SuspenseComponent />) : null}    div>  );};

❀ webpack 代码分割

https://webpack.js.org/guides/code-splitting/

Code Splitting 有多种方法

1. Entry Points

entry: {  index: './src/index.js',  another: './src/another-module.js',}

但是可能会有重复代码模块,所以通常用来做多入口的打包

entry: {  index: { import: './src/index.js', dependOn: 'shared' },  another: { import: './src/another-module.js', dependOn: 'shared' },  shared: 'lodash',}

dependOn 可以指定依赖的模块,以此来避免打包时的代码重复

2. SplitChunksPlugin

把公共模块提取出来,添加到一个已有的 entry chunk 中,或者生成一个新的 chunk

entry: {...},optimization: {  splitChunks: {    chunks: 'all',  },}

3. Dynamic Imports

3-1. 用 require.ensure 动态加载模块,是 webpack 特有的,已被 import() 取代

require.ensure(  dependencies: String[],  callback: function(require),  errorCallback: function(error),  chunkName: String)

dependencies 对应的文件会被拆分到一个单独的 bundle 中

3-2. 用 import() 动态加载模块, webpack 解析的时候会自动进行代码分割

import() 类型是 function(string path): Promise

因为是 Promise 类型,所以老版本浏览器要加上 es6-promise 或者 promise-polyfill

import("./comp.js").then((obj) => {  console.log(obj);})

需要配置 @babel/plugin-syntax-dynamic-import 来支持

.babelrc{  "plugins": [  "@babel/plugin-syntax-dynamic-import",  ...  ],  ...}

- import 'comp.js' 会被打到 mainChunk 里面去

- import('comp.js') 会被分割成单独的 chunk 文件,在需要的时候异步加载进来

- // 还可以指定 chunkName

- import(/* webpackChunkName: "comp" */ 'comp.js')

这个叫‘魔法注释’

0369a2cc37e29f56fcf6fb42ba15a863.png

https://webpack.js.org/api/module-methods/#import-1

const loader = () => import('./comp.js')

首先会由 './comp.js' 生成一个 chunk,上面那行编译之后会变成这样

const loader = () => __webpack_require__.e(/*! import() */ 1)  .then(__webpack_require__.bind(null, /*! ./comp.js */ "./src/comp.js"));

__webpack_require__.e 的定义在 bundle.js 里面,根据 chunkId 异步加载模块,返回一个 promise

__webpack_require__.e = function requireEnsure(chunkId) {   // 返回的是一个 promise    var promises = [];    var installedChunkData = installedChunks[chunkId];  // installedChunks 用来记录加载过的 chunks    if (installedChunkData !== 0) { // 0 表示已经加载过了.        if (installedChunkData) {            promises.push(installedChunkData[2]);        } else {            // setup Promise in chunk cache            var promise = new Promise(function (resolve, reject) {                installedChunkData = installedChunks[chunkId] = [resolve, reject];            });            promises.push(installedChunkData[2] = promise);            // start chunk loading            var script = document.createElement('script');            script.src = jsonpScriptSrc(chunkId); // "static/js/1.chunk.js"            // 超时或报错的回调            var onScriptComplete = function (event) {                // avoid mem leaks in IE.                script.onerror = script.onload = null;                clearTimeout(timeout);                var chunk = installedChunks[chunkId];                if (chunk !== 0) {                    if (chunk) {                        chunk[1](error); // reject(error)                    }                    installedChunks[chunkId] = undefined;                }            };            var timeout = setTimeout(function () {                onScriptComplete({ type: 'timeout', target: script });            }, 120000);            script.onerror = script.onload = onScriptComplete;            document.head.appendChild(script);  // 开始下载        }    }    return Promise.all(promises);};

'./comp.js' 生成的 chunk 是这样的

(this["webpackJsonpmy-app"] = this["webpackJsonpmy-app"] || []).push([ [1], {    "xxx1.js": (function (module, exports, __webpack_require__) {    ...    }),    "xxx2.js": (function (module, exports, __webpack_require__) {    ...    })} ])

事实上所有的 chunk 都是这样的,含义就是一个一个的代码模块,而 bundle 是用来处理加载模块的

bundle 里面重新定义了 this["webpackJsonpmy-app"] 的 push 方法

var jsonpArray = this["webpackJsonpmy-app"] = this["webpackJsonpmy-app"] || [];var oldJsonpFunction = jsonpArray.push.bind(jsonpArray);jsonpArray.push = webpackJsonpCallback;for (var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);var parentJsonpFunction = oldJsonpFunction;

webpackJsonpCallback 可以看做 chunk 加载完毕之后的回调,用来执行 requireEnsure 的时候注册的 resolve 方法,然后把模块内容注册到 modules 中以方便其他文件调用

function webpackJsonpCallback(data) {    var chunkIds = data[0];    var moreModules = data[1];    var executeModules = data[2];    // add "moreModules" to the modules object,    // then flag all "chunkIds" as loaded and fire callback    var resolves = [];    for (var i = 0; i < chunkIds.length; i++) {        var chunkId = chunkIds[i];        if (Object.prototype.hasOwnProperty.call(installedChunks, chunkId) && installedChunks[chunkId]) {            resolves.push(installedChunks[chunkId][0]);     // promise.resolve        }        installedChunks[chunkId] = 0;   // 表示已加载完成,下次在 requireEnsure 就不会再重复获取了    }    for (var moduleId in moreModules) {        if (Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {            modules[moduleId] = moreModules[moduleId];        }    }    if (parentJsonpFunction) parentJsonpFunction(data);    while (resolves.length) {        resolves.shift()();     // 执行所有的 resolve    }    // add entry modules from loaded chunk to deferred list    deferredModules.push.apply(deferredModules, executeModules || []);    // run deferred modules when all chunks ready    return checkDeferredModules();};

试一试byMe

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值