前提:

如今开发方式都是采用前后台分离的方式,前台采用的方式则是单页面应用开发简称SPA,这种开发模式最大的一个特点就是将有所代码打包成了一个文件,

这会导致了一个问题就是如果这个应用过大,打出来的这个文件也会越大,性能也就随之降低了,那么如何优化这个问题呢,今天就记录下懒加载策略吧。

那么什么是懒加载呢~

懒加载概念:

懒加载也就是按需加载,只要有需要才会加载的一种策略,常用于性能优化上(个人理解)通过懒加载可以避免加载不必要的资源。

工程级别:

在微服务架构里,前台也需要同步,这就需要每个模块都需要build出属于自己的js跟css,当跳转到目标模块时,我们不需要加载其他模块的文件,于是我们就

可以利用懒加载策略进行优化

利用第三方lazyload.js对目标模块进行文件读写

第三方lazyload文件

/*jslint browser: true, eqeqeq: true, bitwise: true, newcap: true, immed: true, regexp: false */

/**
LazyLoad makes it easy and painless to lazily load one or more external
JavaScript or CSS files on demand either during or after the rendering of a web
page. Supported browsers include Firefox 2+, IE6+, Safari 3+ (including Mobile
Safari), Google Chrome, and Opera 9+. Other browsers may or may not work and
are not officially supported. Visit https://github.com/rgrove/lazyload/ for more info. Copyright (c) 2011 Ryan Grove <ryan@wonko.com>
All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the 'Software'), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions: The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. @module lazyload
@class LazyLoad
@static
*/ LazyLoad = (function (doc) {
// -- Private Variables ------------------------------------------------------ // User agent and feature test information.
var env, // Reference to the <head> element (populated lazily).
head, // Requests currently in progress, if any.
pending = {}, // Number of times we've polled to check whether a pending stylesheet has
// finished loading. If this gets too high, we're probably stalled.
pollCount = 0, // Queued requests.
queue = { css: [], js: [] }, // Reference to the browser's list of stylesheets.
styleSheets = doc.styleSheets; // -- Private Methods -------------------------------------------------------- /**
Creates and returns an HTML element with the specified name and attributes. @method createNode
@param {String} name element name
@param {Object} attrs name/value mapping of element attributes
@return {HTMLElement}
@private
*/
function createNode(name, attrs) {
var node = doc.createElement(name), attr; for (attr in attrs) {
if (attrs.hasOwnProperty(attr)) {
node.setAttribute(attr, attrs[attr]);
}
} return node;
} /**
Called when the current pending resource of the specified type has finished
loading. Executes the associated callback (if any) and loads the next
resource in the queue. @method finish
@param {String} type resource type ('css' or 'js')
@private
*/
function finish(type) {
var p = pending[type],
callback,
urls; if (p) {
callback = p.callback;
urls = p.urls; urls.shift();
pollCount = 0; // If this is the last of the pending URLs, execute the callback and
// start the next request in the queue (if any).
if (!urls.length) {
callback && callback.call(p.context, p.obj);
pending[type] = null;
queue[type].length && load(type);
}
}
} /**
Populates the <code>env</code> variable with user agent and feature test
information. @method getEnv
@private
*/
function getEnv() {
var ua = navigator.userAgent; env = {
// True if this browser supports disabling async mode on dynamically
// created script nodes. See
// http://wiki.whatwg.org/wiki/Dynamic_Script_Execution_Order
async: doc.createElement('script').async === true
}; (env.webkit = /AppleWebKit\//.test(ua))
|| (env.ie = /MSIE|Trident/.test(ua))
|| (env.opera = /Opera/.test(ua))
|| (env.gecko = /Gecko\//.test(ua))
|| (env.unknown = true);
} /**
Loads the specified resources, or the next resource of the specified type
in the queue if no resources are specified. If a resource of the specified
type is already being loaded, the new request will be queued until the
first request has been finished. When an array of resource URLs is specified, those URLs will be loaded in
parallel if it is possible to do so while preserving execution order. All
browsers support parallel loading of CSS, but only Firefox and Opera
support parallel loading of scripts. In other browsers, scripts will be
queued and loaded one at a time to ensure correct execution order. @method load
@param {String} type resource type ('css' or 'js')
@param {String|Array} urls (optional) URL or array of URLs to load
@param {Function} callback (optional) callback function to execute when the
resource is loaded
@param {Object} obj (optional) object to pass to the callback function
@param {Object} context (optional) if provided, the callback function will
be executed in this object's context
@private
*/
function load(type, urls, callback, obj, context) {
var _finish = function () { finish(type); },
isCSS = type === 'css',
nodes = [],
i, len, node, p, pendingUrls, url; env || getEnv(); if (urls) {
// If urls is a string, wrap it in an array. Otherwise assume it's an
// array and create a copy of it so modifications won't be made to the
// original.
urls = typeof urls === 'string' ? [urls] : urls.concat(); // Create a request object for each URL. If multiple URLs are specified,
// the callback will only be executed after all URLs have been loaded.
//
// Sadly, Firefox and Opera are the only browsers capable of loading
// scripts in parallel while preserving execution order. In all other
// browsers, scripts must be loaded sequentially.
//
// All browsers respect CSS specificity based on the order of the link
// elements in the DOM, regardless of the order in which the stylesheets
// are actually downloaded.
if (isCSS || env.async || env.gecko || env.opera) {
// Load in parallel.
queue[type].push({
urls: urls,
callback: callback,
obj: obj,
context: context
});
} else {
// Load sequentially.
for (i = 0, len = urls.length; i < len; ++i) {
queue[type].push({
urls: [urls[i]],
callback: i === len - 1 ? callback : null, // callback is only added to the last URL
obj: obj,
context: context
});
}
}
} // If a previous load request of this type is currently in progress, we'll
// wait our turn. Otherwise, grab the next item in the queue.
if (pending[type] || !(p = pending[type] = queue[type].shift())) {
return;
} head || (head = doc.head || doc.getElementsByTagName('head')[0]);
pendingUrls = p.urls.concat(); for (i = 0, len = pendingUrls.length; i < len; ++i) {
url = pendingUrls[i]; if (isCSS) {
node = env.gecko ? createNode('style') : createNode('link', {
href: url,
rel: 'stylesheet'
});
} else {
node = createNode('script', { src: url });
node.async = false;
} node.className = 'lazyload';
node.setAttribute('charset', 'utf-8'); if (env.ie && !isCSS && 'onreadystatechange' in node && !('draggable' in node)) {
node.onreadystatechange = function () {
if (/loaded|complete/.test(node.readyState)) {
node.onreadystatechange = null;
_finish();
}
};
} else if (isCSS && (env.gecko || env.webkit)) {
// Gecko and WebKit don't support the onload event on link nodes.
if (env.webkit) {
// In WebKit, we can poll for changes to document.styleSheets to
// figure out when stylesheets have loaded.
p.urls[i] = node.href; // resolve relative URLs (or polling won't work)
pollWebKit();
} else {
// In Gecko, we can import the requested URL into a <style> node and
// poll for the existence of node.sheet.cssRules. Props to Zach
// Leatherman for calling my attention to this technique.
node.innerHTML = '@import "' + url + '";';
pollGecko(node);
}
} else {
node.onload = node.onerror = _finish;
} nodes.push(node);
} for (i = 0, len = nodes.length; i < len; ++i) {
head.appendChild(nodes[i]);
}
} /**
Begins polling to determine when the specified stylesheet has finished loading
in Gecko. Polling stops when all pending stylesheets have loaded or after 10
seconds (to prevent stalls). Thanks to Zach Leatherman for calling my attention to the @import-based
cross-domain technique used here, and to Oleg Slobodskoi for an earlier
same-domain implementation. See Zach's blog for more details:
http://www.zachleat.com/web/2010/07/29/load-css-dynamically/ @method pollGecko
@param {HTMLElement} node Style node to poll.
@private
*/
function pollGecko(node) {
var hasRules; try {
// We don't really need to store this value or ever refer to it again, but
// if we don't store it, Closure Compiler assumes the code is useless and
// removes it.
hasRules = !!node.sheet.cssRules;
} catch (ex) {
// An exception means the stylesheet is still loading.
pollCount += 1; if (pollCount < 200) {
setTimeout(function () { pollGecko(node); }, 50);
} else {
// We've been polling for 10 seconds and nothing's happened. Stop
// polling and finish the pending requests to avoid blocking further
// requests.
hasRules && finish('css');
} return;
} // If we get here, the stylesheet has loaded.
finish('css');
} /**
Begins polling to determine when pending stylesheets have finished loading
in WebKit. Polling stops when all pending stylesheets have loaded or after 10
seconds (to prevent stalls). @method pollWebKit
@private
*/
function pollWebKit() {
var css = pending.css, i; if (css) {
i = styleSheets.length; // Look for a stylesheet matching the pending URL.
while (--i >= 0) {
if (styleSheets[i].href === css.urls[0]) {
finish('css');
break;
}
} pollCount += 1; if (css) {
if (pollCount < 200) {
setTimeout(pollWebKit, 50);
} else {
// We've been polling for 10 seconds and nothing's happened, which may
// indicate that the stylesheet has been removed from the document
// before it had a chance to load. Stop polling and finish the pending
// request to prevent blocking further requests.
finish('css');
}
}
}
} return { /**
Requests the specified CSS URL or URLs and executes the specified
callback (if any) when they have finished loading. If an array of URLs is
specified, the stylesheets will be loaded in parallel and the callback
will be executed after all stylesheets have finished loading. @method css
@param {String|Array} urls CSS URL or array of CSS URLs to load
@param {Function} callback (optional) callback function to execute when
the specified stylesheets are loaded
@param {Object} obj (optional) object to pass to the callback function
@param {Object} context (optional) if provided, the callback function
will be executed in this object's context
@static
*/
css: function (urls, callback, obj, context) {
load('css', urls, callback, obj, context);
}, /**
Requests the specified JavaScript URL or URLs and executes the specified
callback (if any) when they have finished loading. If an array of URLs is
specified and the browser supports it, the scripts will be loaded in
parallel and the callback will be executed after all scripts have
finished loading. Currently, only Firefox and Opera support parallel loading of scripts while
preserving execution order. In other browsers, scripts will be
queued and loaded one at a time to ensure correct execution order. @method js
@param {String|Array} urls JS URL or array of JS URLs to load
@param {Function} callback (optional) callback function to execute when
the specified scripts are loaded
@param {Object} obj (optional) object to pass to the callback function
@param {Object} context (optional) if provided, the callback function
will be executed in this object's context
@static
*/
js: function (urls, callback, obj, context) {
load('js', urls, callback, obj, context);
} };
})(this.document);

封装路由

class LazyLoader extends React.Component {
constructor(props) {
super(props);
this.state = {
module: null
}
} componentWillMount() {
this.load(this.props.module, this.props.path);
} componentWillReceiveProps(newProps) {
this.load(newProps.module, newProps.path);
} load(module, path) {
let cssPath = path + '',
jsPath = path + '';
LazyLoad.css(cssPath);
LazyLoad.js(jsPath, () => {
this.state.module = Modules[module];
})
} render() {
if (!this.state.module) return null;
else this.props.children(this.state.module);
}
} const HTMLModule = ({ name, path }) => {
let param = location.param;
location.param = null;
let ModuleComponent = Modules[name];
return (
<React.Fragment>
{
ModuleComponent ?
<ModuleComponent param={param} /> :
<LazyLoader path={path} module={name}>
{(Item) => <Item param={param} />}
</LazyLoader>
}
</React.Fragment>
);
}; class ModulesRouter extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<Switch>
<Route path={'/Module/Target'} render={() => <HTMLModule name='Target' path={'./Module/Target'} />} />
<Redirect from='*' to='/403' />
</Switch>
);
}
} export default ModulesRouter;

路由与组件级别:

这里我们利用React16.6.X以后的版本新特性React.lazy跟React.Suspense进行路由以及组件部分的优化

路由设计

root路由

const RootRouter = () => {
return <HashRouter>
<React.Suspense fallback={<div>loading...</div>}>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/oAuthPromisition" component={oAuthPromisition} />
<Route path="/WeChatPromisitionCheck" component={WeChatPromisitionCheck} />
<Route path="/403" component={Forbidden} />
<Route path="/sub" component={ModuleRouter} />
</Switch>
</React.Suspense>
</HashRouter>
}

子路由设计

import {
Route,
Switch
} from 'react-router-dom'; function lazyWithPreload(factory) {
const Component = React.lazy(factory);
Component.preload = factory;
return Component;
} const LeaveMsg = lazyWithPreload(() => { return import(/* webpackChunkName: "leavemessage" */ '../JSX/Components/LeaveMessage/LeaveMessage') });
const Blog = lazyWithPreload(() => { return import(/* webpackChunkName: "blog" */ '../JSX/Components/Blog/Blog') });
const Article = lazyWithPreload(() => { return import(/* webpackChunkName: "article" */ '../JSX/Components/Blog/Article') });
const Production = lazyWithPreload(() => { return import(/* webpackChunkName: "production" */ '../JSX/Components/Production/index') });
const About = lazyWithPreload(() => { return import(/* webpackChunkName: "about" */ '../JSX/Components/About/About') });
const Write = lazyWithPreload(() => { return import(/* webpackChunkName: "write" */ '../JSX/Components/Blog/Write') });
const Overview = lazyWithPreload(() => { return import(/* webpackChunkName: "overview" */ '../JSX/Components/Overview/Overview') }); function moduleHtml(component) {
component.preload()
return component;
} const SubRouter = () => {
return <Switch>
<Route path="/sub/leavemessage" component={moduleHtml(LeaveMsg)} />
<Route path="/sub/blog" component={moduleHtml(Blog)} />
<Route path="/sub/article" component={moduleHtml(Article)} />
<Route path="/sub/production" component={moduleHtml(Production)} />
<Route path="/sub/about" component={moduleHtml(About)} />
<Route path="/sub/write" component={moduleHtml(Write)} />
<Route path="/sub/overview" component={moduleHtml(Overview)} />
</Switch>
}
export default SubRouter;

路由设计总结:

利用import动态加载组件,以达到按需加载的目的,但是在动态加载的时候webpack会发生代码切割,所有我们同时也要进行webpack配置

首先配置分割规则

output: {
path: path.resolve(__dirname, "./public/"),
filename: '[name].bundle.js',
chunkFilename: '[name].module.js', //非入口文件命名规则
},

配置chunkFilename即非入口文件命名规则,就是这些由webpack分割的文件命名规范

由于webpack分割之后的路径查找方式是通过build输出路径查找的,因此如果我们单纯的将静态文件(index.html)当做根文件来加载,是找不到分割之后的代码路径的,

因此需要配置HtmlWebpackPlugin

new HtmlWebpackPlugin({
template: 'Index.html',
filename: 'index.html',
chunks: [
'index'
]
}),

然后将资源文件,以及第三方文件拷贝到build输出文件夹下

new CopyWebpackPlugin([
{
from: __dirname + '/Lib/editor',
to: __dirname + '/public/editor',
// toType: 'directory', //file 或者 dir 可选,默认是文件
// ignore: ['.*']
},{
from: __dirname + '/picSrc',
to: __dirname + '/public/picSrc',
}
]),

这样webpack配置修改完成了

组件级别跟路由级别基本类似,但是我们要考虑到,如果这么拆分代码,会产生很多文件,根据实际情况使用。

SPA应用性能优化(懒加载)的更多相关文章

  1. [转]JavaScript 的性能优化:加载和执行

    原文链接:http://www.ibm.com/developerworks/cn/web/1308_caiys_jsload/index.html?ca=drs- JavaScript 的性能优化: ...

  2. 【转】js JavaScript 的性能优化:加载和执行

    JavaScript 的性能优化:加载和执行 转自:https://www.ibm.com/developerworks/cn/web/1308_caiys_jsload/ 随着 Web2.0 技术的 ...

  3. vue性能优化1--懒加载

    懒加载也叫延迟加载,即在需要的时候进行加载.随用随载.为什么需要懒加载?像vue这种单页面应用,如果没有应用懒加载,运用webpack打包后的文件将会异常的大,造成进入首页时,需要加载的内容过多,时间 ...

  4. JavaScript 的性能优化:加载和执行

    随着 Web2.0 技术的不断推广,越来越多的应用使用 javascript 技术在客户端进行处理,从而使 JavaScript 在浏览器中的性能成为开发者所面临的最重要的可用性问题.而这个问题又因 ...

  5. JavaScript的性能优化:加载和执行

    随着 Web2.0 技术的不断推广,越来越多的应用使用 javascript 技术在客户端进行处理,从而使 JavaScript 在浏览器中的性能成为开发者所面临的最重要的可用性问题.而这个问题又因 ...

  6. Android进阶:ListView性能优化异步加载图片 使滑动效果流畅

    ListView 是一种可以显示一系列项目并能进行滚动显示的 View,每一行的Item可能包含复杂的结构,可能会从网络上获取icon等的一些图标信息,就现在的网络速度要想保持ListView运行的很 ...

  7. 【摘要】JavaScript 的性能优化:加载和执行

    1.浏览器遇到js代码会暂停页面的下载和渲染,谁晓得js代码会不会把html给强奸(改变)了: 2.延迟脚本加载:defer 属性 <html> <head> <titl ...

  8. 前端性能优化_css加载会造成哪些阻塞现象?

    css的加载是不会阻塞DOM的解析,但是会阻塞DOM的渲染,会阻塞link后面js语句的执行.这是由于浏览器为了防止html页面的重复渲染而降低性能,所以浏览器只会在加载的时候去解析dom树,然后等在 ...

  9. 移动端性能优化动态加载JS、CSS

    JS CODE (function() { /** * update: * 1.0 */ var version = "insure 1.1.0"; var Zepto = Zep ...

  10. web前端如何性能优化提高加载速度

    前端优化有以下几种途径: 一.减少HTTP请求数量和次数: 二.使用CDN: 三.添加Expires头: 四.压缩组件: 五.将样式表放在头部: 六.将脚本放在底部: 七.避免CSS表达式: 八.使用 ...

随机推荐

  1. Big Data(三)伪分布式和完全分布式的搭建

    关于伪分布式的配置全程 伪分布式图示 1.安装VMWare WorkStation,直接下一步,输入激活码即可安装 2.安装Linux(需要100GB) 引导分区Boot200MB 交换分区Swap2 ...

  2. 如何在 Visual C# 中执行基本的文件 I/O

    演示文件 I/O 操作 本文中的示例讲述基本的文件 I/O 操作.“分步示例”部分说明如何创建一个演示下列六种文件 I/O 操作的示例程序: 注意:如果要直接使用下列示例代码,请注意下列事项: 必须包 ...

  3. [SCOI2016]幸运数字(线性基,倍增)

    [SCOI2016]幸运数字 题目描述 A 国共有 n 座城市,这些城市由 n-1 条道路相连,使得任意两座城市可以互达,且路径唯一.每座城市都有一个幸运数字,以纪念碑的形式矗立在这座城市的正中心,作 ...

  4. C#基础知识之理解HTTP协议

    在互联网时代HTTP协议的重要性无需多言,对于技术岗位的同学们来说理解掌握HTTP协议是必须的.本篇博客就从HTTP协议的演进.特性.重要知识点和工作中常见问题的总结等方面进行简单的介绍.理解掌握了这 ...

  5. 【串线篇】SpringBoot数据访问【数据源/mybatis/指定映射文件位置】

    一.配置数据源 1.1.jdbc版本 JDBC(.tomcat.jdbc.pool.DataSource作为数据源) <?xml version="1.0" encoding ...

  6. RabbitMQ 启用页面管理功能并设置权限

    RabbitMQ 启用页面管理功能并设置权限 RabbitMQ guest administrator  在安装完 rabbitmq 后,默认有一个 guest/guest 账号密码,但是为了安全,此 ...

  7. 大数据(量上GB的)查看命令

    大数据查看指令 cmd中(tab键可补全) type 文件名:全部查看 more 文件名:Enter键,按一下查看一页

  8. Ionic4 & Cordova 打包Android,含签名

    IOS打包必须在Mac环境,以下讲述安卓打包并签名的过程,若是不签名,那么APP不能在手机上安装. 1.添加对应的android框架 ionic cordova prepare android 1)修 ...

  9. python tkinter菜单

    python3中,Tkinter编写菜单界面案例 from tkinter import * top=Tk() top.wm_title("菜单") top.geometry(&q ...

  10. ESP8266-Station模式--我想连上谁

    Station模式又叫做站点工作模式,类似于无线终端 处于Station模式下的ESP8266,可以连接到AP.通过Station(简称为“STA”)模式,ESP8266作为客户端连接到路由的wifi ...