<address id="ttjl9"></address>

      <noframes id="ttjl9"><address id="ttjl9"><nobr id="ttjl9"></nobr></address>
      <form id="ttjl9"></form>
        <em id="ttjl9"><span id="ttjl9"></span></em>
        <address id="ttjl9"></address>

          <noframes id="ttjl9"><form id="ttjl9"></form>

          首頁

          如果使用 JavaScript 原型實現繼承

          seo達人

          在這篇文章中,我們將討論原型以及如何在 JS 中使用它們進行繼承。我們還將會看到原型方法與基于類的繼承有何不同。


          繼承

          繼承是編程語言的一個顯著特征,隨著面向對象編程語言的引入而出現。這些語言大多是基于類的語言。在這里,類就像一個藍圖,對象是它的展現形式。就是說,要創建一個對象,首先我們必須創建一個類,然后我們可以從一個類創建任意數量的對象。


          想象一下,我們有一個表示智能手機的類。這個類具有像其他智能手機一樣的可以拍照、有GPS定位等功能。下面是使用 c++ 來描述這樣的一個類:


          class SmartPhone {

           public:

           void captureImages() {}

          }


          SmartPhone x;

          x.captureImages()

          我們創建了一個名為SmartPhone的類,它有一個名為capturePictures的方法用來拍照。


          如果我們需要一個iPhone類,它可以捕捉圖像和一些特殊的功能,比如面部ID掃描。下面是兩種可能的解決方案:


          1.將捕獲圖像功能與其他常見的智能手機功能,以及iPhone的特定功能一起重寫到一個新類中。但是這種方法需要更多的時間和精力,并且會引入更多的bug。


          重用SmartPhone類中的功能,這就是繼承的作用,繼承也是重用其他類/對象中功能的一種方式。

          這里是我們如何從SmartPhone類中繼承capturePictures方法,使用 c++ 實現如下:


          class Iphone: public SmartPhone {

           public:

           void faceIDScan() {}

          }


          Iphone x


          x.faceIDScan()


          x.captureImages()

          上面是一個簡單的繼承示例。 但是,它表明繼承可以使我們以某種方式重用代碼,從而使所生成的程序更不易出錯,并且花費更少的時間進行開發。


          以下是關于類的一些重要信息:


          繼承該功能的類稱為子類

          被繼承的類稱為父類

          一個類可以同時從多個類中繼承

          我們可以具有多個繼承級別。 例如,類C繼承自類B,而類B繼承自類A

          值得注意的是,類本身并沒有做任何事情。在從類創建對象之前,實際上沒有完成任何工作。我們將看到它為什么不同于JavaScript。


          大家都說簡歷沒項目寫,我就幫大家找了一個項目,還附贈【搭建教程】。


          原型是什么?

          在 JS 中,所有對象都有一個特殊的內部屬性,該屬性基本上是對另一個對象的引用。 此引用取決于對象的創建方式。 在 ECMAScript/JavaScript規范中,它表示為[[Prototype]]。


          由于[[Prototype]]鏈接到一個對象,所以該對象有自己的[[Prototype]]引用。這就是建立原型鏈的方式。


          這個[[Prototype]]鏈是 JS 中繼承的構建塊。


          __proto__ 對象

          為了訪問對象的[[Prototype]],大多數瀏覽器都提供__proto__屬性。訪問方式如下:


          obj.__proto__

          需要注意的是,這個屬性不是 ECMAScript 標準的一部分,它實際上是由瀏覽器實現的。


          獲取和設置原型方法

          除了__proto__屬性外,還有一種訪問[[Prototype]]的標準方法:


          Object.getPrototypeOf(obj);

          對應的有個類似的方法來設置對象的[[Prototype]]:


          Object.setPrototypeOf(obj, prototype);

          [[Prototype]]和.prototype屬性

          [[Prototype]] 只不過是一種用來表示物體原型的標準符號。 許多開發人員將其與.prototype屬性混淆,這是完全不同的事情,接著我們來研究一下.prototype屬性。


          在 JS 中,有許多創建對象的方法。一種方法是使用構造函數,像這樣使用new關鍵字來調用它:


          function SmartPhone(os) {

           this.os = os

          }


          let phone = new SmartPhone('Android')

          在控制臺打印 phone 對象:


          {

           os: "IPhone"

           __proto__{

             constructor: ? SmartPhone(os)

            __proto__: Object

           }

          }

          現在,如果我們希望在phone對象上有一些方法,我們可以在函數上使用.prototype屬性,如下所示:


          SmartPhone.prototype.isAndroid = function () {

           return this.os === 'Android' || 'android'

          }

          再次創建phone對象時,打印 phone 對象如下:


          {

           os: "Android"

           __proto__{

             isAndroid: ?()

             constructor: ? SmartPhone(os)

            __proto__: Object

           }

          }

          我們可以在對象的[[Prototype]]中看到isAndroid()方法。


          簡而言之,.prototype屬性基本上就像由給定的構造函數創建的[[Prototype]]對象的藍圖。 在.prototype屬性/對象中聲明的所有內容都會在對象的[[Prototype]]中彈出。


          實上,如果將 SmartPhone.prototype 與phone 的[[Prototype]]進行比較,就會發現它們是相同的:


          console.log(Object.getPrototypeOf(phone) === SmartPhone.prototype);

          // true

          值得注意的是,我們還可以在構造函數中創建方法:


          function ObjectA() {

           this.methodA = function () {}

          }


          let firstObj = new ObjectA()

          console.log(firstObj)

          這種方法的問題是當我們初始化一個新對象時。所有實例都有自己methodA的副本。相反,當我們在函數的原型上創建它時,對象的所有實例只共享方法的一個副本,顯然使用原型的方式效率會過高。


          大家都說簡歷沒項目寫,我就幫大家找了一個項目,還附贈【搭建教程】。


          當我們訪問屬性時這里發生了什么?

          當我們訪問一個屬性以獲取它時,會發生以下情況:


          JS 引擎查找對象上的屬性,如果找到了該屬性,然后返回它。否則,JS 引擎將通過查看[[Prototype]]來檢查對象的繼承屬性,如果找到該屬性,則返回它,否則,它會查找 [[Prototype]]的[[Prototype]]。 找到屬性或沒有[[Prototype]]時,該鏈結束,這意味著我們已經到達原型鏈的末端。


          當我們設置/創建屬性時,JS 總是在對象本身上進行設置。 即使[[Prototype]]鏈上存在相同的屬性,下面是一個例子:


          function MyObject() {}

          MyObject.prototype.propA = 10; // 在原型上創建屬性


          let myObject = new MyObject();

          console.log(myObject.propA); // [[Prototype]]上的屬性

          // 10


          myObject.propA = 20; // 對象的屬性

          console.log(myObject.propA);

          // 20

          在上面的示例中,我們創建了一個構造函數,該函數的[[Prototype]]上具有屬性propA。 當我們嘗試對其進行讀取操作時,會在控制臺中看到該值。 但是,當我們嘗試在對象本身上設置相同的屬性時;JS 使用給定值在對象上創建一個新屬性。 現在,如果我們不能直接訪問[[Prototype]]上的屬性。


          值得注意的是,普通對象的[[Prototype]]鏈的末尾是內置的Object.prototype。 這就是為什么大多數對象共享許多方法(例如toString())的原因。 因為它們實際上是在Object.prototype上定義的。


          使用原型繼承的各種方法

          在 JS 中,無論我們如何創建對象,只有原型繼承,但這些方式還有一些區別,來看看:


          對象字面量

          在JavaScript中創建對象的最簡單方法是使用對象字面量:


          let obj = {}

          如果在瀏覽器的控制臺中打印obj,我們將看到以下內容:


          clipboard.png


          基本上,所有用文字面量創建的對象都繼承了Object.prototype的屬性。


          需要注意的是__proto__對象引用了創建它的構造函數。 在這種情況下,constructor屬性指向Object構造函數。


          使用對象構造函數

          另一種不太常見的創建對象的方法是使用對象構造函數。JS 提供了一個名為Object的內置構造函數方法來創建對象。


          let obj = new Object();

          這種方法的結果與對象字面量的方式相同。它從Object.prototype繼承屬性。因為我們使用Object作為構造函數。


          Object.create 方法

          使用此輔助方法,我們可以創建一個帶有[[Prototype]]的對象,如下所示:


          let SmartPhone = {

           captureImages: function() {}

          }


          let Iphone = Object.create(SmartPhone)


          Iphone.captureImages()

          這是在 JS 中使用繼承的最簡單方法之一。猜猜我們如何在沒有任何[[Prototype]]引用的情況下創建對象?


          構造方法

          與 JS 運行時提供的對象構造函數相似。 我們還可以創建自己的構造函數,以創建適合我們需求的對象,如下所示:


          function SmartPhone(os) {

           this.os = os;

          }


          SmartPhone.prototype.isAndroid = function() {

           return this.os === 'Android';

          };


          SmartPhone.prototype.isIOS = function() {

           return this.os === 'iOS';

          };

          現在,我們想創建一個iPhone類,它應該有'iOS'作為它 os 屬性的值。它還應該有faceIDScan方法。


          首先,我們必須創建一個Iphone構造函數,在其中,我們應該調用SmartPhone構造函數,如下所示:


          function Iphone() {

            SmartPhone.call(this, 'iOS');

          }

          這會將Iphone構造函數中的this.os屬性設置為’iOS‘。


          之所以調用SmartPhone.call方法,是因為我們需要更改 this 值以引用Iphone。 這類似于在面向對象的世界中調用父級的構造函數。


          接下來的事情是,我們必須從SmartPhone構造函數繼承方法。 我們可以在此處使用Object.create朋友,如下所示:


          Iphone.prototype = Object.create(SmartPhone.prototype);

          現在,我們可以使用.prototype為Iphone添加方法,如下所示:


          Iphone.prototype.faceIDScan = function() {};

          最后,我們可以使用Iphone創建一個對象,如下所示:


          let x = new Iphone();


          // calling inherited method

          console.log(x.isIOS()):

          // true


          ES6 class

          使用ES6,整個過程非常簡單。 我們可以創建類(它們與C ++或其他任何基于類的語言中的類不同,只是在原型繼承之上的語法糖),然后從其他類派生新的類。


          下面是我們如何在ES6中創建類:


          class SmartPhone {

           constructor(os) {

             this.os = os;

           }

           isAndroid() {

             return this.os === 'Android';

           }

           isIos() {

             return this.os === 'iOS';

           }

          };

          現在,我們可以創建一個派生自SmartPhone的新類,如下所示:


          class Iphone extends SmartPhone {

            constructor() {

              super.call('iOS');

            }

            faceIDScan() {}

          }

          我們不是調用SmartPhone.call,而是調用super.call。 在內部,JavaScript引擎會自動為我們執行此操作。


          最后,我們可以使用Iphone創建一個對象,如下所示


          let x = new Iphone();


          x.faceIDScan();


          // calling inherited method

          console.log(x.isIos()):

          // true

          該ES6示例與先前的構造方法示例相同。 但是閱讀和理解起來要干凈得多。


          原文:https://javascript.info/proto...


          代碼部署后可能存在的BUG沒法實時知道,事后為了解決這些BUG,花了大量的時間進行log 調試,這邊順便給大家推薦一個好用的BUG監控工具 Fundebug。

          Emscripten教程之emcc編譯命令

          seo達人

          語法


          emcc [options] file ...

          這個輸入文件file,既可以是clang可以編譯的C/C++語言,也可以是二進制形式的llvm bitcode或者人類可讀形式的llvm assembly文件。

          參數

          大部分clang或者gcc的選項(option)都是可以工作的,比如:

          # 顯示信息 emcc --help # 顯示編譯器版本信息 emcc --version

          如果想看當前Emscripten中clang版本支持的全部選項列表,可以直接使用命令:
          clang --help.

          emcc修改的或者emcc中新的選項列在下面:

          首先是一些編譯優化flag,它們-O0,-O1,-O2,-Os,-Oz,-O3。

          -O0:
          不進行編譯優化(這是默認情況)。當你剛開始移植項目是推薦使用它,因為它會包含許多斷言。

          -O1:
          簡單優化。推薦你在既想縮短編譯時間又想編譯優化時使用。它畢竟比-O2級別的優化編譯起來快多了。它會進行asm.js和llvm的-O1進行優化,它會relooping,會刪除運行時斷言和C++異常捕獲,它也會使得-s ALIASING_FUNCTION_POINTERS=1。

          想要C++異常捕獲重新可用,請設置:-s DISABLE_EXCEPTION_CATCHING=0。

          -O2:
          和-O1類似,不過多了JavaScript級別的優化以及一些llvm -O3的優化項。當你想發布項目的時候,推薦使用本級別優化。

          -O3:
          和-O2類似,不過比-O2又多了一些JavaScript優化,而且編譯時間明顯比-O2長。這個也推薦在發布版本的時候使用。

          -Os:
          和-O3類似,不過增加了額外的優化以減小生成的代碼體積,代價是比-O3性能差一點。-Os優化會同時影響llvm bitcode 和JavaScript文件的生成。

          -Oz:
          和-Os類似,不過進一步減小了代碼體積。

          -s OPTION=VALUE
          傳給編譯器的所有涉及到JavaScript代碼生成的選項。選項列表,請見settings.js

          對于某個選項的值,不僅可以直接在emcc命令行里面設定,也可以把他們寫成json文件。比如下面,就是將DEAD_FUNCTIONS選項的值放到了path/to/file文件里,emcc里面傳這個文件的路徑。

          -s DEAD_FUNCTIONS=@/path/to/file
          note: 1、文件內容可以是:["_func1","_func2"]; 2、文件路徑必須是絕對的,不能是相對的。

          -g:
          這是保留調試信息flag。

          • 如果只是編譯到bitcode,那就和clang和gcc中的-g一樣。
          • 如果是要編譯到JavaScript,-g就等于-g3。

          -g<level>
          控制打印的調試信息數量,每一個level都是在前一個level的基礎上編譯的:

          • -g0:不保留調試信息。
          • -g1:保留空格,不壓縮。
          • -g2:保留函數名。
          • -g3:保留變量名,與-g同。變量名一般不是必須編譯后保留的,但是如果保留了,可以推斷變量的目的,對吧。
          • -g4:保留llvm 調試信息,這是能調試的最高級別。
          note:
          優化級別越高,編譯時間越長

          --profiling:

          --profiling-funcs:

          --tracing:
          啟用Emscripten的tracing API。

          --emit-symbol-map:

          --js-opts<level>:
          允許JavaScript優化,有兩個值:
          0:不允許JavaScript優化器允許;
          1:使用JavaScript優化器。
          通常用不到我們設置這一項, 因為設置-O后面的level的時候,這個項就能順便取到一個合適的值。

          note:
          有些選項會重寫這個flag的值,比如EMTERPRETIFY, DEAD_FUNCTIONS, OUTLINING_LIMIT, SAFE_HEAP 和 SPLIT_MEMORY會將js-opts=1,因為他們依賴js優化器。

          --llvm-opts<level>:
          啟用llvm優化。它的取值有有:

          • 0:不使用llvm優化
          • 1:llvm -O1優化
          • 2:llvm -O2優化
          • 3:llvm -O3優化

          和--js-opts<level>一樣,通常用不到我們設置這一項, 因為設置-O后面的level的時候,這個項就能順便取到一個合適的值。

          --llvm-lto<level>:
          啟用llvm 連接時 優化。可以取值0,1,2,3。

          --closure <on>:
          運行壓縮編譯器(Closure Compiler),可能的取值有,0,1,2:

          • 0:是不啟用壓縮編譯器。
          • 1:啟用。
          • 2:啟用。

          --pre-js <file>
          生成代碼前,指定一個要把內容添加進來的文件。

          --post-js <file>
          生成代碼后,指定一個要把內容添加進來的文件。

          --embed-file <file>
          指定一個帶路徑的文件嵌入到編譯生成的js代碼里。路徑是相對于編譯時的當前路徑。如果傳的是一個目錄,則目錄下所有文件的內容都會被嵌入到將來生成的js代碼中。

          --preload-file <name>
          異步運行編譯代碼前,指定一個預加載的文件。路徑是相對于編譯時的當前路徑。如果傳的是一個目錄,則目錄下所有文件的內容都會被預加載到一個.data文件中。

          --exclude-file <name>
          從 –embed-file and –preload-file后面的目錄中排除一些文件,支持使用通配符*。

          --use-preload-plugins
          告訴文件打包器當文件加載時,運行預加載插件。它用來執行諸如使用瀏覽器解碼器解碼圖片和音頻等。

          --shell-file <path>
          指定要生成HTML的模板文件。

          --source-map-base <base-url>

          --minify 0
          等于-g1。

          --js-transform <cmd>
          優化之前,生成代碼之后,設定這一條命令。這條命令可以讓你修改JavaScript代碼。之后,編譯器會將修改的和未修改的一起進行編譯優化。

          --bind
          啟用bingdings編譯源代碼。bingings是Emscripten中連接C++和JavaScript代碼的一類API。

          --ignore-dynamic-linking
          告訴編譯器忽視動態鏈接,之后用戶就得手動鏈接到共享庫。

          --js-library <lib>
          定義除了核心庫(src/library_*)以外的js庫。

          -v
          打開詳細輸出。
          這個設置為把-v傳給clang,并且啟用EMCC_DEBUG生成編譯階段的中間文件。它也會運行Emscripten關于工具鏈的內部的完整性檢查。

          tip: emcc -v是診斷錯誤的有用工具,不管你是否附加其他參數。

          --cache

          --clear-cache

          --clear-ports

          --show-ports

          --save-bc PATH

          --memory-init-file <on>
          規定是否單獨生成一個內存初始化文件。取值包括0和1.

          • 0:不單獨生成.mem文件。
          • 1:單獨生成.mem文件。

          -Wwarn-absolute-paths
          啟用在-I和-L命令行指令中使用絕對路徑的警告。這是用來警告無意中使用了絕對路徑的。在引用非可移植的本地系統頭文件時,使用絕對路徑有時是很危險的。

          --proxy-to-worker

          --emrun
          使生成的代碼能夠感知emrun命令行工具。當運行emran生成的應用程序時,這樣設置就允許stdout、stderr和exit(returncode)被捕獲。

          --cpuprofiler
          在生成的頁面上嵌入一個簡單的CPU分析器。使用這個來執行粗略的交互式性能分析。

          --memoryprofiler
          在生成的頁面上嵌入內存分配跟蹤器,使用它來分析應用程序Emscripten堆的使用情況。

          --threadprofiler
          在生成的頁面上嵌入一個線程活動分析器。當進行多線程編譯時,使用它來分析多線程應用程序。
          --em-config

          --default-obj-ext .ext

          --valid-abspath path
          設置一個絕對路徑的白名單,以防止關于絕對路徑的警告。

          -o <target>
          編譯輸出的文件格式。target可以取值為:

          • name.js:JavaScript文件;
          • name.html:HTML+js文件。把js單獨生成是為了減小頁面加載時間。
          • name.bc:llvm bitcode。這是默認值。
          • name.o:和上面一樣。
          note:
          如果你用了--memory-init-file,則還會從js文件中再單獨分出一部分代碼為.mem文件。

          -c
          生成llvm bitcode代碼,而不是JavaScript。

          --separate-asm
          把asm.js文件單獨生成到一個文件中。這樣可以減少啟動時的內存加載。

          --output_eol windows|linux
          規定生成的文本文件的行尾,如果是–output_eol windows,就是windows rn行尾,如果是–output_eol linux,則生成Linux行尾的文本文件。

          --cflags

          環境變量


          emcc會受到幾個環境變量的影響,如下:

          • EMMAKEN_JUST_CONFIGURE
          • EMMAKEN_JUST_CONFIGURE_RECURSE
          • EMCONFIGURE_JS
          • EMCONFIGURE_CC
          • EMMAKEN_CXX
          • EMMAKEN_COMPILER
          • EMMAKEN_CFLAGS
          • EMCC_DEBUG

          這幾個里面比較有意思的是EMCC_DEBUG。比如,如果你在編譯之前設置set EMCC_DEBUG=1,那么編譯的時候會把編譯過程的調試信息和編譯各個階段的中間文件輸出到一個臨時目錄,這算是給開發者提供一些編譯期間的幫助或者說調試信息吧。


          Emscripten主題系列文章是emscripten中文站點的一部分內容。
          第一個主題介紹代碼可移植性與限制
          第二個主題介紹Emscripten的運行時環境
          第三個主題第一篇文章介紹連接C++和JavaScript
          第三個主題第二篇文章介紹embind
          第四個主題介紹文件和文件系統
          第六個主題介紹Emscripten如何調試代碼

          vue + vuex + koa2開發環境搭建及示例開發

          seo達人

          寫在前面

          這篇文章的主要目的是學會使用koa框架搭建web服務,從而提供一些后端接口,供前端調用。
          搭建這個環境的目的是: 前端工程師在跟后臺工程師商定了接口但還未聯調之前,涉及到向后端請求數據的功能能夠走前端工程師自己搭建的http路徑,而不是直接在前端寫幾個死數據。即,模擬后端接口。

          當然在這整個過程(搭建環境 + 開發示例demo)中,涉及到以下幾點知識點。
          包括:

          • koa2的知識點
          • node的知識點
          • 跨域問題
          • fetch的使用
          • axios的使用
          • promise的涉及
          • vuex -> state、mutations、actions的使用

          第一部分:環境搭建

          vue + vuex環境

          首先是vue + vue-router + vuex的環境。我們用vue-cli腳手架生成項目,會用vue的同學對這塊應該很熟了。

          // 全局安裝腳手架工具 npm i vue-cli -g // 驗證腳手架工具安裝成功與否 vue --version // 構建項目 vue init webpack 項目名 // 測試vue項目是否運行成功 npm run dev

          因為腳手架生成的vue項目不包含vuex,所以再安裝vuex。

          // 安裝vuex npm i vuex --save

          koa2環境

          前端項目構建好了,就開始構建我們的后端服務。

          首先在你的開發工具(不管是webstorm還是sublime)里新建一個目錄,用來搭建基于koa的web服務。

          在這里,我們不妨給這個目錄起名為koa-demo。

          然后執行:

          // 進入目錄 cd koa-demo // 生成package.json npm init -y // 安裝以下依賴項 npm i koa npm i koa-router npm i koa-cors

          安裝好koa和兩個中間件,環境就算搭建完成了。

          第二部分:示例開發

          搭建環境是為了使用,所以我們立馬來寫一個demo出來。
          demo開發既是一個練習如何在開發環境中寫代碼的過程,反過來,也是一個驗證環境搭建的對不對、好不好用的過程。

          后端接口開發

          本例中,后端我們只提供一個服務,就是給前端提供一個返回json數據的接口。代碼中包含注釋,所以直接上代碼。

          server.js文件

           // server.js文件 let Koa = require('koa'); let Router = require('koa-router'); let cors = require('koa-cors'); // 引入modejs的文件系統API let fs = require('fs'); const app = new Koa(); const router = new Router(); // 提供一個/getJson接口 router
              .get('/getJson', async ctx => { // 后端允許cors跨域請求 await cors(); // 返回給前端的數據 ctx.body = JSON.parse(fs.readFileSync( './static/material.json'));
          
              }); // 將koa和兩個中間件連起來 app.use(router.routes()).use(router.allowedMethods()); // 監聽3000端口 app.listen(3000);

          這里面用到了一個json文件,在'./static/material.json'路徑,該json文件的代碼是:

          // material.json文件 [{ "id": 1, "date": "2016-05-02", "name": "張三", "address": "北京 清華大學",
          }, { "id": 2, "date": "2016-05-04", "name": "李四", "address": "上海 復旦大學",
          }, { "id": 3, "date": "2016-05-01", "name": "王五", "address": "廣東 中山大學",
          }, { "id": 4, "date": "2016-05-03", "name": "趙六", "address": "廣東 深圳大學",
          }, { "id": 5, "date": "2016-05-05", "name": "韓梅梅", "address": "四川 四川大學",
          }, { "id": 6, "date": "2016-05-11", "name": "劉小律", "address": "湖南 中南大學",
          }, { "id": 7, "date": "2016-04-13", "name": "曾坦", "address": "江蘇 南京大學",
          }] 

          然后我們是用以下命令將服務啟動

          node server.js

          測試接口是否良好

          打開瀏覽器,輸入http://127.0.0.1:3000/getJson。看一看頁面上是否將json文件中的json數據顯示出來,如果能夠顯示出來,則說明這個提供json數據的服務,我們已經搭建好了。

          前端調用后端接口示例

          為突出重點,排除干擾,方便理解。我們的前端就寫一個組件,組件有兩部分:首先是一個按鈕,用來調用web服務的getJson接口;然后是一個內容展示區域,拿到后端返回的數據以后,將其在組件的這塊區域顯示出來

          首先我們看組件文件

          <template> <div class="test"> <button type="button" @click="getJson">從后端取json</button> <div class="showJson">{{json}}</div> </div> </template> <script> import {store} from '../vuex' export default { computed: {
                    json(){ return store.state.json;
                    }
                  }, methods: {
                    getJson(){
                        store.dispatch("getJson");
                    }
                  }
              } </script> <style scoped> .showJson{ width:500px; margin:10px auto; min-height:500px; background-color: palegreen;
            } </style> 

          非常簡單,就不多解釋了。
          然后看我們的vuex文件。

          import Vue from 'vue' import Vuex from 'vuex';
          
          Vue.use(Vuex) const state = { json: [],
          }; const mutations = {
            setJson(state, db){
              state.json = db;
            }
          } const actions = {
            getJson(context){ // 調用我們的后端getJson接口 fetch('http://127.0.0.1:3000/json', { method: 'GET', // mode:'cors', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json',
                },
              }).then(function (res) { if(res.status === 200){ return res.json()
                }
              }).then(function (json) { //console.log(typeof Array.from(json), Array.from(json)); context.commit('setJson', Array.from(json));
              })
            }
          }; export const store = new Vuex.Store({ state: state, mutations: mutations, actions: actions,
          })

          ok, 代碼擼完了,獲取后端數據之前是這樣的。

          獲取后端數據之后是這樣的。

          說說axios

          想要把本demo的fetch改為axios方式,要做的工作有以下幾處:
          1、安裝axios、在vuex文件引用axios

          npm i axios import axios from 'axios'

          2、將fetch部分代碼替換為:

          const actions = {
            getJson(context){
              axios.get('/json', { method: 'GET', // mode:'cors', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json',
                },
              }).then(function (res) { if(res.status === 200){ return res.data
                }
              }).then(function (json) { //console.log(typeof Array.from(json), Array.from(json)); context.commit('setJson', Array.from(json));
              })
            }
          };

          3、又會遇到跨域,在webpack中修改,路徑config/index.js文件中添加proxyTable項的配置:

          proxyTable: { '/json': { target: 'http://127.0.0.1:3000', changeOrigin: true, pathRewrite: { '^/json': '/json' }
                }
              },

          后記

          基于vue腳手架搭建的項目,模擬異步取數據,也可以直接在腳手架生成的static文件夾下放置數據,假裝是后臺拿過來的數據。

          不過搭建一個基于express或者koa的web服務,確實也該是一個前端工程師應該掌握的。

          OK,以上就是全文了。
          如果這篇文章使你有所收獲,不勝榮幸。
          歡迎點贊,以期能幫助更多同學!

          深入理解vue中的slot與slot-scope

          seo達人

          寫在前面

          vue中關于插槽的文檔說明很短,語言又寫的很凝練,再加上其和methods,data,computed等常用選項使用頻率、使用先后上的差別,這就有可能造成初次接觸插槽的開發者容易產生“算了吧,回頭再學,反正已經可以寫基礎組件了”,于是就關閉了vue說明文檔。

          實際上,插槽的概念很簡單,下面通過分三部分來講。這個部分也是按照vue說明文檔的順序來寫的。

          進入三部分之前,先讓還沒接觸過插槽的同學對什么是插槽有一個簡單的概念:插槽,也就是slot,是組件的一塊HTML模板,這塊模板顯示不顯示、以及怎樣顯示由父組件來決定。 實際上,一個slot最核心的兩個問題這里就點出來了,是顯示不顯示怎樣顯示

          由于插槽是一塊模板,所以,對于任何一個組件,從模板種類的角度來分,其實都可以分為非插槽模板插槽模板兩大類。
          非插槽模板指的是html模板,指的是‘div、span、ul、table’這些,非插槽模板的顯示與隱藏以及怎樣顯示由插件自身控制;插槽模板是slot,它是一個空殼子,因為它顯示與隱藏以及最后用什么樣的html模板顯示由父組件控制。但是插槽顯示的位置確由子組件自身決定,slot寫在組件template的哪塊,父組件傳過來的模板將來就顯示在哪塊

          單個插槽 | 默認插槽 | 匿名插槽

          首先是單個插槽,單個插槽是vue的官方叫法,但是其實也可以叫它默認插槽,或者與具名插槽相對,我們可以叫它匿名插槽。因為它不用設置name屬性。

          單個插槽可以放置在組件的任意位置,但是就像它的名字一樣,一個組件中只能有一個該類插槽。相對應的,具名插槽就可以有很多個,只要名字(name屬性)不同就可以了。

          下面通過一個例子來展示。

          父組件:

          
              
          1. <template>
          2. <div class="father">
          3. <h3>這里是父組件</h3>
          4. <child>
          5. <div class="tmpl">
          6. <span>菜單1</span>
          7. <span>菜單2</span>
          8. <span>菜單3</span>
          9. <span>菜單4</span>
          10. <span>菜單5</span>
          11. <span>菜單6</span>
          12. </div>
          13. </child>
          14. </div>
          15. </template>

          子組件:

          
              
          1. <template>
          2. <div class="child">
          3. <h3>這里是子組件</h3>
          4. <slot></slot>
          5. </div>
          6. </template>

          在這個例子里,因為父組件在<child></child>里面寫了html模板,那么子組件的匿名插槽這塊模板就是下面這樣。也就是說,子組件的匿名插槽被使用了,是被下面這塊模板使用了。

          
              
          1. <div class="tmpl">
          2. <span>菜單1</span>
          3. <span>菜單2</span>
          4. <span>菜單3</span>
          5. <span>菜單4</span>
          6. <span>菜單5</span>
          7. <span>菜單6</span>
          8. </div>

          最終的渲染結果如圖所示:


          
              
          1. 注:所有demo都加了樣式,以方便觀察。其中,父組件以灰色背景填充,子組件都以淺藍色填充。

          具名插槽

          匿名插槽沒有name屬性,所以是匿名插槽,那么,插槽加了name屬性,就變成了具名插槽。具名插槽可以在一個組件中出現N次。出現在不同的位置。下面的例子,就是一個有兩個具名插槽單個插槽的組件,這三個插槽被父組件用同一套css樣式顯示了出來,不同的是內容上略有區別。

          父組件:

          
              
          1. <template>
          2. <div class="father">
          3. <h3>這里是父組件</h3>
          4. <child>
          5. <div class="tmpl" slot="up">
          6. <span>菜單1</span>
          7. <span>菜單2</span>
          8. <span>菜單3</span>
          9. <span>菜單4</span>
          10. <span>菜單5</span>
          11. <span>菜單6</span>
          12. </div>
          13. <div class="tmpl" slot="down">
          14. <span>菜單-1</span>
          15. <span>菜單-2</span>
          16. <span>菜單-3</span>
          17. <span>菜單-4</span>
          18. <span>菜單-5</span>
          19. <span>菜單-6</span>
          20. </div>
          21. <div class="tmpl">
          22. <span>菜單->1</span>
          23. <span>菜單->2</span>
          24. <span>菜單->3</span>
          25. <span>菜單->4</span>
          26. <span>菜單->5</span>
          27. <span>菜單->6</span>
          28. </div>
          29. </child>
          30. </div>
          31. </template>

          子組件:

          
              
          1. <template>
          2. <div class="child">
          3. // 具名插槽
          4. <slot name="up"></slot>
          5. <h3>這里是子組件</h3>
          6. // 具名插槽
          7. <slot name="down"></slot>
          8. // 匿名插槽
          9. <slot></slot>
          10. </div>
          11. </template>

          顯示結果如圖:


          可以看到,父組件通過html模板上的slot屬性關聯具名插槽。沒有slot屬性的html模板默認關聯匿名插槽。

          作用域插槽 | 帶數據的插槽

          最后,就是我們的作用域插槽。這個稍微難理解一點。官方叫它作用域插槽,實際上,對比前面兩種插槽,我們可以叫它帶數據的插槽。什么意思呢,就是前面兩種,都是在組件的template里面寫

          
              
          1. 匿名插槽
          2. <slot></slot>
          3. 具名插槽
          4. <slot name="up"></slot>

          但是作用域插槽要求,在slot上面綁定數據。也就是你得寫成大概下面這個樣子。

          
              
          1. <slot name="up" :data="data"></slot>
          2. export default {
          3. data: function(){
          4. return {
          5. data: ['zhangsan','lisi','wanwu','zhaoliu','tianqi','xiaoba']
          6. }
          7. },
          8. }

          我們前面說了,插槽最后顯示不顯示是看父組件有沒有在child下面寫模板,像下面那樣。

          
              
          1. <child>
          2. html模板
          3. </child>

          寫了,插槽就總得在瀏覽器上顯示點東西,東西就是html該有的模樣,沒寫,插槽就是空殼子,啥都沒有。
          OK,我們說有html模板的情況,就是父組件會往子組件插模板的情況,那到底插一套什么樣的樣式呢,這由父組件的html+css共同決定,但是這套樣式里面的內容呢?

          正因為作用域插槽綁定了一套數據,父組件可以拿來用。于是,情況就變成了這樣:樣式父組件說了算,但內容可以顯示子組件插槽綁定的。

          我們再來對比,作用域插槽和單個插槽和具名插槽的區別,因為單個插槽和具名插槽不綁定數據,所以父組件是提供的模板要既包括樣式由包括內容的,上面的例子中,你看到的文字,“菜單1”,“菜單2”都是父組件自己提供的內容;而作用域插槽,父組件只需要提供一套樣式(在確實用作用域插槽綁定的數據的前提下)。

          下面的例子,你就能看到,父組件提供了三種樣式(分別是flex、ul、直接顯示),都沒有提供數據,數據使用的都是子組件插槽自己綁定的那個人名數組。

          父組件:

          
              
          1. <template>
          2. <div class="father">
          3. <h3>這里是父組件</h3>
          4. <!--第一次使用:用flex展示數據-->
          5. <child>
          6. <template slot-scope="user">
          7. <div class="tmpl">
          8. <span v-for="item in user.data">{{item}}</span>
          9. </div>
          10. </template>
          11. </child>
          12. <!--第二次使用:用列表展示數據-->
          13. <child>
          14. <template slot-scope="user">
          15. <ul>
          16. <li v-for="item in user.data">{{item}}</li>
          17. </ul>
          18. </template>
          19. </child>
          20. <!--第三次使用:直接顯示數據-->
          21. <child>
          22. <template slot-scope="user">
          23. {{user.data}}
          24. </template>
          25. </child>
          26. <!--第四次使用:不使用其提供的數據, 作用域插槽退變成匿名插槽-->
          27. <child>
          28. 我就是模板
          29. </child>
          30. </div>
          31. </template>

          子組件:

          
              
          1. <template>
          2. <div class="child">
          3. <h3>這里是子組件</h3>
          4. // 作用域插槽
          5. <slot :data="data"></slot>
          6. </div>
          7. </template>
          8. export default {
          9. data: function(){
          10. return {
          11. data: ['zhangsan','lisi','wanwu','zhaoliu','tianqi','xiaoba']
          12. }
          13. }
          14. }

          結果如圖所示:

          github

          以上三個demo就放在GitHub了,有需要的可以去取。使用非常方便,是基于vue-cli搭建工程。

          https://github.com/cunzaizhuyi/vue-slot-demo

          uni-app uni.request接口封裝

          seo達人

          uni-app uni.request接口封裝

          今天在做uni-app項目時,發現在uni-app中 調取后臺接口需要大量的重復編輯,就在想能不能封裝一個如同Vue項目中的this.$axios.get(url,data).then();格式,這樣就減少了很多代碼重復?。?br />
          封裝為如同this.$axios.get(url,data).then();格式

          第一步、

          我們先在index首頁中的組件部分,創建一個js文件;





          第二步、

          我們在uni-app的入口文件中引入request.js文件;

          在入口文件中掛載到uni-app實例上;





          第三步、

          開始接口封裝:

          (以下為js文件代碼)



          //先把接口暴露出去

          export default{

          //我們先定一個uni-app方法 以便于以下操作使用uni-app調取接口時便利

          request(options){

          ///我們使用Promise方法來實現調用接口時后面多個.then()的方法

          //只有Promise能實現如同$axios后面連續多個.then()的方法

          return new Promise((reslove,reject)=>{

          uni.request({

          ...options,

          success:res=>{

          //判斷我們在使用封裝的自定義時第三個參數是否為native

          //當native為true時 我們返回原數據

          if(options.native){

          reslove(res)

          }

          //當native為false時 我們直接返回data中的數據

          if(res.statusCode === 200){

          reslove(res.data)

          }else{

          //加入接口參數錯誤或接口地址錯誤時 我們返回原錯誤提示

          reject(res)

          }

          }

          })

          })

          },

          //在方法中 第二個參數和第三個參數用ES6新語法來添加默認值

          //接口調取get方法

          get(url,data={},options={}){

          //我們把傳過來的參數賦給options,這樣我們在使用uni-app

          //this.request()方法時 傳遞一個參數就可以

          options.url = url;

          options.data = data;

          options.method = 'get';

          //調用上面自己定義的this.request()方法傳遞參數

          return this.request(options)

          },

          //接口調取post方法

          post(url,data={},options={}){

          options.url = url;

          options.data = data;

          options.method = 'post';

          return this.request(options)

          }

          }



          這樣我們就已經封裝完成啦,接下來就是 在頁面內使用!

          第四步、

          我們可以在頁面中來調取已經封裝好的自定義事件啦



          例一:

          個人建議使用ES6新語法 箭頭函數 不然使用this還要重新在外面聲明定義,太麻煩了,使用箭頭函數就會方便很多



          // 已封裝好的接口方法

          //本案例調取接口時 沒有參數上傳 直接調用的

          //這樣使用方法時只傳遞了一個參數,也就是接口地址

          //第二個參數沒有寫,默認為空;假如有參數的話 可以直接填寫

          //后面的參數都為接口內已經定義好的默認值:{}空對象

          //里面的res為接口返回數據中的data里面的內容

          this.$H.get('/api/getIndexCarousel.jsp').then(res=>{

          //res打印出來是接口返回數據中data里面的數據

          console.log(res)

          //賦給數據區的變量,方便本頁面使用

          this.swiperData = res

          });



          例二、



          // 已封裝好的接口方法

          //本案例使用時 傳遞了三個參數

          //第一個為:接口地址

          //第二個為:調取接口傳遞的參數,方法使用時不用傳參,寫空對象就好

          //第三個為:自定義事件中 native 的屬性 若為true 則返回原數據

          //若想返回原數據,必須要填寫第二個參數,若沒有參數,也要寫空對象

          //因為方法調用時 是按照傳參順序調用的,若不寫 參數傳遞就會出錯

          this.$H.get('/api/getIndexCarousel.jsp',{},{

          native:true

          }).then(res=>{

          //res打印出來的數據是接口返回來的原數據

          console.log(res)

          //賦給數據區的變量,方便本頁面使用

          this.swiperData = res

          });




          每天學習一個Android中的常用框架——1.Litepal

          seo達人

          文章目錄

          1.簡介

          2.特性

          3.演示

          3.1 集成

          3.2 配置

          3.3 創建數據庫

          3.4 升級數據庫

          3.5 插入數據

          3.6 查詢數據

          3.7 更新數據

          3.8 刪除數據

          4.版本異同

          5.源碼地址

          1.簡介

          Litepal——作為帶我入行的第一本教學書籍《Android第一行代碼》的作者郭霖老師所寫出來的持久化框架,幾乎算是我接觸Android世界之后第一個遇到的框架,故將該框架列為一系列學習框架博客的首位。

          根據Litepal的GitHub主頁:Litepal,可以看到該框架的一些簡介:



          LitePal is an open source Android library that allows developers to use SQLite database extremely easy. You can finish most of the database operations without writing even a SQL statement, including create or upgrade tables, crud operations, aggregate functions, etc. The setup of LitePal is quite simple as well, you can integrate it into your project in less than 5 minutes.



          事實上,正如這段簡介所說,集成Litepal相當簡單,不需要超過五分鐘時間。使用Litepal,也適合對sql語言還不熟悉的開發者快速上手。



          2.特性

          讓我們繼續瀏覽Litepal的GitHub主頁,可以發掘Litepal的一些特性:



          Using object-relational mapping (ORM) pattern.

          Almost zero-configuration(only one configuration file with few properties).

          Maintains all tables automatically(e.g. create, alter or drop tables).

          Multi databases supported.

          Encapsulated APIs for avoiding writing SQL statements.

          Awesome fluent query API.

          Alternative choice to use SQL still, but easier and better APIs than the originals.

          More for you to explore.

          用大白話來描述的話,可以列舉如下:



          Litepal使用了ORM(對象關系映射)模型

          Litepal幾乎是無配置的,僅需極少的配置文件

          Litepal幾乎包括所有的CRUD操作,也支持多張表格的操作

          Litepal可以僅調用api進行CRUD操作而避免編寫sql語句

          總之,看到Litepal具有這么多良好的特性,讀者是否心動了呢。理論的話不多說,我們現在就開始正式地使用Litepal進行數據庫的相關操作

          PS:如果有曾經學習過Java的ORM框架——Mybatis的讀者,應該不會對Litepal的使用太陌生,因為它們都使用了xml文件進行相應的配置



          3.演示

          3.1 集成

          現在Android框架的集成相比于IDE還為ADT的時代,要方便了許多。原因是現在的主流IDE是Android Studio,而AS默認使用了Gradle進行版本的配置管理,這讓集成框架變得簡單了許多。

          在build.gradle下,添加以下語句,然后重新sync,即可將Litepal集成到你的項目中:



          implementation 'org.litepal.android:java:3.0.0'

          1

          當然,目前Android的主流開發語言,除了Java之外,還有Kotlin,Litepal同樣具有Kotlin版本的(這里的演示僅針對Java,Kotlin版本的異曲同工)依賴:



          implementation 'org.litepal.android:kotlin:3.0.0'

          1

          可以根據個人需求進行配置。



          3.2 配置

          集成了Litepal之后,要想正式使用它還需要進行一些配置



          在assets目錄下新建litepal.xml,作為Litepal的全局配置文件,相應的條目信息已作出注釋,代碼如下:

          <?xml version="1.0" encoding="utf-8"?>

          <litepal>

              <!--  數據庫名  -->

              <dbname value="androidframelearn"/>



              <!--  數據庫版本號  -->

              <version value="1"/>



              <!--  指定映射模型  -->

              <list>

                 

              </list>



              <!--  指定文件的存儲方式  -->

              <!--  <storage value="external" />-->

          </litepal>



          在你的應用下配置Litepal,有兩種方式可以實現:

          修改清單文件,將你的應用名修改為:android:name="org.litepal.LitePalApplication"

          新建一個自己寫的MyOwnApplication類,然后將清單文件中的應用名定位到該類,即:android:name="com.example.MyOwnApplication",然后再編寫MyOwnApplication類,代碼如下:

          public class MyOwnApplication extends Application {



          @Override

          public void onCreate() {

              super.onCreate();

              LitePal.initialize(this);

          }

          ...

          }



          兩種方式亦可,Litepal的作者建議若使用第二種方式,需要盡快地調用LitePal.initialize(this);所以將其放在onCreate()方法是最好的。



          3.3 創建數據庫

          剛才在介紹的時候已經說過,Litepal采取的是對象關系映射(ORM)的模式,那么什么是對象關系映射呢?簡單點說,我們使用的編程語言是面向對象語言,而使用的數據庫則是關系型數據庫,那么將面向對象的語言和面向關系的數據庫之間建立一種映射關系,這就是對象關系映射了。

          不過你可千萬不要小看對象關系映射模式,它賦予了我們一個強大的功能,就是可以用面向對象的思維來操作數據庫,而不用再和SQL語句打交道了,不信的話我們現在就來體驗一下。像往常使用SQLiteOpenHelper類,為了創建一張Book表需要先分析表中應該包含哪些列,然后再編寫出一條建表語句,最后在自定義的SQLiteOpenHelper中去執行這條建表語句。但是使用LitePal,你就可以用面向對象的思維來實現同樣的功能了,定義一個Book類,代碼如下所示:



          package com.androidframelearn.dao_litapal;



          import org.litepal.crud.LitePalSupport;



          public class Book extends LitePalSupport {

              private int id;

              private String author;

              private double price;

              private int pages;

              private String name;

              public int getId(){

                  return id;

              }

              public void setId(int id){

                  this.id = id;

              }



              public String getAuthor(){

                  return author;

              }

              public void setauthor(String author){

                  this.author = author;

              }



              public double getPrice(){

                  return price;

              }

              public void setPrice(double price){

                  this.price = price;

              }



              public int getPages(){

                  return pages;

              }

              public void setPages(int pages){

                  this.pages = pages;

              }



              public String getName(){

                  return name;

              }

              public void setName(String name){

                  this.name = name;

              }

          }



          這里使用標簽來聲明我們要配置的映射模型類,注意一定要使用完整的類名。不管有多少模型類需要映射,都使用同樣的方式配置在標簽下即可。

          沒錯,這樣就已經把所有工作都完成了,現在只要進行任意一次數據庫的操作,BookStore.db數據庫應該就會自動創建出來。為了更好地演示代碼,我們將布局文件所需要的功能一次性編寫好,activity_main.xml代碼如下:



          <?xml version="1.0" encoding="utf-8"?>

          <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

              xmlns:tools="http://schemas.android.com/tools"

              android:layout_width="match_parent"

              android:layout_height="match_parent"

              tools:context=".MainActivity"

              android:orientation="vertical">



              <Button

                  android:id="@+id/btn_db_create"

                  android:layout_width="match_parent"

                  android:layout_height="wrap_content"

                  android:text="創建數據庫"/>



              <Button

                  android:id="@+id/btn_db_query"

                  android:layout_width="match_parent"

                  android:layout_height="wrap_content"

                  android:text="查詢數據"/>



              <Button

                  android:id="@+id/btn_db_insert"

                  android:layout_width="match_parent"

                  android:layout_height="wrap_content"

                  android:text="插入數據"/>



              <Button

                  android:id="@+id/btn_db_update"

                  android:layout_width="match_parent"

                  android:layout_height="wrap_content"

                  android:text="更新數據"/>



              <Button

                  android:id="@+id/btn_db_delete"

                  android:layout_width="match_parent"

                  android:layout_height="wrap_content"

                  android:text="刪除數據"/>



          </LinearLayout>





          接下來,修改MainActivity,除了給按鈕注冊點擊事件,還需要編寫不同的方法代表不同的邏輯,其中,創建數據庫的方法代碼如下:



          private void createDBbyLitePal() {

                  btn_db_create.setOnClickListener(new View.OnClickListener() {

                      @Override

                      public void onClick(View v) {

                          Log.i(TAG,"創建數據庫成功");

                          LitePal.getDatabase();

                      }

                  });

              }



          僅僅通過點擊按鈕,調用LitePal.getDatabase();這句api,就可以創建出數據庫,讓我們實際進入項目中嘗試一下吧!點擊該按鈕,然后查看控制臺,如圖所示:



          出現該句日記,說明數據庫創建成功,接下來我們看看這個數據庫是否按照我們所設置好的格式創建出來了,進入data/data/你的項目包名/databases,即可查看到該數據庫已經放置到該目錄下,如圖所示:





          3.4 升級數據庫

          事實上,若想對現有數據庫進行升級,也是可以實現的。以前我們使用SQLiteOpenHelper來升級數據庫的方式,雖說功能是實現了,但你有沒有發現一個問題,,就是升級數據庫的時候我們需要先把之前的表drop掉,然后再重新創建才行。這其實是一個非常嚴重的問題,因為這樣會造成數據丟失,每當升級一次數據庫,之前表中的數據就全沒了。

          而使用Litepal,就可以很好地避免這個問題。假設我們現在有一張新的表Category要加進去,同樣編寫它的實體類,代碼如下:



          package com.androidframelearn.dao_litapal;



          public class Category {

              private int id;

              private String categoryName;

              private int categoryCode;

              public int getId(){

                  return id;

              }

              public void setId(int id){

                  this.id = id;

              }



              public String getCategoryName(){

                  return categoryName;

              }

              public void setCategoryName(String categoryName){

                  this.categoryName = categoryName;

              }



              public int getCategoryCode(){

                  return categoryCode;

              }

              public void setCategoryCode(int categoryCode){

                  this.categoryCode = categoryCode;

              }

          }



          改完了所有我們想改的東西,只需要記得在litepal.xml將版本號加1就行了。當然由于這里還添加了一個新的模型類,因此也需要將它添加到映射模型列表中。修改litepal.xml中的代碼,如下所示:



          <?xml version="1.0" encoding="utf-8"?>

          <litepal>

              <!--  數據庫名  -->

              <dbname value="androidframelearn"/>



              <!--  數據庫版本號  -->

              <version value="2"/>



              <!--  指定映射模型  -->

              <list>

                  <mapping class="com.androidframelearn.dao_litapal.Book"/>

                  <mapping class="com.androidframelearn.dao_litapal.Category"/>

              </list>



              <!--  指定文件的存儲方式  -->

              <!--  <storage value="external" />-->

          </litepal>



          重新運行一下程序,再次創建數據庫,就可以完美地完成數據庫的升級了。這里的調試可以使用sqlite工具,這里不再贅述。



          3.5 插入數據

          在講述本節時,首先回顧一下之前添加數據的方法,我們需要創建出一個Contentvalues對象,然后將所有要添加的數據put到這個Contentvalues對象當中,最后再調用SQLiteDatabase的insert() 方法將數據添加到數據庫表當中,步驟相當繁瑣。

          而使用LitePal來添加數據,這些操作可以簡單到讓你驚嘆!我們只需要創建出模型類的實例,再將所有要存儲的數據設置好,最后調用一下save()方法就可以了。

          同樣地,修改MainActivity,增加插入數據的事件方法,代碼如下:



          private void insertDatabyLitePal() {

                  btn_db_insert.setOnClickListener(new View.OnClickListener() {

                      @Override

                      public void onClick(View v) {

                          Book book = new Book();

                          book.setName("The Da Vinci Code");

                          book.setauthor("Dan Brown");

                          book.setPages(454);

                          book.setPrice(16.96);

                          book.save();

                          Log.i(TAG,"插入數據成功");

                      }

                  });

              }



          同樣運行程序,查看控制臺,如圖所示:



          當點擊查詢數據(下一節將介紹該邏輯)時,控制臺打印剛剛插入的數據,如圖所示:





          3.6 查詢數據

          使用Litepal同樣可以很輕易地查詢數據,當然了,由于篇幅限制,這里僅僅貼出最簡單的查詢方式,至于關聯查詢等稍復雜的查詢方式,可以去GItHub上參考Litepal的官方文檔進行相關調用即可。

          同樣地,修改MainActivity,增加查看數據的事件方法,代碼如下:



          private void queryDatabyLitePal() {

                  btn_db_query.setOnClickListener(new View.OnClickListener() {

                      @Override

                      public void onClick(View v) {

                          List<Book> books = LitePal.findAll(Book.class);

                          for (Book book : books){

                              Log.i(TAG,"查詢數據成功");

                              Log.d("MainActivity","書名是"+book.getName());

                              Log.d("MainActivity","書的作者是"+book.getAuthor());

                              Log.d("MainActivity","書的頁數是"+book.getPages());

                              Log.d("MainActivity","書的價格是"+book.getPrice());

                          }

                      }

                  });

              }



          相關的運行結果上一小節以貼出,這里不再重復。



          3.7 更新數據

          更新數據要比添加數據稍微復雜一點,因為它的API接口比較多,這里我們只介紹最常用的幾種更新方式。

          首先,最簡單的一種更新方式就是對已存儲的對象重新設值,然后重新調用save()方法即可。那么這里我們就要了解一個概念,什么是已存儲的對象?

          對于LitePal來說,對象是否已存儲就是根據調用model.isSaved()方法的結果來判斷的, 返回true就表示已存儲,返回false就表示未存儲。那么接下來的問題就是,什么情況下會返回true,什么情況下會返回false呢?

          實際上只有在兩種情況下model.isSave()方法才會返回true, 一種情況是已經調用過model. save()方法去添加數據了,此時model會被認為是已存儲的對象。另一種情況是model對象是通過LitePal提供的查詢API查岀來的,由于是從數據庫中查到的對象,因此也會被認為是已存儲的對象。

          由于查詢API相對復雜,因此只能先通過第一種情況來進行驗證。修改MainActivity中的代碼,如下所示:



          private void updateDatabyLitePal() {

                  btn_db_update.setOnClickListener(new View.OnClickListener() {

                      @Override

                      public void onClick(View v) {

                          Book book = new Book();

                          book.setName("The Lost Symbol");

                          book.setauthor("Dan Brown");

                          book.setPages(510);

                          book.setPrice(19.95); // 第一次設置商品價格

                          book.save();

                          book.setPrice(10.99); // 第二次設置商品價格

                          book.save();

                          Log.i(TAG,"更新數據成功");

                      }

                  });

              }



          可以看到,我們做了跟插入數據類似的事情,但是我們對數據的價格進行了設置,運行程序,如圖所示:



          可以看到,除了剛剛插入的數據,還有第二條剛剛更新過后的數據。然而這種更新方式只能對已存儲的對象進行操作,限制性比較大,接下來我們學習另外一種更加靈巧的更新方式,可以調用以下api:



          book.updateAll("name = ? and author = ?","The Lost Symbol","Dan Brown");

          1

          這里僅貼出其中一條api,其他的可以參考官方文檔,這里不再贅述。



          3.8 刪除數據

          使用Litepal刪除數據的方式主要有兩種,第一種比較簡單,就是直接調用已存儲對象的delete()方法就可以了,對于已存儲對象的概念,我們在之前已經學習過了。也就是說,調用過save()方法的對象,或者是通過LitePal提供的查詢API查出來的對象,都是可以直接使用delete()方法來刪除數據的。這種方式比較簡單,我們就不進行代碼演示了,下面直接來看另外一種刪除數據的方式。

          代碼如下:



          private void deleteDatabyLitePal() {

                  btn_db_delete.setOnClickListener(new View.OnClickListener() {

                      @Override

                      public void onClick(View v) {

                          LitePal.deleteAll(Book.class,"price < ?","15");

                          Log.i(TAG,"刪除成功");

                      }

                  });

              }



          運行程序,刪除過后,按照代碼邏輯,已經刪除掉了所有price小于15的條目,如圖所示:





          4.版本異同

          之前閱讀了郭霖老師所著《Android第一行代碼 第二版》時,所記載的Litepal版本為:



          compile 'org.litepal.android:core:1.4.1'

          1

          而的Litepal版本(Java版本,另有Kotlin版本,導入的依賴稍有不同)為:



          implementation 'org.litepal.android:java:3.0.0'

          1

          新舊版本的主要區別是一些類名的劃分,例如老板本的DataSupport變成了LitePalSupport,除此之外,一些api的名稱也稍有變動,讀者在使用時最好可以參考GitHub上的官方文檔,及時更新代碼,做到與時俱進。



          5.源碼地址

          AFL——Android框架學習


          TinyUI-TUIListView最簡單的使用

          seo達人

                在TinyUI簡介的博客中提到其特點中包含一條,即多數大控件的使用方法和android一直,除了語言差異之外,本篇我們就介紹列表控件TUIListView最簡單的使用方法。



                  列表組件/控件作為目前Android/iOS的APP中最常用的控件,該控件的設計同時參考Android、windows、Qt等使用的經驗進行篩選,最終選擇了Android的ListView設計,其他平臺的列表中使用難以程度或設計上略遜于Android,因為Android給與了開發者最大的發揮控件,你可以在列表中可以顯示任何控件。



                  TUIListView中的每一行每一列你可以放置任何UI組件,使用TUIListView需要配合TUIAdapter進行使用,而TinyUI和Android一樣提供了內置的簡單使用的TUISimpleAdapter。TUISimpleAdapter主要用于顯示文本(即每一行都是只能顯示文字),如果需要在列表中顯示其他UI組件,則需要自定義一個Adapter,關于自定義Adapter將在后續進行詳細講解。



                  本篇既然是TUIListView最簡單的使用,我們就使用TUISimpleAdapter來顯示文本你列表,TUISimpleAdapter最好只用于數據步發生變化的情況,因為其存放的數據使用了C++標準庫的vector容器,而非使用list容器,vector容器的特點是訪問速度快,但其缺點是vector的內存是連續的,因此內容發生變化可能會造成內存申請和拷貝的動作;而list容器使用的雙向鏈表,其特點是插入數據快,但訪問速度慢。



                  本篇我們仍然使用上一篇中自定義的MyWindow來顯示TUIListView。



          使用方法/步驟

          1. 定義listView和andapter



                    MyWindow中包含TUISimpleAdapter.h的頭文件,并定義listView和adapter



            MyWindow.h


            ifndef MY_WINDOW_H

            define MY_WINDOW_H

            include <TUIWindow.h>

            include <TUISimpleAdapter.h>

             

             

             

            class MyWindow : public TUIWindow

            {

            public:

                MyWindow(TUIWindow* parent = nullptr);

                virtual ~MyWindow();

             

                void onShow();

                void onClose();

             

            private:

                TUIListView listView;

                TUISimpleAdapter adapter;

            };

             

            endif // !MY_WINDOW_H

             


          2. 填充數據,并把adapter設置到listView中



            MyWindow.cpp


            include "MyWindow.h"

             

             

             

            MyWindow::MyWindow(TUIWindow* parent)

                : TUIWindow(parent)

            {

                setContentView(&this->listView); // 把listView作為當前窗口的內容視圖

             

             

                vector<string> data; // 使用vector<string>類型的data存放數據

             

                for (int32_t i = 0; i < 20; i++)

                {

                    data.push_back(to_string(i)); // 生成0~20的數值-轉換成字符串,放到data中

                }

             

                this->adapter.setData(data); // 把data設置到adapter中

             

                this->listView.setAdapter(&this->adapter); // 把adapter設置到listView,作為listView數據來源和操作對象

            }

             

            MyWindow::~MyWindow()

            {

            }

             

            void MyWindow::onShow()

            {

            }

             

            void MyWindow::onClose()

            {

            }

            到目前為止窗口顯示列表控件已全部完成,接下來和上一篇一樣調用MyWindow的show()方法即可顯示,最終結果如下圖所示:


          call、apply、bind 原理實現

          seo達人

          目錄

          1. call 的模擬實現
          2. apply 的模擬實現
          3. bind 的模擬實現
          4. 三者異同



            學習并參考于:



            JavaScript深入之call和apply的模擬實現



            JS的call,apply與bind詳解,及其模擬實現





            (一)call的模擬實現

            call 用法 : MDN Function.prototype.call()



            call() 方法使用一個指定的 this 值和可選的參數列表來調用一個函數。



            call() 提供新的 this 值給當前調用的函數/方法。


          5. call 實現主要思路:

            將函數設為對象的屬性



            執行該函數



            刪除該函數



            另外還有考慮:



            call 函數還能給定參數執行函數

            this 參數不傳,或者傳null,undefined, this指向window對象

            函數是可以有返回值的
          6. 實現:

            Function.prototype.myCall = function () {

              if (typeof this !== 'function') {

                throw new TypeError('error!')

              }

              let context = arguments[0] || window   //this 參數可以傳 null,當為 null 的時候,視為指向 window

              context.fn = this  // 首先要獲取調用call的函數,用this可以獲取

              let args = [...arguments].slice(1) //從 Arguments 對象中取值,取出第二個到最后一個參數   

              let result = context.fn(...args)  //函數是可以有返回值的

              delete context.fn

              return result

            }


          7. 測試:

            // 測試一下上面實現的myCall

            var value = 2;



            var obj = {

                value: 1

            }



            function bar(name, age) {

                console.log(this.value);

                return {

                    value: this.value,

                    name: name,

                    age: age

                }

            }



            bar.call(null); // 2



            console.log(bar.myCall(obj, 'kevin', 18));

            // 1

            // Object {

            //    value: 1,

            //    name: 'kevin',

            //    age: 18

            // }



            (二)apply 的模擬實現

            apply 用法:MDN Function.prototype.apply()



            apply() 方法使用一個指定的 this 值和可選的參數數組 來調用一個函數。



            apply 的實現跟 call 類似。


          8. 實現:

            Function.prototype.myApply = function () {

              if (typeof this !== 'function') {

                throw new TypeError('error!')

              }

              let context = arguments[0] || window

              context.fn = this

              let result = arguments[1] ? context.fn(...arguments[1]) : context.fn()

              delete context.fn

              return result

            }


          9. 測試:

            var foo = {

                value: 1

            }

            function bar(name, age) {

                console.log(name)

                console.log(age)

                console.log(this.value);

            }

            bar.myApply(foo, ['black', '18']) // black 18 1



            (三)bind 的模擬實現

            bind 用法:MDN Function.prototype.bind()



            bind()方法會創建一個新函數,稱為綁定函數。當這個新函數被調用時,bind() 的第一個參數將作為它運行時的 this,之后的一序列參數將會在傳遞的實參前傳入作為它的參數。



            bind是ES5新增的一個方法,不會執行對應的函數,而是返回對綁定函數的引用。


          10. 實現:

            Function.prototype.customBind = function () {

              if (typeof this !== 'function') {

                throw new TypeError('error!')

              }

              const that = this   // 首先要獲取調用bind的函數,用this獲取并存放在that中

              let context = arguments[0] || window

              const args = [...arguments].slice(1)

              return function() {

                return that.apply(context, args.concat([...arguments]))

              }

            }



            (四)三者異同
          11. 相同:

            改變函數體內 this 的指向
          12. 不同:

            call、apply的區別:call方法接受的是參數列表,而apply方法接受的是一個參數數組。

            bind不立即執行。而call或apply會自動執行對應的函數。


          關于HTTP請求出現 405狀態碼 not allowed的解決辦法

          seo達人

          發現httppost請求目標網站會出現405 狀態碼,原因為 Apache、IIS、Nginx等絕大多數web服務器,都不允許靜態文件響應POST請求

          所以將post請求改為get請求即可

          跨域,請求按要求配置完畢之后,options預請求老是報錯。原因是webapi 默認的web.config有配置

          有這么個配置,導致不行。要把他刪掉,還要加上

          淺談JavaScript實現可視化展示冒泡排序過程

          seo達人



          <!DOCTYPE html>

          <html>

          <head>

          <title>JavaScript實現可視化展示冒泡排序過程</title>

          <style>

          #boxes{

          border:1px solid grey;

          width:1320px;

          height:300px;

          margin-top:10px;

          position:relative;

          }

          .box{

          background:red;

          width:20px;

          line-height:30px;

          text-align:center;

          font-family:Microsoft Yahei;

          font-size:15px;

          color:white;

          margin:0 1px;

          position:absolute;

          }

          </style>

          </head>

          <body>

          <div id="boxes"></div>

          <script>

          function random(){

          var numbers = [];

          for (var i = 0; i < 60; i++) {

          var number = Math.floor(Math.random() 90 + 10);

          numbers.push(number);

          var divElement = document.createElement("div");

          var parentElement = document.getElementById("boxes");

          divElement.style.left = i
          20 + i 2 + "px";

          divElement.style.top = 300 - 3
          number + "px";

          divElement.style.height = 3 number + "px";

          divElement.setAttribute("class","box");

          parentElement.appendChild(divElement);

          }

          return numbers;

          }

          function sort(){

          var numbers = random();

          var parentElement = document.getElementById("boxes");

          var i = 0, j = 0;

          var time = setInterval(function() {

          if (i < numbers.length) {

          if (j < numbers.length - i) {

          if (numbers[j] > numbers[j + 1]) {

          var temp = numbers[j];

          numbers[j] = numbers[j + 1];

          numbers[j + 1] = temp;

          parentElement.innerHTML = "";

          for (var k = 0; k < numbers.length; k++) {

          var textNode = document.createTextNode(numbers[k]);

          var divElement = document.createElement("div");

          divElement.appendChild(textNode);

          divElement.style.left = k
          20 + k 2 + "px";

          divElement.style.top = 300 - 3
          numbers[k] + "px";

          divElement.style.height = 3 * numbers[k] + "px";

          divElement.setAttribute("class","box");

          parentElement.appendChild(divElement);

          }

          }

          j++;

          }

          else{

          i++;

          j = 0;

          }

          }

          else {

          clearInterval(time); 

          return;

          }

          }, 100);  

          }

          sort();

          </script>

          </body>

          </html>

          ————————————————

          版權聲明:本文為CSDN博主「筱葭」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。

          原文鏈接:https://blog.csdn.net/zhouziyu2011/java/article/details/53899692

          日歷

          鏈接

          個人資料

          藍藍設計的小編 http://www.syprn.cn

          存檔

          亚洲va欧美va天堂v国产综合