It’s great that wordpress has made it possible for us to custom the navigation menus with just several lines of codes. More details are in codex but this is not our today’s topic.

wp_nav_menu is the function to display our custom menus created in Appearance->Menus panel. This is a great function but it’s not perfect. It may generate quite a few SQL queries and more if there are too many items in your menu. As a result it may hurt your blog speed performance.

In general your  menus cannot be updated frequently so it’s a better idea to cache it by setting an expired period. To make this possible, let’s welcome Transients API and pre_wp_nav_menu (Requires WP 3.9+).

Long story short, put following codes to your functions.php and you are done.

/**
 * Cache wp_nav_menu to speed up your wordpress blog
 * http://clonetemplates.com/?p=562
 */
add_filter( 'pre_wp_nav_menu', 'ct_get_nav_menu_cache', 10, 2 );
function ct_get_nav_menu_cache( $nav_menu, $args ) {
    $cache_key      = ct_get_nav_menu_cache_key($args);
    $cached_menu    = get_transient( $cache_key );
    if ( ! empty( $cached_menu ) )
        return $cached_menu;

    return $nav_menu;
}

add_filter( 'wp_nav_menu', 'ct_set_nav_menu_cache', 10, 2 );
function ct_set_nav_menu_cache( $nav_menu, $args ) {
    $cache_key      = ct_get_nav_menu_cache_key($args);
    set_transient( $cache_key, $nav_menu, 86400 );

    return $nav_menu;
}

function ct_get_nav_menu_cache_key($args){
    $timestamp = get_transient('nav-menu-cache-timestamp');
    if($time === false){
        $timestamp = time();
        set_transient( 'nav-menu-cache-timestamp', $time, 86400 );
    }
    return apply_filters( 'nav_menu_cache_key' , 'nav-menu-' . md5( serialize( $args ) ) . $timestamp );
}

// delete the cache when update the menu
add_action( 'wp_update_nav_menu', 'ct_delete_nav_menu_cache' );
function ct_delete_nav_menu_cache( $menu_id, $menu_data){
    set_transient( 'nav-menu-cache-timestamp', time(), 86400 );
}