Vue(讀音 /vjuː/,相似於 view),做者尤雨溪,是一套用於構建用戶界面的漸進式框架。與其它大型框架不一樣的是,Vue 被設計爲能夠自底向上逐層應用。Vue 的核心庫只關注視圖層,不只易於上手,還便於與第三方庫(如:vue-rounter:跳轉
,vue-resource:通訊
,vuex:管理
)或既有項目整合。另外一方面,當與現代化的工具鏈以及各類支持類庫結合使用時,Vue也徹底可以爲複雜的單頁應用提供驅動。javascript
官網:https://cn.vuejs.org/css
想要成爲真正的「互聯網 Java 全棧工程師」 還有很長的一段路要走,其中「我大前端」 是繞不開的一門必修課。Java 程序員認識前端、瞭解前端、掌握前端,爲實現成爲「互聯網 Java 全棧工程師」 再向前邁進一步。html
將前端頁面比做一棟房子前端
HTML 是用來描述網頁的一種語言,使用標籤來描述網頁。vue
標記標籤一般被稱爲 HTML 標籤,java
<html>
HTML 文檔描述網頁,包含 HTML 標籤和純文本。HTML 文檔也稱爲網頁node
CSS 層疊樣式表是一門標記語言,並非編程語言,所以不能夠自定義變量,不能夠引用等,不具有任何語法支持。python
優勢:jquery
缺點:webpack
這就致使了咱們在工做中增長了許多工做量。
爲了解決這個問題,前端開發人員會使用一種稱之爲【CSS 預處理器】的工具,提供CSS 確實的樣式層複用機制、減小冗餘代碼,提升樣式代碼的可維護性。大大提升了前端在樣式上的開發效率。
什麼是 CSS 預處理器?
CSS 預處理器定義了一種新的語言。其基本思想是:用一種專門的編程語言,爲 CSS 增長一些編程的特性,將 CSS 做爲目表生成文件,而後開發者就只要使用這種語言進行 CSS 的編碼工做。也就是說「用一種專門的編程語言,進行 Web 頁面樣式設計。再經過編譯器轉化爲正常的 CSS 文件,以供項目使用」
經常使用的 CSS預處理器:
JavaScript 是一門弱類型語言,其源代碼在發往客戶端運行以前不需通過編譯,而是將文本格式的字符代碼發送給瀏覽器,由瀏覽器解釋運行。
原生 JavaScript 開發
原生 JavaScript 開發,也就是讓咱們按照【ECMAScript】 標準的開發方式,簡稱 ES,特色是全部瀏覽器都支持。
ES 標準已發佈以下版本:
區別就是逐步增長新特性。
TypeScript 微軟的標準
TypeScipt 是一種由微軟開發的自由和開源的編程語言。它是 JavaScript 的一個超集,並且本質上向這個語言添加了可選的靜態類型和基於類的面向對象編程。由安德斯-海爾斯伯格(C#、Delphi、TypeScrpt之父:.NET 創立者)主導。
該語言的特色就是除了具有 ES 的特性以外,還歸入許多不在標準範圍內的新特性,因此會致使不少瀏覽器不能支持 TypeScript 語法,須要編譯後(編譯成 JS)才能被瀏覽器正確執行。
主要目的是實現一套代碼三端統一(PC、Android:.apk、IOS:.ipa),並可以調用到設備底層硬件(如:傳感器、GPS、攝像頭等),打包方式主要由如下兩種:
詳見微信官網,這裏就是介紹一個方便微信小程序 UI 開發的框架:WeUI
前端人員爲了方便開發也須要掌握必定的後端技術,但咱們 Java 後臺人員知道後臺知識體系極其龐大複雜,因此爲了方便前端人員開發後臺應用,就出現了 NodeJS 這樣的技術。
NodeJS 的做者已經聲稱放棄 NodeJS (說是架構作得很差再加上笨重的 node_modules,可能讓做者爽了吧),開始開發全新架構的 Deno (於2020年5月13日發佈1.0版本)
既然是後臺技術,那確定也須要框架和項目管理工具,NodeJS 框架及項目管理工具以下:
iView 是一個強大的基於 Vue 的 UI 庫,由不少使用的基礎組件,比 Element UI 的組件更豐富,主要服務於 PC 界面的中後臺產品。使用單文件的 Vue 組件化開發模式,基於 npm + webpack + babel 開發,支持 ES2015 高質量、功能豐富、友好的 API,自由靈活地使用空間
特色:移動端支持較多
Element UI 是餓了麼前端開源維護的 Vue UI 組件庫,組件齊全,基本涵蓋後臺所需的全部組件,文檔講解詳細,例子也很豐富。主要用於開發 PC 端的頁面,是一個質量比較高的 Vue UI 組件庫。
GitHub:https://github.com/elemefe
vue-element-admin:https://panjiachen.github.io/vue-element-admin
特色:桌面端支持較多
飛冰是阿里巴巴團隊基於 React/Angular/Vue 的中後臺應用解決方案。在阿里巴巴內部,已經由270 多個來自幾乎全部 BU 的項目在使用。飛冰包含了一條從設計段到開發段的完整鏈路,幫助用戶快速搭建屬於本身的中後臺應用。
特色:主要組件仍是以 React 爲主,目前對 Vue 的支持還不太完善,目前尚處於官網階段。
Vant UI 是有贊前端團隊基於有贊統一的規範實現的 Vue 組件庫,提供了一整套 UI 基礎組件和業務組件。經過 Vant ,能夠快速搭建出風格統一的頁面,提高開發效率。
at-ui 是一款基於 Vue 2.x 的前端 UI 組件庫,主要用於快速開發 PC 網站產品,它提供了一套 npm + webpack + bable 前端開發工做流程,CSS 樣式獨立,即便採用不一樣的框架實現都能保持統一的 UI 風格。
cube-ui 是滴滴團隊開發的基於 Vue.js 實現的精緻移動端組件庫。支持按需引入和後編譯,輕量靈活,拓展性強,能夠方便地基於現有組件實現二次開發。
混合開發
Flutter 是谷歌的移動端 UI 框架,可在極短的時間內構建 Android 和 IOS 尚高質量的原生級應用。Flutter 可與現有代碼一塊兒工做,它被世界各地的開發者和組織使用。而且 Flutter 是免費和開源的。
特色:Google 出品,能夠快速構建原生 APP 應用程序。作混合應用,該框架爲必選框架。
Ionic 既是一個 CSS 框架,也是一個 JavaScript UI 庫。Ionic 是目前最有潛力的一款 HTML5 手機應用開發框架。經過 SASS 構建應用程序。它提供了不少 UI 組件 來邦族開發者開發強大的應用。它 使用 JavaScript MVVM 框架 和 AngularJS/Vue 來加強應用。提供給數據的雙向綁定,使用它稱爲 Web 和移動開發者的共同選擇。
微信小程序
mpvue 是美團開發的一個使用 Vue.js 開發小程序的前端框架。目前支持 微信小程序、百度智能小程序、頭條小程序和支付寶小程序。框架基於 Vue.js,修改了運行時框架 runtime
和代碼編譯器 compiler
實現。使其可運行在小程序環境中,從而爲小程序開發引入 Vue.js 開發體驗。
WeUI 時一套同微信原生視覺體驗一致的基礎樣式庫。由微信官方設計團隊爲微信內網頁和微信小程序量身設計,令用戶的使用感知更加統一。包含 button、cell、dialog、toast、article、icon等各式元素。
GitHub:https://github.com/Tencent/weui
爲何須要先後端分離?
爲了下降開發的複雜度,之後端爲出發點,!好比:Struts、SpringMVC 等框架的使用,就是後端的 MVC 時代;
下圖爲 MVC 模式
流程圖:
咱們這裏以 SpringMVC
流程圖 爲例:
(圖引自狂神說公衆號)
圖爲 SpringMVC 的一個較完整的流程圖,實線表示 SpringMVC框架提供的技術,不須要開發者實現,虛線表示須要開發者實現。
須要分析執行流程
DispatcherServlet 表示前置控制器,是整個 SpringMVC 的控制中心。用戶發出請求,DispatcherServlet 接收請求並攔截請求。
咱們假設請求的url爲:http://localhost:8080/SpringMVC/hello
如上url拆分紅三部分:
http://localhost:8080:服務器域名
SpringMVC: 部署在服務器上的 web 站點
hello: 表示控制器
經過分析,如上url表示爲:請求位於http://localhost:8080 上的 SpringMVC 站點的 hello 控制器
HandlerMapping 爲處理器映射。DispatcherServlet 調用HandelerMapping,HandlerMapping 根據請求 url 查找 Handler。
HandlerExecution表示具體的 Handler,其主要做用是根據 url 查找控制器,如上 url 被查找控制器爲 :hello。
HandlerExecution 將解析後的信息傳遞給 DispatcherServlet,如解析控制器映射等。
HandlerAdapter 表示處理器適配器,其按照特定的規則去執行 Handler。
Handler 讓具體的 Controller 執行。
Controller 將具體的執行信息返回給 HandlerAdapter ,如 ModelAndView。
HandlerAdapter 將視圖邏輯名或模型傳遞給 DispatcherServlet。
DispatcherServlet 調用視圖解析器(ViewResolver)來解析 HandlerAdapter 傳遞的邏輯視圖名。
視圖解析器將解析的邏輯視圖名傳給 DispatcherServlet。
視圖解析器將解析的邏輯視圖結果,調用具體的視圖。
最終視圖呈現給用戶。
若是看不懂上面的圖,這裏還有SpringMVC流程圖簡化版的圖:
當發起請求時被前置的控制器攔截到請求,根據請求參數生成代理請求,找到請求對應的實際控制器,控制器處理請求,建立數據模型,訪問數據庫,將模型響應給中心控制器,控制器使用模型與視圖渲染視圖結果,將結果返回給中心控制器。再將結果返回給請求者。
優勢:
MVC 模式時一個很是好的協做模式,可以有效下降代碼的耦合度。從架構尚可以讓開發者明白代碼應該寫在哪裏。爲了讓 View 更純粹,還能夠使用 Thymeleaf、Freemark 等模板引擎,使模板裏沒法寫入 Java 代碼,讓先後端分工更加清晰。
缺點:
Controller
。頁面路由等功能本應該是前端最關注的,但倒是由後端來實現。Controller
自己與 Model
每每也會糾纏不清,看了讓人咬牙的業務代碼常常會出如今 Controller
層。這些問題不能歸結於程序員的素養,不然 JSP 就夠了。 2005 年,AJAX(Asynchronous JavaScript And Xml,異步 JavaScript 和 XML)被正式提出,並開始使用 CDN
做爲靜態資源存儲。因而出現了 JavaScript 王者歸來(在這以前,JS 都是用來在網頁上貼狗皮膏藥廣告的)的 SPA (Single Page Application) 單頁面應用時代。
優勢:
這種模式下,先後端的分工很是清晰,先後端的關鍵協做點是 AJAX 接口,看起來是如此美妙。但回過頭來看的話,這與 JSP 時代區別不大。複雜度從服務端的 JSP 裏移到了瀏覽器的 JavaScript。瀏覽器端變得很複雜。相似 SpringMVC ,這個時代開始出現瀏覽器端的分層架構。
缺點:
此處的 MV* 模式以下:
爲了下降前端開發的複雜度,涌現了大量的前端框架,好比:AngularJS
、React
、Vue.js
、EmberJS
等,這些框架總的原則是先按類型分層,好比 Templates、Controllers、Models,而後再在層內作切分。
優勢:
缺點:
前端爲主的 MV* 模式解決了不少問題。但如上所述,一就存在很多不足之處。隨着 NodeJS 的興起,JavaScript 開始由能力運行在服務端。這意味着能夠有一種新的研發模式:
在這種研發模式下,先後端的職責很清晰。對前端來講,兩個 UI 各司其職:
經過 Node、Web Server 層也是 JavaScript 代碼,這意味着部分代碼可先後複用,須要 Seo 的場景能夠在服務端同步渲染,因爲異步請求太多致使的性能問題也能夠經過服務端來緩解。前一種模式的不足,經過這種模式幾乎都能完美解決掉。
與 JSP 模式相比,全棧模式看起來是一種迴歸,也的確是一種向原始開發模式的迴歸,不過是一種螺旋上升式的迴歸。
基於 NodeJS 的全棧模式,依舊面臨不少挑戰:
綜上所述,模式也好,技術也罷,沒有好壞優劣之分,只有適合和不適合;先後端分離的開發思想主要是基於 SoC (關注度分離原則),上面種種模式,都是讓先後端的職責更清晰,分工更何理高效。
MVVM (Model-View-ViewModel)是一種軟件架構設計模式,由微軟 WPF(用於替代 WinForm,之前就是用這個技術開發桌面應用程序的)和 Silverlight(相似於 JavaApplet,簡單點說就是在瀏覽器上運行 WPF)的架構師 Ken-Cooper 和 Ted-Peters 開發,是一種簡化用戶界面的事件驅動編程方式。由 John-Gossman(一樣也是 WPF 和 Silverlight 的架構師)於 2005 年在 它的博客上發表。
MVVM 源自於經典的 MVC(Model-View-Controller)模式。MVVM 的核心是 ViewModel 層,負責轉換 Model 中的數據對象來讓數據變得更容易管理和使用,其做用以下:
MVVM 已經至關成熟了,主要應用但不只僅在網絡應用程序開發中。當下流行的 MVVM 框架有 Vue.js
,Angular.js
等。
MVVM 模式和 MVC 模式同樣,主要目的是分離視圖(View)和模型(Model)。有如下幾大好處:
View 是視圖層,也就是用戶界面。前端主要由 HTML 和 CSS 來構建,爲了更方便地展示 ViewModel
或者 Model
層的數據,已經產生了各類各樣的先後端模板語言,好比 FreeMark、Thymeleaf 等等,各大 MVVM 框架 如 Vue,js,AugularJS,EJS 等也都有本身用來構建用戶界面的內置模板語言。
Model 是指數據模型,泛指後端進行的各類業務邏輯處理和數據操控。主要圍繞數據庫系統展開。這裏的難點主要在於須要和前端約定統一的接口規則
ViewModel 是由前端開發人員組織生成和維護的視圖數據層。在這一層,前端開發者對從後端獲取的 Model 數據進行轉換處理,沒作二次封裝。以生成符合 View 層使用於其的視圖數據模型。
須要注意的是 ViewModel 所封裝出來的數據模型包括視圖的狀態和行爲兩部分。而 Model 層的數據模型是隻包含狀態的
視圖狀態和行爲都封裝在了 ViewModel 中。這樣的封裝使得 ViewModel 能夠完整地去描述 View 層。因爲實現了雙向綁定,ViewModel 的內容會實時展示在 View 層,這是激動人心的,由於前端開發者不再必低效又麻煩地經過操縱 DOM 去更新視圖了。
MVVM 框架已經把最髒最累地一塊作好了,咱們開發者只須要處理和維護 ViewModel,更新數據視圖就會自動獲得相應更新,真正實現 事件驅動程序。
View 層展示地不是 Model 層的數據,而是 ViewModel 的數據,由 ViewModel 負責與 Model 層交互,這就徹底解耦了 View 層和 Model 層,這個解耦是相當重要的,它是先後端分離方案實施的重要一環。
在 MVVM 架構中,是不容許數據和視圖直接通訊的,只能經過 ViewModel 來通訊,而 ViewModel 就是定義了一個 Observer 觀察者。
至此,咱們就明白了,Vue.js 就是一個 MVVM 的實現者,它的核心就是實現了 DOM 監聽與數據綁定
官網下載:
CDN:
開發版本:
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
生產版本:
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
原生 ES Modules
<script type="module"> import Vue from 'https://cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.esm.browser.js' </script>
注:開發版本中包含源代碼,生產版本是刪除註釋和警告後壓縮的js文件
idea 安裝 Vue.js 插件
如果搜索不到顯示search results are not loaded check the internet connection
解決方案:
若重啓idea後仍是不能搜索到插件,則重啓多幾回,若還不行,則電腦關機後等待5分鐘後重啓電腦去安裝插件。
若都未解決,網上有人說改用開手機熱點來下載插件或離線安裝插件,能夠嘗試一下。
導入vue.min.js ,並編寫 demo1.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!-- View 層 模板 --> <div id="app"> // {{變量}},獲取Vue對象的屬性 {{msg}} </div> <!-- 導入vue.js --> <script src="../js/vue.min.js"></script> <script> var vm = new Vue({ el: "#app", data:{ msg:"hello,Vue!" } }); </script> </body> </html>
用瀏覽器打開這個 html 文件,並在控制檯中測試 Vue 的雙向綁定
在控制檯中咱們使用 對象名.屬性 = "修改後的值"
來實現對 vm 對象的修改。在這裏,咱們輸入 vm.msg="hello",頁面顯示內容及控制檯以下圖:
能夠看出,咱們在控制檯中修改 vm 對象,前端頁面會發生改變。也就是說,如今能夠在不刷新網頁的基礎上,實現改變後臺數據,前端頁面數據也改變,這就是雙向綁定。
注意咱們再也不和 HTML 直接交互了。一個 Vue 應用會將其掛載到一個 DOM 元素上 (對於這個例子是 #app
) 而後對其進行徹底控制。那個 HTML 是咱們的入口,但其他都會發生在新建立的 Vue 實例內部。
咱們已經成功建立了第一個 Vue 應用!看起來這跟渲染一個字符串模板很是相似。可是, Vue 在背後作了大量工做。如今數據和 DOM 已經創建了關聯,全部東西都是響應式的。咱們在控制檯操做對象屬性,界面能夠實時更新!
咱們還能夠使用 v-bind
來綁定元素特性!
<!DOCTYPE html> <html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <span v-bind:title="msg"> 鼠標懸停幾秒鐘查看此處動態綁定的提示信息! </span> </div> <!-- 導入vue.js --> <script src="../js/vue.min.js"></script> <script> var vm = new Vue({ el: "#app", data:{ msg:'頁面加載於' + new Date().toLocaleString() } }); </script> </body> </html>
這裏咱們遇到了一點新東西。你看到的 v-bind
attribute被稱爲指令。指令帶有前綴 v-
,以表示他們是 Vue 提供的特殊 attribute。它們會在渲染的 DOM 上應用特殊的響應式行爲。在這裏,該指令是將這個元素的tittle
和Vue 實例的 msg
保持一致。
若是咱們再像剛剛同樣再控制檯中修改 vm 對象中的 msg屬性,鼠標停留在這個 span 標籤的上方,內容也會相對應發生改變。
這個就很簡單了,v-if
等於的值若是是true,則顯示該標籤,不然則不顯示該標籤
<!DOCTYPE html> <html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <span v-if="flag"> 這是條件判斷 </span> <span v-if="falseFlag"> 由於是false,因此看不到 </span> </div> <!-- 導入vue.js --> <script src="../js/vue.min.js"></script> <script> var vm = new Vue({ el: "#app", data:{ flag: true, falseFlag: false } }); </script> </body> </html>
頁面顯示以下:
v-if
還能夠跟 v-else-if
和 v-else
搭配使用。(2.1.0新增的特性)實例代碼以下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <span v-if="msg === 'A'">A</span> <span v-else-if="msg === 'B'">B</span> <span v-else-if="msg === 'C'">C</span> <span v-else>D</span> </div> <!-- 導入vue.js --> <script src="../js/vue.min.js"></script> <script> var mv = new Vue({ el: "#app", data: { msg : 'B' } }); </script> </body> </html>
這裏前端頁面顯示的結果是 B
語法 v-for="數組中遍歷出來的元素名稱 in 數組名稱"
,而後能夠使用 {{...}}
獲取遍歷出來的元素
<!DOCTYPE html> <html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <li v-for="item in items">{{item.msg}}</li> </div> <!-- 導入vue.js --> <script src="../js/vue.min.js"></script> <script> var mv = new Vue({ el: "#app", data:{ items:[ {msg:"Java"}, {msg:"Linux"}, {msg:"Python"}, {msg:"C++"} ] } }); </script> </body> </html>
在控制檯裏,輸入 app.items.push({msg: '新項目' })
,你會發現列表最後添加了一個新項目。
循環中能夠有兩個參數,第二個參數爲循環時的下標。具體核心代碼以下:
<div id="app"> <li v-for="(item,index) in items">{{item.msg}} --> {{index}}</li> </div>
補充: v-for 會遍歷出來三個值:index,key,value ,有興趣的話能夠本身百度進行了解。這裏有一篇關於 這三個值對 v-bind:key 值形成差別的實驗:https://www.cnblogs.com/tim100/p/7262963.html?tdsourcetag=s_pcqq_aiomsg
你可能注意到這中事件監聽的方式違背了關注點分離(separation of concern) 這個長期依賴的優良傳統。但沒必要擔憂,由於全部的 Vue.js 使勁啊處理方法和表達式都嚴格綁定在當前視圖的 ViewModel 上,它不會致使任何維護上的困難。實際上,使用 v-on
有幾個好處:
語法: v-on:事件名="方法名"
v-on
指令能夠監聽 DOM 事件,並在觸發時運行一些 JavaScript 代碼。
實例以下:
<!DOCTYPE html> <html lang="en" xmlns:v-on="http://www.w3.org/1999/xhtml"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <button v-on:click="hello">hello</button> </div> <script src="../js/vue.min.js"></script> <script> var vm = new Vue({ el: "#app", data: { }, methods: { // 方法必須定義在 Vue 的 Methods 中 hello: function (event) { alert("hello!"); } } }); </script> </body> </html>
咱們在點擊 hello
這個按鈕的時候,會執行 vue 對象中 methods 屬性中的 hello 方法,hello 方法是用 JavaScript 寫的。也就是說,v-on
指令能夠監聽 DOM 事件,並在觸發時運行一些 JavaScript 代碼。
在事件處理程序中調用 event.preventDefault()
或 event.stopPropagation()
是很是常見的需求。儘管咱們能夠在方法中輕鬆實現這點,但更好的方式是:方法只有存粹的數據邏輯,而不是去處理 DOM 事件細節。
爲了解決這個問題, Vue.js 爲 v-on
提供了事件修飾符。
.stop
.prevent
.capture
.self
.once
.passive
<!-- 阻止單擊事件繼續傳播 --> <a v-on:click.stop="doThis"></a> <!-- 提交事件再也不重載頁面 --> <form v-on:submit.prevent="onSubmit"></form> <!-- 修飾符能夠串聯 --> <a v-on:click.stop.prevent="doThat"></a> <!-- 只有修飾符 --> <form v-on:submit.prevent></form> <!-- 添加事件監聽器時使用事件捕獲模式 --> <!-- 即內部元素觸發的事件先在此處理,而後才交由內部元素進行處理 --> <div v-on:click.capture="doThis">...</div> <!-- 只當在 event.target 是當前元素自身時觸發處理函數 --> <!-- 即事件不是從內部元素觸發的 --> <div v-on:click.self="doThat">...</div>
注:使用修飾符時,順序很重要;響應的代碼會以一樣的順序產生。所以,用 v-on:click.prevent.self
會阻止全部的點擊,而 v-on:click.self.prevent 只會阻止元素自身的點擊。
在監聽鍵盤事件時,咱們常常須要檢查詳細的按鍵。 Vue 容許爲 v-on
在監聽鍵盤事件時添加按鍵修飾符:
<!-- 只有在 `key` 是 `Enter` 時調用 `vm.submit()` --> <input v-on:keyup.enter="submit">
你能夠直接將 KeyboardEvent.key
暴露的任意有效按鍵名轉換爲 kebab-case 來做爲修飾符。
<input v-on:keyup.page-down="onPageDown">
在上述示例中,處理函數只會在 $event.key
等於 PageDown
時被調用。
補充:
Vue 經常使用的7個屬性
Vue.js 是一個 MVVM 框架,即數據雙向綁定。即當數據發生變化的時候,視圖也就發生變化,當視圖發生變化的時候,數據也會跟着同步變化。
值得注意的是,咱們所說的數據雙向綁定,必定是對於 UI 控件來講的,非 UI 控件不會涉及到數據雙向綁定。單向數據綁定是使用狀態管理工具的前提。若是咱們使用 vuex
,那麼數據流也是單向的,這是就會和雙向數據綁定有衝突。
在 Vue,js 中,若是使用 vuex
,實際上數據仍是單向的。之因此說是數據雙向綁定,這是用的 UI 空間來講。對於咱們處理表單, Vue.js 的雙向數據綁定應用起來就特別舒服了。即二者並不互斥,在全局性數據流使用單向,方便跟蹤;局部性數據使用雙向,簡單易操做。
你能夠使用 v-model
指令在表單 <input>
、 <textarea>
、 <select>
元素上建立雙向數據綁定。它會根據控件類型自動選取正確的方法來更新元素。景觀有些神奇,但 v-model
本質上不過是語法糖。它負責監聽用戶的輸入事件以更新數據,並對一些極端場景進行一些特殊處理。
注: v-model 會忽略全部表單元素的 value、checked、selected 特性的初始值而老是將 Vue 實例的數據做爲數據來源。應該經過 JavaScript 在組件的 data 選項中聲明初始值。
<!DOCTYPE html> <html lang="en" > <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <!-- 單行文本 --> <input v-model="message" placeholder="請輸入文本"/> <p>Message is: {{message}}</p> </div> <!-- 導入vue.js --> <script src="../js/vue.min.js"></script> <script> var vm = new Vue({ el: "#app", data: { mess!age: '' // 必定先在 data 中聲明 v-model 中的初始化 } }); </script> </body> </html>
<!DOCTYPE html> <html lang="en" > <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <!-- 多行文本 --> <span>多行文本輸入的信息:</span> <p style="white-space: pre-line;">{{ mulMessage }}</p> <br> <textarea v-model="mulMessage" placeholder="請輸入多行文本"></textarea> </div> <!-- 導入vue.js --> <script src="../js/vue.min.js"></script> <script> var vm = new Vue({ el: "#app", data: { mulMessage: '' } }); </script> </body> </html>
<!DOCTYPE html> <html lang="en" > <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <!-- 複選框 --> <div> <input type="checkbox" value="張三" v-model="checkName"> <label>張三</label> <input type="checkbox" value="李四" v-model="checkName"> <label>李四</label> <input type="checkbox" value="王五" v-model="checkName"> <label>王五</label> <p>你選的是:{{checkName}}</p> </div> </div> <!-- 導入vue.js --> <script src="../js/vue.min.js"></script> <script> var vm = new Vue({ el: "#app", data: { checkName: [] } }); </script> </body> </html>
<!DOCTYPE html> <html lang="en" > <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <!-- 單選按鈕 --> <div> <p>性別:</p> <input type="radio" value="男" v-model="sex"> <label>男</label> <input type="radio" value="女" v-model="sex"> <label>女</label> <p>你選擇了:{{sex}}</p> </div> </div> <!-- 導入vue.js --> <script src="../js/vue.min.js"></script> <script> var vm = new Vue({ el: "#app", data: { sex: '' } }); </script> </body> </html>
<!DOCTYPE html> <html lang="en" > <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <!-- 單選框 --> <div> <select v-model="selected"> <option disabled>請選擇</option> <option >A</option> <option >B</option> <option >C</option> <option >D</option> </select> <p>你選擇了:{{selected}}</p> </div> </div> <!-- 導入vue.js --> <script src="../js/vue.min.js"></script> <script> var vm = new Vue({ el: "#app", data: { selected: '' } }); </script> </body> </html>
注:若是 v-model
表達式的初始值未能匹配任何選項, <select>
元素將被渲染爲「未選中」狀態。在 ios 中,這會使用戶沒法選擇第一個選項。由於這樣的狀況下, ios 不會觸發 change 事件。所以,更推薦像上面這樣提供一個值爲空的禁用選項。
能夠使用 v-for 動態渲染(綁定到一個數組),核心代碼以下:
<select v-model="selected"> <option v-for="option in options" v-bind:value="option.value"> {{ option.text }} </option> </select> <span>Selected: {{ selected }}</span> new Vue({ el: '...', data: { selected: 'A', options: [ { text: 'One', value: 'A' }, { text: 'Two', value: 'B' }, { text: 'Three', value: 'C' } ] } })
.lazy
在默認狀況下, v-model
在每次 input
事件觸發後將輸入框的值與數據進行同步(除了8.3.1輸入法組合文字時)。你能夠添加上 lazy
修飾符,從而轉爲在 change
事件以後進行同步:
<!-- 在「change」時而非「input」時更新 --> <input v-model.lazy="msg">
.number
若是像自動將用戶的輸入值轉爲數值類型,能夠給 v-model
添加 number
修飾
<input v-model.number="age" type="number">
這一般頗有用,由於即便在 type="number"
時,HTML 輸入元素的值也總會發揮字符串。若是這個值沒法被 parseFloat()
解析,則會返回原始的值
.trim
若是要自動過濾用戶輸入的首尾空白字符,能夠給 v-model
添加 trim
修飾符:
組件是可複用的 Vue 實例,說白了就是一組能夠重複使用的模板,跟 JSTL 的自定義標籤、Thymeleaf 的 th:fragment
等框架有着殊途同歸之妙。一般一個應用會以一棵嵌套的組件樹的形式來組織:
例如,你可能會有頁頭、側邊欄、內容區等組件,每一個組件又包含了其它的像導航鏈接、博文之類的組件。
注:在實際開發中,咱們並不會用上面的方式開發組件,而是採用 vue-cli 建立 .vue 模板文件的方式開發,如下方法知識爲了讓你們理解什麼是組件。
使用 Vue.component()
方法註冊組件,格式以下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <li-list v-for="item in items" v-bind:language="item"></li-list> </div> <!-- 引入 vue.js --> <script src="../js/vue.min.js"></script> <script> /* 使用 component() 方法註冊組件 */ Vue.component('li-list',{ props: ['language'], template: "<li>{{language}}</li>" }); new Vue({ el: "#app", data:{ items: ["Java","Linux","JavaScript"] }, }); </script> </body> </html>
說明:
由於組件是可複用的 Vue 實例,因此他們與 new Vue
接收相同的選項,例如 data
、computed
、watch
、methods
以及生命週期鉤子等。僅有的例外是像 el
這樣根實例特有的選項。注:new Vue
必需要在所有組件註冊完後再建立!不然 new Vue
後面註冊的組件將不會生效。
你能夠將組件進行任意次數的複用:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <click-component></click-component> <click-component></click-component> <click-component></click-component> </div> <!-- 引入 vue.js --> <script src="../js/vue.min.js"></script> <script> Vue.component('click-component',{ data: function () { return{ count: 0 } }, template: '<button v-on:click="count++">已經點擊了{{ count }}次</button>' }); vm = new Vue({ el: "#app" }); </script> </body> </html>
當點擊按鈕時,每一個組件都會各自獨立維護它的 count
。由於每用一次組件,就會又它的新實例被建立
data
組件中的 data
選項必須是一個函數,所以每一個實例能夠維護一份被返回對象的獨立的拷貝。若是 Vue 沒有這條規則,點擊一個按鈕就可能會改變全部按鈕中的 count
。
Axios 是一個開源的能夠用在瀏覽器端和 NodeJS
的異步通訊框架。它的主要做用就是實現 AJAX 異步通訊,其功能特色以下:
XMLHttpRequest
GitHub:https://github.com/axios/axios
因爲 Vue.js 是一個視圖層框架,而且做者嚴格遵照 SoC(關注度分離原則),因此 Vue.js 並不包含 AJAX 的通訊功能。爲了解決通訊問題,做者單獨開發了一個名爲 vue-resource
的插件。不過在進入 2.x 版本之後中止了對該插件的維護並推薦了 Axios
框架,少用 jQuery,由於它操縱 DOM 太頻繁了!
咱們開發的接口大部分都是採用 JSON 格式,能夠如今項目裏模擬一段 JSON 數據,數據內容以下:建立一個名爲 data.json 的文件並填入上面的內容,放在項目根目錄下:
data.json
{ "name": "bilibili", "url": "www.bilibili.com", "page": 1, "isNonProfit": true, "address": { "street": "黃石路", "city": "廣州市", "country": "中國" }, "links": [ { "name": "百度", "url": "www.baidu.com" }, { "name": "谷歌", "url": "www.google.com" } ] }
引入 axios
npm install --save axios vue-axios -g
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
經過 axios 獲取 data.json
<!DOCTYPE html> <html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml"> <head> <meta charset="UTF-8"> <title>Title</title> <!-- 解決閃爍問題 --> <style> [v-clack]{ display: none; } </style> </head> <body> <div id="app" v-clack> <div> {{info}} </div> <a v-bind:href="info.url">跳轉</a> </div> <!-- 導入 vue.js --> <script src="../js/vue.min.js"></script> <!-- 導入 axios --> <script src="../js/axios.min.js"></script> <script> var vm = new Vue({ el: "#app", data(){ return { // 請求的返回參數格式,必須和json字符串同樣 name: null, address: { country: null, city: null, street: null }, url: null } }, mounted() { axios.get("../../data.json").then(response => (this.info = response.data)) } }); </script> </body> </html>
說明:
Vue 實例有一個完整的生命週期,也就是從建立、初始化數據、編譯模板、掛載 DOM、渲染 -> 更新 -> 渲染,卸載等一系列過程,咱們稱這是 Vue 的生命週期。通俗地說就是 Vue 實例從建立到銷燬的過程,就是生命週期。
每一個 Vue 實例在被建立時都要通過一系列的初始化過程,例如,須要設置數據監聽、編譯模板、將實例掛載到 DOM 並在數據變化時更新 DOM 等。同時在這個過程當中也會運行一些叫作生命週期鉤子的函數,這給了用戶在不一樣階段添加本身的代碼的機會。
好比 10.3 中就使用了鉤子函數 mounted 來實現 axios 獲取的數據綁定到 data() 的屬性。
也有一些其它的鉤子,在實例生命週期的不一樣階段被調用,如 created
、updated
、destroyed
。生命週期鉤子的 this
上下文指向調用它的 Vue 實例。
注:不要再選項 property 或回調上使用箭頭函數,好比 created: () => console.log(this.msg)
,或 vm.$watch('msg', newValue => this.myMethod())
。由於箭頭函數並無 this
,this
會做爲變量一致向上級語法做用域查找,直至找到爲止,常常致使Uncaught TypeError: Cannot read property of undefined
或 Uncaught TypeError: this.myMethod is not a function
之類的錯誤。
生命週期圖示
下圖展現了實例的生命週期,隨着你的不斷學習和使用,它的參考價值會愈來愈高。
計算屬性的重點突出在 屬性
兩個字上(屬性是名詞)。首先它是個 屬性
,其次這個屬性有 計算
的能力(計算式動詞),這裏的 計算
就是個函數:簡單點說,它就是一個可以計算結果緩存起來的屬性(將行爲轉化成靜態屬性),僅此而已:能夠想象爲緩存!
內存中運行:虛擬 DOM。計算屬性式 Vue 的特點!
直接看代碼:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <div>當前時間1:{{currentTime()}}</div> <div>當前時間2:{{currentTime1}}</div> <div>{{msg}}</div> </div> <script src="../js/vue.min.js"></script> <script> var vm = new Vue({ el: "#app", data: { msg: '修改前,計算書屬性在緩存中不會改變' }, methods: { currentTime: function () { return Date.now(); } }, computed: { /* methods 中的方法名和 computed 中的方法名不能重名 重名的時候只會調用 methods 中的方法 */ currentTime1: function () { // 此處演示中,msg發生了修改 this.msg; return Date.now(); } } }); </script> </body> </html>
打開控制檯測試截圖以下:
注意:methods 和 computed 中的方法名不能重名
說明:
經過上圖,咱們能很是清楚的看出:在調用方法時,methods 中方法的返回值一直在改變,而計算屬性中的方法的返回值並無發生改變,這是由於計算屬性在內存中運行,是虛擬 DOM。計算屬性只有在方法中內容修改的時候,纔會刷新緩存,方法的返回值纔會發生改變。
結論:
調用方法時,每次都須要進行計算。既然有計算過程,則一定產生系統開銷。那若是這個結果是不常常變化的呢?此時就能夠考慮將這個結果緩存起來,採用計算屬性能夠很方便作到這一點。計算屬性的主要特性就是爲了將不常常變化的計算結果進行緩存,以節約咱們的系統開銷!
在 Vue.js 中咱們使用 <slot>
元素做爲承載分發內容的出口,翻譯過來就是插槽,能夠應用在組合組件的場景中。實例代碼以下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!-- view層 --> <div id="app"> <todo > <!-- 往 slot 插槽中插入 language-title 組件 slot="language-title" : 將組件 language-title(<language-title></language-title>) 綁定 todo組件中 name="language-title" 的插槽 : 是 v-bind: 的簡寫,就像 $ 是 jQuery 的簡寫同樣 :title="title" :title 是用來綁定組件language-title中的title屬性 ="title" 是給組件中的title屬性賦給 Vue實例中的data屬性中title變量的值 --> <language-title slot="language-title" :title="title"></language-title> <!-- 往 slot 插槽中插入 language-li 組件 slot="language-li" : 將組件 language-li(<language-li></language-li>) 綁定 todo組件中 name="language-li" 的插槽 :language="language" :language 是用來綁定組件language-li中的language屬性 ="language" 是給組件中的title屬性賦給 v-for循環中獲取的language --> <language-li slot="language-li" :language="language" v-for="language in languages"></language-li> </todo> </div> <script src="../js/vue.min.js"></script> <script> /* todo組件 */ Vue.component('todo',{ template: /* ‘\’表明換行 */ '<div>\ <slot name="language-title"></slot>\ <ul>\ <slot name="language-li"></slot>\ </ul>\ \</div>' }); /* language-title 組件 */ Vue.component('language-title',{ props: ['title'], template: '<div>{{title}}</div>' }); /* language-li 組件 */ Vue.component('language-li',{ props: ['language'], template: '<li>\ {{language}}\ \</li>' }); /* Vue 實例 */ new Vue({ el: "#app", data: { title: '編程語言', languages: ['Java','C++','JavaScript','Python'] } }); </script> </body> </html>
說明:
<slot></slot>
標籤訂義,上面代碼中 <slot name="language-title"></slot>
和 <slot name="language-li"></slot>
就是定義插槽名字。沒定義或者使用時寫錯插槽名字,則沒法使用該插槽。:title="title"
: :title
是用來綁定組件language-title中的title屬性 ="title"
是給組件中的title屬性賦給 Vue實例中的data屬性中title變量的值。:language="language"
: :language
是用來綁定組件language-li中的language屬性 ="language"
是給組件中的title屬性賦給 v-for循環中獲取的language上面代碼書寫步驟:
el:
綁定 View 層,在 data屬性中定義須要展現的數據。props
獲取 View 層的變量,並在 template 中寫好插槽中的模板或預留插槽。 經過上面代碼能夠發現,數據項在 Vue 得實例中,但刪除操做要在組件中完成,那麼組件如何才嗯那個刪除 Vue 實例中得數據呢? 此時就涉及到參數傳遞與事件分發了。Vue 爲咱們提供了自定義事件得功能能很好地幫助咱們解決這個問題。使用 this.$emit('自定義事件名',參數),操做具體以下:
在 Vue 實例中,增長 methods 對象並定義一個名爲 removeLanguage1 的方法
new Vue({ el: "#app", data: { title: '編程語言', languages: ['Java','C++','JavaScript','Python','PHP'] }, methods: { removeLanguage1 : function (index) { this.languages.splice(index,1); } } });
在組件 language-li 增長 methods 對象,並定義一個名爲 removeLanguage2 的方法
Vue.component('language-li',{ props: ['language','index','key'], // 只能綁定當前組件的方法 template: '<li>{{index}} ---> {{language}}<button @click="deleteLanguage2">刪除</button></li>', methods: { deleteLanguage2: function (index) { // this.$emit : 自定義事件分發 this.$emit('remove',index) } } });
在 Vue 層定義自定義事件,並綁定 Vue 實例中的事件方法
<div id="app"> <todo> <language-title slot="language-title" :title="title"></language-title> <!-- @remove="removeLanguage1(index)" 綁定自定義事件,使得組件能夠使用 Vue 實例中的方法 --> <language-li slot="language-li" v-for="(language,index) in languages" :language="language" :index="index" @remove="removeLanguage1(index)"></language-li> </todo> </div>
完整測試代碼:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <todo> <language-title slot="language-title" :title="title"></language-title> <!-- @remove="removeLanguage1(index)" 綁定自定義事件,使得組件能夠使用 Vue 實例中的方法 --> <language-li slot="language-li" v-for="(language,index) in languages" :language="language" :index="index" @remove="removeLanguage1(index)"></language-li> </todo> </div> <script src="../js/vue.min.js"></script> <script> Vue.component('todo',{ template: '<div>\ <slot name="language-title"></slot>\ <ul>\ <slot name="language-li"></slot>\ </ul>\ </div>' }); Vue.component('language-title',{ props: ['title'], template: '<div>{{title}}</div>' }); Vue.component('language-li',{ props: ['language','index','key'], // 只能綁定當前組件的方法 template: '<li>{{index}} ---> {{key}} ---> {{language}}<button @click="deleteLanguage2">刪除</button></li>', methods: { deleteLanguage2: function (index) { // this.$emit : 自定義事件分發 this.$emit('remove',index) } } }); new Vue({ el: "#app", data: { title: '編程語言', languages: ['Java','C++','JavaScript','Python','PHP'] }, methods: { removeLanguage1 : function (index) { this.languages.splice(index,1); } } }); </script> </body> </html>
vue-cli 官方提供的一個腳手架,用於快速生成一個 vue 的項目模板;
預先定義好的目錄結構及基礎代碼,就比如咋門在建立 Maven 項目時能夠選擇建立一個骨架項目,這個骨架項目就是腳手架,讓咱們的開發更加的快速;
主要的功能:
傻瓜式安裝 Node.js,除了安裝路徑不想安裝c盤外,所有均可以點 下一步。
確認 node.js 是否安裝成功:
這個 npm,就是一個軟件包管理工具,就和 python 中 pip,Linux 中 apt 軟件安裝差很少。
Node.js 換源:使用淘寶鏡像加速器 (cnpm)
換國內源,可讓下載速度提高。
# -g 就是全局安裝 npm install -g cnpm --registry=https://registry.npm.taobao.org # 永久更換源(使用淘寶鏡像下載時能夠直接使用npm而非cnpm) npm config set registry https://registry.npm.taobao.org
安裝 vue-cli :
npm install vue-cli -g
dos 窗口以管理員身份運行,而後進入須要建立程序的目錄,這裏演示路徑 D:\Code\code\h5\first-vue-cli
輸入命令 vue init webpack "項目名"(這裏演示項目名爲 myvue)
vue init webpack myvue
進入 myvue 目錄,並安裝項目依賴。安裝項目的依賴信息存在項目的 package.json
文件中,執行命令後,會去 package.json
文件中查找依賴安裝。
# 進入myvue 目錄 cd myvue # 安裝依賴 npm install
啓動 vue-cli 程序,並查看是否啓動成功
# 啓動 vue-cli 程序 npm run dev
啓動程序後,根據啓動的地址去訪問,若出現以下頁面,則表明程序建立啓動成功。咱們的第一個 vue-cli 程序就建立好了!
本質上, webpack 是一個現代 JavaScript 應用的靜態模塊打包器(modeule bundler)。當 webpack 處理應用程序時,它會遞歸地構建一個依賴關係圖(dependency graph),其中包含應用程序須要的每一個木塊,而後將全部這些模塊打包成一個或多個bundle。
Webpack 是當下熱門的前端資源模塊化管理和打包工具,它能夠將許多鬆散耦合的木塊按照依賴和規則打包稱符合生產環境部署的前端資源。還能夠按需加載的木塊進行代碼分離,等到實際須要時再異步加載。經過 loader 轉換,任何形式的資源均可以看成模塊。好比 ComminsJS、AMD、ES六、CSS、JSON、CoffeeScript、LESS等。
伴隨着移動互聯網的大潮,當今愈來愈多的網站已經從網頁模式進化到了 WebApp 模式。他們運行在現代瀏覽器裏,使用 HTML五、CSS三、ES6 等新的技術來開發豐富的功能。網頁已經不只僅是完成瀏覽器的基本需求。WebApp 一般是一個 SPA (單頁面應用),每個視圖經過異步的方式加載,這致使頁面初始化和使用過程當中會加載愈來愈多的 JS 代碼,這給前端的開發流程和資源組織帶來了巨大挑戰。
前端開發和其它開發工做的主要區別:首先是前端基於多語言、多層次的編碼和組織工做;其次,前端產品的交互式基於瀏覽器的,這些資源式經過增量加載的方式運行到瀏覽器端。如何在開發環境組織好這些碎片化的代碼和資源,而且保證他們在瀏覽器端快速、優雅地加載和更新,就須要一個模塊化系統,這個理想中地模塊化系統式前端工程師多年來一直探索地難題。
<script src="module1.js"></script> <script src="module2.js"></script> <script src="module3.js"></script>
這是最原始的 JavaScript 文件加載的方式。若是把每個文件看做是一個模塊,那麼他們的接口一般是暴露在全局做用域下。也就是說定義在 window 對象中,不一樣模塊的調用都是一個做用域。
這種原始的加載方式暴露了一些顯而易見的弊端:
<script>
的書寫順序進行加載 服務器端的 NodeJS 遵循 ConmmonsJS 規範,該規範核心思想是容許模塊經過 require
方法來同步加載所需依賴的其它模塊,而後經過 exports
或 module.exports
來導出須要暴露的接口。
優勢:
缺點:
實現:
Asynchronous Module Definition 規範其實主要是一個主要接口 define(id?。dependencies?,factory);它要生命模塊的時候指定全部的依賴 dependencies ,而且要看成形參傳到 factory 中,對於模塊提早執行。
define("module",["dep1","dep2]),function(d1,d2){ return someExportedBalue; }); require(["module","../file,js"]),function(module, file {});
優勢:
缺點:
實現:
Commons Modules Definition 規範和 AMD 很類似,儘可能保持簡單,並與 CommonsJS 和 NodeJS 和 Modules 規範保持了很大的兼容性
define(function(require,exports,module)){ var $ = require("jquery"); var Spinning = require("./spinning"); exports.doSomething = ....; }
優勢:
缺點:
實現
EcmaScipt6 標準增長了 JavaScript 語言層面的模板體系定義。 ES6 模塊的設計思想,是儘可能靜態化,使編譯時就能肯定模塊的依賴關係,以及輸入和輸出的變量。CommonsJS 和 AMD 模塊都只能在運行時肯定這些東西。
import "jquery"; exports function doStuff(){} module "locaModule" {}
優勢:
缺點:
實現:
你們指望的模塊系統
能夠兼容多種模塊風格,儘可能能夠利用已有代碼,不只僅是 JavaScript 模塊化,還有 CSS、圖片、字體等資源也須要模塊化
WebPack 是一款模塊加載器兼打包工具,它能把各類資源,入 JS、JSX、ES六、SASS、LESS、圖片等都做爲模塊來處理和使用。
安裝:
npm install webpack -g npm install webpack-cli -g
測試安裝成功:
webpack -v webpack-cli -v
配置:
建立 webpack.config.js
配置文件
module.exports = { entry = "", output: { path: "", filename: "" }, module:{ loaders: { {test: /\.js$/, loader: ""} } }, plugins:{}, resolve:{}, watch: true }
直接運行 webpack
命令打包
建立項目(跟 vue-cli 項目差很少,先建立一個文件夾,而後用idea打開)
建立一個名爲 modules 的目錄,用於放置 JS 模塊等靜態資源
在 modules 目錄下建立模塊文件,如 hello.js ,用於編寫 JS 模塊相關的代碼
// 暴露一個方法:hello exports.hello = function () { document.write("<h1>hello,webpack</h1>"); };
在 modules 目錄下建立一個名爲 main.js 的入口文件,用於打包時設置 entry 屬性
// require 導入一個模塊,就能夠調用這個模塊中的方法了 var hello = require("./hello"); hello.hello();
在項目根目錄下建立 webpack.comnfig.js 配置文件,使用 webpack 命令打包
module.exports = { entry: './modules/main.js', output:{ filename: "./js/bundle.js" } };
注:webpack.config.js 的名字不能寫錯,還有 webpack.config.js 文件必須放在項目根目錄下!
此時,項目會生成 dist/js 目錄,並生成了 bundle.js 文件,bundle.js 文件就是打包後的 js 文件,裏面的代碼是用 ES5 規範書寫的
在項目根目錄下建立 index.html,測試打包是否成功
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!-- 加載模塊主入口 --> <script src="dist/js/bundle.js"></script> </body> </html>
說明:webpack --watch 能夠對 webpack 項目進行熱部署,即修改源代碼後,會自動打包。
Vue Router 是 Vue.js 官方的路由管理器。它和 Vue.js 的核心深度集成,讓構建單頁面應用變得易如反掌。包含的功能有:
基於第一個 vue-cli
進行測試學習,先看 node——modules 中是否存在 vue-router
vue- router 是一個插件包,因此咱們仍是須要用 npm/cnpm 來進行安裝。打開命令行工具,進入你的項目目錄,輸入下面命令。
npm install vue-router --save-dev
若是這裏 error 的話,就使用 cnpm 執行這個命令。
若是在一個模塊化工程中使用它,必須 經過 Vue.use() 明確地安裝路由功能:
import Vue from 'vue' import VueRouter from 'vue-router' Vue.use(VueRouter);
刪除沒有用的東西
component 目錄下存放咱們本身的組件
Content.vue
<template> <h1>123</h1> </template> <script> export default { name: "Content" } </script> <style scoped> </style>
Main.vue
<template> <h1>首頁</h1> </template> <script> export default { name: "Main" } </script> <style scoped> </style>
建立文件加 router ,並在該文件夾下建立 index.js 配置路由
index.js
import Vue from 'vue' import VueRouter from 'vue-router' // 導入組件 import Content from '../components/Content' import Main from '../components/Main' // 安裝路由 Vue.use(VueRouter); // 配置導出路由 export default new VueRouter({ routes: [ // 這裏是routes,不是routers,單詞不要寫錯!不然 router-view 將不會渲染 { // 路由路徑 path: '/content', // 路由名字,能夠省略 name: 'content', // 路由組件 component: Content }, { path: '/main', name: 'main', component: Main } ] });
在 main.js 中配置路由
main.js
import Vue from 'vue' import App from './App' // 導入router import router from './router' Vue.config.productionTip = false; new Vue({ el: '#app', // 配置路由 router, // 這裏是 router,自定義命名須要以鍵值對的形式在 vue 實例中聲明 components: { App }, template: '<App/>' });
在 App.vue 中測試路由是否配置成功
<template> <div id="app"> <h1>hello,vue</h1> <router-link to="/main">首頁</router-link> <router-link to="/content">內容頁</router-link> <router-view></router-view> </div> </template> <script> export default { name: 'App', } </script> <style> #app { font-family: 'Avenir', Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 60px; } </style>
說明:
routes
而不是 routers
,注意單詞拼寫,不然 <router-view></router-view>
視圖將不會渲染Vue | Error in render: "TypeError: Cannot read property 'matched' of undefined"
的錯路由模式有兩種
修改路由配置,代碼以下:
export default new Router({ mode: 'history', routes: [ .... ] });
處理 404
建立一個名爲 NotFound.vue 的視圖組件
<template> <h1>404,你的頁面走丟了!</h1> </template> <script> export default { name: "NotFound" } </script> <style scoped> </style>
在路由的中配置一個 404 的路由
import NotFound from '../components/NotFound'; { path: '*', component: NotFound }
咱們結合 ElementUI 組件庫,將所須要的知識點應用到實際中,以最快的速度來掌握 Vue 的使用。
dos 窗口進入到須要建立工程的文件夾
建立工程,導入依賴
# 建立工程項目 vue init webpack 工程名 # 安裝開發環境 npm install vue-router // 安裝 vue-router npm i element-ui -S // 安裝 element-ui cnpm install sass-loader node-sass --save-dev // 安裝 SASS,建議使用 國內員安裝 npm install // 安裝依賴
啓動項目,查看是否安裝成功
npm run dev
刪除多餘的配置和組件,編寫須要的組件
Main.vue
<template> <h1>主頁</h1> </template> <script> export default { name: "Main" } </script> <style scoped> </style>
Login.vue
<template> <div> <el-form ref="loginForm" :model="form" :rules="rules" label-width="80px" class="login-box"> <h3 class="login-title">歡迎登陸</h3> <el-form-item label="帳號" prop="username"> <el-input type="text" placeholder="請輸入帳號" v-model="form.username"/> </el-form-item> <el-form-item label="密碼" prop="password"> <el-input type="password" placeholder="請輸入密碼" v-model="form.password"/> </el-form-item> <el-form-item> <el-button type="primary" v-on:click="onSubmit('loginForm')">登陸</el-button> </el-form-item> </el-form> <el-dialog title="舒適提示" :visible.sync="dialogVisible" width="30%" :before-close="handleClose"> <span>請輸入帳號和密碼</span> <span slot="footer" class="dialog-footer"> <el-button type="primary" @click="dialogVisible = false">確 定</el-button> </span> </el-dialog> </div> </template> <script> export default { name: "Login", data() { return { form: { username: '', password: '' }, // 表單驗證,須要在 el-form-item 元素中增長 prop 屬性 rules: { username: [ {required: true, message: '帳號不可爲空', trigger: 'blur'} ], password: [ {required: true, message: '密碼不可爲空', trigger: 'blur'} ] }, // 對話框顯示和隱藏 dialogVisible: false } }, methods: { onSubmit(formName) { // 爲表單綁定驗證功能 this.$refs[formName].validate((valid) => { if (valid) { // 使用 vue-router 路由到指定頁面,該方式稱之爲編程式導航 this.$router.push("/main"); } else { this.dialogVisible = true; return false; } }); } } } </script> <style scoped> .login-box { border: 1px solid #DCDFE6; width: 350px; margin: 180px auto; padding: 35px 35px 15px 35px; border-radius: 5px; -webkit-border-radius: 5px; -moz-border-radius: 5px; box-shadow: 0 0 25px #909399; } .login-title { text-align: center; margin: 0 auto 40px auto; color: #303133; } </style>
建立文件夾 router ,並配置路由 index.js
index.js
import Vue from 'vue'; import Router from 'vue-router'; import Login from '../components/Login'; import Main from '../components/Main'; Vue.use(Router); export default new Router({ routes: [ { path: '/main', name: 'main', component: Main }, { path: '/login', name: 'login', component: Login } ] });
在 main.js 中註冊 Element-UI 和 路由器
import Vue from 'vue' import App from './App' // 導入element-ui import ElementUI from 'element-ui'; import 'element-ui/lib/theme-chalk/index.css'; import router from './router' Vue.use(ElementUI); Vue.config.productionTip = false; /* eslint-disable no-new */ new Vue({ el: '#app', router, components: { App }, template: '<App/>' });
驗證
App.vue
<template> <div id="app"> <h1>app</h1> <router-link to="/main">主頁</router-link> <router-link to="/login">登陸</router-link> <router-view></router-view> </div> </template> <script> export default { name: 'App', } </script> <style> #app { font-family: 'Avenir', Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 60px; } </style>
標籤 | 解釋 |
---|---|
el-col | 總體,默認佔24柵格 |
el-container | 主題區域 |
el-tooltip | 提示框信息 |
el-header | 內容頭部區域 |
el-aside | 左側內容區域 |
el-main | 主要內容區域 |
el-menu | 整個導航欄 |
el-submenu | 單獨一個導航欄 |
el-menu-item | 單獨一個導航欄裏面的單獨一個欄目 |
el-menu-item-group | 一組導航欄 el-date-picker 組件事件格式化方式 |
el-dialog | 彈出對話框 |
el-table | 表格 |
el-table-column | 表格列 |
el-pagination | 新增分頁 |
el-select | 選擇框 |
el-button | 按鈕 |
el-form | 表單提交 |
el-form-item lable = "活動區域" | 表單域 |
el-input-number : (@change = handlechange -- change 事件) | 數字輸入框,能夠實現加減 |
el-tab-pane | 是 el-table 的分頁 |
嵌套路由又稱子路由,在實際應用中,一般由多層嵌套的組件組合而成。一樣的,URL 中各端動態路徑也按某種結構對應嵌套的各層組件。
根據上面的路由工程項目爲基礎,增長嵌套路由。
增長鬚要使用的組件
PrivateInfo.vue
<template> <h1>信息頁</h1> </template> <script> export default { name: "info" } </script> <style scoped> </style>
TeamInfo.vue
<template> <h1>團隊信息</h1> </template> <script> export default { name: "Team" } </script> <style scoped> </style>
編寫路由展現頁面
代碼是從 ElementUI 官網上拷貝後修改了一些內容,並放到 Main.vue 中
Main.vue
<template> <div class="mainDiv"> <!-- 側邊欄 --> <div > <el-col :span="4" class="navMenuDiv"> <el-menu default-active="2" class="el-menu-vertical-demo" @open="handleOpen" @close="handleClose"> <el-submenu index="1"> <template slot="title"> <i class="el-icon-location"></i> <span>信息管理</span> </template> <el-menu-item-group> <el-menu-item index="1-1" > <router-link to="/privateInfo">我的信息</router-link> </el-menu-item> <el-menu-item index="1-2"> <router-link to="/teamInfo">團隊信息</router-link> </el-menu-item> <el-menu-item index="1-3"> <router-link to="/main">返回首頁</router-link> </el-menu-item> </el-menu-item-group> <el-submenu index="1-4"> <template slot="title">選項4</template> <el-menu-item index="1-4-1">選項1</el-menu-item> </el-submenu> </el-submenu> <el-menu-item index="2"> <i class="el-icon-menu"></i> <span slot="title">導航二</span> </el-menu-item> <el-menu-item index="3"> <i class="el-icon-document"></i> <span slot="title">導航三</span> </el-menu-item> <el-menu-item index="4"> <i class="el-icon-setting"></i> <span slot="title">導航四</span> </el-menu-item> </el-menu> </el-col> </div> <!-- 導航欄 --> <div > <el-col :span="20"> <el-menu :default-active="activeIndex2" class="el-menu-demo" mode="horizontal" @select="handleSelect" background-color="#545c64" text-color="#fff" active-text-color="#ffd04b" > <el-menu-item index="1">處理中心</el-menu-item> <el-submenu index="2"> <template slot="title">個人工做臺</template> <el-menu-item index="2-1">選項1</el-menu-item> <el-menu-item index="2-2">選項2</el-menu-item> <el-menu-item index="2-3">選項3</el-menu-item> <el-submenu index="2-4"> <template slot="title">選項4</template> <el-menu-item index="2-4-1">選項1</el-menu-item> <el-menu-item index="2-4-2">選項2</el-menu-item> <el-menu-item index="2-4-3">選項3</el-menu-item> </el-submenu> </el-submenu> <el-menu-item index="3" disabled>消息中心</el-menu-item> <el-menu-item index="4"><a href="https://www.ele.me" target="_blank">訂單管理</a></el-menu-item> </el-menu> </el-col> </div> <!-- 展現信息 --> <el-main> <router-view/> </el-main> </div> </template> <script> export default { name: "Main", data() { return { activeIndex: '1', activeIndex2: '1' }; }, methods: { handleSelect(key, keyPath) { console.log(key, keyPath); }, handleOpen(key, keyPath) { console.log(key, keyPath); }, handleClose(key, keyPath) { console.log(key, keyPath); } } } </script> <style scoped> .navMenuDiv{ text-align: left; } .mainDiv { margin-top: auto; } </style>
配置嵌套路由
index.js
import Vue from 'vue'; import Router from 'vue-router'; import Login from '../components/Login'; import Main from '../components/Main'; import PrivateInfo from '../components/PrivateInfo'; import TeamInfo from '../components/TeamInfo' Vue.use(Router); export default new Router({ routes: [ { path: '/main', component: Main, children: [ { path: '/privateInfo', component: PrivateInfo }, { path: '/teamInfo', component: TeamInfo} ] }, { path: '/login', component: Login }, ] });
說明:
啓動項目,測試是否跳轉
npm run dev
概念
RESTful 就是一個資源定位及資源操做的風格。不是標準,也不是協議,只是一種風格。基於這個風格設計的軟件能夠更簡潔,更由層次,更易於實現緩存機制。
功能
資源:互聯網全部事物均可以被抽象爲資源。
資源操做:使用 POST、DELETE、PUT、GET,使用不一樣方法對資源進行操做。分別對應 添加、刪除、修改、查詢。
傳統方式操做資源
http://localhost:8080/item/action?id=1 或 http://localhost:8080/item/action?id=1&age=18 或 http://localhost:8080/item/action
使用 RESTful 操做資源
http://loaclhos:8080/item/1 或 http://localhost:8080/item/1/18 或 http://loaclhost:8080/item/action
經過對比能夠看出,RESTful 風格更加簡潔,且不會把咱們的參數信息暴露給用戶,更加安全。如今大部分網站都是使用 RESTful 風格。
在上面代碼的基礎上進行修改
在 index.js 中修改路由的 path,使用path接收參數
import Vue from 'vue'; import Router from 'vue-router'; import Login from '../components/Login'; import Main from '../components/Main'; import PrivateInfo from '../components/PrivateInfo'; import TeamInfo from '../components/TeamInfo'; import NotFound from '../components/NotFound'; Vue.use(Router); export default new Router({ mode: 'history', routes: [ { path: '/main', component: Main, children: [ // :id :接收 URL 中傳來的 id 參數 , name :視圖層經過name綁定路由 { path: '/privateInfo/:id', name: 'privateInfo', component: PrivateInfo }, { path: '/teamInfo', component: TeamInfo} ] }, { path: '/login', component: Login }, { path: '/goHome', redirect: 'main' }, { path: '*', component: NotFound } ] });
說明:
/privateInfo/:id
中 :id
表示接收 URL 中傳來的 id 參數name
:視圖層經過 name 綁定路由修改 router-link 標籤內的內容,使其可以經過 URL 將參數傳到路由,而且能讓 vue 實例獲取到參數
<el-menu-item index="1-1" > <!-- :to :讓 params 中的參數綁定到 vm 中,讓組件能夠經過 props 獲取到, 默認是不用寫 name 的,但若是須要數據傳輸,則須要 name 綁定路由 parms: 傳遞參數 --> <router-link :to="{name: 'privateInfo', params:{id: 1}}">我的信息</router-link> </el-menu-item>
說明:
:to
:讓 params 中的參數綁定到 vm 中,讓組件能夠經過 props
獲取到能夠使用兩種方式來獲取參數
經過路由得到參數 $route.params.id
<template> <div> <h1>信息頁</h1> {{ $route.params.id }} </div> </template> <script> export default { name: "info", } </script> <style scoped> </style>
經過 props 得到參數
使用這種方式前,須要在路由中開啓 props 傳參,讓其值爲 true 則是開啓
// props:true :開啓 props 傳參,只有等於 true,組件才能經過 props 接收到路由的參數 { path: '/privateInfo/:id', name: 'privateInfo', component: PrivateInfo, props: true },
修改 PrivateInfo 的內容,讓組件經過 props
接收參數。而後經過 {{ id }}
獲取參數
<template> <div> <h1>信息頁</h1> {{ id }} </div> </template> <script> export default { name: "info", props: ['id'] } </script> <style scoped> </style>
第二種更符合咱們 java 程序員的思惟。
beforRouteEnter
:在進入路由前執行
beforRouteLeave
:在離開路由前執行
示例代碼:
<script> export default { name: "info", /* * 跟 Java 的過濾器差很少 * to:去哪裏 * from:從哪來 * next:跟過濾器的 chain 差很少,若是不調用next方法,則會停在這裏不會繼續前進 * */ beforeRouteEnter: (to,from,next)=>{ console.log('進入路由以前'); }, beforeRouteLeave: (to,from,next)=>{ console.log('離開路由以前'); next(); }, } </script>
在開發者工具的控制檯中查看效果
咱們每進一次路由,就會觸發路由鉤子的beforRouteEnter
,跳轉到其它頁面時會觸發路由鉤子的beforRouteLeave
參數說明:
安裝 Axios
npm install --save axios vue-axios
main.js 引入 Axios
// 引入 Axios import axios from 'axios'; import VueAxios from 'vue-axios';
將data.json 文件放在 static/mock 文件夾內,並在 beforRouteEnter 中進行異步加載
data.json
{ "name": "bilibili", "url": "www.bilibili.com", "page": 1, "isNonProfit": true, "address": { "street": "黃石路", "city": "廣州市", "country": "中國" }, "links": [ { "name": "百度", "url": "www.baidu.com" }, { "name": "谷歌", "url": "www.google.com" } ] }
beforRouteEnter 中進行異步加載
<template> <div> <h1>信息頁</h1> {{ id }} </div> </template> <script> export default { name: "info", props: ['id'], /* * 跟 Java 的過濾器差很少 * to:去哪裏 * from:從哪來 * next:跟過濾器的 chain 差很少 * */ beforeRouteEnter: (to,from,next)=>{ console.log('進入路由以前'); next(vm => { vm.getData(); // 進入路由以前執行 getData }); next(); }, beforeRouteLeave: (to,from,next)=>{ console.log('離開路由以前'); next(); }, methods: { getData : function () { this.axios.get('../static/mock/data.json').then(function (response) { console.log(response.data); }); } } } </script> <style scoped> </style>
測試,並在控制檯中查看輸出