vue 項目開發要點總結

vue-cli 腳手架工具的使用

vue-cli 的依賴
clipboard.pnghtml

vue-cli 的使用流程
clipboard.pngvue

node 的安裝
node 是經過js 操做系統接口的語言; npm 是node的包管理工具;因此,安裝了node 就能夠直接使用npm 下載咱們須要的包;
node 能夠直接去node官網下載對應系統的安裝包,安裝提示完成;
而後在 命令行中 經過 npm -v 查看npm 版本,若是有出來表明安裝成功;node

npm 代理 到cnpm
你直接經過添加 npm 參數 alias 一個新命令:webpack

alias cnpm="npm --registry=https://registry.npm.taobao.org \
--cache=$HOME/.npm/.cache/cnpm \
--disturl=https://npm.taobao.org/dist \
--userconfig=$HOME/.cnpmrc"

而後運行es6

cnpm  install xx -g  //全局安裝xx

使用 vue-cliweb

vue init webpack demoName //生成 webpack 爲模板的vue項目

clipboard.png

運行熱更新頁面ajax

npm run dev

生成項目能夠直接經過 http://localhost:8080/?#/ 訪問vue-router

若是你是拉別人的項目運行的,要先運行下面的命令,安裝依賴包vuex

npm install 
//或者
npm i

生成一個 靜態項目文件(直接打開的靜態頁面)vue-cli

npm run build
大多數狀況 ,生成的頁面是不能夠直接打開的;提示只能部署到 服務器上才能正常訪問;
若是須要直接本地打開;到項目目錄下的config文件夾裏的index.js文件中,將build對象下的assetsPublicPath中的「/」,改成「./」便可

clipboard.png
生成的文件都是通過壓縮的,帶md5的文件

es6 的引入與輸出

export default{} , module.exports = {}, exports { }

在node 的模塊中,exports 是 module.exports 的引用;可是在vue 中 不能寫成

exports = {
    porps:['text']
}

// 提示 text 未定義;

exports default {} 定義的輸出;能夠經過 import xx 直接使用(標準ES6);寫法是:

exports default {a:123}    

import a;

module.exports = {} 與 exports default 是等價的

可是若是 同一個js 同時出現 module.exports ={} 和 import 就會報錯

module.exports = {a:123} //定義和導出

import a;  //引入

//上面兩個關鍵詞 不能同時出如今同一個js 中

報錯緣由以下:

webpack可使用require和export ,可是不能混合使用import 和module.exports ,否則會報錯Cannot
assign to read only property 'exports' of object '#<Object>'
ES6 統一的寫法 是 import 和 exports default
若是你要import 組件 ;建議 統一使用 exports default {} 編寫組件

clipboard.png

clipboard.png

vue-router 路由插件安裝

安裝

cnpm install vue-router

引入爲全局插件

clipboard.png

配置路由表

clipboard.png

能夠經過router-link 組件 連接到 對應組件;它會解析成相似a 標籤

<router-link :to="{path:'forms'}">forms</router-link>  //注意書寫格式必定要正確
<router-link :to="{path:'father'}">father</router-link>

clipboard.png

注意到 生成的url 中前面自動加了個 「#」;能夠經過添加 mode:'history', 去掉
clipboard.png

原理:https://router.vuejs.org/zh-c...

路由配置中,path 能夠設置 url 帶參;

帶參path定義
clipboard.png

表示 apple 後面跟的是參數,不是路徑;

帶參路徑
clipboard.png

獲取參數
clipboard.png

使用路由要注意的地方

  • 全部的路由都是定義在 routes 生成的頁面中的;不能在子組件中從新定義;例子以下:
var router = new VueRouter()

import browseMode from './components/browse-mode.vue'
import blogList from './components/blog-list.vue'
import blogArticle from './components/blog-article.vue'
import writePanel from './components/write-panel.vue'

router.map({
    '/': {
      component: browseMode,
      subRoutes: {
        '/': {
          component: blogList
        },
        '/details/:artId': {
          component: blogArticle
        }
      }
    },
    '/edit/:mode': {
      component: writePanel
    },
    '/search/tag/:tag': {
      component: browseMode,
      subRoutes: {
        '/': {
          component: blogList
        }
      }
    },
    '/search/time/:time': {
      component: browseMode,
      subRoutes: {
        '/': {
          component: blogList
        }
      }
    },
    '/search/title/:title': {
      component: browseMode,
      subRoutes: {
        '/': {
          component: blogList
        }
      }
    },
})
router.start(App, 'app')
  • 路由不是最適合作tab切換的;最適合作tab的是 components 內置組件 ;經過is 控制切換;
<component v-bind:is="currentView">

var vm = new Vue({
  el: '#example',
  data: {
    currentView: 'home' //改變這個值就會切換內容;
  },
  components: {
    home: { /* ... */ },
    posts: { /* ... */ },
    archive: { /* ... */ }
  }
})
  • 定義帶參路由 的時候 注意格式是 path:'/box1/:text', 不是path:'/box1:text',(能夠理解爲/: 後面是參數 ,/後面是路徑;)

clipboard.png

  • 定義好路由後, router-link 是能夠放在任何地方的;它就是一個a標籤;點擊會切換第一個父級的router-view 中的頁面;還要注意格式和傳參
<li class="tab1"><router-link :to="{path:'box1/666'}">box1</router-link></li>

 // to的 值是一個對象 ,key 固定是path ,value 是 一個字符串;
 // 後面的666 是參數;它的前面不是 「 ?或者 & 」 而是 「 / 」;
  • 獲取參數的時候,直接去到參數的值是 this.$route.params.text (text是你定義路由時的 :text )

clipboard.png

  • 由於$route 是一個全局變量,你還能夠直接在html 中使用它的url參數
<div>{{$route.params.text}}</div>

定義一個多參數 路由的 url
clipboard.png

使用時的url多是這樣:

http://localhost:8080/#/apple/red/detail/fool
//後面的detail 是固定的。

獲取的方法是同樣的;

var a = $route.params;

//結果
a={
    color:red,
    type:fool
}

子路由的定義

{
  path:'/box1',
  component:box1,
  children:[
    {
      // 當 /box1/box1a匹配成功,
      // box1a會被渲染在 box1的 <router-view> 中
      path:'box1a', //這裏不加 「 / 」
      component:box1a
    },
    {
      path:'box1b',
      component:box1b
    }
  ]
},
// 要注意,以 / 開頭的嵌套路徑會被看成根路徑。 這讓你充分的使用嵌套組件而無須設置嵌套的路徑。



//在box1 中定義一行 

<router-view/>

//子組件 box1a 會自動渲染 到頁面中;

//若是添加兩個 router-link 組件 就能夠作tab切換(不建議用路由作tab)
//router-link 只會切換 第一個父級的 router-view 中的內容,因此不會刷新整個 box1 ;

<ul>
  <li class="tab1"><router-link :to="{path:'/box1/box1a'}">box1a</router-link></li>
  <li class="tab2"><router-link :to="{path:'/box1/box1b'}">box1b</router-link></li>
</ul>

//path 寫成 /box1/box1a  是用了根路徑(絕對路徑)的形式;
//若是寫成 {path:'box1a'} 則是相對路徑;這樣寫點擊 瀏覽器的url會寫成 http://localhost:8080/box1a
//由於頂級路由中是沒有定義 box1a 組件的;因此頁面空白;
帶參子路由
//路由定義
{
  path:'/box1/:text',
  component:box1,
  children:[
    {
      path:'box1a',
      component:box1a
    },
    {
      path:'box1b',
      component:box1b
    }
  ]
},


//跳轉到 box1 的寫法
 <li class="tab1"><router-link :to="{path:'box1/789'}">box1</router-link></li>

//box1 中渲染 box1a/b 的寫法
<ul>
  <li class="tab1"><router-link :to="{path:url1}">box1a</router-link></li>
  <li class="tab2"><router-link :to="{path:url2}">box1b</router-link></li>
</ul>

//初始化的時候生成url
 mounted: function () {
            this.parms = this.$route.params.text;
            this.url1 = '/box1/'+this.parms+'/box1a'
            this.url2 = '/box1/'+this.parms+'/box1b'
          },
  • router-link 中的to 屬性,若是隻是簡單的跳轉能夠直接寫成 to=「box1」;
  • 若是加上:綁定屬性;後面的屬性能夠是簡短的 表達式,所以上面的例子能夠簡化爲:
//路由定義
{
  path:'/box1/:text',
  component:box1,
  children:[
    {
      path:'box1a',
      component:box1a
    },
    {
      path:'box1b',
      component:box1b
    }
  ]
},


//跳轉到 box1 的寫法
 <li class="tab1"><router-link :to="{path:'box1/789'}">box1</router-link></li>

//box1 中渲染 box1a/ b 的寫法
//to 屬性會自動 運算出來;
<ul>
  <li class="tab1"><router-link :to="/box1/'+parms+'/box1a'">box1a</router-link></li>
  <li class="tab2"><router-link :to="'/box1/'+parms+'/box1b'">box1b</router-link></li>
</ul>
 


//初始化的時候生成url
 mounted: function () {
            this.parms = this.$route.params.text;
          },
具名路由

router-link 還能夠經過 name 指定跳轉(具名路由);經過例如 tag="li" 指定包裹標籤爲a覺得的tag;

children:[
        {
          path:'box1a',
          name:"nameBox",
          component:box1a
        }
        ]
        
 <router-link :to="{name:'nameBox'}" tag="li">nameBox</router-link>       
        
//結果:會生成li, 點擊router-view 會渲染出 box1a ;注意,path是不可缺乏的

用具名路由佈局(代替iframe的方式)

export default new Router({
  mode:'history',
  routes: [
    {
      path:'/index',
      components:{   //注意這裏是有 「 s 」 的
        default:vuexIndex,  //對應 <router-view/>
        left:vuexLeft,    // 對應<router-view name="left"></router-view>
        right:vuexRight  //對應<router-view name="right"></router-view>
      }
    }
  ]
})


<template>
  <div id="app">
    <router-view/>
    <div class="content">
      <router-view name="left"></router-view>
      <router-view name="right"></router-view>
    </div>
  </div>
</template>

//注意訪問的路徑 是在components 中定義的path ;

在某個頁面中定義具名路由

{
  path:'/layout',
  name:'layout',
  component:layout,
  children: [
    {
      path: '/',
      components:{
        default:main,
        leftView:leftView,
        topBar:topBar
      },
    }
  ]
}
路由的重定向
routes: [
    { path:'/', redirect:'/box' //路徑不存在就跳轉到box
    },
    { path: '/a', redirect: '/b' } //訪問a的路徑,都跳轉到b
]

vuex 狀態管理插件安裝

當vue項目中 組件過多並設計到功能某些數據的時候,管理數據變得複雜,VUEX 就是經過自身的一套數據管理流程幫助你管理 項目數據(狀態);

clipboard.png

安裝

cnpm install vuex --save

實例化

//在main.js 中

import Vuex from 'vuex'

Vue.use(Vuex);
let store = new Vuex.Store({  //Store 是vuex 方法
  state:{ //存放變量的地方
    total:0,
    appleTotal:0,
    bananaTotal:0,
  },
  mutations:{     //定義同步變量的方法
    aSetValue(state,value){
      state.appleTotal = value
      state.total = state.appleTotal + state.bananaTotal;
    },
    bSetValue(state,value){
      state.bananaTotal = value
      state.total = state.appleTotal + state.bananaTotal;
    }
  }
})


new Vue({
  el: '#app',
  store,   //在實例中 引入
  router,
  components: { App },
  template: '<App/>'
})

獲取值

vuex 的變量store 全局之後能夠經過如下語句獲取

this.$Store.state.total 

//組件中
 <h3>總價:<span>{{$store.state.total}}</span></h3>

還能夠經過設置getter函數 暴露對應的值

getters:{
    getTotal(state){
      return state.total
    }
  },

//組件中

<h3>總價:<span>{{$store.getters.getTotal}}</span></h3>

改變vuex變量的值

經過在組件中調用如下特定語句改變

methods:{
      add(){
        this.cnt += 1;
        //bSetValue 是在vuex實例的mutations中定義的方法
        //經過 bSetValue方法 去改變實例的值
        this.$store.commit('bSetValue',this.cnt*5) 
      },
      minus(){
        this.cnt-=1;
        this.$store.commit('bSetValue',this.cnt*5)
      }
    },
    
 // 在vuex 中只有 commit 方法能夠改變數據;這裏是不建議直接在組件中調用 cmmit 的
 // 由於cmmit中只能是同步操做;可是交互每每須要同步數據庫
 // 更加好的方法是在 actions的方法 中觸發 commit;actions 方法支持異步操做;
 // 全部涉及 後端API 的接口都是放在actions 中進行

actions 屬性

mutations:{
        aSetValue(state,value){
          state.appleTotal = value
          state.total = state.appleTotal + state.bananaTotal;
        },
        bSetValue(state,value){
          state.bananaTotal = value
          state.total = state.appleTotal + state.bananaTotal;
        }
  },
actions:{ 
         // 定義doAfn 方法,間接觸發 aSetValue;
         // 在doAfn 方法是能夠用ajax 的
        doAfn(context,price){
          context.commit('aSetValue',price)
        },
        doBfn(context,price){
          context.commit('bSetValue',price)
        }
  }

組件中的觸發方式 經過 dispatch 觸發doAfn;

this.$store.dispatch('doAfn',val*10)

vuex 的 modules

Vuex 容許咱們將 store 分割成模塊(module)。每一個模塊擁有本身的 state、mutation、action、getter、甚至是嵌套子模塊——從上至下進行一樣方式的分割:

官方文檔代碼

const moduleA = {
  state: { ... },
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}

const moduleB = {
  state: { ... },
  mutations: { ... },
  actions: { ... }
}

const store = new Vuex.Store({
  modules: {
    a: moduleA,
    b: moduleB
  }
})

store.state.a // -> moduleA 的狀態
store.state.b // -> moduleB 的狀態

官方的文檔組織建議:

  • 應用層級的狀態應該集中到單個 store 對象中。
  • 提交 mutation 是更改狀態的惟一方法,而且這個過程是同步的。
  • 異步邏輯都應該封裝到 action 裏面。

對於大型應用,咱們會但願把 Vuex 相關代碼分割到模塊中。下面是項目結構示例:

├── index.html
├── main.js
├── api
│   └── ... # 抽取出API請求
├── components
│   ├── App.vue
│   └── ...
└── store
    ├── index.js          # 咱們組裝模塊並導出 store 的地方
    ├── actions.js        # 根級別的 action
    ├── mutations.js      # 根級別的 mutation
    └── modules
        ├── cart.js       # 購物車模塊
        └── products.js   # 產品模塊

https://vuex.vuejs.org/zh-cn/...

一個 vuex 小例子

clipboard.png

父組件
<template>
  <div class="left">
    <h1>我是left</h1>
    <div class="tips">(我是{{text?text:'路由的方式'}}來的)</div>
    <div class="left-content">
      <div class="item-box">
        <apple/>
        <banana/>
      </div>
      <div class="price">
        <h3>總價:<span>{{$store.getters.getTotal}}</span></h3>
      </div>
    </div>
  </div>
</template>
<script>
    import apple from '@/components/vuex/apple.vue'
    import banana from '@/components/vuex/banana.vue'
    export default {
        data: function () {
            return {
            }
        },
      components:{
        apple,
        banana
      },
      props:['text']
    }
</script>
<style scoped>
  h1{
    text-align: center;
  }
  .left-content{
    margin-top: 20px;
  }
  .item-box{
    width: 48%;
    display: inline-block;
  }
  .price{
    width: 50%;
    display: inline-block;
    vertical-align: bottom;
  }
  .item{
    font-size: 15px;
    margin-top: 20px;
    margin-left: 20px;
  }

</style>
apple 組件
<template>
  <div class="item">
    <span>蘋果</span>
    <button @click="add">add</button>
    <button @click="minus">minus</button>
    <input v-model.number.lazy="cnt">
    <span v-if="noEnough" class="red">庫存不夠</span>
  </div>
</template>
<script>
    module.exports = {
      data: function () {
          return {
            cnt:0,
            noEnough:false
          }
      },
      methods:{
          add(){
            this.cnt += 1;
          },
          minus(){
            this.cnt-=1;
          }
      },
      watch:{
          cnt(val){
            if(val>10){
              this.noEnough = true;
            }
            if(val<0){
              this.cnt=0
            }
            if(val<10){
              this.noEnough = false;
            }
            this.$store.dispatch('doAfn',val*10)
          }
      }
    }
</script>
<style scoped>
  .item input{
    width: 40px;
    display: inline-block;
  }
  .red{
    color:red;
    font-size: 12px;
  }
</style>
banana 組件
<template>
  <div class="item">
    <span>香蕉</span>
    <button @click="add">add</button>
    <button @click="minus">minus</button>
    <input v-model.number.lazy="cnt">
    <span v-if="noEnough" class="red">庫存不夠</span>
  </div>
</template>
<script>
  module.exports = {
    data: function () {
      return {
        cnt:0,
        noEnough:false
      }
    },
    methods:{
      add(){
        this.cnt += 1;
      },
      minus(){
        this.cnt-=1;
      }
    },
    watch:{
      cnt(val){
        if(val>10){
          this.noEnough = true;
        }
        if(val<10){
          this.noEnough = false;
        }
        if(val<0){
          this.cnt=0
        }
        this.$store.dispatch('doBfn',val*5)
      }
    }
  }
</script>
<style scoped>
  .item input{
    width: 40px;
    display: inline-block;
  }
  .red{
    color:red;
    font-size: 12px;
  }
</style>
main.js
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import Vuex from 'vuex'
import App from './App'
import router from './router'


Vue.config.productionTip = false

Vue.use(Vuex);
let store = new Vuex.Store({
  state:{
    total:0,
    appleTotal:0,
    bananaTotal:0,
  },
  getters:{
    getTotal(state){
      return state.total
    }
  },
  mutations:{
    aSetValue(state,value){
      state.appleTotal = value
      state.total = state.appleTotal + state.bananaTotal;
    },
    bSetValue(state,value){
      state.bananaTotal = value
      state.total = state.appleTotal + state.bananaTotal;
    }
  },
  actions:{
    doAfn(context,price){
      context.commit('aSetValue',price)
    },
    doBfn(context,price){
      context.commit('bSetValue',price)
    }
  }
})



/* eslint-disable no-new */
new Vue({
  el: '#app',
  store,
  router,
  components: { App },
  template: '<App/>'
})
相關文章
相關標籤/搜索