vue全家桶。javascript
使用過vue的程序員通常這樣評價它,「vue.js兼具angular.js和react.js的優勢」。Vue.js 是一個JavaScript MVVM(Model-View-ViewModel)庫,用於漸近式構建用戶界面。它以數據驅動和組件化思想構建,採用自底向上增量開發的設計思想。相比Angular.js,Vue.js API更加簡潔;相比 React + Redux 複雜的架構,Vue.js 上手更加容易。css
目錄html
1、vue全家桶包括什麼前端
vue-router路由 vuex vue-resource 構建工具vue-cli 調度工具Devtools 關於UI組件庫
2、vue工程目錄結構vue
編輯器
3、vue使用簡介java
數據代理 vue實例生命週期圖解
4、vue的運行原理python
雙向綁定圖解 模板是如何解析的
5、發佈前優化react
UI組件按需加載
路由懶加載
使用異步組件(動態組件)
圖片壓縮與合併
使用CDN加速vue類庫
壓縮代碼
v-for和v-if不要同時使用 使用Object.freeze凍結大數據 使用Keep-alive標籤優化組件建立 使用Set 在scope中少用元素選擇器 關於template的優化
1、vue全家桶包括什麼webpack
vue-router路由git
網站:http://router.vuejs.org。使用npm工具來安裝vue-router
npm install vue-router
經過import導入Vue模塊、vue-router模塊及其它組件。
import Vue from’vue’
importRouter from’vue-router’
在使用路由前,必需要經過 Vue.use() 明確地安裝路由功能。
Vue.use(Router)
經過const router= new VueRouter()定義路由,並傳入對應的配置,包括路徑path和組件components等。
在使用newVue來建立和掛載vue根實例的時候,記得要經過 router配置參數注入路由。使用router-link:
有兩種模式:
hash 模式
history 模式
vuex
在vue開發實戰中,多個組件共享數據時,單向數據流的簡潔性很容易被破壞。爲解決多個視圖使用同一數據及多個視圖驅動同一數據更新的問題,vuex應運而生。
當網站足夠大時,一個狀態樹下,根的部分字段繁多,解決這個問題就要模塊化 vuex,官網提供了模塊化方案,容許咱們在初始化 vuex 的時候配置 modules。每個 module 裏面又分別包含 state 、action 等,看似是多個狀態樹,其實仍是基於 rootState 的子樹。細分後整個 state 結構就清晰了,管理起來也方便許多。
Vuex 是一個專爲 Vue.js 應用程序開發的狀態管理模式。它採用集中式存儲管理應用的全部組件的狀態,並以相應的規則保證狀態以一種可預測的方式發生變化。Vuex 的四個核心概念是:
The state tree:Vuex 使用單一狀態樹,用一個對象就包含了所有的應用層級狀態。至此它便做爲一個惟一數據源(SSOT)而存在。這也意味着,每一個應用將僅僅包含一個 store 實例。單狀態樹讓咱們可以直接地定位任一特定的狀態片斷,在調試的過程當中也能輕易地取得整個當前應用狀態的快照。
Getters:用來從 store 獲取 Vue 組件數據。
Mutators:事件處理器用來驅動狀態的變化。
Actions:能夠給組件使用的函數,以此用來驅動事件處理器 mutations。(注:此許或許稱之爲EventHandler更爲恰當。)
Vuex和簡單的全局對象是不一樣的。當Vuex從store中讀取狀態值的時候,若狀態發生了變化,那麼相應的組件也會更新。而且改變store中狀態的惟一途徑就是提交commit mutations。只要發生了狀態的變化,必定伴隨着mutation的提交。 例如:
經過 store.state 來獲取狀態對象,以及經過 store.commit 方法觸發狀態變動:
因爲 vuex 的靈活性,帶來了編碼不統一的狀況,完整的閉環是 store.dispatch('action') -> action -> commit -> mutation -> getter -> computed,實際上中間的環節有的能夠省略,由於 API 文檔提供瞭如下幾個方法 mapState、mapGetters、mapActions、mapMutations,而後在組件裏能夠直接調取任何一步,仍是項目小想怎麼調用均可以,項目大的時候,就要考慮 vuex 使用的統一性,有人建議是不論多簡單的流程都跑完整個閉環,造成代碼的統一,方便後期管理,在組件裏只容許出現 dispatch 和 mapGetters,其他的流程都在名爲 store 的 vuex 文件夾裏進行。
注:mapGetters 工具函數會將 store 中的 getter 映射到局部計算屬性中。它的功能和 mapState 很是相似。
vue-resource
網站:https://github.com/pagekit/vue-resource
使用npm來安裝Vue-resource:
$ npm install vue-resource
在安裝並引入vue-resource後,能夠基於全局的Vue對象使用http,也能夠基於某個Vue實例使用http。
在發送請求後,使用then方法來處理響應結果,then方法有兩個參數,第一個參數是響應成功時的回調函數,第二個參數是響應失敗時的回調函數。
vue-resource的請求API是按照REST風格設計的,它提供了7種請求API:
· get(url,[options])
· head(url,[options])
· delete(url,[options])
· jsonp(url,[options])
· post(url,[body], [options])
· put(url, [body],[options])
· patch(url,[body], [options])
構建工具vue-cli
vue-cli是vue標準的開發工具。網站:https://cli.vuejs.org/
安裝
npm install -g @vue/cli
最新版本爲3.4.0。
建立項目
vue create my-project
以上是命令行建立。也能夠經過 vue ui 命令以圖形化界面建立和管理項目:
vue ui
運行
npm run serve
調度工具Devtools
vue在調試方面,能夠選擇安裝chrome插件vue Devtools。打開vue項目,在調試vue應用的時候,chrome開發者工具中會看一個vue的一欄,點擊以後就能夠看見當前頁面vue對象的一些信息。
在Devtools工具中,能夠選擇組件,查看對應組件內的數據信息。也能夠選擇Vuex選項,查看該項目內Vuex的狀態變量信息。
關於UI組件庫
能夠本身寫,爲提升開發效率也能夠複用第三方組件庫。element(https://github.com/ElemeFE/element)是一個最好支持vue2.0的UI組件庫。
2、vue工程目錄結構
這是一個簡單的vue項目的大概結構:
components/文件夾:用來存放Vue 組件。我的建議,把每個組件中使用到的image圖片放置到對應的組件子文件目錄下,便於統一的管理
Node_modules/:npm安裝的該項目的依賴庫
vuex/文件夾:存放的是和 Vuex store 相關的東西(state對象,actions,mutations)
router/文件夾:存放的是跟vue-router相關的路由配置項
build/文件:是 webpack 的打包編譯配置文件
static/文件夾:存放一些靜態的、較少變更的image或者css文件
config/文件夾:存放的是一些配置項,好比服務器訪問的端口配置等
dist/該文件夾:一開始是不存在,在咱們的項目通過 build 以後纔會產出
App.vue根組件,全部的子組件都將在這裏被引用
index.html整個項目的入口文件,將會引用咱們的根組件 App.vue
main.js入口文件的 js 邏輯,在webpack 打包以後將被注入到 index.html 中
編輯器
VSCode with Vetur
3、vue使用簡介
數據代理
每一個 Vue.js 應用都是經過構造函數 Vue 建立一個 Vue 的根實例 啓動的。每一個 Vue 實例都會代理其 data 對象裏全部的屬性:
var data = { a: 1 }
var vm = new Vue({
data: data
})
vm.a === data.a // -> true
設置新值也會同步影響:
vm.a = 2
data.a // -> 2
// ... 反之亦然
data.a = 3
vm.a // -> 3
實現數據代理的僞代碼以下:
var self = this; // this爲vue實例, 即vm
Object.keys(this.data).forEach(function(key) {
Object.defineProperty(this, key, { // this.title, 即vm.title enumerable: false, configurable: true, get: function getter () { return self.data[key]; //觸發對應data[key]的getter }, set: function setter (newVal) { self.data[key] = newVal; //觸發對應data[key]的setter } });
}
Vue 實例暴露了一些有用的實例屬性與方法。這些屬性與方法都有前綴 $,以便與代理的 data 屬性區分。例如:
vm.$data === data // -> true
vm.$el === document.getElementById('example') // -> true
vue實例生命週期圖解
4、vue的運行原理
Vue採用簡潔的模板語法,以聲明的方式將數據渲染進 DOM。vue代碼是沒有辦法直接被瀏覽器解析的,必須通過「編譯」,變爲瀏覽器能夠識別爲html、js與css代碼。這種聲明式開發方式把方便留給了程序員,轉換工做交給了自動化工具。
注:el是element的縮寫,指Vue實例掛載的元素節點。
雙向綁定圖解
通常說的雙向綁定,指:
數據變更 --> 視圖更新
視圖更新 --> 數據變更
視圖更新 --> 數據變更,這個方向的綁定比較簡單。主要經過事件監聽來改變數據,好比input控件能夠監聽input事件,一旦事件觸發,調用JS改變data。
模型層(model)只是普通 JavaScript 對象,修改它,DOM本是不能更新的。當程序員把一個普通 JavaScript 對象傳給 Vue 實例的 data 選項,Vue 將遍歷此對象全部的屬性,並使用 Object.defineProperty 把這些屬性所有轉爲 getter/setter。在每一個setter中,能夠作許多事件,使表面看起來數據變了,視圖就更新了。而且這種數據更新,和原來同樣,只是 vm.a=123 這樣的簡單更新。
如上所求,每一個vue組件實例都有相應的 watcher 實例對象,它會在vue組件渲染的過程當中把須要用到的屬性(getter)記錄爲依賴。以後,當依賴項的 setter 被(其它JS代碼)調用時,setter 會通知 watcher 從新計算,從而導致它關聯的組件得以更新。
此處實現的是一個觀察者模式。
經過object.defineProperty遍歷設置this.data裏面全部屬性,在每一個屬性的setter裏面去通知對應的回調函數,這裏的回調函數包括dom視圖從新渲染的函數、使用$watch添加的回調函數等,這樣咱們就經過object.defineProperty劫持了數據,當咱們對數據從新賦值時,如this.title = 'hello vue',就會觸發setter函數,從而觸發dom視圖從新渲染的函數,實現數據變更,對應視圖更新。
那麼,如何在setter裏面觸發全部綁定該數據的回調函數呢?
既然綁定該數據的回調函數不止一個,咱們就把全部的回調函數放在一個數組裏面,一旦觸發該數據的setter,就遍歷數組觸發裏面全部的回調函數,咱們把這些回調函數稱爲訂閱者。數組最好就定義在setter函數的最近的上級做用域中,以下面實例代碼所示。
> Object.keys(this.data).forEach(function(key) { > > var subs = []; // 在這裏放置添加全部訂閱者的數組 > > Object.defineProperty(this.data, key, { // this.data.title > > enumerable: false, > > configurable: true, > > get: function getter () { > > console.log('訪問數據啦啦啦') > > return this.data[key]; //返回對應數據的值 > > }, > > set: function setter (newVal) { > > if (newVal === this.data[key]) { > > return; // 若是數據沒有變更,函數結束,不執行下面的代碼 > > } > > this.data[key] = newVal; //數據從新賦值 > > subs.forEach(function () { > > // 通知subs裏面的全部的訂閱者 > > }) > > } > > }); > > }
那麼,怎麼把綁定數據的全部回調函數放到一個數組裏面呢?這是經過gettter內部的代碼完成的。
咱們知道只要訪問數據就會觸發對應數據的getter,那咱們能夠先設置一個全局變量target,若是咱們要在data裏面title屬性添加一個訂閱者(changeTitle函數),咱們能夠先設置target = changeTitle,把changeTitle函數緩存在target中,而後訪問this.title去觸發title的getter,在getter裏面把target這個全局變量的值添加到subs數組裏面,添加完成後再把全局變量target設置爲null,以便添加其餘訂閱者。
僞代碼以下:
> target = changeTitle
>
> ...
>
> Object.keys(this.data).forEach(function(key) { > > var subs = []; // 在這裏放置添加全部訂閱者的數組 > > Object.defineProperty(this.data, key, { // this.data.title > > enumerable: false, > > configurable: true, > > get: function getter () { > > console.log('訪問數據啦啦啦') > > if (target) { > > subs.push(target); > > } > > return this.data[key]; //返回對應數據的值 > > }, > > set: function setter (newVal) { > > if (newVal === this.data[key]) { > > return; // 若是數據沒有變更,函數結束,不執行下面的代碼 > > } > > this.data[key] = newVal; //數據從新賦值 > > subs.forEach(function () { > > // 通知subs裏面的全部的訂閱者 > > }) > > } > > }); > > }
上面代碼中提到的changeTitle,便是上面最近一張圖解中的watcher。vue經過getter收集watcher集合。由於vue充許在運行時添加代碼,因此該收集行爲不能僅限制於模板「編譯」以前。(注:vue中是不存在嚴格的編譯的,js是解析執行型語言,像C、Go等語言將源碼編譯爲目標平臺的二進制文件,纔是真的編譯。)
模板是如何解析的
假如說有下面這一段代碼,咱們怎麼把它解析成對應的html呢?
> <input v-model="title"> > > <h1>{{title}}</h1> > > <button v-on:click="changeTitle">change title<button>
注:該示例實現的效果是,在input輸入框內輸入任何內容,下方h1文本同步更新。
先簡單介紹視圖更新函數的用途,好比解析指令v-model="title",v-on:click="changeTitle",還有把{{title}}替換爲對應的數據等。
回到上面那個問題,如何解析模板?咱們只要去遍歷全部dom節點包括其子節點:
若是節點屬性含有v-model,視圖更新函數就爲把input的value設置爲title的值
若是節點爲文本節點,視圖更新函數就爲先用正則表達式取出大括號裏面的值'title',再設置文本節點的值爲data['title']
若是節點屬性含有v-on:xxxx,視圖更新函數就爲先用正則獲取事件類型爲click,而後獲取該屬性的值爲changeTitle,則事件的回調函數爲this.methods['changeTitle'],接着用addEventListener監聽節點click事件。
5、發佈前優化
使用vue-cli部署生產包時,發現資源包很大,打包後的vendor.js達到了1M+。
UI組件按需加載
若是使用了第三方組件/UI庫,如element-ui, mint-ui,echarts等,若是所有引入,項目體積很是大,這時能夠按需引入組件。
安裝 babel-plugin-component
npm install babel-plugin-component -D
而後,將.babelrc 修改成:
> {
>
> "presets": [["es2015", { "modules": false }]], > "plugins": [ > [ > "component", > { > "libraryName": "element-ui", > "styleLibraryName": "theme-chalk" > } > ] > ] > }
而後引入部分組件,這樣一來,就不須要引入樣式了,插件會幫咱們處理。
> // main.js > import Vue from 'vue' > import { Dialog, Loading } from 'element-ui' > Vue.use(Dialog) > Vue.use(Loading.directive) > Vue.prototype.$loading = Loading.service > // 而後正常使用組件
注:Babel是一個普遍使用的轉碼器,能夠將ES6代碼轉爲ES5代碼。讓不支持ES6的宿主環境,支持使用一套源碼開發。
mint-ui是element-ui的移動端組件,因此它的使用和引入幾乎和element-ui同樣。
路由懶加載
vue-router官方推薦syntax-dynamic-import插件,不過它要求同時安裝@bable/core^7.0.0,若是你安裝了babel-core6,可能有版本衝突的,解決方法以下:
npm install babel-plugin-syntax-dynamic-import --save-dev(^6.18.0)
當打包構建應用時,Javascript 包會變得很是大,影響頁面加載。若是咱們能把不一樣路由對應的組件分割成不一樣的代碼塊,而後當路由被訪問的時候才加載對應組件,這樣就更加高效了。結合 Vue 的異步組件和 Webpack 的代碼分割功能,輕鬆實現路由組件的懶加載。
> // router.js > const login = () => import('@/components/login') > const router = new VueRouter({ > routes: [ > { path: '/login', component: login } > ] > })
還有一種魔法註釋用法,不推薦使用。
使用異步組件(動態組件)
app bundle 文件過大,能夠嘗試經過組件懶加載優化。
動態組件主頁面加載是不會加載,等到觸發條件時才加載該組件,而且加載一次後就有緩存。若是組件在頁面加載時不須要,只在調用時用到,這時可使用異步組件的寫法。僅僅是引入和組件註冊寫法不一樣:
> // template > <test v-if="showTest"></test> > > // script > > components: { > test: () => import('./test') // 將組件異步引入,告訴webpack,將該部分代碼分割打包 > }, > > methods:{ > clickTest () { > this.showTest = !this.showTest > } > }
圖片壓縮與合併
無損壓縮圖片:https://tinypng.com/。能夠將圖片製成雪碧精靈圖。
使用CDN加速vue類庫
通常項目裏用到的第三方js庫主要有:vue、vue-router、vuex、vue-resource、axio、qiniu等。這些依賴庫的js文件被一塊兒打包到vender那個js文件裏面,致使vender這個文件很大,那首屏加載速度確定會被拖慢。
類庫文件使用cdn加速
> <!-- built files will be auto injected --> > > <script src="https://cdn.bootcss.com/vue/2.5.2/vue.min.js"></script> > > <script src="https://cdn.bootcss.com/vue-router/3.0.1/vue-router.min.js"></script> > > <script src="https://cdn.bootcss.com/vuex/3.0.1/vuex.min.js"></script> > > <script src="https://cdn.bootcss.com/vue-resource/1.5.1/vue-resource.min.js"></script>
修改 build/webpack.base.conf.js
> module.exports = { > > context: path.resolve(__dirname, '../'), > > entry: { > > app: './src/main.js' > > }, > > externals:{ > > 'vue': 'Vue', > > 'vue-router': 'VueRouter', > > 'vuex':'Vuex', > > 'vue-resource': 'VueResource' > > }
排除已經手動收入的js文件
利用webpack的externals。具體作法就是在 build/webpack.base.conf.js文件的module裏面與rules同層加入externals。具體作法,修改src/main.js src/router/index.js 註釋掉import引入的vue,vue-resource等:
> // import Vue from 'vue' > > // import VueResource from 'vue-resource' > > // Vue.use(VueResource)
上面已經引用過。
壓縮代碼
vue-cli已經使用UglifyJsPlugin 插件來壓縮代碼,能夠設置成以下配置:
> new webpack.optimize.UglifyJsPlugin({ > > compress: { > > warnings: false, > > drop_console: true, > > pure_funcs: ['console.log'] > > }, > > sourceMap: false > > })
其中sourceMap: false是禁用除錯功能。若是設爲true,在部署包中會生成.map結尾的js文件。它用於在代碼混淆壓縮的狀況下仍可進行調試。這個功能雖好,但會大大增長總體資源包的體積,因此將其禁用。
v-for和v-if不要同時使用
在vue中v-for和v-if不要放在同一個元素上使用。因爲 v-for 和 v-if 放在同一個元素上使用會帶來一些性能上的影響,在計算屬性上過濾以後再進行遍歷。反例:
使用Object.freeze凍結大數據
對於前端純大數據展現(純大數據指:拿到數據就是直接用於展現的,不須要作修改其中字段等處理的,並且數據量比較大)的狀況下,使用Object.freeze方法來包裹變量,那邊vue內部不會使用defineproperty去監聽數據內部的變化,只有自己變化時纔會觸發,在大量數據的狀況下,vue內部不在去監聽數據的變化會提升性能。使用demo以下:
使用Keep-alive標籤優化組件建立
vue提供了keep-alive標籤來存儲緩存,對於一些視頻控件object或圖表類的使用,咱們常常會使用v-if指令,而v-if是會建立和銷燬的,若是頻繁操做在ie下的內存會持續上升,而keep-alive能夠有效的緩存,抑制內存的持續上升。
見:https://cn.vuejs.org/v2/api/#keep-alive
使用Set
Es6集合Set()可優化遍歷速度,set集合是可用於查找該集合內是否存在某個元素。但若是使用了Bable自動轉化,該優化無效。
在scope中少用元素選擇器
scope中元素選擇器儘可能少用。在 scoped 樣式中,類選擇器比元素選擇器更好,由於大量使用元素選擇器是很慢的。
爲了給樣式設置做用域,Vue 會爲元素添加一個獨一無二的特性,例如 data-v-f3f3eg9。而後修改選擇器,使得在匹配選擇器的元素中,只有帶這個特性纔會真正生效 (好比 button[data-v-f3f3eg9])。問題在於大量的元素和特性組合的選擇器 (好比 button[data-v-f3f3eg9]) 會比類和特性組合的選擇器 慢,因此應該儘量選用類選擇器。
關於template的優化
v-show,v-if 用哪一個?在我來看要分兩個維度去思考問題,第一個維度是權限問題,只要涉及到權限相關的展現無疑要用 v-if,第二個維度在沒有權限限制下根據用戶點擊的頻次選擇,頻繁切換的使用 v-show,不頻繁切換的使用 v-if,這裏要說的優化點在於減小頁面中 dom 總數,我比較傾向於使用 v-if,由於減小了 dom 數量,加快首屏渲染,至於性能方面我感受肉眼看不出來切換的渲染過程,也不會影響用戶的體驗。
不要在模板裏面寫過多的表達式與判斷 v-if="isShow && isAdmin && (a || b)",這種表達式雖然說能夠識別,可是不是長久之計,當看着不舒服時,適當的寫到 methods 和 computed 裏面封裝成一個方法,這樣的好處是方便咱們在多處判斷相同的表達式,其餘權限相同的元素再判斷展現的時候調用同一個方法便可。
循環調用子組件時添加 key,key 能夠惟一標識一個循環個體,可使用例如 item.id 做爲 key,假如數組數據是這樣的 ['a' , 'b', 'c', 'a'],使用 :key="item" 顯然沒有意義,更好的辦法就是在循環的時候 (item, index) in arr,而後 :key="index"來確保 key 的惟一性。