亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

在react-router4中進行代碼拆分的方法(基于webpack)

發布時間:2020-10-17 11:38:01 來源:腳本之家 閱讀:183 作者:AlienZHOU 欄目:web開發

前言

隨著前端項目的不斷擴大,一個原本簡單的網頁應用所引用的js文件可能變得越來越龐大。尤其在近期流行的單頁面應用中,越來越依賴一些打包工具(例如webpack),通過這些打包工具將需要處理、相互依賴的模塊直接打包成一個單獨的bundle文件,在頁面第一次載入時,就會將所有的js全部載入。但是,往往有許多的場景,我們并不需要在一次性將單頁應用的全部依賴都載下來。例如:我們現在有一個帶有權限的"訂單后臺管理"單頁應用,普通管理員只能進入"訂單管理"部分,而超級用戶則可以進行"系統管理";或者,我們有一個龐大的單頁應用,用戶在第一次打開頁面時,需要等待較長時間加載無關資源。這些時候,我們就可以考慮進行一定的代碼拆分(code splitting)。

實現方式

簡單的按需加載

代碼拆分的核心目的,就是實現資源的按需加載。考慮這么一個場景,在我們的網站中,右下角有一個類似聊天框的組件,當我們點擊圓形按鈕時,頁面展示聊天組件。

btn.addEventListener('click', function(e) {
  // 在這里加載chat組件相關資源 chat.js
});

從這個例子中我們可以看出,通過將加載chat.js的操作綁定在btn點擊事件上,可以實現點擊聊天按鈕后聊天組件的按需加載。而要動態加載js資源的方式也非常簡單(方式類似熟悉的jsonp)。通過動態在頁面中添加<scrpt>標簽,并將src屬性指向該資源即可。

btn.addEventListener('click', function(e) {
  // 在這里加載chat組件相關資源 chat.js
  var ele = document.createElement('script');
  ele.setAttribute('src','/static/chat.js');
  document.getElementsByTagName('head')[0].appendChild(ele);
});

代碼拆分就是為了要實現按需加載所做的工作。想象一下,我們使用打包工具,將所有的js全部打包到了bundle.js這個文件,這種情況下是沒有辦法做到上面所述的按需加載的,因此,我們需要講按需加載的代碼在打包的過程中拆分出來,這就是代碼拆分。那么,對于這些資源,我們需要手動拆分么?當然不是,還是要借助打包工具。下面就來介紹webpack中的代碼拆分。

代碼拆分

這里回到應用場景,介紹如何在webpack中進行代碼拆分。在webpack有多種方式來實現構建是的代碼拆分。

import()

這里的import不同于模塊引入時的import,可以理解為一個動態加載的模塊的函數(function-like),傳入其中的參數就是相應的模塊。例如對于原有的模塊引入import react from 'react'可以寫為import('react')。但是需要注意的是,import()會返回一個Promise對象。因此,可以通過如下方式使用:

btn.addEventListener('click', e => {
  // 在這里加載chat組件相關資源 chat.js
  import('/components/chart').then(mod => {
    someOperate(mod);
  });
});

可以看到,使用方式非常簡單,和平時我們使用的Promise并沒有區別。當然,也可以再加入一些異常處理:

btn.addEventListener('click', e => {
  import('/components/chart').then(mod => {
    someOperate(mod);
  }).catch(err => {
    console.log('failed');
  });
});

當然,由于import()會返回一個Promise對象,因此要注意一些兼容性問題。解決這個問題也不困難,可以使用一些Promise的polyfill來實現兼容。可以看到,動態import()的方式不論在語意上還是語法使用上都是比較清晰簡潔的。

require.ensure()

在webpack 2的官網上寫了這么一句話:

require.ensure() is specific to webpack and superseded by import().

所以,在webpack 2里面應該是不建議使用require.ensure()這個方法的。但是目前該方法仍然有效,所以可以簡單介紹一下。包括在webpack 1中也是可以使用。下面是require.ensure()的語法:

復制代碼 代碼如下:

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

require.ensure()接受三個參數:

  1. 第一個參數dependencies是一個數組,代表了當前require進來的模塊的一些依賴;
  2. 第二個參數callback就是一個回調函數。其中需要注意的是,這個回調函數有一個參數require,通過這個require就可以在回調函數內動態引入其他模塊。值得注意的是,雖然這個require是回調函數的參數,理論上可以換其他名稱,但是實際上是不能換的,否則webpack就無法靜態分析的時候處理它;
  3. 第三個參數errorCallback比較好理解,就是處理error的回調;
  4. 第四個參數chunkName則是指定打包的chunk名稱。

因此,require.ensure()具體的用法如下:

btn.addEventListener('click', e => {
  require.ensure([], require => {
    let chat = require('/components/chart');
    someOperate(chat);
  }, error => {
    console.log('failed');
  }, 'mychat');
});

Bundle Loader

除了使用上述兩種方法,還可以使用webpack的一些組件。例如使用Bundle Loader:

npm i --save bundle-loader

使用require("bundle-loader!./file.js")來進行相應chunk的加載。該方法會返回一個function,這個function接受一個回調函數作為參數。

let chatChunk = require("bundle-loader?lazy!./components/chat");
chatChunk(function(file) {
  someOperate(file);
});

和其他loader類似,Bundle Loader也需要在webpack的配置文件中進行相應配置。Bundle-Loader的代碼也很簡短,如果閱讀一下可以發現,其實際上也是使用require.ensure()來實現的,通過給Bundle-Loader返回的函數中傳入相應的模塊處理回調函數即可在require.ensure()的中處理,代碼最后也列出了相應的輸出格式:

/*
Output format:
  var cbs = [],
    data;
  module.exports = function(cb) {
    if(cbs) cbs.push(cb);
      else cb(data);
  }
  require.ensure([], function(require) {
    data = require("xxx");
    var callbacks = cbs;
    cbs = null;
    for(var i = 0, l = callbacks.length; i < l; i++) {
      callbacks[i](data);
    }
  });
*/

react-router v4 中的代碼拆分

最后,回到實際的工作中,基于webpack,在react-router4中實現代碼拆分。react-router 4相較于react-router 3有了較大的變動。其中,在代碼拆分方面,react-router 4的使用方式也與react-router 3有了較大的差別。

在react-router 3中,可以使用Route組件中getComponent這個API來進行代碼拆分。getComponent是異步的,只有在路由匹配時才會調用。但是,在react-router 4中并沒有找到這個API,那么如何來進行代碼拆分呢?

在react-router 4官網上有一個代碼拆分的例子。其中,應用了Bundle Loader來進行按需加載與動態引入

import loadSomething from 'bundle-loader?lazy!./Something'

然而,在項目中使用類似的方式后,出現了這樣的警告:

Unexpected '!' in 'bundle-loader?lazy!./component/chat'. Do not use import syntax to configure webpack loaders import/no-webpack-loader-syntax
Search for the keywords to learn more about each error.

在webpack 2中已經不能使用import這樣的方式來引入loader了(no-webpack-loader-syntax)

Webpack allows specifying the loaders to use in the import source string using a special syntax like this:

var moduleWithOneLoader = require("my-loader!./my-awesome-module");

This syntax is non-standard, so it couples the code to Webpack. The recommended way to specify Webpack loader configuration is in a Webpack configuration file.

我的應用使用了create-react-app作為腳手架,屏蔽了webpack的一些配置。當然,也可以通過運行npm run eject使其暴露webpack等配置文件。然而,是否可以用其他方法呢?當然。

這里就可以使用之前說到的兩種方式來處理:import()或require.ensure()。

和官方實例類似,我們首先需要一個異步加載的包裝組件Bundle。Bundle的主要功能就是接收一個組件異步加載的方法,并返回相應的react組件:

export default class Bundle extends Component {
  constructor(props) {
    super(props);
    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.state.mod ? this.props.children(this.state.mod) : null;
  }
}

在原有的例子中,通過Bundle Loader來引入模塊:

import loadSomething from 'bundle-loader?lazy!./About'

const About = (props) => (
  <Bundle load={loadAbout}>
    {(About) => <About {...props}/>}
  </Bundle>
)

由于不再使用Bundle Loader,我們可以使用import()對該段代碼進行改寫:

const Chat = (props) => (
  <Bundle load={() => import('./component/chat')}>
    {(Chat) => <Chat {...props}/>}
  </Bundle>
);

需要注意的是,由于import()會返回一個Promise對象,因此Bundle組件中的代碼也需要相應進行調整

export default class Bundle extends Component {
  constructor(props) {
    super(props);
    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
    });
    //注意這里,使用Promise對象; mod.default導出默認
    props.load().then((mod) => {
      this.setState({
        mod: mod.default ? mod.default : mod
      });
    });
  }

  render() {
    return this.state.mod ? this.props.children(this.state.mod) : null;
  }
}

路由部分沒有變化

<Route path="/chat" component={Chat}/>

這時候,執行npm run start,可以看到在載入最初的頁面時加載的資源如下

在react-router4中進行代碼拆分的方法(基于webpack)

而當點擊觸發到/chat路徑時,可以看到

在react-router4中進行代碼拆分的方法(基于webpack)

動態加載了2.chunk.js這個js文件,如果打開這個文件查看,就可以發現這個就是我們剛才動態import()進來的模塊。

當然,除了使用import()仍然可以使用require.ensure()來進行模塊的異步加載。相關示例代碼如下:

const Chat = (props) => (
  <Bundle load={(cb) => {
    require.ensure([], require => {
      cb(require('./component/chat'));
    });
  }}>
  {(Chat) => <Chat {...props}/>}
 </Bundle>
);
export default class Bundle extends Component {
  constructor(props) {
    super(props);
    this.state = {
      mod: null
    };
  }

  load = props => {
    this.setState({
      mod: null
    });
    props.load(mod => {
      this.setState({
        mod: mod ? mod : null
      });
    });
  }

  componentWillMount() {
    this.load(this.props);
  }

  render() {
    return this.state.mod ? this.props.children(this.state.mod) : null
  }
}

此外,如果是直接使用webpack config的話,也可以進行如下配置

output: {
  // The build folder.
  path: paths.appBuild,
  // There will be one main bundle, and one file per asynchronous chunk.
  filename: 'static/js/[name].[chunkhash:8].js',
  chunkFilename: 'static/js/[name].[chunkhash:8].chunk.js',
 },

結束

代碼拆分在單頁應用中非常常見,對于提高單頁應用的性能與體驗具有一定的幫助。我們通過將第一次訪問應用時,并不需要的模塊拆分出來,通過scipt標簽動態加載的原理,可以實現有效的代碼拆分。在實際項目中,使用webpack中的import()、require.ensure()或者一些loader(例如Bundle Loader)來做代碼拆分與組件按需加載。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

江油市| 随州市| 洪湖市| 曲周县| 南平市| 沾化县| 将乐县| 颍上县| 天气| 米泉市| 九寨沟县| 武安市| 井研县| 仪征市| 怀集县| 怀安县| 眉山市| 温宿县| 定南县| 米脂县| 巴彦淖尔市| 仲巴县| 新安县| 双峰县| 平远县| 宁明县| 甘泉县| 泾阳县| 洪雅县| 陆良县| 衡阳市| 鹤峰县| 锦州市| 巩留县| 宜宾县| 万宁市| 泗洪县| 朝阳区| 田东县| 铜山县| 开阳县|