Vue+ 項目學(xué)習(xí)總結(jié)

1600178283331.jpg

近日獨立完成一個小項目搭建,總結(jié)?個人踩坑和基礎(chǔ)內(nèi)容知識。內(nèi)容比較基礎(chǔ),也希望能有高手看到后可以指點一二。項目中引入的都是必須和用到的內(nèi)容,研究后選用?:Vue, vue-router, vuex, vant, axios


Vue 之 computed & watch & method用法區(qū)分

Vue 中我們都可以通過這method實現(xiàn)方法和參數(shù)改變,但是細(xì)讀Vue組件,我認(rèn)為這幾個方法的使用被我們開發(fā)過程中忽視了。

computed 計算屬性

  • 有緩存,可以提高性能, 在HTML DOM加載后馬上執(zhí)行的,如賦值
  • 你可以像綁定普通 property 一樣在模板中綁定計算屬性。創(chuàng)建依賴關(guān)系:計算屬性的 getter 函數(shù)是沒有副作用 (side effect) 的,這使它更易于測試和理解。
  • VS methods :,在methods可以達(dá)到和computed一樣的效果。然而,不同的是計算屬性是基于它們的響應(yīng)式依賴進(jìn)行緩存的。只在相關(guān)響應(yīng)式依賴發(fā)生改變時它們才會重新求值。這就意味著只要 message 還沒有發(fā)生改變,多次訪問 reversedMessage 計算屬性會立即返回之前的計算結(jié)果,而不必再次執(zhí)行函數(shù)。計算屬性有緩存,只要依賴的數(shù)據(jù)不變,就可以避免多次請求getter函數(shù)。

watch

異步返回參數(shù)的可以用watch,開銷比較大
— 觀察 Vue 實例上的一個表達(dá)式或者一個函數(shù)計算結(jié)果的變化。回調(diào)函數(shù)得到的參數(shù)為新值和舊值。表達(dá)式只接受簡單的鍵路徑。
— options: immediate:
在選項參數(shù)中指定 immediate: true 將立即以表達(dá)式的當(dāng)前值觸發(fā)回調(diào)
— options: deep 為了發(fā)現(xiàn)對象內(nèi)部值的變化,可以在選項參數(shù)中指定

項目中,當(dāng)遇到觀察對象是數(shù)組,并且每項是對象的情況下,需要設(shè)置這兩個選擇

methods

需要一定的出發(fā)條件才能執(zhí)行的


Vue 中內(nèi)置對象的數(shù)據(jù)更新

Vue data 是一個方法,返回的是一個對象。實例化后每個實例的data是不同的對象。

  • 由于 JavaScript 的限制,Vue 不能檢測數(shù)組和對象的變化,所以提供了set方法。向響應(yīng)式對象中添加一個 property,并確保這個新 property 同樣是響應(yīng)式的,且觸發(fā)視圖更新。 Vue.set( target, propertyName/index, value )
    this.$set(this.goodList[this.index], ‘customPrice’, this.price)
    當(dāng)商品的價格更新了后,同時也更新視圖。
  • Vue.delete( target, propertyName/index ) 刪除對象的 property。如果對象是響應(yīng)式的,確保刪除能觸發(fā)更新視圖。這個方法主要用于避開 Vue 不能檢測到 property 被刪除的限制
    this.$delete( this.goodList[this.activeKey], col )

在組件內(nèi)使用 vm.$nextTick(). Vue 在更新 DOM 時是異步執(zhí)行的. 只要偵聽到數(shù)據(jù)變化,Vue 將開啟一個隊列,并緩沖在同一事件循環(huán)中發(fā)生的所有數(shù)據(jù)變更。如果同一個 watcher 被多次觸發(fā),只會被推入到隊列中一次。

所以當(dāng)someData更新后,不會立馬渲染,如果想插隊的話,可以使用Vue.nextTick(callback)方法。
因為 $nextTick() 返回一個 Promise 對象,所以你可以使用新的 ES2017 async/await 語法完成相同的事情:

methods: {
  updateMessage: async function () {
    this.message = '已更新'
    console.log(this.$el.textContent) // => '未更新'
    await this.$nextTick()
    console.log(this.$el.textContent) // => '已更新'
  }
}

VUE 父子組件的通信問題

父組件給子組件傳參props

所有的 prop 都使得其父子 prop 之間形成了一個單向下行綁定:父級 prop 的更新會向下流動到子組件中,但是反過來則不行。

每次父級組件發(fā)生變更時,子組件中所有的 prop 都將會刷新為最新的值。這意味著你應(yīng)該在一個子組件內(nèi)部改變 prop。如果你這樣做了,Vue 會在瀏覽器的控制臺中發(fā)出警告。故要么在data重新定義參數(shù),用props值進(jìn)行初始化,或者用computed進(jìn)行轉(zhuǎn)換

Props驗證中遇到問題:
Props with type Object/Array must use a factory function to return the default value.

 // 報錯代碼
    props: {
        cartList: {
            type: Array,
            default:[]
        }
    },

/// 正解1   箭頭函數(shù)/
    props: {
        cartList: {
            type: Array,
            default:()=>[]
        }
    }
    /// 正解2/
    props: {
        cartList: {
            type: Array,
            default:function(){
                return [];
            }
        }
    }
父組件使用子組件方法

可以給子組件一個”ref = “item””的名字,那么在父組件中可以this.$refs.item.closePopup() 通過refs去調(diào)用子組件方法。
Ref 被用來給元素或子組件注冊引用信息。引用信息將會注冊在父組件的 $refs 對象上。
關(guān)于 ref 注冊時間的重要說明:因為 ref 本身是作為渲染結(jié)果被創(chuàng)建的,在初始渲染的時候你不能訪問它們 - 它們還不存在!$refs 也不是響應(yīng)式的,因此你不應(yīng)該試圖用它在模板中做數(shù)據(jù)綁定。

如何對一個props進(jìn)行“雙向綁定”

無法實現(xiàn)真正的雙向綁定,vue是單向數(shù)據(jù)流的,所以可以使用update:myPropName 的模式觸發(fā)事件取而代之。

// 父組件可以監(jiān)聽那個事件并根據(jù)需要更新一個本地的數(shù)據(jù) property
<text-document
  v-bind:title="doc.title"
  v-on:update:title="doc.title = $event"
></text-document>

// 子組件中
this.$emit('update:title', newTitle)

父組件也可以用sync來簡寫:

<text-document v-bind:title.sync="doc.title"></text-document>


Vue中的事件修飾符

  • .stop: 阻止冒泡(通俗講就是阻止事件向上級DOM元素傳遞)
    點擊內(nèi)層的div后,不會向外層的div傳遞
  • . prevent:阻止默認(rèn)事件的發(fā)生
    默認(rèn)事件指對DOM的操作會引起自動執(zhí)行的動作,比如點擊超鏈接的時候會進(jìn)行頁面的跳轉(zhuǎn),點擊表單提交按鈕時會重新加載頁面等,使用”.prevent”修飾符可以阻止這些事件的發(fā)生。
  • . capture:捕獲冒泡,即有冒泡發(fā)生時,有該修飾符的dom元素會先執(zhí)行,如果有多個,從外到內(nèi)依次執(zhí)行,然后再按自然順序執(zhí)行觸發(fā)的事件。
  • . self:將事件綁定到自身,只有自身才能觸發(fā),通常用于避免冒泡事件的影響
  • . once:設(shè)置事件只能觸發(fā)一次,比如按鈕的點擊等。
  • . native:在父組件中給子組件綁定一個原生的事件,就將子組件變成了普通的HTML標(biāo)簽,不加’. native’事件是無法觸 發(fā)的。

Vue 中.env 環(huán)境配置搭建

達(dá)到的效果
在不同環(huán)境中配置環(huán)境參數(shù),需要使用的時候,直接用環(huán)境變量就可以,可以不用hard code。
在運行打包環(huán)境的時候,可以直接用不同的命令行就可區(qū)分環(huán)境打包。
如下所示:

"scripts": {
    "serve": "vue-cli-service serve --open",
    "build": "vue-cli-service build",
    "beta": "vue-cli-service build --mode beta",
    "lint": "vue-cli-service lint"
  },

Beta 環(huán)境 下就可以直接yarn beta

.env 文件需要手動自己新建,

VUE_APP_URL = '/'   // 用來設(shè)置項目的 baseUrl 和路由的 base 選項 默認(rèn)是根目錄
VUE_APP_MODE = ''   // 設(shè)置一個變量 區(qū)分線上生產(chǎn)環(huán)境和線上測試環(huán)境 默認(rèn)是生產(chǎn)環(huán)境
outputDir = 'dist'  // 用來設(shè)置打包后生成的文件夾的名字,默認(rèn)為 dist 文件夾

.env.beta

NODE_ENV = 'production'
VUE_APP_URL = 'https://aaa.bbb.cn'
VUE_APP_TITLE = A(beta)

.env.production

NODE_ENV = 'production'
VUE_APP_TITLE = A
VUE_APP_URL = 'https://ccc.bbb.cn'

踩坑的地方是,NODE_ENV的配置。我開始認(rèn)為在beta環(huán)境下 NODE_ENV = ‘beta’ 用來區(qū)分不同環(huán)境的配置。這樣配置的話,在運行yarn beta后,結(jié)果沒有打包出js,和css 這樣就需要自己去寫vue.config.js的配置文件。
可是我也沒有特殊需求,所以最后把NODE_ENV 寫成生成環(huán)境即可。


Vue Router 的使用筆記

導(dǎo)航守衛(wèi)

  • 全局守衛(wèi) [beforeEach] / [afterEach]
    當(dāng)一個導(dǎo)航觸發(fā)時,全局前置守衛(wèi)按照創(chuàng)建順序調(diào)用。守衛(wèi)是異步解析執(zhí)行,此時導(dǎo)航在所有守衛(wèi) resolve 完之前一直處于 等待中
const router = new VueRouter({ ... })
router.beforeEach((to, from, next) => {
  // ...
})
  • 路由獨享的守衛(wèi)
    你可以在路由配置上直接定義 beforeEnter 守衛(wèi):
const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      beforeEnter: (to, from, next) => {
        // ...
      }
    }
  ]
})
  • 組件內(nèi)的守衛(wèi)
const Foo = {
  template: `...`,
  beforeRouteEnter (to, from, next) {
    // 在渲染該組件的對應(yīng)路由被 confirm 前調(diào)用
    // 不!能!獲取組件實例 `this`
    // 因為當(dāng)守衛(wèi)執(zhí)行前,組件實例還沒被創(chuàng)建
  },
  beforeRouteUpdate (to, from, next) {
    // 在當(dāng)前路由改變,但是該組件被復(fù)用時調(diào)用
    // 舉例來說,對于一個帶有動態(tài)參數(shù)的路徑 /foo/:id,在 /foo/1 和 /foo/2 之間跳轉(zhuǎn)的時候,
    // 由于會渲染同樣的 Foo 組件,因此組件實例會被復(fù)用。而這個鉤子就會在這個情況下被調(diào)用。
    // 可以訪問組件實例 `this`
  },
  beforeRouteLeave (to, from, next) {
    // 導(dǎo)航離開該組件的對應(yīng)路由時調(diào)用
    // 可以訪問組件實例 `this`
  }
}

完整的導(dǎo)航解析流程

  1. 導(dǎo)航被觸發(fā)。
  2. 在失活的組件里調(diào)用 beforeRouteLeave 守衛(wèi)。
  3. 調(diào)用全局的 beforeEach 守衛(wèi)。
  4. 在重用的組件里調(diào)用 beforeRouteUpdate 守衛(wèi) (2.2+)。
  5. 在路由配置里調(diào)用 beforeEnter。
  6. 解析異步路由組件。
  7. 在被激活的組件里調(diào)用 beforeRouteEnter。
  8. 調(diào)用全局的 beforeResolve 守衛(wèi) (2.5+)。
  9. 導(dǎo)航被確認(rèn)。
  10. 調(diào)用全局的 afterEach 鉤子。
  11. 觸發(fā) DOM 更新。
  12. 調(diào)用 beforeRouteEnter 守衛(wèi)中傳給 next 的回調(diào)函數(shù),創(chuàng)建好的組件實例會作為回調(diào)函數(shù)的參數(shù)傳入。

項目中運用到 beforeRouteEnter & beforeEnter
Router 配置信息內(nèi)的路由守衛(wèi)beforeEnter先調(diào)用,后在調(diào)用組件內(nèi)的beforeRouteEnter。在執(zhí)行的順序上是不一樣的。


CSS & vant

  • 在修改vant 組件樣式的時候,需要/deep/ 才能讓深層的樣式起作用。至于原因還沒有查清楚

Array 常用方法總結(jié)

  • Array 賦值: 如何創(chuàng)建一個8位初始值是0的數(shù)組
    Let b = Array(8).fill(0) => (8) [0, 0, 0, 0, 0, 0, 0, 0]

  • String -> Array split() 把string文本按照格式分隔成數(shù)組
    var a = 'hello'; a.split('') => (5) ["h", "e", "l", "l", "o"]

  • Array splice 添加/刪除/替換 數(shù)組項
    let arr.splice(2,3,”William”)

  • Array 數(shù)組扁平化(Array.prototype.flat() - JavaScript | MDN)
    var newArray = arr.flat([depth])

使用 reduce 與 concat

var arr = [1, 2, [3, 4]];

// 展開一層數(shù)組
arr.flat();
// 等效于
arr.reduce((acc, val) => acc.concat(val), []);
// [1, 2, 3, 4]

// 使用擴(kuò)展運算符 ...
const flattened = arr => [].concat(...arr);
reduce + concat + isArray + recursivity

reduce + concat + isArray + recursivity

// 使用 reduce、concat 和遞歸展開無限多層嵌套的數(shù)組
var arr1 = [1,2,3,[1,2,3,4, [2,3,4]]];

function flatDeep(arr, d = 1) {
   return d > 0 ? arr.reduce((acc, val) => acc.concat(Array.isArray(val) ? flatDeep(val, d - 1) : val), [])
                : arr.slice();
};

flatDeep(arr1, Infinity);
// [1, 2, 3, 1, 2, 3, 4, 2, 3, 4]

  • Array : map, forEach,的區(qū)別
    — 返回值: map()方法會得到一個新的數(shù)組并返回 / forEach()會修改原來的數(shù)組。
arr.forEach((value, key) => {
 return arr[key] = value * value;
});
-> (8) [1, 4, 0, 9, 4, 4, 4, 4]

let list = arr.map(value => {
 return value * value;
});

-> 修改的是list,
list
(8) [1, 16, 0, 81, 16, 16, 16, 16]
arr
(8) [1, 4, 0, 9, 4, 4, 4, 4]

— 速度: forEach()的執(zhí)行速度 < map()的執(zhí)行速度

forEach適合于你并不打算改變數(shù)據(jù)的時候,而只是想用數(shù)據(jù)做一些事情 – 比如存入數(shù)據(jù)庫或則打印出來。

map()適用于你要改變數(shù)據(jù)值的時候。不僅僅在于它更快,而且返回一個新的數(shù)組。這樣的優(yōu)點在于你可以使用復(fù)合(composition)(map(), filter(), reduce()等組合使用)來玩出更多的花樣。

var sum = [0, 1, 2, 3].reduce(function (accumulator, currentValue) {
  return accumulator + currentValue;
}, 0);
// 和為 6

0 是accumulator的初始值
//累加對象數(shù)組里的值
var initialValue = 0;
var sum = [{x: 1}, {x:2}, {x:3}].reduce(function (accumulator, currentValue) {
    return accumulator + currentValue.x;
},initialValue)

console.log(sum) // logs 6

//計算數(shù)組中每個元素出現(xiàn)的次數(shù)
var names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice'];

var countedNames = names.reduce(function (allNames, name) { 
  if (name in allNames) {
    allNames[name]++;
  }
  else {
    allNames[name] = 1;
  }
  return allNames;
}, {});
// countedNames is:
// { 'Alice': 2, 'Bob': 1, 'Tiff': 1, 'Bruce': 1 }


以上是此次項目中總結(jié)到的關(guān)鍵性的內(nèi)容。樣式css這塊還沒有總結(jié)的很多,后面會慢慢積累。

參考文獻(xiàn):

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容