圖片來源:pixiv 63737968
學習 vue 過程中的筆記,未完更新中 ... 完整【示例程式碼】請去我的 GitHub 倉庫 pero-vue 檢視
更新於 2017.12.24
首發於夏味的部落格: xiaweiss.com
1. 環境配置
注意本筆記使用的版本為當時的最新穩定版
- Vue 2.x
- webpack 2
- node 8.9.0
- npm 5.6.0
1.1 使用到的技術文件
1.2 需要安裝的相關依賴,未來不一定正確,以官方文件為準
首先需要安裝 node, 然後使用命令 npm install 依賴名稱
來安裝
- 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
配置檔案
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
配置檔案
babel-preset-env
相當於 es2015 ,es2016 ,es2017 及最新版本
{
"presets": ["env"]
}
複製程式碼
1.4 執行
package.json 檔案裡新增配置
{
// ...其他引數
"scripts": {
"build": "webpack",
"dev": "webpack-dev-server"
}
}
複製程式碼
然後使用 npm run dev
來啟動 server 使用 npm run build
來打包輸出
這裡的 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.aaa
和Vue.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.news
、this.$store.state.users
注意不同業務模組裡,getters 裡函式重名了會報錯, mutations 裡函式重名了會兩邊都執行