隨著前端專案的不斷擴大,一個原本簡單的網頁應用程式所引用的js檔案可能變得越來越龐大。尤其在近期流行的單頁面應用程式中,越來越依賴一些打包工具(例如webpack),透過這些打包工具將需要處理、相互依賴的模組直接打包成一個單獨的bundle文件,在頁面第一次載入時,就會將所有的js全部載入。但是,往往有許多的場景,我們並不需要在一次性將單頁應用的全部依賴都載下來。例如:我們現在有一個帶有權限的"訂單後台管理"單頁應用,普通管理員只能進入"訂單管理"部分,而超級用戶則可以進行"系統管理";或者,我們有一個龐大的單頁應用,使用者在第一次開啟頁面時,需要等待較長時間載入無關資源。這些時候,我們就可以考慮進行一定的程式碼分割(code splitting)。
實作方式
簡單的按需載入
#程式碼分割的核心目的,就是實作資源的按需載入.考慮這麼一個場景,在我們的網站中,右下角有一個類似聊天框的元件,當我們點擊圓形按鈕時,頁面會顯示聊天元件。
btn.addEventListener('click', function(e) { // 在这里加载chat组件相关资源 chat.js });
從這個例子中我們可以看出,透過將載入chat.js的操作綁定在btn點擊事件上,可以實現點擊聊天按鈕後聊天元件的按需加載。而要動態載入js資源的方式也非常簡單(方式類似熟悉的jsonp)。透過動態在頁面中加入
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()接受三個參數:
第一個參數dependencies是一個數組,代表了目前require進來的模組的一些依賴;
第二個參數callback就是回呼函數。其中要注意的是,這個回呼函數有一個參數require,透過這個require就可以在回呼函數內動態引入其他模組。值得注意的是,雖然這個require是回呼函數的參數,理論上可以換其他名稱,但其實是不能換的,否則webpack就無法靜態分析的時候處理它;
第三個參數errorCallback比較好理解,就是處理error的回呼;
第四個參數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,可以看到在载入最初的页面时加载的资源如下
而当点击触发到/chat路径时,可以看到
动态加载了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)来做代码拆分与组件按需加载。
以上是react-router4中程式碼拆分的方法的詳細內容。更多資訊請關注PHP中文網其他相關文章!

不同的电脑系统在调整屏幕亮度的操作方法上会有些不同,最近就有使用win7系统的网友不知道win7怎么调整屏幕亮度,看久了电脑眼睛比较酸痛。下面小编就教下大家win7调整屏幕亮度的方法。具体的操作步骤如下:1、点击win7电脑左下角的“开始”,在弹出的开始菜单中选择“控制面板”打开。2、在打开的控制面板中找到“电源选项”打开。3、也可以用鼠标右键电脑右下角的电源图标,在弹出的菜单中,点击“调整屏幕亮度”,如下图所示。两种方法都可以用。4、在打开的电源选项窗口的最下面可以看到屏幕亮度调整的滚动条,直

如果我们手头没有手机,只有电脑,但我们必须拍照,我们可以使用电脑内置的监控摄像头拍照,那么如何打开win10监控摄像头,事实上,我们只需要下载一个相机应用程序。打开win10监控摄像头的具体方法。win10监控摄像头打开照片的方法:1.首先,盘快捷键Win+i打开设置。2.打开后,进入个人隐私设置。3.然后在相机手机权限下打开访问限制。4.打开后,您只需打开相机应用软件。(如果没有,可以去微软店下载一个)5.打开后,如果计算机内置监控摄像头或组装了外部监控摄像头,则可以拍照。(因为人们没有安装摄

随着科技的不断发展,机器视觉技术在各个领域得到了广泛应用,如工业自动化、医疗诊断、安防监控等。Java作为一种流行的编程语言,其在机器视觉领域也有着重要的应用。本文将介绍基于Java的机器视觉实践和相关方法。一、Java在机器视觉中的应用Java作为一种跨平台的编程语言,具有跨操作系统、易于维护、高度可扩展等优点,对于机器视觉的应用具有一定的优越性。Java

目前有很多屏幕亮度调整软件,我们可以通过使用软件进行快速调整或者通过显示器上自带的亮度功能进行调整。以下是详细的Win7屏幕亮度调整方式,您可以通过教程中的方法进行快速调整即可。Win7系统电脑怎么调节屏幕亮度教程:1、依次点击“计算机—右键—控制面板”,如果没有也可以在搜索框中进行搜索。2、点击控制面板下的“硬件和声音”,或者点击“外观和个性化”都可以。3、点击“NVIDIA控制面板”,有些显卡可能是AMD或者Intel的,请根据实际情况选择。4、调节图示中亮度滑块即可。5、还有一种方法,就是

PHP是一个广泛使用的服务器端编程语言,它的许多功能和特性可以将其用于各种任务,包括文件下载。在本文中,我们将了解如何使用PHP创建文件下载脚本,并解决文件下载过程中可能出现的常见问题。一、文件下载方法要在PHP中下载文件,我们需要创建一个PHP脚本。让我们看一下如何实现这一点。创建下载文件的链接通过HTML或PHP在页面上创建一个链接,让用户能够下载文件。

随着前端开发的快速发展,越来越多的框架被用来构建复杂的Web应用程序。Vue.js是流行的前端框架之一,它提供了许多功能和工具来简化开发人员构建高质量的Web应用程序。createApp()方法是Vue.js中的一个核心方法之一,它提供了一种简单的方式来创建Vue实例和应用程序。本文将深入探讨Vue中createApp方法的作用,其如何使用以及使用时需要了解

Go语言是近年来备受青睐的编程语言,因其简洁、高效、并发等特点而备受开发者喜爱。其中,方法(Method)也是Go语言中非常重要的概念。接下来,本文就将详细介绍Go语言中方法的定义和使用。一、方法的定义Go语言中的方法是带有接收器(Receiver)的函数,它是一个与某个类型绑定的函数。接收器可以是值类型或者指针类型。用于接收者的参数可以在方法名

使用PHP数组实现数据缓存和存储的方法和技巧随着互联网的发展和数据量的急剧增长,数据缓存和存储成为了我们在开发过程中必须要考虑的问题之一。PHP作为一门广泛应用的编程语言,也提供了丰富的方法和技巧来实现数据缓存和存储。其中,使用PHP数组进行数据缓存和存储是一种简单而高效的方法。一、数据缓存数据缓存的目的是为了减少对数据库或其他外部数据源的访问次数,从而提高


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

Dreamweaver Mac版
視覺化網頁開發工具

PhpStorm Mac 版本
最新(2018.2.1 )專業的PHP整合開發工具

ZendStudio 13.5.1 Mac
強大的PHP整合開發環境

SublimeText3漢化版
中文版,非常好用