图片来源: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 里函数重名了会两边都执行