Nhân viên dịch vụ trong sản xuất

Ảnh chụp màn hình dọc

Tóm tắt

Tìm hiểu cách chúng tôi sử dụng thư viện trình chạy dịch vụ để giúp ứng dụng web Google I/O 2015 hoạt động nhanh và ưu tiên chế độ ngoại tuyến.

Tổng quan

Ứng dụng web Google I/O 2015 năm nay do nhóm Quan hệ nhà phát triển của Google viết, dựa trên thiết kế của các bạn bè tại Instrument. Họ đã viết nên thử nghiệm âm thanh/hình ảnh tiện lợi. Sứ mệnh của nhóm chúng tôi là đảm bảo rằng ứng dụng web I/O (mà tôi gọi bằng tên mã là IOWA) thể hiện mọi thứ mà web hiện đại có thể làm. Trải nghiệm hoàn toàn có chế độ ngoại tuyến nằm ở đầu danh sách các tính năng phải có.

Nếu gần đây bạn đã đọc bất kỳ bài viết nào khác trên trang web này, chắc chắn bạn đã gặp service worker và bạn sẽ không ngạc nhiên khi biết rằng hỗ trợ ngoại tuyến của IOWA phụ thuộc rất nhiều vào chúng. Dựa trên nhu cầu thực tế của IOWA, chúng tôi đã phát triển 2 thư viện để xử lý 2 trường hợp sử dụng ngoại tuyến: sw-precache để tự động hoá việc lưu trước các tài nguyên tĩnh và sw-toolbox để xử lý các chiến lược dự phòng và lưu vào bộ nhớ đệm trong thời gian chạy.

Các thư viện bổ trợ cho nhau rất tốt và cho phép chúng tôi triển khai một chiến lược hiệu quả, trong đó nội dung tĩnh của IOWA luôn được phân phát trực tiếp từ bộ nhớ đệm, còn các tài nguyên động hoặc tài nguyên từ xa được phân phát từ mạng, với tính năng dự phòng cho các phản hồi tĩnh hoặc phản hồi lưu vào bộ nhớ đệm khi cần.

Đang lưu trước vào bộ nhớ đệm bằng sw-precache

Tài nguyên tĩnh của IOWA — HTML, JavaScript, CSS và hình ảnh của IOWA — cung cấp lớp vỏ cốt lõi cho ứng dụng web. Có 2 yêu cầu cụ thể quan trọng khi cân nhắc việc lưu các tài nguyên này vào bộ nhớ đệm: chúng tôi muốn đảm bảo rằng hầu hết các tài nguyên tĩnh đều được lưu vào bộ nhớ đệm và luôn cập nhật. Chúng tôi đã xây dựng sw-precache dựa trên các yêu cầu đó.

Tích hợp tại thời điểm tạo bản dựng

sw-precache với quy trình xây dựng dựa trên gulp của IOWA, và chúng tôi dựa vào một loạt mẫu toàn cầu để đảm bảo tạo ra một danh sách đầy đủ gồm tất cả tài nguyên tĩnh mà IOWA sử dụng.

staticFileGlobs: [
    rootDir + '/bower_components/**/*.{html,js,css}',
    rootDir + '/elements/**',
    rootDir + '/fonts/**',
    rootDir + '/images/**',
    rootDir + '/scripts/**',
    rootDir + '/styles/**/*.css',
    rootDir + '/data-worker-scripts.js'
]

Các phương pháp thay thế, chẳng hạn như mã hoá cứng danh sách tên tệp vào một mảng và nhớ tăng số phiên bản bộ nhớ đệm mỗi khi có thay đổi về tệp gây ra lỗi, đặc biệt là khi chúng tôi có nhiều thành viên trong nhóm đã kiểm tra mã. Không ai muốn làm gián đoạn tính năng hỗ trợ ngoại tuyến bằng cách bỏ đi một tệp mới trong một mảng được duy trì theo cách thủ công! Việc tích hợp tại thời điểm tạo bản dựng đồng nghĩa với việc chúng tôi có thể thực hiện thay đổi đối với các tệp hiện có và thêm tệp mới mà không phải lo những điều đó.

Cập nhật tài nguyên được lưu vào bộ nhớ đệm

sw-precache tạo một tập lệnh trình chạy dịch vụ cơ sở bao gồm một hàm băm MD5 duy nhất cho mỗi tài nguyên được lưu trước vào bộ nhớ đệm. Mỗi khi một tài nguyên hiện có thay đổi hoặc một tài nguyên mới được thêm vào, tập lệnh trình chạy dịch vụ sẽ được tạo lại. Thao tác này sẽ tự động kích hoạt quy trình cập nhật trình chạy dịch vụ, trong đó các tài nguyên mới được lưu vào bộ nhớ đệm và các tài nguyên lỗi thời sẽ bị xoá hoàn toàn. Mọi tài nguyên hiện có có hàm băm MD5 giống hệt nhau vẫn được giữ nguyên. Điều đó có nghĩa là những người dùng đã truy cập trang web trước khi chỉ tải xuống một tập hợp tối thiểu các tài nguyên đã thay đổi, dẫn đến trải nghiệm hiệu quả hơn nhiều so với khi toàn bộ bộ nhớ đệm đã hết hạn nhiều.

Mỗi tệp khớp với một trong các mẫu toàn cầu sẽ được tải xuống và lưu vào bộ nhớ đệm vào lần đầu tiên người dùng truy cập IOWA. Chúng tôi đã cố gắng đảm bảo rằng chỉ các tài nguyên quan trọng cần thiết để hiển thị trang mới được lưu trước vào bộ nhớ đệm. Nội dung phụ (như nội dung nghe nhìn dùng trong thử nghiệm âm thanh/hình ảnh hoặc ảnh hồ sơ của diễn giả trong các phiên sự kiện) đã cố ý không được lưu vào bộ nhớ đệm trước. Thay vào đó, chúng tôi đã dùng thư viện sw-toolbox để xử lý các yêu cầu ngoại tuyến cho những tài nguyên đó.

sw-toolbox đáp ứng mọi nhu cầu linh động

Như đã đề cập, việc lưu trước mọi tài nguyên mà một trang web cần để hoạt động khi không có mạng là điều không khả thi. Một số tài nguyên có kích thước quá lớn hoặc không thường xuyên sử dụng nên không đáng để dùng, còn các tài nguyên khác thì lại động, chẳng hạn như phản hồi từ một API hoặc dịch vụ từ xa. Tuy nhiên, việc một yêu cầu không được lưu trước vào bộ nhớ đệm không có nghĩa là yêu cầu đó phải dẫn đến NetworkError. sw-toolbox mang lại cho chúng tôi tính linh hoạt trong việc triển khai trình xử lý yêu cầu giúp xử lý việc lưu vào bộ nhớ đệm trong thời gian chạy đối với một số tài nguyên và phương án dự phòng tuỳ chỉnh cho những tài nguyên khác. Chúng tôi cũng dùng công cụ này để cập nhật các tài nguyên đã lưu vào bộ nhớ đệm trước đây để phản hồi các thông báo đẩy.

Dưới đây là một vài ví dụ về trình xử lý yêu cầu tuỳ chỉnh mà chúng tôi đã xây dựng dựa trên sw-toolbox. Bạn có thể dễ dàng tích hợp chúng với tập lệnh trình chạy dịch vụ cơ sở thông qua importScripts parameter của sw-precache. Tập lệnh này sẽ đưa các tệp JavaScript độc lập vào phạm vi của trình chạy dịch vụ.

Thử nghiệm âm thanh/hình ảnh

Đối với thử nghiệm về âm thanh/hình ảnh, chúng tôi đã sử dụng chiến lược bộ nhớ đệm networkFirst của sw-toolbox. Trước tiên, tất cả các yêu cầu HTTP khớp với mẫu URL cho thử nghiệm sẽ được thực hiện trên mạng và nếu một phản hồi thành công được trả về, thì phản hồi đó sẽ được lưu trữ bằng cách sử dụng API Bộ nhớ bộ nhớ đệm. Nếu bạn đưa ra yêu cầu tiếp theo khi mạng không hoạt động, thì phản hồi đã lưu vào bộ nhớ đệm trước đó sẽ được sử dụng.

Vì bộ nhớ đệm được tự động cập nhật mỗi khi có phản hồi mạng thành công, nên chúng tôi không cần phải tạo phiên bản cụ thể cho tài nguyên hoặc mục nhập hết hạn.

toolbox.router.get('/experiment/(.+)', toolbox.networkFirst);

Ảnh hồ sơ người nói

Đối với hình ảnh hồ sơ người nói, mục tiêu của chúng tôi là hiển thị phiên bản hình ảnh của một người nói đã lưu vào bộ nhớ đệm trước đó (nếu có). Nếu không có, hãy quay lại mạng để truy xuất hình ảnh. Nếu yêu cầu mạng đó không thành công, để dự phòng, chúng tôi đã sử dụng hình ảnh giữ chỗ chung được lưu trước vào bộ nhớ đệm (và do đó sẽ luôn có sẵn). Đây là một chiến lược phổ biến nên sử dụng khi xử lý các hình ảnh có thể được thay thế bằng một phần giữ chỗ chung, và có thể dễ dàng triển khai bằng cách tạo chuỗi trình xử lý cacheFirstcacheOnly của sw-toolbox.

var DEFAULT_PROFILE_IMAGE = 'images/touch/homescreen96.png';

function profileImageRequest(request) {
    return toolbox.cacheFirst(request).catch(function() {
    return toolbox.cacheOnly(new Request(DEFAULT_PROFILE_IMAGE));
    });
}

toolbox.precache([DEFAULT_PROFILE_IMAGE]);
toolbox.router.get('/(.+)/images/speakers/(.*)',
                    profileImageRequest,
                    {origin: /.*\.googleapis\.com/});
Ảnh hồ sơ trên một trang của phiên hoạt động
Ảnh hồ sơ từ trang của phiên hoạt động.

Cập nhật lịch biểu của người dùng

Một trong những tính năng chính của IOWA là cho phép người dùng đã đăng nhập tạo và duy trì lịch biểu cho các phiên mà họ dự định tham gia. Như bạn mong đợi, các phiên cập nhật được thực hiện thông qua các yêu cầu HTTP POST tới máy chủ phụ trợ và chúng tôi đã dành thời gian để tìm cách tốt nhất để xử lý các yêu cầu sửa đổi trạng thái đó khi người dùng không kết nối mạng. Chúng tôi đã kết hợp việc kết hợp các yêu cầu không thành công đã đưa vào hàng đợi trong IndexedDB, kết hợp với logic trên trang web chính để kiểm tra IndexedDB cho các yêu cầu đã xếp hàng và thử lại mọi yêu cầu tìm thấy.

var DB_NAME = 'shed-offline-session-updates';

function queueFailedSessionUpdateRequest(request) {
    simpleDB.open(DB_NAME).then(function(db) {
    db.set(request.url, request.method);
    });
}

function handleSessionUpdateRequest(request) {
    return global.fetch(request).then(function(response) {
    if (response.status >= 500) {
        return Response.error();
    }
    return response;
    }).catch(function() {
    queueFailedSessionUpdateRequest(request);
    });
}

toolbox.router.put('/(.+)api/v1/user/schedule/(.+)',
                    handleSessionUpdateRequest);
toolbox.router.delete('/(.+)api/v1/user/schedule/(.+)',
                        handleSessionUpdateRequest);

Vì các lần thử lại được thực hiện từ ngữ cảnh của trang chính, nên chúng tôi có thể chắc chắn rằng các lần thử lại bao gồm một bộ thông tin đăng nhập người dùng mới. Sau khi thử lại thành công, chúng tôi sẽ hiển thị thông báo để người dùng biết rằng các bản cập nhật trong hàng đợi trước đó của họ đã được áp dụng.

simpleDB.open(QUEUED_SESSION_UPDATES_DB_NAME).then(function(db) {
    var replayPromises = [];
    return db.forEach(function(url, method) {
    var promise = IOWA.Request.xhrPromise(method, url, true).then(function() {
        return db.delete(url).then(function() {
        return true;
        });
    });
    replayPromises.push(promise);
    }).then(function() {
    if (replayPromises.length) {
        return Promise.all(replayPromises).then(function() {
        IOWA.Elements.Toast.showMessage(
            'My Schedule was updated with offline changes.');
        });
    }
    });
}).catch(function() {
    IOWA.Elements.Toast.showMessage(
    'Offline changes could not be applied to My Schedule.');
});

Google Analytics ngoại tuyến

Tương tự như vậy, chúng tôi đã triển khai một trình xử lý để đưa mọi yêu cầu Google Analytics không thành công vào hàng đợi và cố gắng phát lại các yêu cầu đó sau đó khi mạng hy vọng đã có thể sử dụng được. Với cách tiếp cận này, việc ngoại tuyến không có nghĩa là hy sinh thông tin chi tiết mà Google Analytics cung cấp. Chúng tôi thêm thông số qt vào mỗi yêu cầu đã xếp hàng đợi, đặt thành khoảng thời gian đã trôi qua kể từ lần đầu yêu cầu, để đảm bảo rằng thời gian phân bổ sự kiện thích hợp đã được đưa đến phần phụ trợ của Google Analytics. Google Analytics chính thức hỗ trợ các giá trị qt chỉ trong tối đa 4 giờ. Vì vậy, chúng tôi đã cố gắng hết sức để phát lại các yêu cầu đó càng sớm càng tốt, mỗi khi trình chạy dịch vụ khởi động.

var DB_NAME = 'offline-analytics';
var EXPIRATION_TIME_DELTA = 86400000;
var ORIGIN = /https?:\/\/((www|ssl)\.)?google-analytics\.com/;

function replayQueuedAnalyticsRequests() {
    simpleDB.open(DB_NAME).then(function(db) {
    db.forEach(function(url, originalTimestamp) {
        var timeDelta = Date.now() - originalTimestamp;
        var replayUrl = url + '&qt=' + timeDelta;
        fetch(replayUrl).then(function(response) {
        if (response.status >= 500) {
            return Response.error();
        }
        db.delete(url);
        }).catch(function(error) {
        if (timeDelta > EXPIRATION_TIME_DELTA) {
            db.delete(url);
        }
        });
    });
    });
}

function queueFailedAnalyticsRequest(request) {
    simpleDB.open(DB_NAME).then(function(db) {
    db.set(request.url, Date.now());
    });
}

function handleAnalyticsCollectionRequest(request) {
    return global.fetch(request).then(function(response) {
    if (response.status >= 500) {
        return Response.error();
    }
    return response;
    }).catch(function() {
    queueFailedAnalyticsRequest(request);
    });
}

toolbox.router.get('/collect',
                    handleAnalyticsCollectionRequest,
                    {origin: ORIGIN});
toolbox.router.get('/analytics.js',
                    toolbox.networkFirst,
                    {origin: ORIGIN});

replayQueuedAnalyticsRequests();

Trang đích của thông báo đẩy

Trình chạy dịch vụ không chỉ xử lý chức năng ngoại tuyến của IOWA, mà còn cung cấp thông báo đẩy mà chúng tôi dùng để thông báo cho người dùng về nội dung cập nhật cho các phiên được đánh dấu. Trang đích liên kết với các thông báo đó hiển thị thông tin chi tiết về phiên được cập nhật. Những trang đích đó đã được lưu vào bộ nhớ đệm dưới dạng một phần của trang web tổng thể, vì vậy, các trang đích đó đã hoạt động khi không có mạng, nhưng chúng tôi cần đảm bảo rằng thông tin về phiên hoạt động trên trang đó đã được cập nhật, ngay cả khi người dùng xem được khi không có mạng. Để làm điều đó, chúng tôi đã sửa đổi siêu dữ liệu về phiên đã lưu vào bộ nhớ đệm trước đó bằng các bản cập nhật đã kích hoạt thông báo đẩy. Sau đó, chúng tôi đã lưu kết quả vào bộ nhớ đệm. Thông tin mới nhất này sẽ được sử dụng vào lần tiếp theo bạn mở trang chi tiết về phiên, cho dù diễn ra trực tuyến hay ngoại tuyến.

caches.open(toolbox.options.cacheName).then(function(cache) {
    cache.match('api/v1/schedule').then(function(response) {
    if (response) {
        parseResponseJSON(response).then(function(schedule) {
        sessions.forEach(function(session) {
            schedule.sessions[session.id] = session;
        });
        cache.put('api/v1/schedule',
                    new Response(JSON.stringify(schedule)));
        });
    } else {
        toolbox.cache('api/v1/schedule');
    }
    });
});

Những vấn đề cần khắc phục và những điểm cần cân nhắc

Tất nhiên, không ai triển khai dự án có quy mô IOWA mà không gặp phải một vài giao diện người dùng. Sau đây là một số vấn đề mà chúng tôi đã gặp phải và cách chúng tôi giải quyết các vấn đề đó.

Nội dung cũ

Bất cứ khi nào bạn lập kế hoạch cho một chiến lược lưu vào bộ nhớ đệm, dù được triển khai thông qua trình chạy dịch vụ hay bộ nhớ đệm tiêu chuẩn của trình duyệt, thì luôn có sự đánh đổi giữa việc phân phối tài nguyên nhanh nhất có thể và phân phối tài nguyên mới nhất. Thông qua sw-precache, chúng tôi đã triển khai chiến lược ưu tiên bộ nhớ đệm linh hoạt cho shell của ứng dụng, tức là trình chạy dịch vụ sẽ không kiểm tra mạng để cập nhật trước khi trả về HTML, JavaScript và CSS trên trang.

May mắn là chúng tôi đã có thể tận dụng các sự kiện trong vòng đời của trình chạy dịch vụ để phát hiện thời điểm có nội dung mới sau khi trang đã tải. Khi phát hiện một trình chạy dịch vụ được cập nhật, chúng tôi sẽ hiển thị một thông báo ngắn cho người dùng để họ biết rằng họ nên tải lại trang để xem nội dung mới nhất.

if (navigator.serviceWorker && navigator.serviceWorker.controller) {
    navigator.serviceWorker.controller.onstatechange = function(e) {
    if (e.target.state === 'redundant') {
        var tapHandler = function() {
        window.location.reload();
        };
        IOWA.Elements.Toast.showMessage(
        'Tap here or refresh the page for the latest content.',
        tapHandler);
    }
    };
}
Thông báo ngắn về nội dung mới nhất
Thông báo ngắn về "nội dung mới nhất".

Hãy đảm bảo rằng nội dung tĩnh là nội dung tĩnh!

sw-precache sử dụng hàm băm MD5 cho nội dung của tệp cục bộ và chỉ tìm nạp các tài nguyên có hàm băm đã thay đổi. Tức là các tài nguyên có sẵn trên trang gần như ngay lập tức, nhưng cũng có nghĩa là sau khi nội dung nào đó được lưu vào bộ nhớ đệm, nội dung đó sẽ vẫn được lưu vào bộ nhớ đệm cho đến khi được gán một hàm băm mới trong một tập lệnh trình chạy dịch vụ đã cập nhật.

Chúng tôi gặp vấn đề về hành vi này trong quá trình I/O do phần phụ trợ của chúng tôi cần phải tự động cập nhật mã video phát trực tiếp trên YouTube cho mỗi ngày diễn ra hội nghị. Do tệp mẫu cơ sở tĩnh và không thay đổi, nên quy trình cập nhật trình chạy dịch vụ của chúng tôi đã không được kích hoạt và là phản hồi động từ máy chủ với việc cập nhật video trên YouTube đã trở thành phản hồi được lưu vào bộ nhớ đệm cho một số người dùng.

Bạn có thể tránh loại vấn đề này bằng cách đảm bảo ứng dụng web của mình có cấu trúc để shell luôn ở dạng tĩnh và có thể được lưu vào bộ nhớ đệm trước một cách an toàn, trong khi mọi tài nguyên động sửa đổi shell đều được tải một cách độc lập.

Xoá các yêu cầu lưu trước vào bộ nhớ đệm

Khi sw-precache yêu cầu tài nguyên lưu trước vào bộ nhớ đệm, ứng dụng sẽ sử dụng các phản hồi đó vô thời hạn, miễn là hàm băm MD5 cho tệp không thay đổi. Điều này có nghĩa là một điều đặc biệt quan trọng là phải đảm bảo rằng phản hồi cho yêu cầu lưu trước vào bộ nhớ đệm là một yêu cầu mới và không được trả về từ bộ nhớ đệm HTTP của trình duyệt. (Có, các yêu cầu fetch() được thực hiện trong một trình chạy dịch vụ có thể phản hồi bằng dữ liệu từ bộ nhớ đệm HTTP của trình duyệt.)

Để đảm bảo các phản hồi chúng tôi lưu vào bộ nhớ đệm trước đều lấy từ mạng chứ không phải bộ nhớ đệm HTTP của trình duyệt, sw-precache sẽ tự động thêm tham số truy vấn chặn truy xuất bộ nhớ đệm vào từng URL mà công cụ này yêu cầu. Nếu bạn không dùng sw-precache và đang sử dụng chiến lược phản hồi ưu tiên bộ nhớ đệm, hãy đảm bảo bạn thực hiện thao tác tương tự trong mã của riêng mình!

Một giải pháp gọn gàng hơn đối với quá trình chặn truy xuất bộ nhớ đệm là đặt chế độ bộ nhớ đệm của mỗi Request dùng để lưu trước vào bộ nhớ đệm thành reload, điều này giúp đảm bảo rằng phản hồi đến từ mạng. Tuy nhiên, kể từ khi viết bài này, tuỳ chọn chế độ bộ nhớ đệm không được hỗ trợ trong Chrome.

Hỗ trợ đăng nhập và đăng xuất

IOWA cho phép người dùng đăng nhập bằng Tài khoản Google và cập nhật lịch biểu sự kiện tuỳ chỉnh, nhưng điều đó cũng có nghĩa là sau đó người dùng có thể đăng xuất. Việc lưu dữ liệu phản hồi được cá nhân hoá vào bộ nhớ đệm rõ ràng là một chủ đề phức tạp và không phải lúc nào cũng có một phương pháp phù hợp.

Vì việc xem lịch biểu cá nhân của bạn, ngay cả khi ngoại tuyến, là yếu tố cốt lõi đối với trải nghiệm IOWA, nên chúng tôi quyết định rằng việc sử dụng dữ liệu đã lưu vào bộ nhớ đệm là phù hợp. Khi người dùng đăng xuất, chúng tôi đảm bảo sẽ xoá dữ liệu về phiên đã lưu vào bộ nhớ đệm trước đó.

    self.addEventListener('message', function(event) {
      if (event.data === 'clear-cached-user-data') {
        caches.open(toolbox.options.cacheName).then(function(cache) {
          cache.keys().then(function(requests) {
            return requests.filter(function(request) {
              return request.url.indexOf('api/v1/user/') !== -1;
            });
          }).then(function(userDataRequests) {
            userDataRequests.forEach(function(userDataRequest) {
              cache.delete(userDataRequest);
            });
          });
        });
      }
    });

Chú ý đến các tham số truy vấn bổ sung!

Khi kiểm tra một phản hồi được lưu vào bộ nhớ đệm, một trình chạy dịch vụ sẽ sử dụng URL yêu cầu làm khoá. Theo mặc định, URL yêu cầu phải khớp chính xác với URL dùng để lưu trữ phản hồi được lưu vào bộ nhớ đệm, bao gồm mọi tham số truy vấn trong phần tìm kiếm của URL.

Điều này đã gây ra một vấn đề cho chúng tôi trong quá trình phát triển, khi chúng tôi bắt đầu sử dụng tham số URL để theo dõi nguồn lưu lượng truy cập. Ví dụ: chúng tôi đã thêm tham số utm_source=notification vào những URL được mở khi người dùng nhấp vào một trong các thông báo của chúng tôi và sử dụng utm_source=web_app_manifest trong start_url cho tệp kê khai ứng dụng web. Những URL trước đây khớp với phản hồi đã lưu vào bộ nhớ đệm sẽ bị bỏ lỡ khi các tham số đó được thêm vào.

Vấn đề này đã được giải quyết một phần bằng tuỳ chọn ignoreSearch. Bạn có thể sử dụng tuỳ chọn này khi gọi Cache.match(). Rất tiếc, Chrome chưa hỗ trợ ignoreSearch và ngay cả khi có hỗ trợ, thì đó cũng là hành vi tất cả. Điều chúng tôi cần là một cách bỏ qua một số tham số truy vấn URL trong khi vẫn cân nhắc đến những tham số khác có ý nghĩa.

Cuối cùng, chúng tôi đã mở rộng sw-precache để loại bỏ một số tham số truy vấn trước khi kiểm tra xem bộ nhớ đệm có trùng khớp hay không, và cho phép nhà phát triển tuỳ chỉnh những tham số sẽ bị bỏ qua thông qua tuỳ chọn ignoreUrlParametersMatching. Dưới đây là cách triển khai cơ bản:

function stripIgnoredUrlParameters(originalUrl, ignoredRegexes) {
    var url = new URL(originalUrl);

    url.search = url.search.slice(1)
    .split('&')
    .map(function(kv) {
        return kv.split('=');
    })
    .filter(function(kv) {
        return ignoredRegexes.every(function(ignoredRegex) {
        return !ignoredRegex.test(kv[0]);
        });
    })
    .map(function(kv) {
        return kv.join('=');
    })
    .join('&');

    return url.toString();
}

Ý nghĩa của điều này đối với bạn

Việc tích hợp trình chạy dịch vụ trong Ứng dụng web Google I/O có thể là cách sử dụng phức tạp nhất trong thực tế đã được triển khai cho đến thời điểm này. Chúng tôi mong muốn cộng đồng nhà phát triển web sử dụng các công cụ chúng tôi tạo ra sw-precachesw-toolbox cũng như các kỹ thuật mà chúng tôi đang mô tả để hỗ trợ ứng dụng web của bạn. Trình chạy dịch vụ là một tính năng nâng cao tiến bộ mà bạn có thể bắt đầu sử dụng ngay hôm nay. Khi được sử dụng như một phần của một ứng dụng web có cấu trúc phù hợp, tốc độ và các lợi ích ngoại tuyến đóng vai trò rất quan trọng đối với người dùng của bạn.