最近連着作了好幾個vue項目,從0到版本迭代,vue教程算是好理解,把vue官方文檔看一遍下來,基本上不會有什麼大問題(嗯若是你看的夠仔細夠透徹的話),此篇是記錄vue踩過的坑以及能夠優化的地方。css
這實際上算不上vue的問題,算是js基礎沒打好的坑吧。html
先來看一個簡單的例子:vue
let obj = {name:'fiona-SUN'}; let copyObj = obj; copyObj.name = 'fiona'; console.log(copyObj.name); // 'fiona' console.log(obj.name); // 'fiona'
在js中也有棧(stack)和堆(heap)的概念:html5
因此上述例子中,屬於淺拷貝,當咱們聲明一個對象,因爲他不屬於五種基本數據類型(即非簡單數據段
),棧中會存放一個咱們聲明的obj
變量,它指向了堆中實際的這個對象的地址
。當咱們把這個引用地址賦值給了copyObj
,實際它得到的是一個與obj
一致的指向堆中的地址。當copyOjb
改變了指向的對象地址的實際的值的時候,obj
拿到的值也就天然而然變化了。看圖理解⬇node
嗯,道理我都懂,可是寫代碼我就天然而然的忽略了,該反思。。。webpack
深拷貝的方法ios
let obj = {name:'fiona-SUN'}; let copyFunc = (originObj) => { let copyObj = {}; for(let key in originObj){ copyObj[key] = originObj[key]; } return copyObj; }; let copyObj = copyFunc(obj); copyObj.name = 'fiona'; console.log(copyObj.name); // 'fiona' console.log(obj.name); // 'fiona-SUN'
let obj = {name:'fiona-SUN'}; let copyObj = JSON.parse(JSON.stringify(obj)); copyObj.name = 'fiona'; console.log(copyObj.name); // 'fiona' console.log(obj.name); // 'fiona-SUN'
let obj = {name:'fiona-SUN'}; let copyObj = Object.assign({}, obj); copyObj.name = 'fiona'; console.log(copyObj.name); // 'fiona' console.log(obj.name); // 'fiona-SUN'
let arr = [1,2,3]; let copyArr = [...obj]; copyArr[2] = 0; console.log(copyArr[2]); // 0 console.log(arr[2]); // 2
如下摘自vue官網APInginx
因爲 JavaScript 的限制,Vue 不能檢測如下變更的數組:git
vm.items[indexOfItem] = newValue
vm.items.length = newLength
爲了解決第一類問題,如下兩種方式均可以實現和 vm.items[indexOfItem] = newValue
相同的效果,同時也將觸發狀態更新:es6
// Vue.set Vue.set(example1.items, indexOfItem, newValue) // Array.prototype.splice example1.items.splice(indexOfItem, 1, newValue)
爲了解決第二類問題,你可使用 splice:
example1.items.splice(newLength)
觸發視圖更新的方法:
仍是因爲 JavaScript 的限制,Vue 不能檢測對象屬性的添加或刪除:
var vm = new Vue({ data: { a: 1 } }) // `vm.a` 如今是響應式的 vm.b = 2 // `vm.b` 不是響應式的
解決方法:
這真的是遇到一個很坑的問題,同一個頁面(router未改變),一旦刷新(刷新或深度刷新),存儲的vuex就立刻和你說拜拜
localStorage
網上推薦最多的方法就是用localStorage。可是我我的以爲不太合適,還得看項目吧。localStorage是永久存儲的。
數據從新獲取
我使用的方法是在須要某些數據以前先判斷一下數據是否存在,若是不存在從新獲取。
將回調延遲到下次 DOM 更新循環以後執行。在修改數據以後當即使用它,而後等待 DOM 更新。它跟全局方法 Vue.nextTick 同樣,不一樣的是回調的 this 自動綁定到調用它的實例上。
簡而言之,等待DOM更新以後再進行操做。
這個是一個亙古不變的話題。
請求後臺數據異步,常不經意的帶來了問題。(處理異步的方法就不詳細描述了,網上一搜一大堆)
計算屬性是基於它們的依賴進行緩存的,一旦依賴發生變化,計算屬性會從新計算
想要改變計算屬性的值。要經過set方法去觸發它所依賴的變量,(相似於觸發它從新計算,單純賦予一個新值,在取的時候也是不會被改變的)
當 Vue.js 用v-for
正在更新已渲染過的元素列表時,它默認用「就地複用」策略。若是數據項的順序被改變,Vue 將不會移動 DOM 元素來匹配數據項的順序, 而是簡單複用此處每一個元素,而且確保它在特定索引下顯示已被渲染過的每一個元素。
爲了給 Vue 一個提示,以便它能跟蹤每一個節點的身份,從而重用和從新排序現有元素,你須要爲每項提供一個惟一 key 屬性。理想的 key 值是每項都有的且惟一的 id。這個特殊的屬性至關於 Vue 1.x 的 track-by ,但它的工做方式相似於一個屬性,因此你須要用 v-bind 來綁定動態值 (在這裏使用簡寫):
當它們處於同一節點,v-for的優先級比v-if更高。
不管是開發環境仍是生成環境都不會自動加前綴,由於vue-loader只管.vue文件裏面的樣式,沒有自動執行autoprefixer loader
在build/utils.js下引入postcss-loader
var postcssLoader = { loader: 'postcss-loader', options: { plugins: (loader) => [ require('autoprefixer')() ], sourceMap: true } }
若是還有問題在改爲
var postcssLoader = { loader: 'postcss-loader', options: { plugins: (loader) => [ require('autoprefixer')({ browsers: [ // 加這個後能夠出現額外的兼容性前綴 "> 0.01%" ] }) ], sourceMap: true } }
對於組件和prop而言,html上用kebab-case (短橫線分隔命名) ,其對應的js上要用
(HTML 中的特性名是大小寫不敏感的,因此瀏覽器會把全部大寫字符解釋爲小寫字符。這意味着當你使用 DOM 中的模板時,camelCase (駝峯命名法) 的 prop 名須要使用其等價的 kebab-case (短橫線分隔命名) 命名:)
【可是:若是你使用字符串模板,那麼這個限制就不存在了。】
components: { kebabCase } --- prop: ['kebabCase']
跟組件和 prop 不一樣,事件名不存在任何自動化的大小寫轉換。而是觸發的事件名須要徹底匹配監聽這個事件所用的名稱。
跟組件和 prop 不一樣,事件名不會被用做一個 JavaScript 變量名或屬性名,因此就沒有理由使用 camelCase 或 PascalCase 了。而且 v-on 事件監聽器在 DOM 模板中會被自動轉換爲全小寫 (由於 HTML 是大小寫不敏感的),因此 v-on:myEvent 將會變成 v-on:myevent——致使 myEvent 不可能被監聽到。
所以,咱們推薦你始終使用 kebab-case 的事件名。
refs是靜態節點
若是父組件中給子組件傳遞了一個prop的值,而後調用子組件的方法去獲取該值,會發現值沒有當即改變。
解決方法:
在vue-cli中使用fetch方式請求的時候。存在必定的兼容問題。
能夠在build/webpack.base.conf.js中添加一個plugins
plugins:[ new webpack.ProvidePlugin({ fetch: 'imports-loader?this=>global!exports-loader?global.fetch!whatwg-fetch', Promise: 'imports-loader?this=>global!exports-loader?global.Promise!es6-promise', }) ]
普通的watch
中只能監聽到某對象的變化纔會調用,當想監聽對象以及對象中屬性的變化都調用函數時,可使用deep:true
data() { return { bet: { pokerState: 53, pokerHistory: 'local' } } }, watch: { bet: { handler(newValue, oldValue) { console.log(newValue) }, deep: true } }
強制刷新頁面,觸發頁面從新渲染。
在xxx/detail/123
和xxx/edit/123
都用了同一個組件,beforeRouteUpdate
不生效,可是watch $route
是生效的?
擴展: 能夠考慮在路由定義處使用別名 alias
官方解釋
beforeRouteUpdate (to, from, next) { // 在當前路由改變,可是該組件被複用時調用 // 舉例來講,對於一個帶有動態參數的路徑 /foo/:id,在 /foo/1 和 /foo/2 之間跳轉的時候, // 因爲會渲染一樣的 Foo 組件,所以組件實例會被複用。而這個鉤子就會在這個狀況下被調用。 // 能夠訪問組件實例 `this` },
在js中對組件命名(設置name屬性)便可以調用自身
常見的場景:
v-for
下,有相同父元素的子元素必須有獨特的key。重複的key會形成渲染錯誤。若在父組件上定義一個事件,至關於定義一個監聽子組件的監聽器。若想要單獨使用父組件的事件而不是監聽,加一個.native事件修飾符便可。
錯誤處理很重要可是這是最容易讓開發忽略的點。
因爲個人請求是使用axios插件或者fetch單獨寫在了一個js,能夠對其進行響應攔截。一旦失敗,或者後臺報錯,就進行相應的錯誤處理以及友好提示,也避免了重複的代碼,提升可維護性
將錯誤提示模塊化,經過vuex來操做錯誤的顯示以及信息等內容。
性能優化是很重要的,特別是對於vue這種首屏加載時間長的。
例若有些項目用到了圖表(echarts),能夠選擇加載依賴包,不用加載整個echarts庫。
不發送多個相同的請求,在點擊觸發請求的同時鎖定請求,直至給出響應/錯誤解鎖。
以上內容,若有錯誤請指出,不甚感激。
如需轉載,請註明出處
爲了讓SCSS之類的文件在CSS中引入中不須要每次都引入var.scss文件,能夠引入一個sass-resources-loader
解決。
在build/utils中引入postcss-loader
loader: 'postcss-loader', options:{ plugins: (loader) => [ require('autoprefixer')() ], sourceMap: true }
entry: { // app: './src/main.js' app: ["babel-polyfill", "./src/main.js"] } // build/webpack.base.config.js // ./src/main.js是入口文件,可能存在差別
plugins: [ new webpack.ProvidePlugin({ // 若是用了fetch可使用如下的進行兼容 // fetch: 'imports-loader?this=>global!exports-loader?global.fetch!whatwg-fetch', Promise: 'imports-loader?this=>global!exports-loader?global.Promise!es6-promise', }) ]
HTML 中的特性名是大小寫不敏感的,因此瀏覽器會把全部大寫字符解釋爲小寫字符。這意味着當你使用 DOM 中的模板時,camelCase (駝峯命名法)
的 prop 名須要使用其等價的 kebab-case (短橫線分隔命名)
命名:
Vue.component('blog-post', { // 在 JavaScript 中是 camelCase 的 props: ['postTitle'], template: '<h3>{{ postTitle }}</h3>' })
<!-- 在 HTML 中是 kebab-case 的 --> <blog-post post-title="hello!"></blog-post>
重申一次,若是你使用字符串模板,那麼這個限制就不存在了。
不一樣於組件和 prop,事件名不存在任何自動化的大小寫轉換。而是觸發的事件名須要徹底匹配監聽這個事件所用的名稱。舉個例子,若是觸發一個 camelCase
名字的事件:
this.$emit('myEvent')
則監聽這個名字的 kebab-case
版本是不會有任何效果的:
<my-component v-on:my-event="doSomething"></my-component>
不一樣於組件和 prop,事件名不會被用做一個 JavaScript 變量名或屬性名,因此就沒有理由使用 camelCase
或 PascalCase
了。而且 v-on
事件監聽器在 DOM 模板中會被自動轉換爲全小寫 (由於 HTML 是大小寫不敏感的),因此 v-on:myEvent
將會變成 v-on:myevent
——致使 myEvent 不可能被監聽到。
所以,咱們推薦你始終使用 kebab-case 的事件名。
以上內容,若有錯誤請指出,不甚感激。 如需轉載,請註明出處