備戰秋招,複習基礎。若有錯誤,歡迎批評指正,共同進步!html
構建用戶界面的漸進式框架前端
只關注圖層,自底向上增量開發(增量是什麼TBC!!!)vue
代碼只須要關注邏輯層,DOM操做由VUE處理。node
經過儘量簡單的API實現響應的數據綁定和組合的組件視圖。ios
資料參考:Vue原理解析——本身寫個Vuevue-router
資料參考:剖析Vue原理&實現雙向綁定MVVMvuex
document.createDocumentFragment()
,方法建立虛擬dom樹。vue的vnode代碼部分位於項目的src/core/vdom文件夾下,vue的Virtual DOM是基於snabbdom修改的,vnode類的數據結構以下npm
export default class VNode {
tag: string | void;
data: VNodeData | void;
children: ?Array<VNode>;
text: string | void;
elm: Node | void;
ns: string | void;
context: Component | void; // rendered in this component’s scope
functionalContext: Component | void; // only for functional component root nodes
key: string | number | void;
componentOptions: VNodeComponentOptions | void;
componentInstance: Component | void; // component instance
parent: VNode | void; // component placeholder node
raw: boolean; // contains raw HTML? (server only)
isStatic: boolean; // hoisted static node
isRootInsert: boolean; // necessary for enter transition check
isComment: boolean; // empty comment placeholder?
isCloned: boolean; // is a cloned node?
isOnce: boolean; // is a v-once node?
asyncFactory: Function | void; // async component factory function
asyncMeta: Object | void;
isAsyncPlaceholder: boolean;
ssrContext: Object | void;
constructor (
tag?: string,
data?: VNodeData,
children?: ?Array<VNode>,
text?: string,
elm?: Node,
context?: Component,
componentOptions?: VNodeComponentOptions,
asyncFactory?: Function
) {
this.tag = tag
this.data = data
this.children = children
this.text = text
this.elm = elm
this.ns = undefined
this.context = context
this.functionalContext = undefined
this.key = data && data.key
this.componentOptions = componentOptions
this.componentInstance = undefined
this.parent = undefined
this.raw = false
this.isStatic = false
this.isRootInsert = true
this.isComment = false
this.isCloned = false
this.isOnce = false
this.asyncFactory = asyncFactory
this.asyncMeta = undefined
this.isAsyncPlaceholder = false
}
// DEPRECATED: alias for componentInstance for backwards compat.
/* istanbul ignore next */
get child (): Component | void {
return this.componentInstance
}
}
複製代碼
Object.defineProperty()
方法會直接在一個對象上定義一個新屬性,或者修改一個對象的現有屬性, 並返回這個對象。 Object.defineProperty(obj, prop, descriptor)
json
將須要observe的數據對象進行遞歸遍歷,包括子屬性對象的屬性,都加上 setter和getter 這樣的話,給這個對象的某個值賦值,就會觸發setter,那麼就能監聽到了數據變化。axios
對其更底層對象屬性的修改或獲取的階段進行了攔截
var data = {name: 'kindeng'};
observe(data);
data.name = 'dmq'; // 哈哈哈,監聽到值變化了 kindeng --> dmq
function observe(data) {
if (!data || typeof data !== 'object') {
return;
}
// 取出全部屬性遍歷
Object.keys(data).forEach(function(key) {
defineReactive(data, key, data[key]);
});
};
function defineReactive(data, key, val) {
observe(val); // 監聽子屬性
Object.defineProperty(data, key, {
enumerable: true, // 可枚舉
configurable: false, // 不能再define
get: function() {
return val;
},
set: function(newVal) {
console.log('哈哈哈,監聽到值變化了 ', val, ' --> ', newVal);
val = newVal;
}
});
}
複製代碼
維護一個數組,用來收集訂閱者,數據變更觸發notify,再調用訂閱者的update方法。
在數據變更時發佈消息給訂閱者,觸發相應的監聽回調。
// ... 省略
function defineReactive(data, key, val) {
var dep = new Dep();
observe(val); // 監聽子屬性
Object.defineProperty(data, key, {
// ... 省略
set: function(newVal) {
if (val === newVal) return;
console.log('哈哈哈,監聽到值變化了 ', val, ' --> ', newVal);
val = newVal;
dep.notify(); // 通知全部訂閱者
}
});
}
function Dep() {
this.subs = [];
}
Dep.prototype = {
addSub: function(sub) {
this.subs.push(sub);
},
notify: function() {
this.subs.forEach(function(sub) {
sub.update();
});
}
};
複製代碼
compile主要作的事情是解析模板指令,將模板中的變量替換成數據,而後初始化渲染頁面視圖,並將每一個指令對應的節點綁定更新函數,添加監聽數據的訂閱者,一旦數據有變更,收到通知,更新視圖
Watcher訂閱者做爲Observer和Compile之間通訊的橋樑,主要作的事情是:
MVVM做爲數據綁定的入口,整合Observer、Compile和Watcher三者,經過Observer來監聽本身的model數據變化,經過Compile來解析編譯模板指令,最終利用Watcher搭起Observer和Compile之間的通訊橋樑,達到數據變化 -> 視圖更新;視圖交互變化(input) -> 數據model變動的雙向綁定效果。
!!!太複雜了!!!還要再研究研究!!!QAQ
var vm = new Vue({
el:'至關於id',
data:{...},
template:{...},
methods:{...},函數調用,不能緩存 → {{methodTest()}}
computed:{...},屬性調用,具備緩存功能,依賴於data中的數據,只有在它的相關依賴數據發生改變時纔會從新求值 → {{computedTest}}
watch:{...},監測Vue實例上的數據變更
render:{...} 渲染優先級:render → template → outerHTML
})
data() {
return {
demo: {
name: ''
},
value: ''
};
},
computed: {
newName() {
return this.demo.name;
}
},
watch: {
newName(val) {
this.value = val;
}
}
複製代碼
文本插值<p>{{message}}</p>
HTML插值<div v-html:"message></div>
屬性v-bind:class="{'class1':use}"
→ 動態賦值
指令v-
→ 如:v-if
v-else
用戶輸入v-model
→ 實現數據雙向綁定
事件監聽 v-on:click
用處 | 範例 |
---|---|
訪問根實例 | this.$root.foo |
訪問父級組件實例 | this.$parent |
訪問子組件實例(非響應式) | 子<input ref="input"> 父this.$refs.input.focus() |
依賴注入(訪問任意上級) | 父 provide:funtion(){ return{getMap:this.getMap}} 子 inject:['getMap'] |
$:
${...}
只有當實例被建立時data中存在的屬性纔是響應式的!即:要先初始化,哪怕爲空也行
實例方法
vm.$el === document.getElementById('example')
vm.$watch('a',function(new,old){在vm.a改變時調用}
複製代碼
不要在選項屬性或回調函數中使用箭頭函數!
參考資料:手把手教Vue--生命週期
方法名 | 狀態 | 含義 | 用法 |
---|---|---|---|
beforeCreate | creating 狀態 | 實例建立以前調用 | 加 Loading 事件 |
created | creating 狀態 | 實例建立成功,此時 data 中的數據顯示出來了 | 頁面沒有加載完成就請求數據,結束 Loading |
beforeMount | mounting 狀態 | 數據中的 data 在模版中先佔一個位置 | |
mounted | mounting 狀態 | 模版中的 data 數據直接顯示出來了 | 發起異步服務端請求 |
beforeUpdate | updating 狀態 | 當 data 數據發生變化調用,發生在虛擬 DOM 從新渲染和打補丁以前 | |
updated | updating 狀態 | 數據更改致使的虛擬 DOM 從新渲染和打補丁 | |
beforeDestroy | destroying 狀態 | 在 vue 實例銷燬以前調用,此時實例任然可用 | 彈出確認刪除 |
destroyed | destroying 狀態 | 在 vue 實例銷燬以後調用,vue實例指示的全部東西都會解綁定,全部的事件監聽器會被移除,全部的子實例也會被銷燬 | 關閉定時器,中止監聽屬性,清除相關數據達到資源的有效利用 |
HTML裏
<div id = "app">
<router-link to="/foo">Go to Foo</router-link>
<router-view></router-view>
</div
複製代碼
JS裏
1 定義路由組件 const Foo = {template:'<div>foo</div>'}
2 定義路由 const routes = [{path:'/foo',component:Foo}]
3 建立router實例 const router = new VueRouter({routes})
4 建立掛載根實例 const app = new Vue({router}}).$mount('#app')
複製代碼
屬性 | 用法 |
---|---|
to | 目標路由的連接 |
replace | 點擊時調用 |
append | 添加相對路徑的基路徑 :to="{path:'relative/path'}" append |
tag | 渲染成某種標籤 如<li> |
active-class | 連接激活時使用的CSS類名 |
exact-active-class | 連接精確匹配時的CSS類名 |
event | 觸發導航的事件,如event="mouseover →鼠標移動到上面時導航html內容改變 |
keep-alive
緩存失活的組件。切換頁面,切換回來,頁面狀態不變
參考資料:vue-router的兩種模式(hash和history)及區別
http://www.abc.com/#/hello
,hash的值爲#/hello
.它的特色在於:hash雖然出如今URL中,但不會被包括在HTTP請求中,對後端徹底沒有影響,所以改變hash不會從新加載頁面。1 $router.push() //調用方法
2 HashHistory.push() //根據hash模式調用,設置hash並添加到瀏覽器歷史記錄(添加到棧頂)(window.location.hash= XXX)
3 History.transitionTo() //監測更新,更新則調用History.updateRoute()
4 History.updateRoute() //更新路由
5 {app._route= route} //替換當前app路由
6 vm.render() //更新視圖
複製代碼
pushState()
和replaceState()
方法。(須要特定瀏覽器支持)1.push
與hash模式相似,只是將window.hash改成history.pushState
2.replace
與hash模式相似,只是將window.replace改成history.replaceState
3.監聽地址變化
在HTML5History的構造函數中監聽popState(window.onpopstate)
複製代碼
區別:
<transition name="動畫名稱" mode="out-in">
<div></div>
</transition>
複製代碼
過渡模式:in-out / out-in
屬性 | 用法 |
---|---|
v-enter | 進入過渡的開始狀態 |
v-enter-active | 進入過渡生效時的狀態,如{transition:opacity 2s} |
v-enter-to | 進入過渡的結束狀態 |
v-leave | 離開過渡的開始狀態 |
v-leave-active | 離開過渡生效時的狀態 |
v-leave-to | 離開過渡的結束狀態 |
mixins:可複用的方法或計算屬性,可包含任意組件選項。
待補充!!!
經過XMLHttpRequest或JSONP發起請求並處理響應
可以使用全局對象方式vue.http
或在組件內部使用this.$http
來發期請求
get:function(){
this.$http.get('/someUrol').then(
function(res){
document.write(res.body);
},
function(){
console.log('error!')
}
);
}
複製代碼
get(url,[options])
head(url,[options])
delete(url,[options])
jsonp(url,[options])
post(url,[body],[options])
put(url,[body],[options])
patch(url,[body],[options])
複製代碼
基於promise的HTTP客戶端
npm install axios
import axios from 'axios'
複製代碼
get:
axios.get(url).then(function(response){...}).catch(function(error){...});
複製代碼
post:
axios({
method:'post',
url:'/user',
data:{
firstName:'a',
lastName:'b'
}
});
複製代碼
axios.all([func1(),func2()]).then{...}
→ 執行多個併發請求
axios.request(config)
axios.get(url[,config])
axios.delete(url[,config])
axios.head(url[,config])
axios.post(url[,data,[config]])
axios.put(url[,data,[config]])
axios.patch(url[,data,[config]])
複製代碼
請求攔截
axios.interceptor.request.use(function(config){
//在發送請求前作的事
return config;
},function(error){
//請求錯誤時作的事
return Promise.reject(error);
});
複製代碼
響應攔截
axios.interceptors.response.use(function(response){
//對響應數據作的事
return config;
},function(error){
//請求錯誤時作的事
return Promise.reject(error);
});
複製代碼
required:true
default:100
validator:function(value){return ['success,'danger'].indexOf(value)!==-1
將子組件中<slot></slot>
替換爲父組件<></>之間的任意代碼
<slot>當父組件中不提供內容時的後備內容</slot>
具名插槽
父組件:<template v-slot:header></template>
子組件:<header></header>
複製代碼
做用域插槽
父組件:<template v-slot:default="slotProps">
{{slotProps.user.firstName}}
</template>
子組件:<slot v-bind:user="user"></slot>
簡寫父組件:<current-user v-slot:"slotProps">slotProps.user.firstName</current-user>
再簡寫父組件:<current-user v-slot:"{user}">{{slotProps.user.firstName}}</current-user>
可定義後備值"{user={firstName:'Guest'}}"
複製代碼
快速原型開發:對於單個 *.vue文件
vue serve
在開發環境模式下零配置爲.js或.vue文件啓動一個服務器
vue build
在生產環境模式下零配置構建一個.js或.vue文件
Preload 預加載首渲內容
Prefetch 預加載將來內容
載入
npm install vue-cookies
var Vue = require('vue')
Vue.use(require('vue-cookies')
或
import Vue from 'vue'
import VueCookies from 'vue-cookies'
Vue.use(VueCookies)
複製代碼
用法 | 範例 |
---|---|
設置cookie | this.$cookies.set(keyName,value[,expireTime[,path[,domain[,secure]]]]) |
獲取cookie | this.$cookies.get(keyName) // return value |
刪除cookie | this.$cookies.remove(keyName[,path[,domain]]) |
存在? | this.$cookies.isKey(keyName) //retuen boolean |
獲得所有 | this.$cookies.keys() |
狀態管理模式 集中式存儲管理應用的全部組件的狀態
--- main.js ---
import Vuex from 'vuex';
Vue.use(Vuex);
const store = new Vuex.Store({
//配置...
});
new Vue({
el: '#app',
router:router,
store:store,
render:h=>{
return h(App)
}
});
複製代碼
Vuex裏的數據都是響應式的!!!
--- main.js ---
const store = new Vuex.Store({
state:{
count:0
}
});
--- index.vue ---
{{ $store.state.count}}
或
export default{
computed:{
count (){
return this.$store.state.count;
}
}
}
複製代碼
輔助函數,獲取多個狀態,幫助生成計算屬性
computed:{
localComputed(){
...
},
...mapState({
...
})
}
複製代碼
計算store,返回值會緩存。依賴改變時會從新計算。可經過屬性(緩存)和方法(不緩存)訪問。
--- main.js ---
const store = new Vuex.Store({
state:{list:[1,5,8,10,30,50]},
getters:{
filteredList: state =>{
return state.list.filter(item => item <10); ← 以state爲參數
},
listCount: (state,getters) =>{
return getters.filteredList.length; ← 以state和getters爲參數
}
}
})
--- index.vue ---
export default{
computed:{
list (){
return this.$store.getters.filteredList;
},
listCount (){
return this.$store.getters.listCount;
}
}
}
複製代碼
輔助函數,映射多個局部計算。
computed:{
...mapGetters([...也可另取名字])
}
複製代碼
mutation
改變store中狀態的惟一方法就是提交mutation(同步)
--- main.js ---
const store = new Vues.Store({
state:{list:[1,5,8,10,30,50]},
mutation:{
increment(state,params){
state.count+=params.count; ← 直接傳入對象
},
descrease (state){
state.count --;
}
}
});
--- index.vue ---
export default{
methods:{
handleIncrement (){
this.$stroe.commit({
type:'increment',
count:10 ← 直接傳入對象
});
},
handleDecrease (){
this.$store.commit('descrease');
}
}
}
複製代碼
提交mutation(而非直接更改狀態),支持異步
--- main.js ---
mutation:{
increment (state){
state.count++
}
},
actions:{
increment(與store實例具備相同方法和屬性的context對象){
context.commit('increment')
}
異步
asyncIncrement (context){
return new Promise(resolve =>{
setTimeout(()=>{
context.commit('increment');
resolve();
},1000)
});
}
--- index.vue ---
methods:{
handleActionIncrement(){
this.$stroe.dispatch('increment');
},
handleAsyncIncrement (){
this.$store.dispatch('asyncIncrement').then(()=>{ ← 異步調用 返回的是Promise
console.log('...');
});
}
}
複製代碼
輔助函數,映射多個dipatch調用
可嵌套:
actionB({dispatch,commit}){
return dispatch('actionA').then(()={
commit('other Mutation')
})
}
複製代碼
可用async
/ await
組合action! → 一個dispatch觸發多個action
涉及改變數據的,就用mutations
,存在業務邏輯的,就用actions
將store分割成模塊
const moduleA = {
state:{...},
mutations:{...},
actions:{...}, ← 可接收rootState 同getter
getters:{
sumCount (state,getters,rootState){ ← 在模塊內部調用根節點狀態:rootState
return state.count + rootState.count;
}
}
}
const moduleB = {...}
const store = new Vuex.store({
modules{
a:moduleA,
b:moduleB
}
})
訪問:
store.state.a
store.state.b
複製代碼
命名空間:
namespaced:true
訪問全局需加一些額外操做~vuex中的state用v-model
會報錯!!應綁定事件回調mutation
~或用set
和get
雙向綁定!