亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > JavaScript > 正文

WebApi+Bootstrap+KnockoutJs打造單頁面程序

2019-11-20 10:02:39
字體:
來源:轉載
供稿:網友

一、前言

在前一個專題快速介紹了KnockoutJs相關知識點,也寫了一些簡單例子,希望通過這些例子大家可以快速入門KnockoutJs。為了讓大家可以清楚地看到KnockoutJs在實際項目中的應用,本專題將介紹如何使用WebApi+Bootstrap+KnockoutJs+Asp.net MVC來打造一個單頁面Web程序。這種模式也是現在大多數公司實際項目中用到的。

二、SPA(單頁面)好處
在介紹具體的實現之前,我覺得有必要詳細介紹了SPA。SPA,即Single Page Web Application的縮寫,是加載單個HTML 頁面并在用戶與應用程序交互時動態更新該頁面的Web應用程序。瀏覽器一開始會加載必需的HTML、CSS和JavaScript,所有的操作都在這張頁面上完成,都由JavaScript來控制。

單頁面程序的好處在于:

更好的用戶體驗,讓用戶在Web app感受native app的速度和流暢。
分離前后端關注點,前端負責界面顯示,后端負責數據存儲和計算,各司其職,不會把前后端的邏輯混雜在一起。
減輕服務器壓力,服務器只用生成數據就可以,不用管展示邏輯和頁面邏輯,增加服務器吞吐量。MVC中Razor語法寫的前端是需要服務器完成頁面的合成再輸出的。
同一套后端程序,可以不用修改直接用于Web界面、手機、平板等多種客戶端。

當然單頁面程序除了上面列出的優點外,也有其不足:

不利于SEO。這點如果是做管理系統的話是沒影響的
初次加載時間相對增加。因為所有的JS、CSS資源會在第一次加載完成,從而使得后面的頁面流暢。對于這點可以使用Asp.net MVC中Bundle來進行文件綁定。關于Bundle的詳細使用參考文章://www.49028c.com/article/84329.htm、//www.49028c.com/article/84329.htm//www.49028c.com/article/82174.htm
導航不可用。如果一定要導航需自行實現前進、后退。對于這點,可以自行實現前進、后退功能來彌補。其實現在手機端網頁就是這么干的,現在還要上面導航的。對于一些企業后臺管理系統,也可以這么做。
對開發人員技能水平、開發成本高。對于這點,也不是事,程序員嘛就需要不斷學習來充電,好在一些前端框架都非常好上手。
三、使用Asp.net MVC+WebAPI+Bootstrap+KnockoutJS實現SPA

前面詳細介紹了SPA的優缺點,接下來,就讓我們使用Asp.net MVC+WebAPI+BS+KO來實現一個單頁面程序,從而體驗下SPA流暢和對原始Asp.net MVC +Razor做出來的頁面進行效果對比。

1.使用VS2013創建Asp.net Web應用程序工程,勾選MVC和WebAPI類庫。具體見下圖:  

2. 創建對應的倉儲和模型。這里演示的是一個簡單任務管理系統。具體的模型和倉儲代碼如下:

任務實體類實現:

public enum TaskState { Active = 1, Completed =2 } /// <summary> /// 任務實體 /// </summary> public class Task { public int Id { get; set; } public string Name { get; set; } public string Description { get; set; } public DateTime CreationTime { get; set; } public DateTime FinishTime { get; set; } public string Owner { get; set; } public TaskState State { get; set; } public Task() { CreationTime = DateTime.Parse(DateTime.Now.ToLongDateString()); State = TaskState.Active; } }

任務倉儲類實現:

/// <summary> /// 這里倉儲直接使用示例數據作為演示,真實項目中需要從數據庫中動態加載 /// </summary> public class TaskRepository { #region Static Filed private static Lazy<TaskRepository> _taskRepository = new Lazy<TaskRepository>(() => new TaskRepository()); public static TaskRepository Current { get { return _taskRepository.Value; } } #endregion #region Fields private readonly List<Task> _tasks = new List<Task>() { new Task { Id =1, Name = "創建一個SPA程序", Description = "SPA(single page web application),SPA的優勢就是少量帶寬,平滑體驗", Owner = "Learning hard", FinishTime = DateTime.Parse(DateTime.Now.AddDays(1).ToString(CultureInfo.InvariantCulture)) }, new Task { Id =2, Name = "學習KnockoutJs", Description = "KnockoutJs是一個MVVM類庫,支持雙向綁定", Owner = "Tommy Li", FinishTime = DateTime.Parse(DateTime.Now.AddDays(2).ToString(CultureInfo.InvariantCulture)) }, new Task { Id =3, Name = "學習AngularJS", Description = "AngularJs是MVVM框架,集MVVM和MVC與一體。", Owner = "李志", FinishTime = DateTime.Parse(DateTime.Now.AddDays(3).ToString(CultureInfo.InvariantCulture)) }, new Task { Id =4, Name = "學習ASP.NET MVC網站", Description = "Glimpse是一款.NET下的性能測試工具,支持asp.net 、asp.net mvc, EF等等,優勢在于,不需要修改原項目任何代碼,且能輸出代碼執行各個環節的執行時間", Owner = "Tonny Li", FinishTime = DateTime.Parse(DateTime.Now.AddDays(4).ToString(CultureInfo.InvariantCulture)) }, }; #endregion #region Public Methods public IEnumerable<Task> GetAll() { return _tasks; } public Task Get(int id) { return _tasks.Find(p => p.Id == id); } public Task Add(Task item) { if (item == null) { throw new ArgumentNullException("item"); } item.Id = _tasks.Count + 1; _tasks.Add(item); return item; } public void Remove(int id) { _tasks.RemoveAll(p => p.Id == id); } public bool Update(Task item) { if (item == null) { throw new ArgumentNullException("item"); } var taskItem = Get(item.Id); if (taskItem == null) { return false; } _tasks.Remove(taskItem); _tasks.Add(item); return true; } #endregion  }

3. 通過Nuget添加Bootstrap和KnockoutJs庫。

4. 實現后端數據服務。這里后端服務使用Asp.net WebAPI實現的。具體的實現代碼如下:

 /// <summary> /// Task WebAPI,提供數據服務 /// </summary> public class TasksController : ApiController { private readonly TaskRepository _taskRepository = TaskRepository.Current; public IEnumerable<Task> GetAll() { return _taskRepository.GetAll().OrderBy(a => a.Id); } public Task Get(int id) { var item = _taskRepository.Get(id); if (item == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } return item; } [Route("api/tasks/GetByState")] public IEnumerable<Task> GetByState(string state) { IEnumerable<Task> results = new List<Task>(); switch (state.ToLower()) { case "": case "all":  results = _taskRepository.GetAll();  break; case "active":  results = _taskRepository.GetAll().Where(t => t.State == TaskState.Active);  break; case "completed":  results = _taskRepository.GetAll().Where(t => t.State == TaskState.Completed);  break; } results = results.OrderBy(t => t.Id); return results; } [HttpPost] public Task Create(Task item) { return _taskRepository.Add(item); } [HttpPut] public void Put(Task item) { if (!_taskRepository.Update(item)) { throw new HttpResponseException(HttpStatusCode.NotFound); } } public void Delete(int id) { _taskRepository.Remove(id); } }

5. 使用Asp.net MVC Bundle對資源進行打包。對應的BundleConfig實現代碼如下:

/// <summary> /// 只需要補充一些缺少的CSS和JS文件。因為創建模板的時候已經添加了一些CSS和JS文件 /// </summary> public class BundleConfig { // For more information on bundling, visit http://go.microsoft.com/fwlink/?LinkId=301862 public static void RegisterBundles(BundleCollection bundles) { bundles.Add(new ScriptBundle("~/bundles/jquery").Include(  "~/Scripts/jquery-{version}.js")); bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(  "~/Scripts/jquery.validate*")); // Use the development version of Modernizr to develop with and learn from. Then, when you're // ready for production, use the build tool at http://modernizr.com to pick only the tests you need. bundles.Add(new ScriptBundle("~/bundles/modernizr").Include(  "~/Scripts/modernizr-*")); bundles.Add(new ScriptBundle("~/bundles/bootstrap").Include(  "~/Scripts/bootstrap.js",  "~/Scripts/bootstrap-datepicker.min.js")); bundles.Add(new StyleBundle("~/Content/css").Include(  "~/Content/bootstrap.css",  "~/Content/bootstrap-datepicker3.min.css",  "~/Content/site.css")); bundles.Add(new ScriptBundle("~/bundles/knockout").Include(  "~/Scripts/knockout-{version}.js",  "~/Scripts/knockout.validation.min.js",  "~/Scripts/knockout.mapping-latest.js")); bundles.Add(new ScriptBundle("~/bundles/app").Include( "~/Scripts/app/app.js")); } }

6. 因為我們需要在頁面上使得枚舉類型顯示為字符串。默認序列化時會將枚舉轉換成數值類型。所以要對WebApiConfig類做如下改動:

public static class WebApiConfig { public static void Register(HttpConfiguration config) { // Web API 配置和服務 // Web API 路由 config.MapHttpAttributeRoutes(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); // 使得序列化使用駝峰式大小寫風格序列化屬性 config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); // 將枚舉類型在序列化時序列化字符串 config.Formatters.JsonFormatter.SerializerSettings.Converters.Add(new StringEnumConverter()); } }

注:如果上面沒有使用駝峰小寫風格序列化的話,在頁面綁定數據的時候也要進行調整。如綁定的Name屬性的時候直接使用Name大寫,如果使用name方式會提示這個屬性沒有定義錯誤。由于JS是使用駝峰小寫風格對變量命名的。所以建議大家加上使用駝峰小寫風格進行序列化,此時綁定的時候只能使用"name"這樣的形式進行綁定。這樣也更符合JS代碼的規范。 

7. 修改對應的Layout文件和Index文件內容。

Layout文件具體代碼如下:

<!DOCTYPE html><html><head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title> Learninghard SPA Application</title> @Styles.Render("~/Content/css") @Scripts.Render("~/bundles/modernizr")</head> <body> <div class="navbar navbar-inverse navbar-fixed-top"> <div class="container"> <div class="navbar-header">  <p class="navbar-brand">簡單任務管理系統</p> </div> <div class="navbar-collapse collapse">  <ul class="nav navbar-nav">  <li class="active"><a href="/">主頁</a></li>  </ul> </div> </div> </div> <div class="container body-content" id="main"> @RenderBody() <hr /> <footer> <p>© @DateTime.Now.Year - Learninghard SPA Application</p> </footer> </div> @Scripts.Render("~/bundles/jquery") @Scripts.Render("~/bundles/bootstrap") @Scripts.Render("~/bundles/knockout") @Scripts.Render("~/bundles/app") </body></html>  Index頁面代碼如下:@{ ViewBag.Title = "Index"; Layout = "~/Views/Shared/_Layout.cshtml";}<div id="list" data-bind="if:canCreate"><h2>Tasks</h2><div class="table-responsive"> <table class="table table-striped"> <thead> <tr> <th>編號</th> <th>名稱</th> <th>描述</th> <th>負責人</th> <th>創建時間</th> <th>完成時間</th> <th>狀態</th> <th></th> </tr> </thead> <tbody data-bind="foreach:tasks"> <tr> <td data-bind="text: id"></td> <td><a data-bind="text: name, click: handleCreateOrUpdate"></a></td> <td data-bind="text: description"></td> <td data-bind="text: owner"></td> <td data-bind="text: creationTime"></td> <td data-bind="text: finishTime"></td> <td data-bind="text: state"></td> <td><a class="btn btn-xs btn-primary" data-bind="click:remove" href="javascript:void(0)">Remove</a></td> </tr> </tbody> </table></div><div class="col-sm-4"> <a href="javascript:void(0)" data-bind="click: function(data, event){ setTaskList('all') }">All </a> | <a href="javascript:void(0)" data-bind="click: function(data, event){ setTaskList('active') }"> Active</a> | <a href="javascript:void(0)" data-bind="click: function(data, event){ setTaskList('completed') }"> Completed</a></div><div class="col-sm-2 col-sm-offset-6"> <a href="javascript:void(0)" data-bind="click: handleCreateOrUpdate">添加任務</a></div></div><div id="create" style="visibility: hidden"> <h2>添加任務</h2> <br/> <div class="form-horizontal"> <div class="form-group"> <label for="taskName" class="col-sm-2 control-label">名稱 *</label> <div class="col-sm-10"> <input type="text" data-bind="value: name" class="form-control" id="taskName" name="taskName" placeholder="名稱"> </div> </div> <div class="form-group"> <label for="taskDesc" class="col-sm-2 control-label">描述</label> <div class="col-sm-10"> <textarea class="form-control" data-bind="value: description" rows="3" id="taskDesc" name="taskDesc" placeholder="描述"></textarea> </div> </div> <div class="form-group"> <label for="taskOwner" class="col-sm-2 control-label">負責人 *</label> <div class="col-sm-10"> <input class="form-control" id="taskOwner" name="taskOwner" data-bind="value: owner" placeholder="負責人"> </div> </div> <div class="form-group"> <label for="taskFinish" class="col-sm-2 control-label">預計完成時間 *</label> <div class="col-sm-10"> <input class="form-control datepicker" id="taskFinish" data-bind="value: finishTime" name="taskFinish"> </div> </div> <div class="form-group"> <label for="taskOwner" class="col-sm-2 control-label">狀態 *</label> <div class="col-sm-10"> <select id="taskState" class="form-control" data-bind="value: state">  <option>Active</option>  <option>Completed</option> </select>  </div> </div> <div class="form-group"> <div class="col-sm-offset-2 col-sm-10"> <button class="btn btn-primary" data-bind="click:handleSaveClick">Save</button> <button data-bind="click: handleBackClick" class="btn btn-primary">Back</button> </div> </div> </div></div>

8. 創建對應的前端腳本邏輯。用JS代碼來請求數據,并創建對應ViewModel對象來進行前端綁定。具體JS實現代碼如下:

var taskListViewModel = { tasks: ko.observableArray(), canCreate:ko.observable(true)};var taskModel = function () { this.id = 0; this.name = ko.observable(); this.description = ko.observable(); this.finishTime = ko.observable(); this.owner = ko.observable(); this.state = ko.observable(); this.fromJS = function(data) { this.id = data.id; this.name(data.name); this.description(data.description); this.finishTime(data.finishTime); this.owner(data.owner); this.state(data.state); };};function getAllTasks() { sendAjaxRequest("GET", function (data) { taskListViewModel.tasks.removeAll(); for (var i = 0; i < data.length; i++) { taskListViewModel.tasks.push(data[i]); } }, 'GetByState', { 'state': 'all' });}function setTaskList(state) { sendAjaxRequest("GET", function(data) { taskListViewModel.tasks.removeAll(); for (var i = 0; i < data.length; i++) { taskListViewModel.tasks.push(data[i]); }},'GetByState',{ 'state': state });}function remove(item) { sendAjaxRequest("DELETE", function () { getAllTasks(); }, item.id);}var task = new taskModel();function handleCreateOrUpdate(item) { task.fromJS(item); initDatePicker(); taskListViewModel.canCreate(false); $('#create').css('visibility', 'visible');}function handleBackClick() { taskListViewModel.canCreate(true); $('#create').css('visibility', 'hidden');}function handleSaveClick(item) { if (item.id == undefined) { sendAjaxRequest("POST", function (newItem) { //newitem是返回的對象。 taskListViewModel.tasks.push(newItem); }, null, { name: item.name, description: item.description, finishTime: item.finishTime, owner: item.owner, state: item.state }); } else { sendAjaxRequest("PUT", function () { getAllTasks(); }, null, { id:item.id, name: item.name, description: item.description, finishTime: item.finishTime, owner: item.owner, state: item.state }); }  taskListViewModel.canCreate(true); $('#create').css('visibility', 'hidden');}function sendAjaxRequest(httpMethod, callback, url, reqData) { $.ajax("/api/tasks" + (url ? "/" + url : ""), { type: httpMethod, success: callback, data: reqData });}var initDatePicker = function() { $('#create .datepicker').datepicker({ autoclose: true });};$('.nav').on('click', 'li', function() { $('.nav li.active').removeClass('active'); $(this).addClass('active');});$(document).ready(function () { getAllTasks(); // 使用KnockoutJs進行綁定 ko.applyBindings(taskListViewModel, $('#list').get(0)); ko.applyBindings(task, $('#create').get(0));});

到此,我們的單頁面程序就開發完畢了,接下來我們來運行看看其效果。

從上面運行結果演示圖可以看出,一旦頁面加載完之后,所有的操作都好像在一個頁面操作,完全感覺瀏覽器頁面轉圈的情況。對比于之前使用Asp.net MVC +Razor開發的頁面,你是否感覺了SPA的流暢呢?之前使用Asp.net MVC +Razor開發的頁面,你只要請求一個頁面,你就可以感受整個頁面刷新的情況,這樣用戶體驗非常不好。

四、與Razor開發模式進行對比
  相信大家從效果上已經看出SPA優勢了,接下來我覺得還是有必要與傳統實現Web頁面方式進行一個對比。與Razor開發方式主要有以下2點不同:

1.頁面被渲染的時候,數據在瀏覽器端得到處理。而不是在服務器上。將渲染壓力分配到各個用戶的瀏覽器端,從而減少網站服務器的壓力。換做是Razor語法,前端頁面綁定語句應該就是如下:

@Model IEnumerable<KnockoutJSSPA.Models.Task> @foreach (var item in Model){ <tr> <td>@item.Name</td> <td>@item.Description</td> </tr>}

這些都是在服務器端由Razor引擎渲染的。這也是使用Razor開發的頁面會看到頁面轉圈的情況的原因。因為你每切換一個頁面的時候,都需要請求服務端進行渲染,服務器渲染完成之后再將html返回給客戶端進行顯示。

2. 綁定的數據是動態的。意味著數據模型的改變會馬上反應到頁面上。這效果歸功于KnockoutJs實現的雙向綁定機制。

采用這種方式,對于程序開發也簡單了,Web API只負責提供數據,而前端頁面也減少了很多DOM操作。由于DOM操作比較繁瑣和容易出錯。這樣也意味著減少了程序隱性的bug。并且,一個后端服務,可以供手機、Web瀏覽器和平臺多個平臺使用,避免重復開發。

五、總結
到此,本文的介紹就介紹了。本篇主要介紹了使用KnockoutJs來完成一個SPA程序。其實在實際工作中,打造單頁面程序的模式更多的采用AngularJS。然后使用KnockoutJs也有很多,但是KnockoutJs只是一個MVVM框架,其路由機制需要借助其他一些類庫,如我們這里使用Asp.net MVC中的路由機制,你還可以使用director.js前端路由框架。相對于KnockoutJs而言,AngularJs是一個MVVM+MVC框架。所以在下一個專題將介紹使用如何使用AngularJs打造一個單頁面程序(SPA)。

本文所有源碼下載:SPAWithKnockoutJs

如果大家還想深入學習,可以點擊這里進行學習,再為大家附3個精彩的專題:

Bootstrap學習教程

Bootstrap實戰教程

Bootstrap插件使用教程

以上就是本文的全部內容,希望對大家的學習有所幫助。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲男女自偷自拍图片另类| 亚洲成人动漫在线播放| 国产精品麻豆va在线播放| 欧美激情亚洲激情| 亚洲电影在线观看| 久久99热这里只有精品国产| 亚洲欧美日韩中文视频| 成人在线视频网| 国产精品video| 欧美多人爱爱视频网站| 日韩欧美中文第一页| 国产精品久久久久久久av大片| 欧美激情一区二区三区在线视频观看| 在线观看成人黄色| 久久国产精品影片| 国产91在线高潮白浆在线观看| 亚洲第一网站男人都懂| 精品亚洲永久免费精品| 欧洲成人在线观看| 亚洲美女精品久久| 欧美精品在线网站| 日韩高清欧美高清| 日韩成人在线视频| 亚洲国产精品久久91精品| 日韩亚洲欧美成人| 青青草国产精品一区二区| 亚洲一区二区三区在线免费观看| 日韩精品电影网| 成人www视频在线观看| 久久久久久com| 久久久久久综合网天天| 精品人伦一区二区三区蜜桃免费| 日本不卡免费高清视频| 亚洲国内精品视频| 欧美精品少妇videofree| 最近2019中文字幕在线高清| 色系列之999| 精品久久久在线观看| 国产亚洲成av人片在线观看桃| 亚洲最新av网址| 欧美贵妇videos办公室| 精品一区二区三区四区在线| 亚洲全黄一级网站| 日韩有码在线视频| 国产精品亚洲综合天堂夜夜| 亚洲激情在线观看视频免费| 久久精品免费播放| www.精品av.com| 国产精品91在线观看| 亚洲а∨天堂久久精品喷水| 亚洲男人天堂手机在线| 国产乱人伦真实精品视频| 日韩av一区二区在线观看| 17婷婷久久www| 欧美成人黑人xx视频免费观看| 亚洲欧美综合另类中字| 国产精品美女网站| 91极品视频在线| 777午夜精品福利在线观看| 久久免费少妇高潮久久精品99| 欧美性xxxxx极品娇小| 91精品啪在线观看麻豆免费| 97视频免费在线看| 97视频国产在线| 欧美疯狂做受xxxx高潮| 亚洲18私人小影院| 夜夜嗨av色一区二区不卡| 国产精品电影久久久久电影网| 7777免费精品视频| 亚洲第一中文字幕| 日韩免费看的电影电视剧大全| 国产午夜精品一区理论片飘花| 91在线观看免费| 亚洲精品国产精品国产自| 成人网页在线免费观看| 欧美日韩午夜剧场| 久操成人在线视频| 懂色av中文一区二区三区天美| 欧美高清视频在线播放| 久久精品国产免费观看| 韩国精品久久久999| 亚洲欧美国产一区二区三区| 亚洲精品一二区| 久久久久久伊人| 亚洲国产另类 国产精品国产免费| 欧美一级成年大片在线观看| 91在线网站视频| 欧美黑人极品猛少妇色xxxxx| 亚洲综合社区网| xx视频.9999.com| 日韩人在线观看| 中文字幕精品网| 在线播放国产精品| 国产激情久久久久| 欧美激情一区二区久久久| 国产日产欧美a一级在线| 久久伊人免费视频| 丝袜美腿精品国产二区| 国产视频久久久久久久| 欧美日韩一区二区在线播放| 92国产精品久久久久首页| 超碰日本道色综合久久综合| 91香蕉亚洲精品| 福利二区91精品bt7086| 精品一区二区三区四区| 国产精品色悠悠| 国产欧美欧洲在线观看| 国产精品一区二区久久国产| 亚洲男人7777| 日韩一区二区在线视频| 欧美激情一二区| 欧美一级成年大片在线观看| 蜜臀久久99精品久久久久久宅男| 91色p视频在线| 欧美激情videoshd| 久久激情五月丁香伊人| 日韩中文字幕免费| 亚洲影院在线看| 精品一区二区三区电影| 久久国产精品99国产精| 国产精品亚洲第一区| 欧美激情性做爰免费视频| 国产色视频一区| 91精品久久久久久久| 国产精品欧美激情在线播放| 黑丝美女久久久| 不卡av在线网站| 中日韩美女免费视频网址在线观看| 欧美电影在线观看高清| 久久九九全国免费精品观看| 国产精品69精品一区二区三区| 91产国在线观看动作片喷水| 国产成人黄色av| 亚洲人成网7777777国产| 插插插亚洲综合网| 国产精品久久久久久久久借妻| 色综合五月天导航| 欧美成人激情图片网| 一区二区成人av| 久久在线免费视频| 81精品国产乱码久久久久久| 国产成人精品最新| 亚洲品质视频自拍网| 欧美性极品xxxx做受| 精品久久久久久电影| 91精品国产乱码久久久久久蜜臀| 在线视频日韩精品| 精品丝袜一区二区三区| 日韩av免费看网站| 亚洲电影免费观看高清完整版| 国产亚洲美女精品久久久| 国产亚洲精品成人av久久ww| 久久久999国产| 日韩在线视频网站| 热久久视久久精品18亚洲精品| 91精品视频免费观看| 欧美综合国产精品久久丁香| 在线电影av不卡网址| 日韩精品免费在线视频观看| 亚洲成人在线网| 国产ts人妖一区二区三区| 亚洲人成网7777777国产| 精品伊人久久97| 亚洲第一精品电影|