MVVM是Model-View-ViewModel的縮寫,Model表明數據模型負責業務邏輯和數據封裝,View表明UI組件負責界面和顯示,ViewModel監聽模型數據的改變和控制視圖行爲,處理用戶交互,簡單來講就是經過雙向數據綁定把View層和Model層鏈接起來。在MVVM架構下,View和Model沒有直接聯繫,而是經過ViewModel進行交互,咱們只關注業務邏輯,不須要手動操做DOM,不須要關注View和Model的同步工做。javascript
vue核心是一個響應的數據綁定系統,mvvm,數據驅動,組件化,輕量,簡潔,高效,快速,模塊友好。html
缺點:不支持低版本瀏覽器,最低到IE9,不利於SEO的優化,首頁加載時間較長,不可使用瀏覽器的導航按鈕須要自行實現前進後退。vue
RESTful是一個api的標準,無狀態請求。請求的路由地址是固定的。 restful:給用戶一個url,根據method不一樣在後端作不一樣處理:好比post 建立數據,get 獲取數據,put和patch修改數據,delete刪除數據java
1.vue-cli名字改成@vue/cli,因此全局安裝了舊版的要經過npm install vue-cli -g
卸載。vue-router
安裝新版vue-clivuex
npm install -g @vue/cli
vue-cli
2.建立一個項目 vue create hello-world
3.assets文件夾是放靜態資源;components是放組件;router是定義路由相關的配置;view視圖;app.vue是一個應用主組件;main.js是入口文件npm
<input v-model="msg" />
複製代碼
至關於編程
<input v-bind:value="msg" v-on:input="msg=$event.target.value" />
複製代碼
v-show只是在display: none和display: block之間切換,只須要切換CSS,DOM仍是一直保留着,v-show在初始渲染時有更高的開銷,可是切換開銷很小,更適合頻繁切換的場景後端
v-if涉及到vue底層的編譯,當屬性初始爲false時組件不會被渲染,直到條件爲true,而且切換條件時會觸發銷燬/掛載組件,切換時開銷更高,更適合不常常切換的場景
route是路由信息對象,包括path,params,hash,query,fullPath,matched,name等路由信息參數。
router是路由實例對象,包括了路由的跳轉方法,鉤子函數。
在router目錄下的index.js文件,對path屬性加上:id,
使用router對象的params.id獲取
active-class是vue-router模塊中router-link組件的屬性
使用children定義嵌套路由
keep-alive是一個內置組件,可以使被包含的組件保留狀態或避免從新渲染,有include(包含的組件緩存)和exclude(排除的組件不緩存)兩個屬性。
.prevent: 提交時間再也不重載頁面
.stop:阻止單擊事件冒泡
.self:當事件發生在該元素自己而不是子元素的時候觸發
.capture:事件偵聽,事件發生的時候會調用
組件複用時全部組件實例都會共享data,若是data是對象就會形成一個組件修改data之後會影響到其餘全部組件,因此須要將data寫成函數,每次用到就調用一次函數得到新的數據
當咱們使用new Vue()的方式的時候,不管咱們將data設置爲對象仍是函數都是能夠的,由於new Vue()的方式是生成一個根組件,該組件不會複用,也就不存在共享data的狀況
1.建立局部指令
directives:{
// 指令名稱
dir1: {
inserted(el){
// 第一個參數是當前使用指令的DOM
el.style.width = '200px';
el.style.height = '200px';
el.style.background = '#000'
}
}
}
複製代碼
2.全局指令
Vue.directive('dir2', {
inserted(el){
console.log(el)
}
})
複製代碼
3.指令的使用
<div v-dir1></div>
<div v-dir2></div>
複製代碼
<input type="text" v-model="msg" />
{{msg | capitalize}}
data(){
return{
msg: ''
}
},
filters: {
capitalize: function(value){
if(!value) return "";
value = value.toString();
return value.charAt(0).toUpperCase()+value.slice(1)
}
}
複製代碼
computed是計算屬性,依賴其餘屬性計算值,而且computed的值有緩存,只有當計算值變化纔會返回內容
watch監聽到值的變化就會執行回調,在回調中能夠進行一些邏輯操做。
通常來講須要依賴別的屬性來動態得到值的時候可使用computed,對於監聽到值的變化須要作一些複雜業務邏輯的狀況可使用watch
另外computed和watch還支持對象的寫法
data: {
firstName: 'Chen',
lastName: 'Miao',
fullName: 'Chen Miao'
},
watch: {
firstName: function(val){
this.fullName = val+ ' '+this.lastName
},
lastName: function(val){
this.fullName = this.firstName+ ' '+val
}
},
computed: {
anoFullName: function(){
return this.firstName+' '+this.lastName
}
}
複製代碼
做用是擴展組件生成一個構造器,一般與$mount一塊兒使用。
// 建立組件構造器
let Component = Vue.extend({
template: '<div>test</div>'
})
// 掛載到#app上
new Component().$mount('#app')
// 擴展已有組件
let SuperComponent = Vue.extend(Component)
new SuperComponent({
created(){
console.log(1)
}
})
new SuperComponent().$mount('#app')
複製代碼
mixin用於全局混入,會影響到每一個組件實例,一般插件都是這樣作初始化的。
Vue.mixin({
beforeCreate(){
// 會影響到每一個組件的beforeCreate鉤子函數
}
})
複製代碼
mixins最經常使用的擴展組件的方式。若是多個組件有相同的業務邏輯,就可將這些邏輯剝離出來,經過mixins混入代碼。須要注意:mixins混入的鉤子函數會先於組件內的鉤子函數執行,而且在遇到同名選項的時候也會有選擇性的進行合併。
nextTick可使咱們在下次DOM更新循環結束以後執行延遲迴調,用於得到更新後的DOM
data:function(){
return {
message: '沒有更新'
}
},
methods: {
updateMessage: function(){
this.message='更新完成'
console.log(this.$el.textContent) // '沒有更新'
this.$nextTick(function(){
console.log(this.$el.textContent)// '更新完成'
})
}
}
複製代碼
<transition name="fade1">
<router-view></router-view>
</transition>
複製代碼
類名介紹:
父組件經過props傳遞數據給子組件,子組件經過emit發送事件傳遞給父組件。
// 父組件
<div>
<child :data="child" @send="getFromChild"></child>
</div>
data(){
return{
toChild: '大兒子',
fromChild: ''
}
},
methods: {
getFromChild(val){
this.fromChild=val
}
}
// 子組件
<div @click="toParent">{{data}}</div>
props:[data],
methods: {
toParent(){
this.$emit('send', '給父親')
}
}
複製代碼
v-model實際上是props,emit的語法糖,v-model默認會解析成名爲value的prop和名爲input的事件。
// 父組件
<children v-model="msg"></children>
<p>{{msg}}</p>
data(){
return{
msg:'model'
}
}
// 子組件
<input :value="value" @input="toInput" />
props: ['value'],
methods: {
toInput(e){
this.$emit('input', e.target.value)
}
}
複製代碼
$children
訪問子組件,在子組件中使用$parent
訪問父組件// 父組件
<child />
data(){
return {
msg: '父組件數據'
}
},
methods: {
test(){
console.log('我是父組件的方法,被執行')
}
},
mounted(){
console.log(this.$children[0].child_msg); // 執行子組件方法
}
// 子組件
<div>{{$parent.msg}}</div>
data(){
return{
child_msg: '子組件數據'
}
},
mounted(){
// 子組件執行父組件方法
this.$parent.test();
}
複製代碼
$listeners
和$attrs
$attrs
--繼承全部父組件屬性(除了prop傳遞的屬性)
inheritAttrs--默認值true,繼承全部父組件屬性(除props),爲true會將attrs中的屬性當作html的data屬性渲染到dom根節點上
$listeners
--屬性,包含了做用在這個組件上全部監聽器,v-on="$listeners"將全部事件監聽器指向這個組件的某個特定子元素
// 父組件
<children :child1="child1" :child2="child2" @test1="onTest1"
@test2="onTest2"></children>
data(){
return {
child1: 'childOne',
child2: 'childTwo'
}
},
methods: {
onTest1(){
console.log('test1 running')
},
onTest2(){
console.log('test2 running')
}
}
// 子組件
<p>{{child1}}</p>
<child v-bind="$attrs" v-on="$listeners"></child>
props: ['child1'],
mounted(){
this.$emit('test1')
}
// 孫組件
<p>{{child2}</p>
<p>{{$attrs}}</p>
props: ['child2'],
mounted(){
this.$emit('test2')
}
複製代碼
在vue1.x中是對prop進行雙向綁定,在vue2只容許單向數據流,也是一個語法糖
// 父組件
<child :count.sync="num" />
data(){
return {
num: 0
}
}
// 子組件
<div @click="handleAdd">add</div>
data(){
return {
counter: this.count
}
},
props: ["count"],
methods: {
handleAdd(){
this.$emit('update:count', ++this.counter)
}
}
複製代碼
能夠經過查找父組件中的子組件實現, this.$parent.$children
在$children
中能夠經過組件name查詢到須要的組件實例,而後進行通訊
可使用provide/inject,雖然文檔中不推薦直接使用在業務中。
假設有父組件A,而後有一個跨多層次的子組件B
// 父組件A
export default{
provide: {
data: 1
}
}
// 子組件B
export default{
inject: ['data'],
mounted(){
// 不管跨幾層都能獲取父組件的data屬性
console.log(this.data); // 1
}
}
複製代碼
能夠用Vuex或Event Bus解決
1.新建一個bus.js文件
import Vue from 'vue';
export default new Vue();
複製代碼
2.使用它
<div @click="addCart">添加</div>
import Bus from 'bus.js';
export default{
methods: {
addCart(event){
Bus.$emit('getTarget', event.target)
}
}
}
// 另外一組件
export default{
created(){
Bus.$on('getTarget', target =>{
console.log(target)
})
}
}
複製代碼
單頁面應用SPA的核心之一是:更新視圖而不從新請求頁面
router.push('home')
router.push({path: 'home')
const router=new VueRouter({
routes: [{
path: '/user',
name: 'user',
component: User
}]
})
複製代碼
<router-link :to="{name: 'user'}"></router-link>
複製代碼
router.push({
name: 'user'
})
複製代碼
<div>{{$route.params.id}}</div>
複製代碼
const router = new VueRouter({
routes: [{
path: '/user/:id',
component: User
}]
})
複製代碼
router.push({name:'user',params: {id: 123})
<h2>{{$route.params.id}}</h2>
<router-view></router-view>
複製代碼
const router = new VueRouter({
routes: [{
path: '/user/:id',
children: [{
// 當/user/:id
path: '',
component: UserHome
},{
// 當/user/:id/profile
path: 'profile',
component: UserProfile
}]
}]
})
複製代碼
router.push({path:'register',query:{plan:'private'})
參數:
1.字符串
router.push('home')
2.對象
router.push({path: 'home'})
3.命名的路由
router.push({ name: 'user', params: { userId: 123 } })
4.帶查詢參數,變成/register?plan=private
router.push({ path: 'register', query: { plan: 'private' } })
不會向history添加新紀錄,替換當前的history記錄
點擊<router-link :to="..." replace>
等同於調用router.replace(...)
在歷史記錄中向前或向後退多少步
// 前進一步,等同history.forward()
router.go(1)
// 後退一步
router.go(-1)
// 前進3步記錄
router.go(3)
複製代碼
<router-view></router-view>
<router-view name="a"></router-view>
<router-view name="b"></router-view>
複製代碼
const router = new VueRouter({
routes: [{
path: '/',
components: {
default: Foo,
a: Bar,
b: Baz
}
}]
})
複製代碼
導航鉤子主要用來攔截導航,讓它完成跳轉或取消。
router.beforEach((to,from,next) => {})
鉤子是異步執行解析的,每一個鉤子方法接收三個參數:
to: Route即將進入的目標路由對象
from: Route當前導航正要離開的路由
next: Function,調用該方法來resolve這個鉤子,執行效果看參數
next():進行下一個鉤子
next(false):中斷當前的導航
next('/')或next({path: '/'}):跳轉到另外一地址
生命週期共分爲8個階段建立前/後,載入前/後,更新前/後,銷燬前/後
建立前/後:在beforeCreated階段,vue實例的掛載元素el和數據對象data都爲undefined,還未初始化。created階段,vue實例的數據對象data有了,el尚未。
載入先後:在beforeMount階段,vue實例的el和data都初始化了,但仍是掛載以前爲虛擬的dom節點,data.message還未替換。在mounted階段,vue實例掛載完成,data.message成功渲染
更新前/後:當data變化時,會觸發beforeUpdated和updated方法
銷燬前/後:beforeDestroy在實例銷燬前調用,實例仍然徹底可用。destroy在實例銷燬以後調用,調用後全部事件監聽器會被移除,全部子實例也會被銷燬。
生命週期中有多個事件鉤子,讓咱們在控制整個Vue實例的過程當中更容易造成好的邏輯。
1.vuex是vue生態系統中的狀態管理,用來管理vue中的全部組件狀態。
2.使用
import Vue from vue;
import Vuex from vuex;
Vue.use(Vuex);
const store = new Vuex.store({
state: {
count: 0
},
getters: {
addTen: state => {
return state.count+10;
}
},
mutations: {
increment(state){
state.count++
}
}
})
複製代碼
store.commit('increment');
console.log(this.$store.state.count);
複製代碼
vuex中有
①state狀態,還有mapState映射狀態
computed: mapState({
count: state => state.count
})
複製代碼
getter至關於store的計算屬性,主要用來過濾一些數據
getters: {
addTen: state => {
return state.count+10;
}
}
store.getters.addTen
複製代碼
mapGetters是將store中的getter映射到局部計算屬性中
computed: {
...mapGetters([
'addTen'
])
}
複製代碼
③Mutation 改變vuex中store狀態惟一方法就是提交mutation,可傳入額外參數,是一個同步函數。 在組件中提交Mutation
import {mapMutations} from 'vuex'
export default{
methods: {
...mapMutations([
'increment'
]),
...mapMutaions({
add: 'increment'
})
}
}
複製代碼
④Action 相似mutation,可是是異步的,view層經過store.dispath分發action
actions:{
increment(context){
context.commit('increment')
}
}
複製代碼
⑤module 當應用比較複雜,能夠將store分割成模塊
3.經常使用的場景有:單頁應用中,組件之間的狀態,音樂播放、登陸狀態、加入購物車等等
vue.js是採用數據劫持結合發佈者-訂閱者模式的方式,經過 Object.definePorperty() 來劫持各個屬性的setter,getter,在數據變更時發佈消息給訂閱者,觸發相應的監聽回調。
MVVM做爲數據綁定的入口,整合Observer,Compile和Watcher三者,經過Observer來監聽本身的model數據變化,經過Compile來解析編譯模板指定(解析{{}}),最終利用Watcher搭起Observer和Compile之間的通訊橋樑,達到數據變化->視圖更新;視圖交互變化input->數據model變動的雙向綁定效果
實現簡單的雙向綁定
<input type="text" id="inp" />
<div id="show"></div>
<script type="text/javascript">
var inp = document.getElementById('inp');
var show = document.getElementById('show');
var obj = {};
function watch(obj, key, callback){
var val = obj[key];
Object.defineProperty(obj, key, {
get: function(){
return val;
},
set: function(newVal){
callback(newVal, this)
}
})
}
watch(obj, "input", function(val){
show.innerHTML = val
})
inp.addEventListener('keyup', function(e){
obj.input = e.target.value
})
</script>
複製代碼
Proxy在目標對象以前架設一層「攔截」,外界對該對象的訪問都必須先經過這層攔截,所以提供一種機制,能夠對外界的訪問進行過濾和改寫。
<input type="text" id="txt" />
<div id="show"></div>
<script type="text/javascript">
var inp = document.getElementById('txt');
var show = document.getElementById('show')
var obj = {}
var objKey = 'text'; // 將鍵保存起來
// Object.defineProperty
Object.defineProperty(obj, objKey, {
get: function(){
return obj[objKey];
},
set: function(newVal){
show.innerHTML = newVal
}
})
inp.addEventListener('keyup', function(e){
obj[objKey] = e.target.value
})
// proxy的實現
const newObj = new Proxy(obj, {
get: function(target, key, receiver){
return Reflect.get(target, key, receiver);
},
set: function(target, key, value,receiver){
if(key === objKey){
show.innerHTML = value
}
}
})
inp.addEventListener('keyup',function(e){
newObj[objKey] = e.target.value;
})
複製代碼
Object.defineProperty的缺點:
1.不能檢測到增長或刪除的屬性
2.數組方面的變更,如根據索引改變元素,以及直接改變數組長度時的變化,不能被檢測到。
先轉化成AST樹,再獲得render函數返回VNode(Vue的虛擬DOM節點)
詳細步驟:首先經過compile編譯器把template編譯出AST語法樹,而後AST會通過generate(將AST語法樹轉化成render function字符串的過程)獲得render函數,render的返回值是VNode,VNode是Vue的虛擬DOM節點,裏面有標籤名,子節點,文本等