惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

D
Docker
AI
AI
博客园 - 叶小钗
人人都是产品经理
人人都是产品经理
The Cloudflare Blog
Apple Machine Learning Research
Apple Machine Learning Research
Jina AI
Jina AI
大猫的无限游戏
大猫的无限游戏
博客园 - 【当耐特】
V
Visual Studio Blog
博客园 - Franky
宝玉的分享
宝玉的分享
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
I
Intezer
C
Cybersecurity and Infrastructure Security Agency CISA
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
S
SegmentFault 最新的问题
腾讯CDC
T
Threat Research - Cisco Blogs
Last Week in AI
Last Week in AI
酷 壳 – CoolShell
酷 壳 – CoolShell
Webroot Blog
Webroot Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
小众软件
小众软件
C
Cyber Attacks, Cyber Crime and Cyber Security
Hacker News: Ask HN
Hacker News: Ask HN
T
Tor Project blog
WordPress大学
WordPress大学
雷峰网
雷峰网
J
Java Code Geeks
GbyAI
GbyAI
Recorded Future
Recorded Future
F
Full Disclosure
Cisco Talos Blog
Cisco Talos Blog
S
Secure Thoughts
I
InfoQ
量子位
Forbes - Security
Forbes - Security
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
T
Threatpost
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Microsoft Security Blog
Microsoft Security Blog
Attack and Defense Labs
Attack and Defense Labs
爱范儿
爱范儿
N
News and Events Feed by Topic
V
Vulnerabilities – Threatpost
L
LINUX DO - 最新话题
A
Arctic Wolf
S
Security Affairs

Quip Blog

Making the Quip editor accessible Paying down our accessibility debt Announcing the 2021 Diversity Scholarship Winners Quip is Back and Better Than Ever A New Developer Experience for our Live Apps Community Announcing the 2021 Quip Diversity Scholarship 3 Ways Salesforce Anywhere Helps Sales Ops Make Their Stakeholders Successful How Cytiva Transformed Account Planning with Salesforce Anywhere Quip and Salesforce Anywhere Customer Support Documentation Has a New Home
Solving Performance Issues with a Multi-Frame Application
By Chris de la Iglesia · 2020-12-15 · via Quip Blog

The Problem to Solve

Salesforce Anywhere is a new communication app, built by Quip, to provide a place for communication and collaboration inside of Salesforce. As part of this new product, we planned to build new chat features into the Salesforce Lightning experience itself. Since Salesforce Anywhere runs on the Quip backend and tech stack, which is separate from the Salesforce tech stack, this required some special design decisions on our part.

Initially, we built a new React app to run Salesforce Anywhere that would re-use client-side code from Quip and connect to the Quip backend. Our first version ran an instance of this app in each chat window inside of an iframe, which communicated using messages with the main Salesforce app to create new chat windows or open up links.

This caused a few technical issues. First of all, it used up a lot of memory. When opening up a page with the chat header and five chat windows, the Salesforce Anywhere app consumed upwards of 500MB of Javascript VM memory alone. While our powerful development machines could easily handle that amount, many of our customers would have trouble if running other intensive applications.

In addition, loading up a full React app plus copies of our data management and other duplicate code takes time. In our initial version, it took roughly two to three seconds to open up a new chat, which is borderline unusable from a productivity perspective. We would need to get that load time down below a second, hopefully below half a second, before we could ship.

The Solution: A Multi-Frame Application

To solve these problems, we decided to rearchitect our app to use one instance of our React app that controlled each of the iframes on the page. Since React can easily render in multiple places, one app could be loaded that would render into each iframe every time a new chat window was opened. This would also solve the data duplication issue, as the same data management could be used for each application.

Normally, cross-frame functionality can only be done using messages sent back and forth between the different sets of Javascript code. However, if every frame comes from the same origin, then even if the app is embedded inside another webpage, each frame can directly access the Javascript memory of every other frame of the same origin. This allowed our code to run basically as-is, only using different window objects.

We chose to use the chat header (shown below) to be the controller frame that would render each other frame (called view frames). This is because the chat header is loaded up when Salesforce Lightning first loads, and so is always reliably loaded. Upon loading of the main Salesforce app, Salesforce Anywhere loads into a hidden iframe normally with no multi-frame shenanigans involved.

When a chat window opens up, it loads a minimal bundle of Javascript and CSS that only handles initialization. Upon loading, this “view frame” sends a message to the controller frame (i.e. the chat header), telling it which frame it is from and to start rendering. The controller frame then renders the React app into the view frame.

This change required a significant amount of refactoring to certain parts of our codebase, as some parts were built with only a single frame in mind. Thankfully, we already had some support code for multiple frames for our desktop app, since certain popovers and modals run in separate frames for a desktop-like experience. This code would allow us to create and manage window-scoped objects for each frame (such as our focus stack, which manages the focus inside each window). Every part of our codebase would need to be frame-agnostic, so we were able to use this support code to separate the logic of the code from the frame that it was operating on.

One unique point is the cross-frame messages sent from Salesforce Anywhere to the main Salesforce app. In our initial app, each frame individually sent messages to communicate with Salesforce. This meant that, from the Salesforce side, messages came from every frame and not just the single controller frame. We wanted to maintain this interface between Anywhere and Salesforce, as it is simpler and easier to understand, but in the new multi-frame world all of the view frames only had a small amount of code to send an initialization message to the controller frame. Thankfully, we had already encapsulated the message sending/receiving handlers inside a convenient bridge object. Each view frame would create its own bridge object that would send messages originating from the view frame but could be used from the controller frame. When the controller frame needed to send a message for a view frame, it would find the corresponding bridge object and send the message using that.

The Result

After implementing this system, we saw large improvements to memory usage and chat window load times. Each chat window went from taking up 40MB of memory per window to <10MB of memory per window, dramatically lowering overall memory usage when users have many windows opened. In addition, load times for each chat window decreased from three seconds to less than a second, improving usability and enjoyability.

As part of this investigation, several lessons became clear. First, measuring exactly what you want to improve is very important. We did a large amount of profiling and investigation to determine what was using up memory and why load times were so bad, which allowed us to proceed with confidence and get the results that we wanted. Second, building a complete but inefficient version of your app first can provide tons of valuable information on what will end up using the most resources in the final product.