Chromium 中chrome.bookmarks扩展接口c++实现

一、扩展接口定义 chrome.bookmarks

使用 chrome.bookmarks API 创建、整理以及以其他方式操纵书签。另请参阅覆盖网页(可用于创建自定义"书签管理器"页面)。

更多参考chrome.bookmarks | API | Chrome for Developers (google.cn)

扩展可以请从 chrome-extension-samples 安装 bookmarks API 示例 存储库

二、扩展接口c++定义

chrome\common\extensions\api\bookmarks.json

cpp 复制代码
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

[
  {
    "namespace": "bookmarks",
    "description": "Use the <code>chrome.bookmarks</code> API to create, organize, and otherwise manipulate bookmarks. Also see <a href='override'>Override Pages</a>, which you can use to create a custom Bookmark Manager page.",
    "properties": {
      "MAX_WRITE_OPERATIONS_PER_HOUR": {
        "value": 1000000,
        "deprecated": "Bookmark write operations are no longer limited by Chrome.",
        "description": ""
      },
      "MAX_SUSTAINED_WRITE_OPERATIONS_PER_MINUTE": {
        "value": 1000000,
        "deprecated": "Bookmark write operations are no longer limited by Chrome.",
        "description": ""
      }
    },
    "types": [
      {
        "id": "BookmarkTreeNodeUnmodifiable",
        "type": "string",
        "enum": ["managed"],
        "description": "Indicates the reason why this node is unmodifiable. The <var>managed</var> value indicates that this node was configured by the system administrator. Omitted if the node can be modified by the user and the extension (default)."
      },
      {
        "id": "BookmarkTreeNode",
        "type": "object",
        "description": "A node (either a bookmark or a folder) in the bookmark tree.  Child nodes are ordered within their parent folder.",
        "properties": {
          "id": {
            "type": "string",
            "minimum": 0,
            "description": "The unique identifier for the node. IDs are unique within the current profile, and they remain valid even after the browser is restarted."
          },
          "parentId": {
            "type": "string",
            "minimum": 0,
            "optional": true,
            "description": "The <code>id</code> of the parent folder.  Omitted for the root node."
          },
          "index": {
            "type": "integer",
            "optional": true,
            "description": "The 0-based position of this node within its parent folder."
          },
          "url": {
            "type": "string",
            "optional": true,
            "description": "The URL navigated to when a user clicks the bookmark. Omitted for folders."
          },
          "title": {
            "type": "string",
            "description": "The text displayed for the node."
          },
          "dateAdded": {
            "type": "number",
            "optional": true,
            "description": "When this node was created, in milliseconds since the epoch (<code>new Date(dateAdded)</code>)."
          },
          "dateLastUsed": {
            "type": "number",
            "optional": true,
            "description": "When this node was last opened, in milliseconds since the epoch. Not set for folders."
          },
          "dateGroupModified": {
            "type": "number",
            "optional": true,
            "description": "When the contents of this folder last changed, in milliseconds since the epoch."
          },
          "unmodifiable": {
            "$ref": "BookmarkTreeNodeUnmodifiable",
            "optional": true,
            "description": "Indicates the reason why this node is unmodifiable. The <var>managed</var> value indicates that this node was configured by the system administrator or by the custodian of a supervised user. Omitted if the node can be modified by the user and the extension (default)."
          },
          "children": {
            "type": "array",
            "optional": true,
            "items": { "$ref": "BookmarkTreeNode" },
            "description": "An ordered list of children of this node."
          }
        }
      },
      {
        "id": "CreateDetails",
        "description": "Object passed to the create() function.",
        "inline_doc": true,
        "type": "object",
        "properties": {
          "parentId": {
            "type": "string",
            "serialized_type": "int64",
            "optional": true,
            "description": "Defaults to the Other Bookmarks folder."
          },
          "index": {
            "type": "integer",
            "minimum": 0,
            "optional": true
          },
          "title": {
            "type": "string",
            "optional": true
          },
          "url": {
            "type": "string",
            "optional": true
          }
        }
      }
    ],
    "functions": [
      {
        "name": "get",
        "type": "function",
        "description": "Retrieves the specified BookmarkTreeNode(s).",
        "parameters": [
          {
            "name": "idOrIdList",
            "description": "A single string-valued id, or an array of string-valued ids",
            "choices": [
              {
                "type": "string",
                "serialized_type": "int64"
              },
              {
                "type": "array",
                "items": {
                  "type": "string",
                  "serialized_type": "int64"
                },
                "minItems": 1
              }
            ]
          }
        ],
        "returns_async": {
          "name": "callback",
          "parameters": [
            {
              "name": "results",
              "type": "array",
              "items": { "$ref": "BookmarkTreeNode" }
            }
          ]
        }
      },
      {
        "name": "getChildren",
        "type": "function",
        "description": "Retrieves the children of the specified BookmarkTreeNode id.",
        "parameters": [
          {
            "type": "string",
            "serialized_type": "int64",
            "name": "id"
          }
        ],
        "returns_async": {
          "name": "callback",
          "parameters": [
            {
              "name": "results",
              "type": "array",
              "items": { "$ref": "BookmarkTreeNode"}
            }
          ]
        }
      },
      {
        "name": "getRecent",
        "type": "function",
        "description": "Retrieves the recently added bookmarks.",
        "parameters": [
          {
            "type": "integer",
            "minimum": 1,
            "name": "numberOfItems",
            "description": "The maximum number of items to return."
          }
        ],
        "returns_async": {
          "name": "callback",
          "parameters": [
            {
              "name": "results",
              "type": "array",
              "items": { "$ref": "BookmarkTreeNode" }
            }
          ]
        }
      },
      {
        "name": "getTree",
        "type": "function",
        "description": "Retrieves the entire Bookmarks hierarchy.",
        "parameters": [],
        "returns_async": {
          "name": "callback",
          "parameters": [
            {
              "name": "results",
              "type": "array",
              "items": { "$ref": "BookmarkTreeNode" }
            }
          ]
        }
      },
      {
        "name": "getSubTree",
        "type": "function",
        "description": "Retrieves part of the Bookmarks hierarchy, starting at the specified node.",
        "parameters": [
          {
            "type": "string",
            "serialized_type": "int64",
            "name": "id",
            "description": "The ID of the root of the subtree to retrieve."
          }
        ],
        "returns_async": {
          "name": "callback",
          "parameters": [
            {
              "name": "results",
              "type": "array",
              "items": { "$ref": "BookmarkTreeNode" }
            }
          ]
        }
      },
      {
        "name": "search",
        "type": "function",
        "description": "Searches for BookmarkTreeNodes matching the given query. Queries specified with an object produce BookmarkTreeNodes matching all specified properties.",
        "parameters": [
          {
            "name": "query",
            "description": "Either a string of words and quoted phrases that are matched against bookmark URLs and titles, or an object. If an object, the properties <code>query</code>, <code>url</code>, and <code>title</code> may be specified and bookmarks matching all specified properties will be produced.",
            "choices": [
              {
                "type": "string",
                "description": "A string of words and quoted phrases that are matched against bookmark URLs and titles."
              },
              {
                "type": "object",
                "description": "An object specifying properties and values to match when searching. Produces bookmarks matching all properties.",
                "properties": {
                  "query": {
                    "type": "string",
                    "optional": true,
                    "description": "A string of words and quoted phrases that are matched against bookmark URLs and titles."
                  },
                  "url": {
                    "type": "string",
                    "optional": true,
                    "description": "The URL of the bookmark; matches verbatim. Note that folders have no URL."
                  },
                  "title": {
                    "type": "string",
                    "optional": true,
                    "description": "The title of the bookmark; matches verbatim."
                  }
                }
              }
            ]
          }
        ],
        "returns_async": {
          "name": "callback",
          "parameters": [
            {
              "name": "results",
              "type": "array",
              "items": { "$ref": "BookmarkTreeNode" }
            }
          ]
        }
      },
      {
        "name": "create",
        "type": "function",
        "description": "Creates a bookmark or folder under the specified parentId.  If url is NULL or missing, it will be a folder.",
        "parameters": [
          {
            "$ref": "CreateDetails",
            "name": "bookmark"
          }
        ],
        "returns_async": {
          "name": "callback",
          "optional": true,
          "parameters": [
            {
              "name": "result",
              "$ref": "BookmarkTreeNode"
            }
          ]
        }
      },
      {
        "name": "move",
        "type": "function",
        "description": "Moves the specified BookmarkTreeNode to the provided location.",
        "parameters": [
          {
            "type": "string",
            "serialized_type": "int64",
            "name": "id"
          },
          {
            "type": "object",
            "name": "destination",
            "properties": {
              "parentId": {
                "type": "string",
                "optional": true
              },
              "index": {
                "type": "integer",
                "minimum": 0,
                "optional": true
              }
            }
          }
        ],
        "returns_async": {
          "name": "callback",
          "optional": true,
          "parameters": [
            {
              "name": "result",
              "$ref": "BookmarkTreeNode"
            }
          ]
        }
      },
      {
        "name": "update",
        "type": "function",
        "description": "Updates the properties of a bookmark or folder. Specify only the properties that you want to change; unspecified properties will be left unchanged.  <b>Note:</b> Currently, only 'title' and 'url' are supported.",
        "parameters": [
          {
            "type": "string",
            "serialized_type": "int64",
            "name": "id"
          },
          {
            "type": "object",
            "name": "changes",
            "properties": {
              "title": {
                "type": "string",
                "optional": true
              },
              "url": {
                "type": "string",
                "optional": true
              }
            }
          }
        ],
        "returns_async": {
          "name": "callback",
          "optional": true,
          "parameters": [
            {
              "name": "result",
              "$ref": "BookmarkTreeNode"
            }
          ]
        }
      },
      {
        "name": "remove",
        "type": "function",
        "description": "Removes a bookmark or an empty bookmark folder.",
        "parameters": [
          {
            "type": "string",
            "serialized_type": "int64",
            "name": "id"
          }
        ],
        "returns_async": {
          "name": "callback",
          "optional": true,
          "parameters": []
        }
      },
      {
        "name": "removeTree",
        "type": "function",
        "description": "Recursively removes a bookmark folder.",
        "parameters": [
          {
            "type": "string",
            "serialized_type": "int64",
            "name": "id"
          }
        ],
        "returns_async": {
          "name": "callback",
          "optional": true,
          "parameters": []
        }
      }
    ],
    "events": [
      {
        "name": "onCreated",
        "type": "function",
        "description": "Fired when a bookmark or folder is created.",
        "parameters": [
          {
            "type": "string",
            "name": "id"
          },
          {
            "$ref": "BookmarkTreeNode",
            "name": "bookmark"
          }
        ]
      },
      {
        "name": "onRemoved",
        "type": "function",
        "description": "Fired when a bookmark or folder is removed.  When a folder is removed recursively, a single notification is fired for the folder, and none for its contents.",
        "parameters": [
          {
            "type": "string",
            "name": "id"
          },
          {
            "type": "object",
            "name": "removeInfo",
            "properties": {
              "parentId": { "type": "string" },
              "index": { "type": "integer" },
              "node": { "$ref": "BookmarkTreeNode" }
            }
          }
        ]
      },
      {
        "name": "onChanged",
        "type": "function",
        "description": "Fired when a bookmark or folder changes.  <b>Note:</b> Currently, only title and url changes trigger this.",
        "parameters": [
          {
            "type": "string",
            "name": "id"
          },
          {
            "type": "object",
            "name": "changeInfo",
            "properties": {
              "title": { "type": "string" },
              "url": {
                "type": "string",
                "optional": true
              }
            }
          }
        ]
      },
      {
        "name": "onMoved",
        "type": "function",
        "description": "Fired when a bookmark or folder is moved to a different parent folder.",
        "parameters": [
          {
            "type": "string",
            "name": "id"
          },
          {
            "type": "object",
            "name": "moveInfo",
            "properties": {
              "parentId": { "type": "string" },
              "index": { "type": "integer" },
              "oldParentId": { "type": "string" },
              "oldIndex": { "type": "integer" }
            }
          }
        ]
      },
      {
        "name": "onChildrenReordered",
        "type": "function",
        "description": "Fired when the children of a folder have changed their order due to the order being sorted in the UI.  This is not called as a result of a move().",
        "parameters": [
          {
            "type": "string",
            "name": "id"
          },
          {
            "type": "object",
            "name": "reorderInfo",
            "properties": {
              "childIds": {
                "type": "array",
                "items": { "type": "string" }
              }
            }
          }
        ]
      },
      {
        "name": "onImportBegan",
        "type": "function",
        "description": "Fired when a bookmark import session is begun.  Expensive observers should ignore onCreated updates until onImportEnded is fired.  Observers should still handle other notifications immediately.",
        "parameters": []
      },
      {
        "name": "onImportEnded",
        "type": "function",
        "description": "Fired when a bookmark import session is ended.",
        "parameters": []
      }
    ]
  }
]

bookmarks.json生成对应文件:

out\Debug\gen\chrome\common\extensions\api\bookmarks.cc

out\Debug\gen\chrome\common\extensions\api\bookmarks.h

三、bookmarks函数实现:

chrome\browser\extensions\api\bookmarks\bookmarks_api.h

chrome\browser\extensions\api\bookmarks\bookmarks_api.cc

cpp 复制代码
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_BROWSER_EXTENSIONS_API_BOOKMARKS_BOOKMARKS_API_H_
#define CHROME_BROWSER_EXTENSIONS_API_BOOKMARKS_BOOKMARKS_API_H_

#include <stdint.h>

#include <memory>
#include <set>
#include <string>
#include <vector>

#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/values.h"
#include "components/bookmarks/browser/base_bookmark_model_observer.h"
#include "components/bookmarks/browser/bookmark_node.h"
#include "extensions/browser/browser_context_keyed_api_factory.h"
#include "extensions/browser/event_router.h"
#include "extensions/browser/extension_function.h"
#include "ui/shell_dialogs/select_file_dialog.h"

class Profile;

namespace base {
class FilePath;
}

namespace bookmarks {
class BookmarkModel;
class ManagedBookmarkService;
}

namespace content {
class BrowserContext;
}

namespace extensions {

namespace api {
namespace bookmarks {
struct CreateDetails;
}
}

// Observes BookmarkModel and then routes the notifications as events to
// the extension system.
class BookmarkEventRouter : public bookmarks::BookmarkModelObserver {
 public:
  explicit BookmarkEventRouter(Profile* profile);
  BookmarkEventRouter(const BookmarkEventRouter&) = delete;
  BookmarkEventRouter& operator=(const BookmarkEventRouter&) = delete;
  ~BookmarkEventRouter() override;

  // bookmarks::BookmarkModelObserver:
  void BookmarkModelLoaded(bookmarks::BookmarkModel* model,
                           bool ids_reassigned) override;
  void BookmarkModelBeingDeleted(bookmarks::BookmarkModel* model) override;
  void BookmarkNodeMoved(bookmarks::BookmarkModel* model,
                         const bookmarks::BookmarkNode* old_parent,
                         size_t old_index,
                         const bookmarks::BookmarkNode* new_parent,
                         size_t new_index) override;
  void BookmarkNodeAdded(bookmarks::BookmarkModel* model,
                         const bookmarks::BookmarkNode* parent,
                         size_t index,
                         bool added_by_user) override;
  void BookmarkNodeRemoved(bookmarks::BookmarkModel* model,
                           const bookmarks::BookmarkNode* parent,
                           size_t old_index,
                           const bookmarks::BookmarkNode* node,
                           const std::set<GURL>& removed_urls) override;
  void BookmarkAllUserNodesRemoved(bookmarks::BookmarkModel* model,
                                   const std::set<GURL>& removed_urls) override;
  void BookmarkNodeChanged(bookmarks::BookmarkModel* model,
                           const bookmarks::BookmarkNode* node) override;
  void BookmarkNodeFaviconChanged(bookmarks::BookmarkModel* model,
                                  const bookmarks::BookmarkNode* node) override;
  void BookmarkNodeChildrenReordered(
      bookmarks::BookmarkModel* model,
      const bookmarks::BookmarkNode* node) override;
  void ExtensiveBookmarkChangesBeginning(
      bookmarks::BookmarkModel* model) override;
  void ExtensiveBookmarkChangesEnded(bookmarks::BookmarkModel* model) override;

 private:
  // Helper to actually dispatch an event to extension listeners.
  void DispatchEvent(events::HistogramValue histogram_value,
                     const std::string& event_name,
                     base::Value::List event_args);

  raw_ptr<content::BrowserContext> browser_context_;
  raw_ptr<bookmarks::BookmarkModel> model_;
  raw_ptr<bookmarks::ManagedBookmarkService> managed_;
};

class BookmarksAPI : public BrowserContextKeyedAPI,
                     public EventRouter::Observer {
 public:
  explicit BookmarksAPI(content::BrowserContext* context);
  ~BookmarksAPI() override;

  // KeyedService implementation.
  void Shutdown() override;

  // BrowserContextKeyedAPI implementation.
  static BrowserContextKeyedAPIFactory<BookmarksAPI>* GetFactoryInstance();

  // EventRouter::Observer implementation.
  void OnListenerAdded(const EventListenerInfo& details) override;

 private:
  friend class BrowserContextKeyedAPIFactory<BookmarksAPI>;

  raw_ptr<content::BrowserContext> browser_context_;

  // BrowserContextKeyedAPI implementation.
  static const char* service_name() {
    return "BookmarksAPI";
  }
  static const bool kServiceIsNULLWhileTesting = true;

  // Created lazily upon OnListenerAdded.
  std::unique_ptr<BookmarkEventRouter> bookmark_event_router_;
};

class BookmarksFunction : public ExtensionFunction,
                          public bookmarks::BaseBookmarkModelObserver {
 public:
  // ExtensionFunction:
  ResponseAction Run() override;

 protected:
  ~BookmarksFunction() override {}

  // Run semantic equivalent called when the bookmarks are ready.
  // Overrides can return nullptr to further delay responding (a.k.a.
  // RespondLater()).
  virtual ResponseValue RunOnReady() = 0;

  // Helper to get the BookmarkModel.
  bookmarks::BookmarkModel* GetBookmarkModel();

  // Helper to get the ManagedBookmarkService.
  bookmarks::ManagedBookmarkService* GetManagedBookmarkService();

  // Helper to get the bookmark node from a given string id.
  // If the given id can't be parsed or doesn't refer to a valid node, sets
  // |error| and returns nullptr.
  const bookmarks::BookmarkNode* GetBookmarkNodeFromId(
      const std::string& id_string,
      std::string* error);

  // Helper to create a bookmark node from a CreateDetails object. If a node
  // can't be created based on the given details, sets |error| and returns
  // nullptr.
  const bookmarks::BookmarkNode* CreateBookmarkNode(
      bookmarks::BookmarkModel* model,
      const api::bookmarks::CreateDetails& details,
      std::string* error);

  // Helper that checks if bookmark editing is enabled.
  bool EditBookmarksEnabled();

  // Helper that checks if |node| can be modified. Returns false if |node|
  // is nullptr, or a managed node, or the root node. In these cases the node
  // can't be edited, can't have new child nodes appended, and its direct
  // children can't be moved or reordered.
  bool CanBeModified(const bookmarks::BookmarkNode* node, std::string* error);

  Profile* GetProfile();

 private:
  // bookmarks::BaseBookmarkModelObserver:
  void BookmarkModelChanged() override;
  void BookmarkModelLoaded(bookmarks::BookmarkModel* model,
                           bool ids_reassigned) override;

  // ExtensionFunction:
  void OnResponded() override;
};

class BookmarksGetFunction : public BookmarksFunction {
 public:
  DECLARE_EXTENSION_FUNCTION("bookmarks.get", BOOKMARKS_GET)

 protected:
  ~BookmarksGetFunction() override {}

  // BookmarksFunction:
  ResponseValue RunOnReady() override;
};

class BookmarksGetChildrenFunction : public BookmarksFunction {
 public:
  DECLARE_EXTENSION_FUNCTION("bookmarks.getChildren", BOOKMARKS_GETCHILDREN)

 protected:
  ~BookmarksGetChildrenFunction() override {}

  // BookmarksFunction:
  ResponseValue RunOnReady() override;
};

class BookmarksGetRecentFunction : public BookmarksFunction {
 public:
  DECLARE_EXTENSION_FUNCTION("bookmarks.getRecent", BOOKMARKS_GETRECENT)

 protected:
  ~BookmarksGetRecentFunction() override {}

  // BookmarksFunction:
  ResponseValue RunOnReady() override;
};

class BookmarksGetTreeFunction : public BookmarksFunction {
 public:
  DECLARE_EXTENSION_FUNCTION("bookmarks.getTree", BOOKMARKS_GETTREE)

 protected:
  ~BookmarksGetTreeFunction() override {}

  // BookmarksFunction:
  ResponseValue RunOnReady() override;
};

class BookmarksGetSubTreeFunction : public BookmarksFunction {
 public:
  DECLARE_EXTENSION_FUNCTION("bookmarks.getSubTree", BOOKMARKS_GETSUBTREE)

 protected:
  ~BookmarksGetSubTreeFunction() override {}

  // BookmarksFunction:
  ResponseValue RunOnReady() override;
};

class BookmarksSearchFunction : public BookmarksFunction {
 public:
  DECLARE_EXTENSION_FUNCTION("bookmarks.search", BOOKMARKS_SEARCH)

 protected:
  ~BookmarksSearchFunction() override {}

  // BookmarksFunction:
  ResponseValue RunOnReady() override;
};

class BookmarksRemoveFunctionBase : public BookmarksFunction {
 protected:
  ~BookmarksRemoveFunctionBase() override {}

  virtual bool is_recursive() const = 0;

  // BookmarksFunction:
  ResponseValue RunOnReady() override;
};

class BookmarksRemoveFunction : public BookmarksRemoveFunctionBase {
 public:
  DECLARE_EXTENSION_FUNCTION("bookmarks.remove", BOOKMARKS_REMOVE)

 protected:
  ~BookmarksRemoveFunction() override {}

  // BookmarksRemoveFunctionBase:
  bool is_recursive() const override;
};

class BookmarksRemoveTreeFunction : public BookmarksRemoveFunctionBase {
 public:
  DECLARE_EXTENSION_FUNCTION("bookmarks.removeTree", BOOKMARKS_REMOVETREE)

 protected:
  ~BookmarksRemoveTreeFunction() override {}

  // BookmarksRemoveFunctionBase:
  bool is_recursive() const override;
};

class BookmarksCreateFunction : public BookmarksFunction {
 public:
  DECLARE_EXTENSION_FUNCTION("bookmarks.create", BOOKMARKS_CREATE)

 protected:
  ~BookmarksCreateFunction() override {}

  // BookmarksFunction:
  ResponseValue RunOnReady() override;
};

class BookmarksMoveFunction : public BookmarksFunction {
 public:
  DECLARE_EXTENSION_FUNCTION("bookmarks.move", BOOKMARKS_MOVE)

 protected:
  ~BookmarksMoveFunction() override {}

  // BookmarksFunction:
  ResponseValue RunOnReady() override;
};

class BookmarksUpdateFunction : public BookmarksFunction {
 public:
  DECLARE_EXTENSION_FUNCTION("bookmarks.update", BOOKMARKS_UPDATE)

 protected:
  ~BookmarksUpdateFunction() override {}

  // BookmarksFunction:
  ResponseValue RunOnReady() override;
};

}  // namespace extensions

#endif  // CHROME_BROWSER_EXTENSIONS_API_BOOKMARKS_BOOKMARKS_API_H_

总结:扩展通过chrome.bookmarks.get 等方法会进入此实现,需要拦截更改可以在此处修改。

相关推荐
崔庆才丨静觅10 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby606111 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了11 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅11 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅11 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅12 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment12 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅12 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊12 小时前
jwt介绍
前端
爱敲代码的小鱼12 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax