Vue.js(讀音 /vjuː/, 相似於 view) 是一套構建用戶界面的 漸進式框架。與其餘重量級框架不一樣的是,Vue 採用自底向上增量開發的設計。Vue 的核心庫只關注視圖層,而且很是容易學習,很是容易與其它庫或已有項目整合。另外一方面,Vue 徹底有能力驅動採用單文件組件和 Vue 生態系統支持的庫開發的複雜單頁應用。javascript
Vue.js 的目標是經過儘量簡單的 API 實現響應的數據綁定和組合的視圖組件。css
輕量級框架:只關注視圖層,是一個構建數據的視圖集合,大小隻有幾十kbhtml
簡單易學:國人開發,中文文檔,不存在語言障礙,易於理解和學習前端
雙向數據綁定:保留了angular的特色,在數據操做方面更爲簡單vue
組件化:保留了react的優勢;實現了html的封裝和重用,在構建單頁面應用方面有着獨特的優點java
視圖,數據,結構分離:是數據的更改更爲簡單,不須要進行邏輯代碼的修改,只須要操做數據就能完成相關操做node
虛擬DOM:dom操做時很是耗費性能的,再也不使用原生的dom操做節點,極大解放dom操做,但具體操做的仍是dom不過是換了另外一種方式react
運行速度更快:像比較與react而言,一樣都是操做虛擬dom,就性能而言,vue存在很大的優點jquery
MVVM定義:MVVM是Model-View-ViewModel的簡寫。即模型-視圖-視圖模型。【模型】指的是後端傳遞的數據。【視圖】指的是所看到的頁面。【視圖模型】mvvm模式的核心,它是鏈接view和model的橋樑。webpack
它有兩個方向:
一是將【模型】轉化成【視圖】,即將後端傳遞的數據轉化成所看到的頁面。實現的方式是:數據綁定。
二是將【視圖】轉化成【模型】,即將所看到的頁面轉化成後端的數據。實現的方式是:DOM 事件監聽。這兩個方向都實現的,咱們稱之爲數據的雙向綁定。
總結:在MVVM的框架下視圖和模型是不能直接通訊的。它們經過ViewModel來通訊,ViewModel一般要實現一個observer觀察者,當數據發生變化,ViewModel可以監聽到數據的這種變化,而後通知到對應的視圖作自動更新,而當用戶操做視圖,ViewModel也能監聽到視圖的變化,而後通知數據作改動,這實際上就實現了數據的雙向綁定。而且MVVM中的View 和 ViewModel能夠互相通訊
在vue中,Model:指的是js中的數據,如對象,數組等等;View:指的是頁面視圖;viewModel:指的是vue實例化對象。爲何說VUE是一個漸進式的javascript框架, 漸進式是什麼意思?
1.若是你已經有一個現成的服務端應用,你能夠將vue 做爲該應用的一部分嵌入其中,帶來更加豐富的交互體驗;
2.若是你但願將更多業務邏輯放到前端來實現,那麼VUE的核心庫及其生態系統也能夠知足你的各式需求(core+vuex+vue-route)。和其它前端框架同樣,VUE容許你將一個網頁分割成可複用的組件,每一個組件都包含屬於本身的HTML、CSS、JAVASCRIPT以用來渲染網頁中相應的地方。
3.若是咱們構建一個大型的應用,在這一點上,咱們可能須要將東西分割成爲各自的組件和文件,vue有一個命令行工具,使快速初始化一個真實的工程變得很是簡單(vue init webpack my-project)。咱們可使用VUE的單文件組件,它包含了各自的HTML、JAVASCRIPT以及帶做用域的CSS或SCSS。以上這三個例子,是一步步遞進的,也就是說對VUE的使用可大可小,它都會有相應的方式來整合到你的項目中。因此說它是一個漸進式的框架。VUE最獨特的特性:響應式系統VUE是響應式的(reactive),也就是說當咱們的數據變動時,VUE會幫你更新全部網頁中用到它的地方。
Vue.component
來建立組件,這些組件是全局註冊的
// 定義一個名爲 button-counter 的新組件 Vue.component('button-counter', { data: function () { return { count: 0 } }, template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>' })
組件的使用
<div id="components-demo"> <button-counter></button-counter> </div>
Prop 是你能夠在組件上註冊的一些自定義特性。當一個值傳遞給一個 prop 特性的時候,它就變成了那個組件實例的一個屬性。爲了給博文組件傳遞一個標題,咱們能夠用一個 props
選項將其包含在該組件可接受的 prop 列表中
Vue.component('blog-post', { props: ['title'], template: '<h3>{{ title }}</h3>' })
經過下面的代碼,便可將數據做爲自定義的特性傳遞進來
<blog-post title="My journey with Vue"></blog-post> <blog-post title="Blogging with Vue"></blog-post> <blog-post title="Why Vue is so fun"></blog-post>
new Vue({ el: '#blog-post-demo', data: { posts: [ { id: 1, title: 'My journey with Vue' }, { id: 2, title: 'Blogging with Vue' }, { id: 3, title: 'Why Vue is so fun' } ] } }) <blog-post v-for="post in posts" v-bind:key="post.id" v-bind:title="post.title" ></blog-post>
如上所示,你會發現咱們可使用 v-bind
來動態傳遞 prop。
定義組件名的方式有兩種:
Vue.component('my-component-name', { /* ... */ })
當使用 kebab-case (短橫線分隔命名) 定義一個組件時,你也必須在引用這個自定義元素時使用 kebab-case,例如 <my-component-name>
。
Vue.component('MyComponentName', { /* ... */ })
當使用 PascalCase (首字母大寫命名) 定義一個組件時,你在引用這個自定義元素時兩種命名法均可以使用。也就是說 <my-component-name>
和 <MyComponentName>
都是可接受的。注意,儘管如此,直接在 DOM (即非字符串的模板) 中使用時只有 kebab-case 是有效的。
局部註冊
new Vue({
el: '#app',
components: {
'component-a': ComponentA,
'component-b': ComponentB
}
})
對於 components
對象中的每一個屬性來講,其屬性名就是自定義元素的名字,其屬性值就是這個組件的選項對象。
注意局部註冊的組件在其子組件中不可用
若是你使用了諸如 Babel 和 webpack 的模塊系統。在這些狀況下,咱們推薦建立一個 components
目錄,並將每一個組件放置在其各自的文件中。
而後你須要在局部註冊以前導入每一個你想使用的組件。例如,在一個假設的 ComponentB.js
或 ComponentB.vue
文件中:
import ComponentA from './ComponentA'
import ComponentC from './ComponentC'
export default {
components: {
ComponentA,
ComponentC
},
// ...
}
如今 ComponentA
和 ComponentC
均可以在 ComponentB
的模板中使用了。
props: {
title: String,
likes: Number,
isPublished: Boolean,
commentIds: Array,
author: Object,
callback: Function,
contactsPromise: Promise // or any other constructor
}
props的類型和傳值:https://cn.vuejs.org/v2/guide/components-props.html
組件通訊見本頁第八個問題
本部分摘自:https://blog.csdn.net/liyunkun888/article/details/83269692
Vue.nextTick():在下次 DOM
更新循環結束以後執行延遲迴調。在修改數據以後當即使用這個方法,獲取更新後的 DOM
。
獲取更新後的DOM
言外之意就是什麼操做須要用到了更新後的DOM
而不能使用以前的DOM
或者使用更新前的DOM
會出問題,因此就衍生出了這個獲取更新後的 DOM
的Vue
方法。因此放在Vue.nextTick()
回調函數中的執行的應該是會對DOM
進行操做的js
代碼。
Vue
生命週期的created()鉤子函數進行的DOM
操做必定要放在Vue.nextTick()
的回調函數中。緣由是什麼呢,緣由是在created()
鉤子函數執行的時候DOM
其實並未進行任何渲染,而此時進行DOM
操做無異於徒勞,因此此處必定要將DOM
操做的js
代碼放進Vue.nextTick()
的回調函數中。與之對應的就是mounted鉤子函數,由於該鉤子函數執行時全部的DOM
掛載和渲染都已完成,此時在該鉤子函數中進行任何DOM
操做都不會有問題 。vm.someData = 'new value'
,DOM
並不會立刻更新,而是在異步隊列被清除,也就是下一個事件循環開始時執行更新時纔會進行必要的DOM
更新。若是此時你想要根據更新的 DOM
狀態去作某些事情,就會出現問題。。爲了在數據變化以後等待 Vue
完成更新 DOM
,能夠在數據變化以後當即使用 Vue.nextTick(callback)
。這樣回調函數在 DOM
更新完成後就會調用。mounted
不會承諾全部的子組件也都一塊兒被掛載。若是你但願等到整個視圖都渲染完畢,能夠用 vm.$nextTick
替換掉 mounted。
建立→初始化數據→編譯模板→掛載DOM→更新→渲染→銷燬
beforeCreate
:在實例初始化以後,數據觀測data observer(props、data、computed
) 和 event/watcher
事件配置以前被調用。created
:實例已經建立完成以後被調用。在這一步,實例已完成如下的配置:數據觀測(data observer),屬性和方法的運算, watch/event
事件回調。然而,掛載階段還沒開始,$el
屬性目前不可見。beforeMount
:在掛載開始以前被調用:相關的 render
函數首次被調用。mounted
: el
被新建立的 vm.$el
替換,並掛載到實例上去以後調用該鉤子。beforeUpdate
:數據更新時調用,發生在虛擬 DOM
從新渲染和打補丁以前。 你能夠在這個鉤子中進一步地更改狀態,這不會觸發附加的重渲染過程。updated
:不管是組件自己的數據變動,仍是從父組件接收到的 props
或者從vuex
裏面拿到的數據有變動,都會觸發虛擬 DOM
從新渲染和打補丁,並在以後調用 updated
。beforeDestroy
:實例銷燬以前調用。在這一步,實例仍然徹底可用。destroyed
:Vue
實例銷燬後調用。調用後,Vue
實例指示的全部東西都會解綁定,全部的事件監聽器會被移除,全部的子實例也會被銷燬。 該鉤子在服務器端渲染期間不被調用。 注意:created
階段的ajax
請求與mounted
請求的區別:前者頁面視圖未出現,若是請求信息過多,頁面會長時間處於白屏狀態。
單個組件的生命週期
beforeCreate/Created/beforeMount/mounted
四個鉤子函數beforeUpdate/updated
鉤子函數beforeDestory/destroyed
鉤子函數beforeUpdate/updated
可屢次執行本部份內容摘自:https://www.jianshu.com/p/46c9d777cab1
Web界面由DOM樹(樹的意思是數據結構)來構建,當其中一部分發生變化時,其實就是對應某個DOM節點發生了變化,
虛擬DOM就是爲了解決瀏覽器性能問題而被設計出來的。如前,若一次操做中有10次更新DOM的動做,虛擬DOM不會當即操做DOM,而是將這10次更新的diff內容保存到本地一個JS對象中,最終將這個JS對象一次性attch到DOM樹上,再進行後續操做,避免大量無謂的計算量。因此,用JS對象模擬DOM節點的好處是,頁面的更新能夠先所有反映在JS對象(虛擬DOM)上,操做內存中的JS對象的速度顯然要更快,等更新完成後,再將最終的JS對象映射成真實的DOM,交由瀏覽器去繪製。
用JavaScript的方法來模擬DOM樹,用新渲染的對象樹去和舊的樹進行對比,記錄下變化的變化,而後應用到真實的DOM樹上,這樣咱們只須要更改與原來視圖不一樣的地方,而不須要所有從新渲染一次。
diff算法只會對相同顏色方框內的DOM節點進行比較,即同一個父節點下的全部子節點。當發現節點不存在,則該節點和子節點會被徹底刪除,不會作進一步的比較。
在實際的代碼中,會對新舊兩棵樹進行深度的遍歷,給每個節點進行標記。而後在新舊兩棵樹的對比中,將不一樣的地方記錄下來。
什麼是雙向數據綁定?Vue是一個MVVM框架,數據綁定簡單來講,就是當數據發生變化時,相應的視圖會進行更新,當視圖更新時,數據也會跟着變化。
實現數據綁定的方式大體有如下幾種:
發佈者-訂閱者模式(backbone.js)
髒值檢查(angular.js)
數據劫持(vue.js)
Vue.js則是經過數據劫持以及結合發佈者-訂閱者來實現的,數據劫持是利用ES5的Object.defineProperty(obj, key, val)來劫持各個屬性的的setter以及getter,在數據變更時發佈消息給訂閱者,從而觸發相應的回調來更新視圖。
Object.defineProperty() 方法會直接在一個對象上定義一個新屬性,或者修改一個對象的現有屬性, 並返回這個對象。
實現數據的雙向綁定,首先要對數據進行劫持監聽,因此咱們須要設置一個監聽器Observer
,用來監聽全部屬性。若是屬性發上變化了,就須要告訴訂閱者Watcher
看是否須要更新。由於訂閱者是有不少個,因此咱們須要有一個消息訂閱器Dep
來專門收集這些訂閱者,而後在監聽器Observer
和訂閱者Watcher
之間進行統一管理的。
//監聽器 function defineReactive(data,key,val) { observe(val); var dep = new Dep(); Object.defineProperty(data, key, { enumerable: true, configurable: true, get: function() { if(Dep.target) { //判斷是否須要添加訂閱者 dep.addSub(Dep.target); } return val; }, set: function(newVal) { if (val === newVal) { return; } val = newVal; console.log('屬性' + key + '已經被監聽了,如今值爲:「' + newVal.toString() + '」'); dep.notify(); // 若是數據變化,通知全部訂閱者 } }); } //訂閱者 function Watcher(vm,exp,cb) { this.vm = vm; //指向SelfVue的做用域 this.exp = exp; //綁定屬性的key值 this.cb = cb; //閉包 this.value = this.get(); } Watcher.prototype = { update:function() { this.run(); }, run:function() { var value = this.vm.data[this.exp]; var oldVal = this.value; if(value !== oldVal) { this.value = value; this.cb.call(this.vm,value,oldVal); } }, get:function() { Dep.target = this; // 緩存本身 var value = this.vm.data[this.exp]; // 強制執行監聽器裏的get函數 Dep.target = null; // 釋放本身 return value; } }
本部分參考:https://segmentfault.com/a/1190000014616977?utm_source=channel-hottest http://www.javashuo.com/article/p-vbvongpy-ea.html
父傳子的實現方式就是經過props屬性,子組件經過props屬性接收從父組件傳過來的值,而父組件傳值的時候使用 v-bind 將子組件中預留的變量名綁定爲data裏面的數據便可。
子 <template> <div id="container"> {{msg}} </div> </template> <script> export default { data() { return {}; }, props:{ msg: String } }; </script> <style scoped> #container{ color: red; margin-top: 50px; } </style> 父 <template> <div id="container"> <input type="text" v-model="text" @change="dataChange"> <Child :msg="text"></Child> </div> </template> <script> import Child from "@/components/Child"; export default { data() { return { text: "父組件的值" }; }, methods: { dataChange(data){ this.msg = data } }, components: { Child } }; </script> <style scoped> </style>
子傳父的實現方式就是用了 this.$emit 來遍歷 getData 事件,首先用按鈕來觸發 setData 事件,在 setData 中 用 this.$emit 來遍歷 getData 事件,最後返回 this.msg。
子 <template> <div id="container"> <input type="text" v-model="msg"> <button @click="setData">傳遞到父組件</button> </div> </template> <script> export default { data() { return { msg: "傳遞給父組件的值" }; }, methods: { setData() { this.$emit("getData", this.msg); } } }; </script> <style scoped> #container { color: red; margin-top: 50px; } </style> 父 <template> <div id="container"> <Child @getData="getData"></Child> <p>{{msg}}</p> </div> </template> <script> import Child from "@/components/Child"; export default { data() { return { msg: "父組件默認值" }; }, methods: { getData(data) { this.msg = data; } }, components: { Child } }; </script> <style scoped> </style>
vue 中沒有直接子對子傳參的方法,建議將須要傳遞數據的子組件,都合併爲一個組件
若是必定須要子對子傳參,能夠先從傳到父組件,再傳到子組件(至關於一個公共bus文件)
爲了便於開發,vue 推出了一個狀態管理工具 vuex,能夠很方便實現組件之間的參數傳遞(詳解見本頁第十三個問題)
其次還能夠經過事件總線eventBus來進行兩個組件間的通訊,EventBus
又稱爲事件總線。在Vue中可使用EventBus
來做爲溝通橋樑的概念,就像是全部組件共用相同的事件中心,能夠向該中心註冊發送事件或接收事件,因此組件均可以上下平行地通知其餘組件,但也就是太方便因此若使用不慎,就會形成難以維護的災難,所以才須要更完善的Vuex做爲狀態管理中心,將通知的概念上升到共享狀態層次。
eventBus原理是建立一個空Vue實例, 而後在上面掛載通信事件, 在掛載事件時, 能夠認爲這個Vue實例(組件), 是全部組件的父組件, 在觸發事件時, 能夠認爲這個Vue實例, 是全部組件的子組件
//eventBus.js的代碼 import Vue from "vue"; let bus = new Vue(); export default bus;
//在須要通訊的兩個組件中分別import import bus from //一個組件中發射 bus.$emit('SUBMITSEARCH_PEOPLE',this.searchContent)
//另外一個組件中接收 bus.$on('SUBMITSEARCH_PEOPLE',searchContentsearchContent)
//存在問題是$on會屢次觸發,解決方法以下
beforeDestroy () {
bus.$off('SUBMITSEARCH_PEOPLE')
}"../utils/eventBus.js";
() => { this.bgC = ; }
常見的基於數據劫持的雙向綁定有兩種實現,一個是目前Vue在用的Object.defineProperty
,另外一個是ES2015中新增的Proxy。
嚴格來說Proxy應該被稱爲『代理』而非『劫持』,不過因爲做用有不少類似之處,咱們在下文中就再也不作區分,統一叫『劫持』。
數據劫持的優點所在。
data.name = '渣渣輝'
後直接觸發變動,而好比Angular的髒檢測則須要顯示調用markForCheck
(能夠用zone.js避免顯示調用,不展開),react須要顯示調用setState
。newVal
,所以在這部分不須要額外的diff操做,不然咱們只知道數據發生了變化而不知道具體哪些數據變化了,這個時候須要大量diff來找出變化值,這是額外性能損耗Object.defineProperty
的全方位增強版。
vue3.0的改進
Proxy能夠直接監聽對象而非屬性
Proxy能夠直接監聽數組的變化
Proxy返回的是一個新對象,咱們能夠只操做新的對象達到目的,而Object.defineProperty
只能遍歷對象屬性直接修改
//proxy數據劫持 observe(data) { const that = this; let handler = { get(target, property) { return target[property]; }, set(target, key, value) { let res = Reflect.set(target, key, value); that.subscribe[key].map(item => { item.update(); }); return res; } } this.$data = new Proxy(data, handler); }
基本用法:let p = new Proxy(target, handler);
target: 是用Proxy包裝的被代理對象(能夠是任何類型的對象,包括原生數組,函數,甚至另外一個代理)。 handler: 是一個對象,其聲明瞭代理target 的一些操做,其屬性是當執行一個操做時定義代理的行爲的函數。
本部分摘自:https://www.jianshu.com/p/2df6dcddb0d7
data:{ firstname:"", lastname:"" }, methods:{}, watch:{ firstname:function(){ console.log(this.firstname) } } watch:{ firstname:function(newValue,OldValue){ console.log(newValue); console.log(OldValue); } }
同時Watch還能夠被用來監聽路由router的變化
computed:{ fullname:function(){ return this.firstname +"-"+this.lastname } }
methods,watch,computed的區別
v-text是用於操做純文本,它會替代顯示對應的數據對象上的值。當綁定的數據對象上的值發生改變,插值處的內容也會隨之更新。注意:此處爲單向綁定,數據對象上的值改變,插值會發生變化;可是當插值發生變化並不會影響數據對象的值。其中:v-text能夠簡寫爲{{}},而且支持邏輯運算。
<div id="app"> {{ message }} </div> var app = new Vue({ el : '#app', data : { message : 'hello world' } })
v-html用於輸出html,它與v-text區別在於v-text輸出的是純文本,瀏覽器不會對其再進行html解析,但v-html會將其當html標籤解析後輸出。
<div id="app"> <p v-html="html"></p> </div> var app = new Vue({ el: "#app", data: { html: "<b style='color:red'>v-html</b>" } });
v-model一般用於表單組件的綁定,例如input,select等。它與v-text的區別在於它實現的表單組件的雙向綁定,若是用於表單控件之外標籤是沒有用的。
<div id="app"> <input v-model="message " /> </div> var app = new Vue({ el : '#app', data : { message : 'hello world' } })
<template> <div id="app"> <div v-if="isIf"> if </div> <div v-show="ifShow"> show </div> <button @click="toggleShow">toggle</button> </div> </template> <script> export default { name: 'app', data() { return { isIf : true, ifShow : true, loginType : "username" } }, methods: { toggleShow : function(){ this.ifShow = this.ifShow ? false : true; this.isIf = this.isIf ? false : true; } } } </script>
v-for是一個循環指令,通常跟數組結合起來使用
也能夠在模板中使用 v-for
v-for 能夠經過一個對象的屬性來迭代數據
v-for 經過一個對象的屬性來迭代數據,能夠提供第二個的參數爲鍵名
v-for 經過一個對象的屬性來迭代數據,以第三個參數爲索引
v-for 也能夠循環整數
<p id="wantuizhijia"> <ol> <li v-for="site in sites"> {{ site.name }} </li> </ol> </p> <script> new Vue({ el: '#wantuizhijia', data: { sites: [ { name: '網推之家' }, { name: '谷歌' }, { name: '淘寶' } ] } }) </script>
<div id="example-1"> <button v-on:click="counter += 1">Add 1</button> <p>The button above has been clicked {{ counter }} times.</p> </div> var example1 = new Vue({ el: '#example-1', data: { counter: 0 } })
用於監聽DOM事件,典型的就是v-on:click,處理的方法放在methods屬性裏會默認傳一個參數。
這裏的evt,是標準的鼠標點擊事件,相似jquery的click事件的回調函數中的參數。
<button @click="test">點擊</button> methods: { test: function (evt) { console.log(evt); } }
若是須要給他一個像上面同樣的鼠標點擊事件時,則使用$event做爲參數(他和不傳參數時的默認鼠標事件對象是相同的)
<div id="app"> <a href="http://www.baidu.com" @click="test(items, $event)">點擊搜索age</a> </div> <script> var test = {name: "test"}; var vm = new Vue({ el: '#app', data: { items: "test" }, methods: { test: function (msg, evt) { console.log(msg); evt.preventDefault();//阻止默認動做,好比這裏是頁面跳轉 } } }) </script>
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>
本部分摘自:https://www.jianshu.com/p/a0680cb09c83
隨着前端應用的業務功能起來越複雜,用戶對於使用體驗的要求愈來愈高,單面(SPA
)成爲前端應用的主流形式。大型單頁應用最顯著特色之一就是採用的前端路由系統,經過改變URL
,在不從新請求頁面的狀況下,更新頁面視圖。
更新視圖但不從新請求頁面,是前端路由原理的核心之一,目前在瀏覽器環境中這一功能的實現主要有2
種方式:
URL
中的hash
("#"
);History interface
在HTML5
中新增的方法;vue-router
是Vue.js
框架的路由插件,它是經過mode
這一參數控制路由的實現模式的。
const router=new VueRouter({ mode:'history', routes:[...] })
隨着ajax流行,異步數據請求交互運行在不刷新瀏覽器的狀況下。而異步交互體驗的更高級版本就是SPA——單頁面應用。單頁面應用不只僅是在頁是無刷新的,連頁面吧跳轉也是無刷新的,爲了實現單頁面應用,因此就有了前端路由。相似於服務端路由,前端路由實現起來其實其實也很簡單,就是匹配不一樣的url路徑,進行解析,而後動態的渲染出該區域的html內容,可是這樣存在一個問題,就是url每次變化的時候,都會形成頁面的刷新,那解決的思路即是在改變url的狀況下,保證頁面不刷新,早2014年以前,你們是經過has來實現路由,url hash就相似於http://www.xxx.com/#/login,這種#後面hash值的變化並不會致使瀏覽器向服務器發送請求,也就不會刷新頁面,另外每次hash值的變化,還會觸發hashchange事件,經過這個事件咱們就能夠知道hash值發生了改變,而後咱們即可以監聽hashchange來實現更新頁面部份內容的操做。
本部分參考連接:https://segmentfault.com/a/1190000014822765?utm_source=tag-newest
首先,在項目的App.vue中獲取當前登錄用戶的permissionList
並使用vuex狀態管理,將用戶的權限存入
以後,在nav.vue這個導航文件中用mapGetters獲取permissionList,並根據獲取到的權限設置當前用戶的菜單
store中的定義
// getters const getters = { userInfo: state => state.userInfo, pmList: state => state.pmList }
nav.vue導航文件部分代碼
data () { return { // pmList: [], menuList: [] // 當前用戶擁有的權限菜單 } }, computed: { ...mapGetters({ user: 'userInfo', pmList: 'pmList', tabList: 'tabList' }) }, // 根據權限設置當前用戶的菜單 setUserMenu () { const permissions = this.pmList const routeList = cloneDeep(this.$router.options.routes[0].children) if (permissions && permissions.length) { routeList.forEach(rootItem => { if (rootItem.isNavMenu && permissions.includes(rootItem.name)) { if (rootItem.children.length) { rootItem.children = rootItem.children.filter(subItem => { return subItem.isTabMenu && permissions.includes(subItem.name) }) } this.menuList.push(rootItem) } }) } else { // this.menuList = routeList.filter(item => { // return item.isNavMenu; // }); } },
{ path: '/goods', icon: 'icon-daohangshangpin-px', index: 1, isNavMenu: true, name: 'MALL_PRODUCT', meta: { title: '商品', }, component: { template: `<router-view></router-view>` }, children: goods },
{ path: 'list', index: 0, isTabMenu: true, name: 'MALL_PRODUCT_MANAGER', meta: { title: '商品管理' }, component: () => import('@views/goods/main/list.vue') }, { path: 'listAddGood', isTabMenu: false, meta: { title: '添加商品' }, component: () => import('@views/goods/main/edit.vue') },
在SPA單頁面組件的開發中 Vue的vuex和React的Redux 都統稱爲同一狀態管理,我的的理解是全局狀態管理更合適;簡單的理解就是你在state中定義了一個數據以後,你能夠在所在項目中的任何一個組件裏進行獲取、進行修改,而且你的修改能夠獲得全局的響應變動。
先聲明一個state變量,並賦值一個空對象給它,裏面隨便定義兩個初始屬性值;而後再在實例化的Vuex.Store裏面傳入一個空對象,並把剛聲明的變量state放裏面。
vuex官方API提供了一個getters,和vue計算屬性computed同樣,來實時監聽state值的變化(最新狀態)
mutattions也是一個對象,這個對象裏面能夠放改變state的初始值的方法,具體的用法就是給裏面的方法傳入參數state或額外的參數,而後利用vue的雙向數據驅動進行值的改變
actions也是個對象變量,最大的做用就是裏面的Action方法 能夠包含任意異步操做,這裏面的方法是用來異步觸發mutations裏面的方法,actions裏面自定義的函數接收一個context參數和要變化的形參,context與store實例具備相同的方法和屬性,因此它能夠執行context.commit(' ')
而在外部組件裏進行全局執行actions裏面方法的時候,你只須要用執行
this.$store.dispatch('hideFooter')
或this.$store.dispatch('showFooter')
以及this.$store.dispatch('getNewNum',6) //6要變化的實參
在組件中獲取vuex中的state
computed: { count () { return this.$store.state.count } }
那爲何要使用computed而不是data獲取vuex中的state呢?
這是由於data 中的內容只會在 created 鉤子觸發前初始化一次,具體來講就是data中設置count: this.$store.state.count 則count的值是created鉤子執行前this.$store.state.count的值,賦值以後屬性的值就是純粹的字面量,以後this.$store.state.count 如何變化均影響不到count的取值。
vuex模塊化
//index.js import Vue from 'vue'; import Vuex from 'vuex'; import footerStatus from './modules/footerStatus' import collection from './modules/collection' Vue.use(Vuex); export default new Vuex.Store({ modules:{ footerStatus, collection } });
//collection.js const state={ collects:[], //初始化一個colects數組 }; const getters={ renderCollects(state){ //承載變化的collects return state.collects; } }; const mutations={ pushCollects(state,items){ //如何變化collects,插入items state.collects.push(items) } }; const actions={ invokePushItems(context,item){ //觸發mutations裏面的pushCollects ,傳入數據形參item 對應到items context.commit('pushCollects',item); } }; export default { namespaced:true,//用於在全局引用此文件裏的方法時標識這一個的文件名 state, getters, mutations, actions }
//footerStatus.js const state={ //要設置的全局訪問的state對象 showFooter: true, changableNum:0 //要設置的初始屬性值 }; const getters = { //實時監聽state值的變化(最新狀態) isShow(state) { //承載變化的showFooter的值 return state.showFooter }, getChangedNum(){ //承載變化的changebleNum的值 return state.changableNum } }; const mutations = { show(state) { //自定義改變state初始值的方法,這裏面的參數除了state以外還能夠再傳額外的參數(變量或對象); state.showFooter = true; }, hide(state) { //同上 state.showFooter = false; }, newNum(state,sum){ //同上,這裏面的參數除了state以外還傳了須要增長的值sum state.changableNum+=sum; } }; const actions = { hideFooter(context) { //自定義觸發mutations裏函數的方法,context與store 實例具備相同方法和屬性 context.commit('hide'); }, showFooter(context) { //同上註釋 context.commit('show'); }, getNewNum(context,num){ //同上註釋,num爲要變化的形參 context.commit('newNum',num) } }; export default { namespaced: true, //用於在全局引用此文裏的方法時標識這一個的文件名 state, getters, mutations, actions }
本部分參考連接:http://www.javashuo.com/article/p-zwfitfjy-eu.html
WebPack
是一個模塊打包工具,你可使用
WebPack
管理你的模塊依賴,並編繹輸出模塊們所需的靜態文件。它可以很好地管理、打包Web開發中所用到的
HTML
、
JavaScript
、
CSS
以及各類靜態文件(圖片、字體等),讓開發過程更加高效。對於不一樣類型的資源,
webpack
有對應的模塊加載器。
webpack
模塊打包器會分析模塊間的依賴關係,最後 生成了優化且合併後的靜態資源。
CommonJS
、 AMD
、ES6
的語法作了兼容js
、css
、圖片等資源文件都支持打包對CoffeeScript
、ES6
的支持webpack.config.js
chunk
,實現按需加載,下降了初始化時間SourceUrls
和SourceMaps
,易於調試Plugin
接口,大可能是內部插件,使用起來比較靈活webpack
使用異步 IO
並具備多級緩存。這使得 webpack
很快且在增量編譯上更加快【Loader】:用於對模塊源碼的轉換,loader描述了webpack如何處理非javascript模塊,而且在buld中引入這些依賴。loader能夠將文件從不一樣的語言(如TypeScript)轉換爲JavaScript,或者將內聯圖像轉換爲data URL。好比說:CSS-Loader,Style-Loader等。
【Plugin】:目的在於解決loader沒法實現的其餘事,從打包優化和壓縮,到從新定義環境變量,功能強大到能夠用來處理各類各樣的任務。webpack提供了不少開箱即用的插件:CommonChunkPlugin主要用於提取第三方庫和公共模塊,避免首屏加載的bundle文件,或者按需加載的bundle文件體積過大,致使加載時間過長,是一把優化的利器。而在多頁面應用中,更是可以爲每一個頁面間的應用程序共享代碼建立bundle。
一切皆模塊: 正如js文件能夠是一個「模塊(module)」同樣,其餘的(如css、image或html)文件也可視做模 塊。所以,你能夠require('myJSfile.js')亦能夠require('myCSSfile.css')。這意味着咱們能夠將事物(業務)分割成更小的易於管理的片斷,從而達到重複利用等的目的。
按需加載: 傳統的模塊打包工具(module bundlers)最終將全部的模塊編譯生成一個龐大的bundle.js文件。可是在真實的app裏邊,「bundle.js」文件可能有10M到15M之大可能會致使應用一直處於加載中狀態。所以Webpack使用許多特性來分割代碼而後生成多個「bundle」文件,並且異步加載部分代碼以實現按需加載。
webpack只是一個打包模塊的機制,只是把依賴的模塊轉化成能夠表明這些包的靜態文件。並非什麼commonjs或者amd之類的模塊化規範。webpack就是識別你的 入口文件。識別你的模塊依賴,來打包你的代碼。至於你的代碼使用的是commonjs仍是amd或者es6的import。webpack都會對其進行分析。來獲取代碼的依賴。webpack作的就是分析代碼。轉換代碼,編譯代碼,輸出代碼。webpack自己是一個node的模塊,因此webpack.config.js是以commonjs形式書寫的(node中的模塊化是commonjs規範的)
初始化:啓動構建,讀取與合併配置參數,加載 Plugin
,實例化 Compiler
。
編譯:從 Entry
發出,針對每一個Module
串行調用對應的Loader
去翻譯文件內容,再找到該Module
依賴的 Module
,遞歸地進行編譯處理。
輸出:對編譯後的Module
組合成 Chunk
,把 Chunk
轉換成文件,輸出到文件系統。
熱更新是:使得應用在運行狀態下,不重載刷新就能更新、增長、移除模塊的機制。簡單來講,就是爲了 提高開發效率。
https://www.jianshu.com/p/652fbae768bf
//使用 DllPlugin的配置 const manifest = require('./dll/vendor-manifest.json'); // ... 其餘完美的配置 plugins: [ new webpack.DllReferencePlugin({ manifest, }), ],
本部分摘自:https://segmentfault.com/a/1190000015088834?utm_source=tag-newest
引入插件
import VueLazyLoad from 'vue-lazyload'
Vue.use(VueLazyload, {
preLoad: 1.3,
error: require('@/assets/img/dou_dou.jpg'), //請求失敗後顯示的圖片
loading: require('@/assets/img/dou_dou.jpg'), //加載的loading過渡圖片
attempt: 1, // 加載圖片數量
}) listenEvents: [ 'scroll' ] //監聽的事件
src屬性改爲 v-lazy
<img v-lazyload="imageSrc" >
監聽的事件列表:['scroll', 'wheel', 'mousewheel', 'resize', 'animationend', 'transitionend']