認識Vue.js+Vue.js的優缺點+和與其他前端框架的區別
首先,我們先了解什麼是MVX框架模式?
MVX框架模式:MVC+MVP+MVVM
1.MVC:Model(模型)+View(檢視)+controller(控制器),主要是基於分層的目的,讓彼此的職責分開。
View通過Controller來和Model聯絡,Controller是View和Model的協調者,View和Model不直接聯絡,基本聯絡都是單向的。
使用者User通過控制器Controller來操作模板Model從而達到檢視View的變化。
2.MVP:是從MVC模式演變而來的,都是通過Controller/Presenter負責邏輯的處理+Model提供資料+View負責顯示。
在MVP中,Presenter完全把View和Model進行了分離,主要的程式邏輯在Presenter裡實現。
並且,Presenter和View是沒有直接關聯的,是通過定義好的介面進行互動,從而使得在變更View的時候可以保持Presenter不變。
MVP模式的框架:Riot,js。
3.MVVM:MVVM是把MVC裡的Controller和MVP裡的Presenter改成了ViewModel。Model+View+ViewModel。
View的變化會自動更新到ViewModel,ViewModel的變化也會自動同步到View上顯示。
這種自動同步是因為ViewModel中的屬性實現了Observer,當屬性變更時都能觸發對應的操作。
MVVM模式的框架有:AngularJS+Vue.js和Knockout+Ember.js後兩種知名度較低以及是早起的框架模式。
Vue.js是什麼?
看到了上面的框架模式介紹,我們可以知道它是屬於MVVM模式的框架。那它有哪些特性呢?
其實Vue.js不是一個框架,因為它只聚焦檢視層,是一個構建資料驅動的Web介面的庫。
Vue.js通過簡單的API(應用程式程式設計介面)提供高效的資料繫結和靈活的元件系統。
Vue.js的特性如下:
1.輕量級的框架
2.雙向資料繫結
3.指令
4.外掛化
Vue.js與其他框架的區別?
1.與AngularJS的區別
相同點:
都支援指令:內建指令和自定義指令。
都支援過濾器:內建過濾器和自定義過濾器。
都支援雙向資料繫結。
都不支援低端瀏覽器。
不同點:
1.AngularJS的學習成本高,比如增加了Dependency Injection
2.在效能上,AngularJS依賴對資料做髒檢查,所以Watcher越多越慢。
Vue.js使用基於依賴追蹤的觀察並且使用非同步佇列更新。所有的資料都是獨立觸發的。
對於龐大的應用來說,這個優化差異還是比較明顯的。
2.與React的區別
相同點:
React採用特殊的JSX語法,Vue.js在元件開發中也推崇編寫.vue特殊檔案格式,對檔案內容都有一些約定,兩者都需要編譯後使用。
中心思想相同:一切都是元件,元件例項之間可以巢狀。
都提供合理的鉤子函式,可以讓開發者定製化地去處理需求。
都不內建列數AJAX,Route等功能到核心包,而是以外掛的方式載入。
在元件開發中都支援mixins的特性。
不同點:
React依賴Virtual DOM,而Vue.js使用的是DOM模板。React採用的Virtual DOM會對渲染出來的結果做髒檢查。
Vue.js在模板中提供了指令,過濾器等,可以非常方便,快捷地操作DOM。
如何使用Vue.js?
1.安裝
(1)script
如果專案直接通過script載入CDN檔案,程式碼示例如下:
<script src="http://webapp.didistatic.com/static/webapp/shield/z/vue/vue/1.0.24/vue.min.js"></script>
(2)npm
如果專案給予npm管理依賴,則可以使用npm來安裝Vue,執行如下命令:
$npm i vue --save-dev
(3)bower
如果專案基於bower管理依賴,則可以使用bower來安裝Vue,執行如下命令:
$bower i vue --save-dev
2018.4.24====================好久沒寫部落格。(太多知識需要整理,從今天開始,有時間就寫博,分享開發過程中的點滴)附上一個最基本的vue元件頁面。
<template> <!--app-actions start--> <div class="app-actions row"> <ul class="btn-list"> <li v-if="permissions.create" :disabled='quota.totalSecurityGroupsUsed >= quota.maxSecurityGroups'> <a href="javascript:;" class="btn btn-primary" @click="action_modal = 'Create'"><i class="icon icon-i446 pr5"></i>{{ $t('access_security.create') }}</a> </li> <li v-if="permissions.edit || permissions.delete"> <v-dropdown> <button type="button" class="btn btn-info btn-icon dropdown-toggle" data-toggle="dropdown"> <i class="icon icon-i692"></i> </button> <ul slot="dropdown-menu" class="dropdown-menu"> <li :class="{'disabled': !acSingle || !menu.edit}" v-if="permissions.edit"><a href="javascript:;" @click="action_modal = 'Edit'">{{ $t('base.edit')}}</a></li> <li :class="{'disabled': !acMulti}" v-if="permissions.delete"><a class="danger" href="javascript:;" @click="action_modal = 'Delete'">{{ $t('base.delete')}}</a> </li> </ul> </v-dropdown> </li> <li> <button type="button" class="btn btn-info btn-icon" @click="ajaxData"> <i class="icon icon-i500"></i></button> </li> </ul> <!--當前頁面資料檢索--> <div class="app-search"> <input type="text" class="form-control app-search-input" v-model="searchKey" placeholder="{{ $t('base.search_name')}}" maxlength="64"> <i class="icon icon-i441"></i> </div> </div> <!--app-actions end--> <!--app-body start--> <div class="app-body row"> <v-grid :overall="false" :count="COUNT" :titles="grid.titles" :items="LIST" :transname="grid.name" :checkeds.sync="checkeds" :orderkey.sync="grid.order_key" :orderitem="grid.order_item" :orderswitch.sync="grid.order_switch" :ready="ready" > <div class="grid-canvas" slot="grid-canvas"> <div class="grid-row" v-for="security_group in LIST | orderBy grid.order_key grid.order_switch" :class="{'selected': checkeds.indexOf(security_group.id) !== -1}"> <div class="grid-cell row-key"><input type="checkbox" value="{{security_group.id}}" v-model="checkeds"></div> <div class="grid-cell r0"> <a v-link="{ name: routeDetail, params: { id:security_group.id , view:detail_view }}"> {{security_group.name_cn}} </a> </div> <div class="grid-cell r1">{{security_group.description}}</div> </div> </div> </v-grid> <!--detailView start--> <v-detailview :show.sync="detail.show" :itemdata.sync="detail.item" :backurl="routeGrid"> <v-tabs slot="detail-body" :active="detail.view" :router="routeDetail" v-bind:classname="['detailTabs']" v-ref:tabs> <v-tab :header="$t('volumes.description')" controls="description" v-if="true"> <c-description :data="detail.item"></c-description> </v-tab> </v-tabs> </v-detailview> <!--detailView end--> </div> <!--app-body end--> <!--=======================action modal start=============================--> <div role="dialog" v-show="action_modal != null" transition="fade" class="modal in"> <component :is="action_modal" transition="zoom" :item="ACTIVE_ITEM"></component> </div> <!--=======================action modal end=============================--> <!--=======================action modal start=============================--> <div role="dialog" v-show="rules_modal != null" transition="fade" class="modal in"> <component :is="rules_modal" :item="ACTIVE_ITEM" :direction="direction" transition="zoom"></component> </div> <!--=======================action modal end=============================--> </template> <script> import { networksAjax } from '../../../ajax.js' import { extend,statusFilter,filterSearchKey} from '../../../lib/function.js' import vDropdown from '../../../lib/strap/Dropdown.vue' import vGrid from '../../../lib/strap/Grid.vue' import vDetailview from '../../../lib/strap/Detailview.vue' import vTabs from '../../../lib/strap/Tabsetview.vue' import vTab from '../../../lib/strap/Tab.vue' import Create from './action_create.vue' //彈出層create元件 import Edit from './action_edit.vue' //彈出層edit元件 import Delete from './action_delete.vue' //彈出層delete元件 import Createrules from './action_create_rules.vue' //彈出層createrules元件 import cDescription from './detail_security_groups.vue' //彈出層description元件 export default { components: { vDropdown, vGrid, vDetailview, vTabs, vTab, Create, Edit, Delete, Createrules, cDescription }, data(){ return { menu:{//設定選單選項初始值 'create':true, 'edit':true, 'delete':true }, routeDetail:'security_groups_view', //側拉路由name routeGrid:'access_security', //主列表路由name AJAX_API:this.$resource( '', {}, networksAjax),//資料介面 DATA:[],//資料來源 COUNT:0, //資料條目總數 searchKey:'', //搜尋篩選值 searchField:['name_cn'],//搜尋欄位 action_modal:null, rules_modal:null, checkeds:[], //已選擇資料id ready:false, //資料是否已經載入 //是否多選(toolbar判斷用) acMulti:false, //是否單選(toolbar判斷用) acSingle:false, detail:{ show:false,//側拉元件開關 id:null,//側拉id item:{}, //側拉顯示物件 view:null,//側拉顯示標籤 }, quota:{ totalSecurityGroupsUsed:0, maxSecurityGroups:10 }, detail_view:'description', //側拉預設選項卡 grid:{ name:'access_security', //當前grid翻譯對應值 titles:['name','description'],//grid列表元件列表標題 order_item:['name','description'], //傳入允許使用排序功能的列的title 例如order_item:['name','size','type'], order_key: null,//列表排序欄位 order_switch: null,//列表正反切換 }, direction:null,//側拉詳情中-上行下行 permissions:COS.permissions.resource_console.access_security.security_groups } }, computed: { LIST:{ get: function(){ this.DATA.forEach(item => { item['name_cn'] = item.name == 'default' ? this.$t('access_security.default') : item.name}) return filterSearchKey(this.DATA,this.searchField,this.searchKey) } }, //根絕LIST的篩選&checkeds選中專案計算 ACTIVE_ITEM: { get: function(){ let items = [] const dataList = this.LIST const checkeds = this.checkeds; for(let n=0; n<dataList.length; n++){ for(let i=0; i<checkeds.length; i++){ if(dataList[n].id == checkeds[i]){ items.push(dataList[n]) } } }; return items; } } }, watch: { //剔除本地篩選後的checkeds 'LIST': function(val) { let new_data = []; for(let n=0; n<this.checkeds.length; n++){ for(let i=0; i<val.length; i++){ if(val[i].id.indexOf(this.checkeds[n]) >= 0){ new_data.push(this.checkeds[n]) } } } this.checkeds = new_data; }, 'detail.id':function () { if(this.detail.show==true){ let arr=[]; for(var k in this.$refs.tabs.renderData){ arr.push(this.$refs.tabs.renderData[k].controls) } if(Array.indexOf(arr,this.detail_view)==-1){ this.detail.view=arr[0] } } }, //根據選中id獲取相應資料物件 'checkeds': function (items) { //多選邏輯 if(items.length == 0){//無勾選 this.$router.replace({name:this.routeGrid})//返回主頁面 this.acMulti = false; this.acSingle = false; }else if(items.length > 1){//多選 this.$router.replace({name:this.routeGrid})//返回主頁面 this.acMulti = true; this.acSingle = false; }else{ this.acMulti = true; this.acSingle = true; } }, 'ACTIVE_ITEM':function(val){ if(val.length == 1 && val[0].name == 'default'){ this.menu.edit = false; }else{ this.menu.edit = true; } }, //資料載入完成後監測是否展示 'ready': function(val){ if(val){this.detailview(this.detail.id,this.detail.view)} } }, ready() { //載入資料 this.ajaxData() this.getQuotas() }, methods: { //獲取列表資料 ajaxData(){ this.ready = false; this.AJAX_API.security_groups().then((response) => { // success callback this.DATA = response.data.data this.COUNT = response.data.data.length this.ready = true }, (response) => { // error callback if(typeof response.data.message =="string"){ this.$dispatch('notice_msg', {msg:response.data.message,type:'info',duration:5}) }else if(typeof response.data.message =="object"){ for(var k in response.data.message){ this.$dispatch('notice_msg', {msg:k+' '+response.data.message[k],type:'info',duration:5}) } } }) }, //根據路由控制detailview顯示 detailview(id,view){ if(id){ //根據ID找到對應物件 for(let i=0; i<this.LIST.length; i++){ if(this.LIST[i].id == id){ this.detail.show = true this.checkeds = [] this.checkeds.push(id) this.detail.item = this.LIST[i] } } }else{ this.detail.show = false this.detail.item = {} }; }, getQuotas:function(){ this.AJAX_API.get_security_groups_quotas().then((response) => { // success callback if(response.data.code == 0){ this.quota = response.data.data } }, (response) => { // error callback if(typeof response.data.message =="string"){ this.$dispatch('notice_msg', {msg:response.data.message,type:'danger',duration:5}) }else if(typeof response.data.message =="object"){ for(var k in response.data.message){ this.$dispatch('notice_msg', {msg:k+' '+response.data.message[k],type:'danger',duration:5}) } } }) } }, events: { //socketIO更新 'IO_networks':function (data) { if(data.option == 'reload'){ this.AJAX_API.get({'id':data.resource_id}).then((response) => { // success callback const DATA = this.DATA; for(let i=0; i<DATA.length; i++) { if (DATA[i].id == data.resource_id) { extend(DATA[i],response.data.data,true) } } }, (response) => { // error callback if(typeof response.data.message =="string"){ this.$dispatch('notice_msg', {msg:response.data.message,type:'danger',duration:5}) }else if(typeof response.data.message =="object"){ for(var k in response.data.message){ this.$dispatch('notice_msg', {msg:k+' '+response.data.message[k],type:'danger',duration:5}) } } }) this.$dispatch('notice_msg', {msg:this.$t('base.volumes') +' "' + data.name + '" ' + this.$t('volumes.status_info.'+ data.state),type:'info',duration:5}) }else if(data.option == 'delete'){ const DATA = this.DATA; for(let i=0; i<DATA.length; i++) { if (DATA[i].id == data.resource_id) { this._delete_update_list(DATA[i].id) this.ajaxData() } } this.$dispatch('notice_msg', {msg:this.$t('base.volumes') +' "' + data.name + '" ' + this.$t('volumes.status_info.'+ data.state),type:'info',duration:5}) }else{ const DATA = this.DATA; for(let i=0; i<DATA.length; i++) { if (DATA[i].id == data.resource_id) { DATA[i].status = data.state; } } this.$dispatch('notice_msg', {msg:this.$t('base.volumes') +' "' + data.name + '" ' + this.$t('volumes.status_info.'+ data.state),type:'info',duration:5}) } } }, //根據路由獲取ID/VIEW route: { data (transition) { this.detail.id = transition.to.params.id if(transition.to.params.view){