Vue 入門指南

圖片來源:pixiv 63737968javascript

學習 vue 過程當中的筆記,未完更新中 ... 完整【示例代碼】請去個人 GitHub 倉庫 pero-vue 查看php

更新於 2017.12.24
首發於夏味的博客: xiaweiss.comcss

1. 環境配置

注意本筆記使用的版本爲當時的最新穩定版html

  • Vue 2.x
  • webpack 2
  • node 8.9.0
  • npm 5.6.0

1.1 使用到的技術文檔

1.2 須要安裝的相關依賴,將來不必定正確,以官方文檔爲準

首先須要安裝 node, 而後使用命令 npm install 依賴名稱 來安裝vue

  • babel-core
  • babel-loader
  • babel-preset-env
  • babel-preset-stage-2 (使用 import() 時才須要)
  • css-loader
  • html-webpack-plugin
  • style-loader
  • vue
  • vue-loader
  • vue-template-compiler
  • webpack
  • webpack-dev-server
  • vue-router
  • axios
  • vuex(選用)

1.3 webpack 配置項簡介

項目根目錄下,建立 webpack.config.js 配置文件java

const path = require('path'); //node 內置模塊
const HtmlWebpackPlugin = require('html-webpack-plugin'); // 用於在 html 頁面裏自動插入資源引用的標籤
const webpack = require('webpack'); //使用 webpack 內置的插件時,須要引入 webpack

module.exports = {
  entry: {
    index: './src/index.js' // 入口文件
    // bbb: './src/bbb.js' // 能夠多個入口文件
  },
  output: {
    path: path.resolve('./dist'), // 輸出路徑,必須是絕對路徑.path.resolve是nodeJS模塊方法,把相對路徑轉爲絕對路徑
    // 或者使用語法__dirname +'/dist' 或 path.join(__dirname,'dist')
    // __dirname 表示當前模塊的目錄的絕對路徑(並不是全局變量,等價於path.dirname(__filename))
    // path.join用於處理鏈接路徑時,統一不一樣系統路徑符\和/問題。
    // publicPath: '/assets/', // 發佈路徑,填寫此項後,打包後文件路徑再也不是相對路徑,而是基於服務器根目錄的路徑,
    filename: 'js/[name].js', // [name] 表示塊的名稱,輸出文件名,能夠包含路徑
    chunkFilename: 'js/[name].js' //webpack 分割後的文件,[id] 表示塊的編號。[name] 表示塊的名稱,沒有名稱時會自動使用編號
  },
  resolve: {
    alias: {
      vue$: 'vue/dist/vue.esm.js' // 默認是運行時構建,這裏使用了template,必須用運行+編譯時構建
    }
  },
  module: {
    rules: [
      { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader' }, //使用 babel 對 js 轉譯
      {
        test: /\.vue$/,
        exclude: /node_modules/,
        loader: 'babel-loader!vue-loader'
      },
      // 先使用 vue-loader對 vue 文件轉譯
      { test: /\.css$/, loader: 'style-loader!css-loader' }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      filename: 'index.html', // 輸出文件名,能夠包含路徑
      template: 'src/index.html', // 模板文件位置
      inject: 'body', //插入位置,也能夠寫 head
      hash: true, // 在文件名後面加 hash 值,默認false
      chunks: ['index'] // 表示插入的塊,對應 webpack 入口文件中的 index,不寫會插入全部的入口文件
    }),
    new webpack.HotModuleReplacementPlugin() //若是 devServer 的配置項裏 hot:true ,則須要配置此項
  ],
  externals: {
    vue: 'Vue' //打包時排除 vue,vue內容不會被寫入js。
    //注意左邊的 vue 是模塊名,右邊的 Vue 是不使用構建工具時的標準變量名,必須是Vue,與import的變量名無關
  },
  devServer: {
    contentBase: './dist/', //表示靜態資源(非webpack編譯產生)文件的目錄位置,
    //這個目錄的資源會被放到一樣當成服務器根目錄去
    //遇到同名文件,webpack編譯後產生的文件優先級更高
    compress: true, //是否壓縮
    port: 9000, //端口號
    host: '0.0.0.0', //默認是localhost,若是想被外部訪問,這樣設置
    historyApiFallback: true, //當使用 history 模式路由時,設置爲true,404頁面會被重定向到主頁,
    hot: true // 熱替換,能夠在不刷新頁面的狀況下更新修改後數據,也能夠配置在package.json 的 scripts 裏,加 --hot參數
  }
};
複製代碼

項目根目錄下,建立 .babelrc 配置文件node

babel-preset-env 至關於 es2015 ,es2016 ,es2017 及最新版本webpack

{
  "presets": ["env"]
}
複製代碼

1.4 運行

package.json 文件裏添加配置ios

{
  // ...其餘參數
  "scripts": {
    "build": "webpack",
    "dev": "webpack-dev-server"
  }
}
複製代碼

而後使用 npm run dev 來啓動 server 使用 npm run build 來打包輸出git

這裏的 build 是本身起的。寫爲 "build": "webpack -p", 打包後時壓縮代碼

1.5 webpack-dev-server 熱替換

熱替換指,在不刷新頁面的狀態下,把修改後的結果更新到頁面上

有兩種配置方式

  • webpack CLI 方式:

package.json 文件裏添加配置

{
  "scripts": {
    "dev": "webpack-dev-server --hot"
  }
}
複製代碼
  • webpack 配置方式:

webpack.config.js 文件裏添加配置

const webpack = require('webpack');
module.exports = {
  // 其餘配置...
  plugins: [
    // 其餘配置...
    new webpack.HotModuleReplacementPlugin()
  ],
  devServer: {
    // 其餘配置...
    hot: true
  }
};
複製代碼

package.json 文件裏依然是 "dev": "webpack-dev-server"

2. vue 語法

2.1 基本用法

<div id="app">
  {{ name }}
</div>
複製代碼
import Vue from 'vue';

let param = {
  el: '#app',
  data: {
    name: 'hello vue'
  }
  //注意這裏的 data 也可使用這種語法
  //data() {
  // return {name:'hello vue'}
  //}
};
new Vue(param);
複製代碼

2.2 基本的組件

<div class="container">
  <my-name></my-name>
</div>
複製代碼
import Vue from 'vue';
// 這是一個組件
let meName = {
  template: '<div>{{name}}</div>', // 組件的模板,渲染後會替換掉 <my-name></my-name>
  data() {
    return {
      name: 'xiawei'
    }; // 組件中 data 必須是函數,數據 return 出去,不能夠寫爲 data:{name:'xiawei'}
  }
};

new Vue({
  el: '.container',
  components: {
    'my-name': meName
  }
});
複製代碼

2.3 vue 文件形式的組件

爲了方便,可使用 vue 文件 來封裝組件,能夠認爲一個 vue 文件是一個組件,子組件繼續使用其餘 vue 文件 引入。

index.js

import Vue from 'vue';
import myname from './components/myname.vue';

new Vue({
  el: '.container',
  components: {
    'my-name': myname
  }
});
複製代碼

myname.vue

<template>
  <div>
    {{name}}
  </div>
</template>
<script> export default { data() { return { name: 'xiawei' }; } }; </script>
<style lang="css" scoped> </style>
複製代碼

template 裏,必須用一個 div 或者一個其餘標籤,包裹住全部的 html 標籤

默認 lang="css" 能夠省略,須要使用 sass 時,能夠寫 lang="scss" 等

scoped 是 vue 提供的屬性 表示這裏的樣式只能在本組件內生效

2.4 組件通信

2.4.1 使用 props 給組件傳參

<myname value="xiawei"></myname>
複製代碼
<template>
  <div>
    {{value}}
  </div>
</template>
<script> export default { props:['value'] }; </script>
複製代碼

2.4.2 訪問其餘組件,獲取參數

能夠經過 $parent 訪問父組件,$children 訪問子組件

user-login 有三個子組件,部分代碼以下

<template>
  <div id="user">
        <h2>User Login</h2>
        <form>
            <user-name></user-name>
            <user-pass></user-pass>
            <user-submit></user-submit>
        </form>
    </div>
</template>
複製代碼

這時在 user-submit 組件

<template>
    <div>
      <button v-on:click="test">submit</button>
      <!-- v-on:click="test" 表示點擊事件時,觸發 test 函數 -->
    </div>
</template>
<script> export default { methods: { test() { this.$parent.$children[0] //訪問 user-name 組件 this.$parent.$children[1] //訪問 user-pass 組件 //獲取 user-name 組件 data 中 username 的值 this.$parent.$children[0].username } } }; </script>
複製代碼

要區分子組件是第幾個,並不方便,可使用 ref 來解決這個問題

相關代碼修改成如下便可

<user-name ref="uname"></user-name>
<user-pass ref="upass"></user-pass>
複製代碼
//獲取 user-name 組件 data 中 username 的值
this.$parent.$refs.uname.username;
複製代碼

2.4.3 父子組件自定義事件通信

父組件 user-login.vue 裏,給子組件 user-name 設置自定義事件 updateUserName

這個事件是綁定在 user-name 組件上的,在 組件對象 .$listeners裏能夠查看到,能夠用 組件對象 .$emit 來觸發

$emit 觸發時,參數 1 是事件名,後幾個參數能夠傳給事件對象(相似 jQuery 的trigger 方法)

<template>
  <user-name ref="uname" v-on:updateUserName="setUserName"></user-name>
</template>
<script> import username from './user/user-name.vue'; export default { data() { return { username: '' }; }, components: { 'user-name': username }, methods: { setUserName(uname) { this.username = uname; } } }; </script>
複製代碼

子組件 user-name.vue,當輸入框內容改變,觸發 change 事件

而後執行了 $emit 來觸發 updateUserName事件,this.username 做爲參數傳給了updateUserName 事件

<template>
  <input type="text" v-model="username" v-on:change="userNameChange">
</template>
<script> export default { data() { return { username: '' }; }, methods: { userNameChange() { this.$emit('updateUserName', this.username); } } }; </script>
複製代碼

2.5 v-if,路由原理

v-if 主要用於渲染模板,下面代碼

當變量 isadmin 爲 true 時,只顯示 Admin Login

反之,只顯示User Login

注意,程序依據 isadmin == true 的結果來判斷

<template>
  <h2 v-if="isadmin">Admin Login</h2>
  <h2 v-else>User Login</h2>
</template>
複製代碼

在 index.js 添加下面代碼

當瀏覽器路徑 hash 部分(#號及其後面的部分)變化時,會觸發 hashchange 事件

判斷 hash 的值,各類值走本身的業務邏輯,就能夠切換頁面、改變數據,這就是路由原理

window.onhashchange = function() {
  if (window.location.hash === '#admin') {
    myvue.$children[0].$data.isadmin = true;
  } else {
    myvue.$children[0].$data.isadmin = false;
  }
};
複製代碼

相關須要掌握的還有 v-for,參見官方文檔

2.6 計算屬性 computed

計算屬性和 data 裏的普通屬性調用時相同的,但定義時不一樣

計算屬性使用函數定義,return 的值,就是計算屬性的值

當計算屬性內的其餘變量的值發生變化時,函數就會執行,運算獲得新的值

因此計算屬性的值是依賴其餘變量的,它沒有初始值,不能夠在 data 裏聲明

下面的例子,經過計算屬性比對輸入的值來篩選 fav.class2

filter 數組方法 返回經過篩選條件的新數組,當 return true 時符合條件被選入。

indexOf 字符串方法 返回符合條件的字符串序號,若是找不到時,會返回數字 -1,能夠用來匹配字符串相似的方法,還有 indexOf 數組方法

<template>
  <input type="text" v-model="inputText" class="form-control">
  <table v-if="isShow()" class="table">
    <thead>
      <tr>
        <th>
          type 1
        </th>
        <th>
          type 2
        </th>
      </tr>
    </thead>
    <tbody>
      <tr v-for="fav in getFavs">
        <td>{{ fav.class1 }}</td>
        <td>{{ fav.class2 }}</td>
      </tr>
    </tbody>
  </table>
</template>
<script> export default { data() { return { favs: [ { class1: 'web', class2: 'js' }, { class1: 'pro', class2: 'java' } ], inputText: '' }; }, methods: { isShow() { return !(this.inputText == ''); } }, computed: { getFavs() { return this.favs.filter(abc => { return abc.class2.indexOf(this.inputText) >= 0; }); } } }; </script>
複製代碼

2.6.1 計算屬性配合過濾方法

vue 2.x 的過濾器方法,與 vue 1.x 語法不一樣,並不適合和 v-for 配合使用,計算屬性配合過濾方法來實現。

上節的例子,更復雜一點,數組的狀況 ( 和上面重複的部分沒寫出來,完整代碼請查看github)

getFavs 決定展現第幾條數據,filterClass2 負責對展現出來的數據篩選

<template>
  <tr v-for="fav in getFavs">
    <td>{{ fav.class1 }}</td>
    <td><a v-for="code in filterClass2(fav.class2)">{{ code }} </a></td>
  </tr>
</template>
<script> export default { data() { return { favs: [ { class1: 'web', class2: ['js', 'html', 'css', 'jssdk'] }, { class1: 'pro', class2: ['java'] } ] }; }, methods: { filterClass2(class2) { return class2.filter(v => { return v.indexOf(this.inputText) >= 0; }); } }, computed: { getFavs() { return this.favs.filter(abc => { return abc.class2.filter(code => { return code.indexOf(this.inputText) >= 0; }).length > 0; }); } } }; </script>
複製代碼

2.7 路由

2.7.1 路由的基本使用

首先 npm 安裝依賴官方的路由插件 vue-router

index.html

<div class="container">
    <page-nav></page-nav>
    <router-view></router-view>
  </div>
複製代碼

index.js 文件

import Vue from 'vue';
import VueRouter from 'vue-router'; // 引入插件
Vue.use(VueRouter); // 使用插件

import pagenav from './components/page-nav.vue';
import newslist from './components/news-list.vue';
import userlogin from './components/user-login.vue';

const routerConfig = new VueRouter({
  routes: [
    { path: '/news', component: newslist },
    { path: '/login', component: userlogin }
  ]
});

// 全局註冊公共組件(也能夠像原先註冊子組件的方式來作)
Vue.component('page-nav', pagenav);

let myvue = new Vue({
  el: '.container',
  router: routerConfig
  // 路由中引入過子組件了,因此此處不須要再引入子組件
  // components: {
  // 'page-nav': pagenav,
  // 'user-login': userlogin
  // }
});
複製代碼

page-nav.vue 的部分代碼

推薦使用 router-link 語法做爲切換按鈕,它默認會渲染成 a 標籤也可使用 a 標籤來作

當某個 router-link 被點擊選中時,vue 會給它的 html 標籤添加上 class router-link-active

能夠經過給 .router-link-active寫 css 樣式, 來給選中的 router-link 添加樣式

<template>
  <ul class="nav navbar-nav">
    <li><router-link to="/login">login</router-link></li>
    <li><a href="#/news">News</a></li>
  </ul>
</template>
複製代碼

2.7.2 axios 的基本使用

引入

import axios from 'axios';
複製代碼

如需全局引入,能夠再加上下面這句,組件內調用時使用 this.$axios 便可

Vue.prototype.$axios = axios;
複製代碼

get 請求

axios
  .get('http://localhost:8000/test.php', {
    params: {
      ID: 12345
    }
  })
  .then(response => {
    alert(response.data);
  });
複製代碼

post 請求參數 axios 默認轉爲 json 格式

axios
  .post('http://localhost:8000/test.php', { name: 'xiawei', age: 20 })
  .then(response => {
    alert(response.data);
  });
複製代碼

鍵值對方式(php 用 $_POST 能夠取到值)

axios.post('http://localhost:8000/test.php', 'name=xiawei&age=20');
複製代碼

也可使用 node 內置模塊來轉換格式

import querystring from 'querystring';
axios.post(
  'http://localhost:8000/test.php',
  querystring.stringifyname({ name: 'xiawei', age: 20 })
);
複製代碼

這部分的 php 代碼,是放置在項目根目錄的 test.php 文件

<?php
  //指定容許其餘域名訪問
  header('Access-Control-Allow-Origin:*');

  //響應類型
  header('Access-Control-Allow-Methods:GET,POST,PUT');
  header('Access-Control-Allow-Headers:x-requested-with,content-type');

  echo file_get_contents('php://input');//顯示接收到的原始數據
  var_export($_POST);
  echo 'hello php';
複製代碼

Mac 內置了 php,直接啓動 php 內置服務:到項目根目錄下,Terminal 裏執行下面命令便可

windows 下載 php 後,把 php 目錄添加到系統環境變量 PATH 裏後,一樣執行下面命令

php -S 0.0.0.0:8000
複製代碼

2.7.3 動態加載新聞詳細頁

在新聞列表頁,點擊標題跳轉到新聞詳細頁,動態加載新聞內容

index.js 部分代碼

import axios from 'axios';
Vue.prototype.$axios = axios;

const routerConfig = new VueRouter({
  routes: [
    { path: '/', component: newslist },// 設置首頁
    { path: '/news', component: newslist, name: 'newslist' },// 能夠給路由設置別名 name
    { path: '/news/:newsid', component: newsdetail, name: 'newsdetail' },// 若是須要參數,使用冒號的來作佔位符
    { path: '/login', component: userlogin, name: 'userlogin' }
  ]
});
複製代碼

new-list.vue 部分代碼

<template>
  <div class="page-header" v-for="news in newslist">
    <h2><router-link :to="{ name: 'newsdetail', params: {newsid: news.newsid} }">{{news.title}}</router-link> <small>{{news.pubtime}}</small></h2>
    <!-- 這裏的 newsdetail 以及 params 裏左邊的 newsid 是和路由定義時的相關參數對應 -->
    <p>{{news.desc}}</p>
  </div>
</template>
複製代碼

news-detail.vue 部分代碼

<template>
  <div>
    <h2> {{ newstTitle }} <small>{{ newsDate }}</small></h2>
    <p>{{ newsContent }}</p>
  </div>
</template>
<style> </style>
<script> export default { // 生命週期,組件被建立時執行 created() { this.$axios .get('http://localhost:8000/news.php?newsid='+ this.$route.params.newsid) // 能夠經過全局變量 $route.params 來訪問路由裏的變量獲取到新聞編號101 .then(response => { this.newstTitle = response.data.title; this.newsDate = response.data.pubtime; this.newsContent = response.data.desc; }); } }; </script>
複製代碼

經過全局變量 $route 來訪問路由裏的各類數據

例如 $route.params.newsid 能夠得到路由佔位符 :newsid 處的新聞編號值 101

2.8 異步加載和 webpack 代碼分割

當項目比較大的時候,可使用異步加載組件的方式來按需加載,而不是一次性加載所有組件。

還能夠配合 webpack 代碼分割功能,把打包後的 js,分割成多個 js 文件,作到按需引用。

以前的引入組件的方式是

import userlogin from './components/user-login.vue';
複製代碼

使用 vue 異步加載的方式引入

var userlogin = function(resolve) {
  resolve(require('./components/user-login.vue'));
};
複製代碼

使用 ES2015 語法,而且簡化參數名,能夠寫爲

const userlogin = r => {
  r(require('./components/user-login.vue'));
};
複製代碼

結合 webpack 代碼分割功能後

const userlogin = r => {
  require.ensure([], () => {
    r(require('./components/user-login.vue'));
  });
};
複製代碼

若是須要把某幾個組件打包爲一組,給它們的 require.ensure() (文檔1文檔2)添加最後一個參數(例如'aaa'),且值相同

require.ensure([], () => {
    r(require('./components/user-login.vue'));
  },'aaa');
複製代碼

也可使用 webpack + ES2015 語法來進行代碼分割

import() (文檔) 是 ES2015 草案的語法,因此使用時須要 babel 轉譯

babel 配置裏須要添加草案語法的轉譯 presets stage-2 ,npm 安裝依賴 babel-preset-stage-2

.babel 文件,注意配置的數組裏,presets 解析的順序是從右到左的,先執行 stage-2

{
  "presets": ["env", "stage-2"]
}
複製代碼
const userlogin = () => import('./components/user-login.vue');
// 也就是 function() { return import('./components/user-login.vue')};
複製代碼

把某幾個文件打包爲一組時,使用這個語法

const userlogin = () => import(/* webpackChunkName: "aaa" */'./components/user-login.vue');
複製代碼

最後分割後的文件名,能夠在 webpack 配置裏 output 配置項裏添加 chunkFilename 配置項來控制

output: {
  filename: 'js/[name].js', // [name] 表示塊的名稱,輸出文件名,能夠包含路徑
  chunkFilename: 'js/[name].js'
  //webpack 分割後的文件,[id] 表示塊的編號。[name] 表示塊的名稱,沒有名稱時會自動使用編號
},
複製代碼

2.9 開發插件

有時現有的插件並不能知足本身的業務需求,這時須要本身開發插件

2.9.1 自定義指令

在 src 文件夾下新建一個 js 文件,好比命名爲 plugin.js

export default {
  install(Vue) {
    // 添加實例方法
    Vue.prototype.$name = 'xiawei';// 能夠在組件內使用 this.$name 取到值 'xiawei'

    // 這裏添加時方法來檢測用戶名是否合法,6~20位合法,不然顯示提示
    Vue.prototype.checkUserName = value => {
      if (value == '') return true;
      return /\w{6,20}/.test(value);
    };
    // 能夠在組件內使用 this.checkUserName(’‘’)

    // 添加全局自定義指令 v-uname
    Vue.directive('uname', {
      bind() {
        console.log('begin');
      },
      update(el, binding, vnode) {
        vnode.context[binding.expression] = !/\w{6,20}/.test(el.value);
      }
    });
  }
};
複製代碼

directive (文檔) 裏的生命週期裏的三個參數:

  • el 參數表示指令所綁定的元素,能夠用來直接操做 dom
  • binding 參數表示綁定對象,binding.expression 取到傳入的表達式,binding.value 能夠取到表達式的值 這裏的表達式也能夠是函數名,取到的值是函數體,binding.oldValue
  • vnode 參數表示 Vue 編譯生成的虛擬節點

關於官方文檔裏,添加全局方法或屬性 Vue.myGlobalMethod 和添加實例方法和屬性 Vue.prototype.$myMethod 兩者區別

全局方法或屬性使用 Vue.名稱 來調用,而實例方法和屬性使用 (實例化後的 Vue 對象).名稱 來調用,也就是組件內的常見 this.名稱 來調用,即便看起來名稱同樣的Vue.aaaVue.prototype.aaa也是兩個不一樣的變量

具體能夠參見這篇文章:js裏面的實例方法和靜態方法

index.js 內加載插件

import plugin from './plugin.js';
Vue.use(plugin);
複製代碼

user-name.vue 添加 v-uname 和 label 元素

<input type="text" v-model="username" v-uname="showErrorLabel" v-on:change="userNameChange" class="form-control" :placeholder="placeholder">
<label v-if="showErrorLabel" class="label label-danger">Please check your username and try again</label>
複製代碼

2.9.2 手動掛載子組件

上面只是控制變量,並非很方便,能夠經過插件動態插入移除提示框

export default {
  install(Vue) {
    // 建立變量,定義初始值
    Vue.errorLabel = null;
    Vue.hasErrorLabel = false;
    // 這個全局變量來標記是否插入了 label,給初始值時必須放在 update 外面

    // 添加全局自定義指令 v-uname
    Vue.directive('uname', {
      bind(el) {
        let error = Vue.extend({
          template:
            '<label class="label label-danger">Please check your username and try again</label>'
        });

        Vue.errorLabel = (new error()).$mount().$el;
        // $mount() 方法不填參數時,表示把 vm 實例對象變成一個能夠掛載的狀態,這時就能夠訪問到 $el 獲取到元素了
      },
      update(el, binding, vnode) {
        // 這裏每次 update 是從組建原始的狀態 update 的,因此不會重複插入多個
        if (/\w{6,20}/.test(el.value)) {
          if (Vue.hasErrorLabel) {
            el.parentNode.removeChild(Vue.errorLabel);
            Vue.hasErrorLabel = !Vue.hasErrorLabel;
          }
        } else {
          if (!Vue.hasErrorLabel) {
            el.parentNode.appendChild(Vue.errorLabel);
            Vue.hasErrorLabel = !Vue.hasErrorLabel;
          }
        }
      }
    });
  }
};
複製代碼

user-name.vue 組件裏,這時不須要寫 label 元素,只須要寫入 v-uname 便可

<input type="text" v-model="username" v-uname v-on:change="userNameChange" class="form-control" :placeholder="placeholder">
複製代碼

2.9.3 插件裏包含子組件

上一小節的代碼,當有多個 input 元素時,就會出現其餘元素顯示不正常的狀況,緣由是多個標籤共用了同一個 Vue.hasErrorLabel

因此當插件不只僅處理數據時,還須要獨立的處理 dom 元素時,使用子組件的方式更加合理,它們是互相獨立的

export default {
  install(Vue) {
    Vue.component('p-username', {
      template: `<div> <input class="form-control" type="text" v-model="textValue" /> <label class="label label-danger" v-if="showErrorLabel">Please check your username and try again</label> </div>`,
        // 這裏使用了 ES2015 的模板字符串語法
      data() {
        return {
          textValue: ''
        };
      },
      computed: {
        showErrorLabel() {
          return !(/\w{6,20}/.test(this.textValue) || this.textValue == '');
        }
      }
    });
  }
};
複製代碼

其中,爲了方便 template 裏使用了 ES2015 的模板字符串語法(參考文檔

user-name.vue 文件(不須要寫 input 元素)

<p-username></p-username>
複製代碼

2.10 全局狀態管理 vuex

應遵循如下規則

  • 應用級的狀態集中放在 store 中
  • 計算屬性使用 getters
  • 改變狀態的方式是提交 mutations,這是個同步的事務
  • 異步邏輯應該封裝在 action 中

也便是與組件的概念相對應的 store -> data getters -> computed mutations/actions -> methods

2.10.1 vuex 基本使用

npm 安裝依賴 vuex

index.js

import Vuex from 'vuex';
Vue.use(Vuex);

const vuex_store = new Vuex.Store({
  state: {
    user_name: ''
  },
  mutations: {
    showUserName(state) {
      alert(state.user_name);
    }
  }
});
複製代碼

賦值:user-name.vue 組件中使用

<div class="page-header" v-for="news in $store.state.newslist">
複製代碼
this.$store.state.user_name = this.username;
複製代碼

觸發:user-submit.vue 組件中使用

this.$store.commit('showUserName');
複製代碼

便可完成簡單的輸入用戶名,點提交按鈕後 alert 出用戶名

2.10.2 vuex 計算屬性

vuex 裏的計算屬性使用的是 getters,用法和 組件裏的計算屬性 computed 相似,只是被觸發的時機不一樣

從數據裏展現沒有刪除的新聞展現

index.js

const vuex_store = new Vuex.Store({
  state: {
    user_name: '',
    newslist: []
  },
  mutations: {
    showUserName(state) {
      alert(state.user_name);
    }
  },
  getters: {
    getNews(state) {
      return state.newslist.filter(news => !news.isdeleted);
    }
  }
});
複製代碼

news-list.vue

<div class="page-header" v-for="news in $store.getters.getNews">
複製代碼
export default {
  created() {
    if (this.$store.state.newslist.length == 0) {
      this.$axios.get('http://localhost:8000/news.php').then(response => {
        this.$store.state.newslist = response.data;
      });
    }
  }
};
複製代碼

2.10.3 actions

mutations 是同步執行的,裏面不能放異步執行的東西 actions 裏放異步執行的,異步執行完後,去手動觸發 mutations

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  },
  actions: {
    increment (context,param) {
      // 異步業務 param -> param2
      context.commit('increment',param2);
    }
  }
})
複製代碼

組件內觸發

this.$store.dispatch('increment',param);
複製代碼

2.10.4 把業務按模塊分類

以前寫的 index.js 是這樣

const vuex_store = new Vuex.Store({
  state: {
    user_name: '',
    newslist: []
  },
  mutations: {
    showUserName(state) {
      alert(state.user_name);
    }
  },
  getters: {
    getNews(state) {
      return state.newslist.filter(news => !news.isdeleted);
    }
  }
});
複製代碼

按模塊分離後

index.js

import news_module from './store/news.js';
import users_module from './store/users.js';

const vuex_store = new Vuex.Store({
  modules: {
    news: news_module,
    users: users_module
  }
});
複製代碼

news.js

export default {
  state: {
    newslist: []
  },
  getters: {
    getNews(state) {
      return state.newslist.filter(news => !news.isdeleted);
    }
  }
}
複製代碼

users.js

export default {
  state: {
    user_name: ''
  },
  mutations: {
    showUserName(state) {
      alert(state.user_name);
    }
  }
}
複製代碼

分離後,注意相關模塊裏的 this.$store.state

按業務模塊名分別改成 this.$store.state.newsthis.$store.state.users

注意不一樣業務模塊裏,getters 裏函數重名了會報錯, mutations 裏函數重名了會兩邊都執行

推薦課程:VUE.JS+PHP 先後端分離實戰視頻電商網站

歡迎添加我我的微信,互相學習交流
相關文章
相關標籤/搜索