
近日獨立完成一個小項目搭建,總結(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)航解析流程
- 導(dǎo)航被觸發(fā)。
- 在失活的組件里調(diào)用 beforeRouteLeave 守衛(wèi)。
- 調(diào)用全局的 beforeEach 守衛(wèi)。
- 在重用的組件里調(diào)用 beforeRouteUpdate 守衛(wèi) (2.2+)。
- 在路由配置里調(diào)用 beforeEnter。
- 解析異步路由組件。
- 在被激活的組件里調(diào)用 beforeRouteEnter。
- 調(diào)用全局的 beforeResolve 守衛(wèi) (2.5+)。
- 導(dǎo)航被確認(rèn)。
- 調(diào)用全局的 afterEach 鉤子。
- 觸發(fā) DOM 更新。
- 調(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()等組合使用)來玩出更多的花樣。
- Array.reduce (https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/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):
- https://cn.vuejs.org/
- https://vuex.vuejs.org/zh/guide/
- https://router.vuejs.org/zh/installation.html
- https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array
- https://blog.csdn.net/wuj1935/article/details/106554826?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param