使用vue脚手架
初始化脚手架
说明
Vue脚手架是Vue官方提供的标准化开发工具(开发平台)。
目前,最新的版本是5.x。
文档:https://cli.vuejs.org/zh/
具体步骤
安装vue脚手架(仅第一次执行):
npm install -g @vue/cli
通过脚手架创建项目:
vue create xxx

进入项目:
cd xxx
启动项目:
npm run serve
vue脚手架项目结构
|-- README.md # 应用描述说明
|-- babel.config.js # bable的配置文件
|-- package.json # 应用包配置文件
|-- .gitignore # git版本管理忽略的配置
|-- public
| |-- favicon.ico
| `-- index.html # 主页面文件
|-- src
| |-- App.vue #
| |-- assets
| | `-- logo.png
| |-- components
| | `-- HelloWorld.vue #示例的vue文件
| `-- main.js # 应用入口js
`-- yarn.lock
应用示例
示例:
目录结构:
.
|-- README.md
|-- babel.config.js
|-- package.json
|-- public
| |-- favicon.ico
| `-- index.html
|-- src
| |-- App.vue
| |-- assets
| | `-- logo.png
| |-- components
| | |-- School.vue
| | `-- Student.vue
| `-- main.js
`-- yarn.lock
components/School.vue
<template>
<div class="demo">
<h2>学校名称:{{ name }}</h2>
<h2>学校地址:{{ address }}</h2>
<button @click="showName">点我提示学校名称</button>
</div>
</template>
<script>
export default {
name: 'School',
data () {
return {
name: '江苏师范大学',
address: '江苏省徐州市泉山区'
};
},
methods: {
showName () {
console.log(this.name);
}
}
};
</script>
<style scoped>
.demo {
background-color: pink;
}
</style>
components/Student.vue
<template>
<div>
<h2>学生姓名:{{ name }}</h2>
<h2>学校年龄:{{ age }}</h2>
</div>
</template>
<script>
export default {
name: 'Student',
data () {
return {
name: '筱晶哥哥',
age: 18
};
}
};
</script>
App.vue
<template>
<div>
<img alt="logo" src="@/assets/logo.png">
<School/>
<Student/>
</div>
</template>
<script>
import School from '@/components/School';
import Student from '@/components/Student';
export default {
name: 'App',
components: {
School,
Student
}
};
</script>
main.js
/*
* 该文件是整个项目的入口文件
*/
// 引入Vue
import Vue from 'vue';
// 引入App组件,该组件是所有组件的父组件
import App from './App';
// 关闭Vue的生产提示
Vue.config.productionTip = false;
/*
* 关于不同版本的Vue:
* 1. vue.js和vue.runtime.xxx.js的区别:
* ①vue.js是完整版的Vue,包含:核心功能+模板解析器。
* ②vue.runtime.xxx.js是运行版的Vue,只包含:核心功能,不包含:模板解析器。
* 2. 因为vue.runtime.xxx.js没有模板解析器,所以不能使用template配置项,需要使用render函数接受到的 createElement函数去指定具体的内容。
*/
// 创建Vue实例对象(vm)
new Vue({
el: '#app',
// 将组件添加到容器中
render: h => h(App),
/*
render (createElement) {
console.log('render', createElement);
return createElement(App);
}
*/
});
ref和props
ref
作用:
-
被用来给元素或子组件注册引用的信息(id的替代者)。 -
应用在html标签上获取的是真实DOM元素,应用在组件标签上是组件实例对象。
打标识:
<h1 ref="xxx"></h1>
<School ref="xxx"/>
读取方式:
this.$refs.xxxx
示例:
<template>
<div>
<!-- 通过ref属性来标识自己 -->
<h1 ref="title" v-text="msg"></h1>
<!-- 通过ref属性来标识自己 -->
<button ref="btn" @click="showDOM">点我输出上方的DOM元素</button>
<!-- 通过ref属性来标识自己 -->
<School ref="sch"/>
</div>
</template>
<script>
import School from './components/School';
export default {
name: 'App',
data () {
return {
msg: '欢迎学习vue'
};
},
methods: {
showDOM () {
console.log(this.$refs.title);
console.log(this.$refs.btn);
console.log(this.$refs.sch);
}
},
components: {
School
}
};
</script>
props
作用:让组件接收外部传过来的数据。
传递数据:
<Demo name="xxx"/>
接收数据:
<script>
// 简单接受
export default {
// 简单声明接收
// props: ['name', 'sex', 'age']
};
</script>
<script>
export default {
// 限制类型
props: {
name: String,
sex: String,
age: Number
}
};
</script>
<script>
export default {
// 限制类型、限制必要性、指定默认值
props: {
name: {
type: String, // name的类型是字符串
required: true // name是必要的
},
age: {
type: Number,
default: 99 // 默认值
},
sex: {
type: String,
required: true
}
}
};
</script>
注意:props是只读的,Vue底层会检测对props的修改,如果进行了修改,就会发出警告。
示例:
App.vue
<template>
<div>
<Student :age="18" name="张三" sex="女"/>
<hr>
<Student :age="19" name="李四" sex="男"/>
</div>
</template>
<script>
import Student from './components/Student';
export default {
name: 'App',
components: {
Student
}
};
</script>
Student.vue
<template>
<div>
<h1>{{ msg }}</h1>
<h2>学生姓名:{{ name }}</h2>
<h2>学生性别:{{ sex }}</h2>
<h2>学生年龄:{{ age }}</h2>
</div>
</template>
<script>
export default {
name: 'Student',
data () {
console.log(this);
return {
msg: '学生信息',
};
},
// 简单声明接收
// props: ['name', 'sex', 'age']
// 接收的同时对数据进行类型限制
/*props: {
name: String,
sex: String,
age: Number
}*/
// 接收的同时对数据:进行类型显示+默认值的指定+必要性的限制
props: {
name: {
type: String, // name的类型是字符串
required: true // name是必要的
},
age: {
type: Number,
default: 99 // 默认值
},
sex: {
type: String,
required: true
}
}
};
</script>
mixin混入
概述
功能:可以将多个组件公用的配置提取成一个换入对象。
使用方式:
-
① 定义混入,如下所示:
export default {
methods: {
showName () {
alert(this.name);
}
}
};
-
② 使用混入,如下所示:
<script>
// 局部混入
// 引入一个混合
import minix from '../mixin';
export default {
mixins: [minix]
};
</script>
import Vue from 'vue';
import App from './App.vue';
import mixin from './mixin';
Vue.config.productionTip = false;
// 全局混入
Vue.mixin(mixin);
new Vue({
el: '#app',
render: h => h(App),
});
应用示例
示例:
项目结构:
|-- README.md
|-- babel.config.js
|-- package.json
|-- public
| |-- favicon.ico
| `-- index.html
|-- src
| |-- App.vue
| |-- assets
| | `-- logo.png
| |-- components
| | |-- School.vue
| | `-- Student.vue
| |-- main.js
| `-- mixin.js
|-- vue.config.js
`-- yarn.lock
mixin.js
export default {
methods: {
showName () {
alert(this.name);
}
}
};
main.js
import Vue from 'vue';
import App from './App.vue';
import mixin from './mixin';
Vue.config.productionTip = false;
// 全局混入
Vue.mixin(mixin);
new Vue({
el: '#app',
render: h => h(App),
});
App.vue
<template>
<div>
<School/>
<Student/>
</div>
</template>
<script>
import Student from './components/Student';
import School from './components/School';
export default {
name: 'App',
components: {
Student,
School
}
};
</script>
School.vue
<template>
<div>
<h2 @click="showName">学校名称:{{ name }}</h2>
<h2>学校地址:{{ address }}</h2>
</div>
</template>
<script>
//引入一个混合
import minix from '../mixin';
export default {
name: 'School',
data () {
return {
name: '江苏师范大学',
address: '江苏省徐州市泉山区'
};
},
mixins: [minix]
};
</script>
<style scoped>
</style>
Student.vue
<template>
<div>
<h2 @click="showName">学生姓名:{{ name }}</h2>
<h2>学生年龄:{{ age }}</h2>
</div>
</template>
<script>
//引入一个混合
import minix from '../mixin';
export default {
name: 'Student',
data () {
return {
name: '筱晶哥哥',
age: 18
};
},
mixins: [minix]
};
</script>
<style scoped>
</style>
插件
概述
Vue插件是一个包含Install方法的对象,install的第一个参数是Vue,第二个以后的参数是插件使用者传递的数据。
通过install方法给Vue或Vue实例添加方法、定义全局指令等。
定义插件:
export default {
install (Vue) {
// 全局过滤器
Vue.filter(...);
// 全局指令
Vue.directive(...);
// 配置全局混入
Vue.mixin(...);
// 添加实例方法
Vue.prototype.$mymethod = function(){...}
// 添加实例属性
Vue.prototype.$myprototype = xxx
}
};
使用插件:
import Vue from 'vue';
import App from './App.vue';
// 引入插件
import plugins from './plugins';
// 关闭生产提示
Vue.config.productionTip = false;
// 使用插件
Vue.use(plugins);
new Vue({
el: '#app',
render: h => h(App),
});
应用示例
示例:
目录结构:
|-- README.md
|-- babel.config.js
|-- package.json
|-- public
| |-- favicon.ico
| `-- index.html
|-- src
| |-- App.vue
| |-- assets
| | `-- logo.png
| |-- components
| | |-- School.vue
| | `-- Student.vue
| |-- main.js
| `-- plugins.js
|-- vue.config.js
`-- yarn.lock
plugins.js
export default {
install (Vue) {
console.log('@@@install', Vue);
//全局过滤器
Vue.filter('capitalize', function (value) {
if (!value) {
return '';
}
value = value.toString();
return value.charAt(0).toUpperCase() + value.slice(1);
});
//全局指令
Vue.directive('fbind', {
//指令和元素成功绑定时
bind (element, binding) {
console.log('bind', this); //注意此处的this是Window
element.value = binding.value;
},
//指令所在元素被插入页面时
inserted (element, binding) {
console.log('inserted', this, binding); //注意此处的this是Window
element.focus();
},
//指令所在的模板被重新解析时
update (element, binding) {
console.log('update', this); //注意此处的this是Window
element.value = binding.value;
}
});
}
};
main.js
import Vue from 'vue';
import App from './App.vue';
//引入插件
import plugins from './plugins';
//关闭生产提示
Vue.config.productionTip = false;
//使用插件
Vue.use(plugins);
new Vue({
el: '#app',
render: h => h(App),
});
App.vue
<template>
<div>
<School/>
<Student/>
</div>
</template>
<script>
import Student from './components/Student';
import School from './components/School';
export default {
name: 'App',
components: {
Student,
School
}
};
</script>
School.vue
<template>
<div>
<h2>学校名称:{{ name | capitalize }}</h2>
<h2>学校地址:{{ address }}</h2>
</div>
</template>
<script>
//引入一个混合
export default {
name: 'School',
data () {
return {
name: '江苏师大',
address: '江苏省徐州市泉山区'
};
},
};
</script>
Student.vue
<template>
<div>
<h2>学生姓名:{{ name }}</h2>
<h2>学生年龄:{{ age }}</h2>
</div>
</template>
<script>
export default {
name: 'Student',
data () {
return {
name: '筱晶哥哥',
age: 18
};
},
};
</script>
scoped样式
概述
样式在局部生效,防止冲突。
写法:
<style lang="less" scoped>
.demo {
background-color: pink;
}
</style>
应用示例
示例:
目录结构:
|-- README.md
|-- babel.config.js
|-- package.json
|-- public
| |-- favicon.ico
| `-- index.html
|-- src
| |-- App.vue
| |-- assets
| | `-- logo.png
| |-- components
| | |-- School.vue
| | `-- Student.vue
| `-- main.js
|-- vue.config.js
`-- yarn.lock
注意:当前vue脚手架使用的webpack版本是4.46.0,所以使用less需要安装对应的版本。
① 安装less-loader:yarn add less-loader@7.3.0。
② 安装less:yarn add less@4.1.1。
●main.js
import Vue from 'vue';
import App from './App.vue';
Vue.config.productionTip = false;
new Vue({
el: '#app',
render: h => h(App),
});
App.vue
<template>
<div>
<School/>
<Student/>
</div>
</template>
<script>
import Student from './components/Student';
import School from './components/School';
export default {
name: 'App',
components: {
Student,
School
}
};
</script>
<style lang="less">
.demo {
background-color: pink;
}
</style>
Student.vue
<template>
<div class="demo">
<h2>学生姓名:{{ name }}</h2>
<h2>学生年龄:{{ age }}</h2>
</div>
</template>
<script>
export default {
name: 'Student',
data () {
return {
name: '筱晶哥哥',
age: 18
};
}
};
</script>
<style scoped>
.demo {
background-color: orange;
}
</style>
School.vue
<template>
<div class="demo">
<h2>学校名称:{{ name }}</h2>
<h2>学校地址:{{ address }}</h2>
</div>
</template>
<script>
export default {
name: 'School',
data () {
return {
name: '江苏师范大学',
address: '江苏省徐州市泉山区'
};
}
};
</script>
<style scoped>
.demo {
background-color: skyblue;
}
</style>
Vue中的组件自定义事件
绑定事件监听
使用v-on
指令绑定自定义事件监听:
<template>
<!-- 使用v-on指令绑定自定义事件demo,绑定的函数是demo函数 -->
<Header v-on:demo="demo"></Header>
</template>
使用ref绑定自定义事件监听:
<template>
<Header ref="demo"></Header>
</template>
<script>
export default {
name:'header',
mounted(){
//绑定自定义事件
this.$refs.demo.$on('demo',this.demo)
},
methods:{
demo(){
}
}
}
</script>
触发事件
在绑定自定义事件监听的组件实例对象上使用this.$emit触发事件:
<script>
export default {
name: 'Student',
methods: {
sendStudentName () {
console.log(this);
//触发Student组件实例身上的demo事件
this.$emit('demo', 'xxx');
}
}
};
</script>
解绑事件监听
在绑定自定义事件监听的组件实例对象上使用this.$off
解绑事件监听:
解绑单个事件监听:
<script>
export default {
name: 'Student',
data () {
return {
name: '筱晶哥哥',
age: 18,
num: 0
};
},
methods: {
unbind () {
//解绑Student组件实例身上的demo事件
this.$off('demo'); //解绑一个自定义事件
}
}
};
</script>
解绑多个事件监听:
<script>
export default {
name: 'Student',
data () {
return {
name: '筱晶哥哥',
age: 18,
num: 0
};
},
methods: {
unbind () {
//解绑Student组件实例身上的demo事件
this.$off(['demo', 'demo1']); //解绑多个自定义事件
}
}
};
</script>
解绑所有的自定义事件:
<script>
export default {
name: 'Student',
data () {
return {
name: '筱晶哥哥',
age: 18,
num: 0
};
},
methods: {
unbind () {
//解绑Student组件实例身上的demo事件
this.$off();//解绑所有的自定义事件
}
}
};
</script>
应用示例
示例:
目录结构:
|-- README.md
|-- babel.config.js
|-- package.json
|-- public
| |-- favicon.ico
| `-- index.html
|-- src
| |-- App.vue
| |-- assets
| | `-- logo.png
| |-- components
| | |-- School.vue
| | `-- Student.vue
| `-- main.js
|-- vue.config.js
`-- yarn.lock
main.js
import Vue from 'vue';
import App from './App.vue';
Vue.config.productionTip = false;
new Vue({
el: '#app',
render: h => h(App),
});
App.vue
<template>
<div class="app">
<h1>{{ msg }}</h1>
<!-- 通过父组件给子组件传递函数类型的props实现:子给父传递数据 -->
<School :getSchoolName="getSchoolName"/>
<!-- 通过父组件给子组件绑定一个自定义事件:子给父传递数据(第一种写法,使用@或v-on) -->
<!-- <Student v-on:demo="getStudentName"/> -->
<!-- 通过父组件给子组件绑定一个自定义事件:子给父传递数据(第二种写法,使用ref) -->
<Student ref="student"/>
</div>
</template>
<script>
import Student from './components/Student';
import School from './components/School';
export default {
name: 'App',
data () {
return { msg: '你好啊' };
},
mounted () {
//绑定自定义事件
this.$refs.student.$on('demo', this.getStudentName);
},
methods: {
getSchoolName (name) {
console.log('App收到了学校的名称:', name);
},
getStudentName (name) {
console.log('App收到了学生的姓名:', name);
}
},
components: {
Student,
School
}
};
</script>
<style lang="less">
.app {
background-color: gray;
padding: 5px;
}
</style>
School.vue
<template>
<div class="school">
<h2>学校名称:{{ name }}</h2>
<h2>学校地址:{{ address }}</h2>
<button @click="sendSchoolName">将学校名称发送给App</button>
</div>
</template>
<script>
export default {
name: 'School',
props: ['getSchoolName'],
methods: {
sendSchoolName () {
this.getSchoolName(this.name);
}
},
data () {
return {
name: '江苏师范大学',
address: '江苏省徐州市泉山区'
};
}
};
</script>
<style scoped>
.school {
background-color: skyblue;
padding: 5px;
}
</style>
Student.vue
<template>
<div class="student">
<h2>学生姓名:{{ name }}</h2>
<h2>学生年龄:{{ age }}</h2>
<button @click="sendStudentName">将学生名称发送给App</button>
</div>
</template>
<script>
export default {
name: 'Student',
data () {
return {
name: '筱晶哥哥',
age: 18
};
},
methods: {
sendStudentName () {
console.log(this);
//触发Student组件实例身上的demo事件
this.$emit('demo', this.name);
}
}
};
</script>
<style scoped>
.student {
background-color: pink;
padding: 5px;
margin-top: 30px;
}
</style>
示例:
目录结构:
|-- README.md
|-- babel.config.js
|-- package.json
|-- public
| |-- favicon.ico
| `-- index.html
|-- src
| |-- App.vue
| |-- assets
| | `-- logo.png
| |-- components
| | |-- School.vue
| | `-- Student.vue
| `-- main.js
|-- vue.config.js
`-- yarn.lock
main.js
import Vue from 'vue';
import App from './App.vue';
Vue.config.productionTip = false;
new Vue({
el: '#app',
render: h => h(App),
});
App.vue
<template>
<div class="app">
<h1>{{ msg }}</h1>
<!-- 通过父组件给子组件传递函数类型的props实现:子给父传递数据 -->
<School :getSchoolName="getSchoolName"/>
<!-- 通过父组件给子组件绑定一个自定义事件:子给父传递数据(第一种写法,使用@或v-on) -->
<!-- <Student v-on:demo="getStudentName"/> -->
<!-- 通过父组件给子组件绑定一个自定义事件:子给父传递数据(第二种写法,使用ref) -->
<Student ref="student" @click.native="show"/>
</div>
</template>
<script>
import Student from './components/Student';
import School from './components/School';
export default {
name: 'App',
data () {
return { msg: '你好啊' };
},
mounted () {
//绑定自定义事件
this.$refs.student.$on('demo', this.getStudentName);
this.$refs.student.$on('demo1', this.demo1);
},
methods: {
getSchoolName (name) {
console.log('App收到了学校的名称:', name);
},
getStudentName (name) {
console.log('App收到了学生的姓名:', name);
},
demo1 () {
console.log('demo1事件被触发了');
},
show () {
alert(123);
}
},
components: {
Student,
School
}
};
</script>
<style lang="less">
.app {
background-color: gray;
padding: 5px;
}
</style>
School.vue
<template>
<div class="school">
<h2>学校名称:{{ name }}</h2>
<h2>学校地址:{{ address }}</h2>
<button @click="sendSchoolName">将学校名称发送给App</button>
</div>
</template>
<script>
export default {
name: 'School',
props: ['getSchoolName'],
methods: {
sendSchoolName () {
this.getSchoolName(this.name);
}
},
data () {
return {
name: '江苏师范大学',
address: '江苏省徐州市泉山区'
};
}
};
</script>
<style scoped>
.school {
background-color: skyblue;
padding: 5px;
}
</style>
Student.vue
<template>
<div class="student">
<h2>学生姓名:{{ name }}</h2>
<h2>学生年龄:{{ age }}</h2>
<h2>求和为:{{ num }}</h2>
<button @click="add">点我+1</button>
<button @click="sendStudentName">将学生名称发送给App</button>
<button @click="unbind">解绑自定义事件</button>
<button @click="dead">销毁当前Student组件的实例(VC)</button>
</div>
</template>
<script>
export default {
name: 'Student',
data () {
return {
name: '筱晶哥哥',
age: 18,
num: 0
};
},
methods: {
add () {
console.log('add回调被调用了');
this.num++;
},
sendStudentName () {
//触发Student组件实例身上的demo事件
this.$emit('demo', this.name);
// this.$emit('demo1');
},
unbind () {
//解绑Student组件实例身上的demo事件
this.$off('demo'); //解绑一个自定义事件
// this.$off(['demo', 'demo1']); //解绑多个自定义事件
// this.$off();//解绑所有的自定义事件
},
dead () {
//销毁了当前Student组件的实例,销毁后所有Student实例的自定义事件全部不奏效
this.$destroy();
}
}
};
</script>
<style scoped>
.student {
background-color: pink;
padding: 5px;
margin-top: 30px;
}
</style>
总结
组件的自定义事件是一种组件间通信的方式,适用于:子组件 => 父组件
。
使用场景:A是父组件,B是子组件,B想给A传递数据,那么就要在A中给B绑定自定义事件(事件的回调在A中
)
绑定自定义事件:
-
① 在父组件中: <Demo @demo="test"/>
或<Demo v-on:demo="test">
。 -
② 在父组件中:
<template>
<Demo ref="demo"/>
</template>
<script>
export default {
mounted(){
this.$refs.demo.$on('demo',this.test);
}
}
</script>
-
触发自定义事件:
this.$emit('demo',数据);
-
解绑自定义事件:
this.$off('demo');
-
组件上也可以绑定原生的DOM事件,但是需要使用
native
修饰符。 -
注意:通过
this.$refs.xxx.$on('demo',回调函数)
绑定自定义事件时,回调要么配置在methods中,要么使用箭头函数
,否则this会指向触发事件的组件。
全局事件总线(GlobalEventBus)
概述
全局事件总线是一种组件间的通信方式,适用于任意组件间通信。
安装全局事件总线:
使用事件总线:
①接收数据:A组件想要接收数据,则在A组件中给$bus
绑定自定义事件,事件的回调留在A组件自身
:
<script>
export default {
...
mounted () {
this.$bus.$on('demo', (data) => {
console.log('School组件接收到的数据:', data);
});
},
beforeDestroy () {
this.$bus.$off('demo');
}
};
</script>
②提供数据:
this.$bus.$emit('xxx',数据);
最好在beforeDestory
钩子中,用$off
去解绑当前组件所用到的事件
。
应用示例
示例:
目录结构:
|-- README.md
|-- babel.config.js
|-- package.json
|-- public
| |-- favicon.ico
| `-- index.html
|-- src
| |-- App.vue
| |-- assets
| | `-- logo.png
| |-- components
| | |-- School.vue
| | `-- Student.vue
| `-- main.js
|-- vue.config.js
`-- yarn.lock
main.js
import Vue from 'vue';
import App from './App.vue';
Vue.config.productionTip = false;
new Vue({
el: '#app',
render: h => h(App),
beforeCreate () {
Vue.prototype.$bus = this; //安装全局事件总线
}
});
App.vue
<template>
<div class="app">
<h1>{{ msg }}</h1>
<School/>
<Student/>
</div>
</template>
<script>
import Student from './components/Student';
import School from './components/School';
export default {
name: 'App',
data () {
return { msg: '你好啊' };
},
components: {
Student,
School
}
};
</script>
<style lang="less">
.app {
background-color: gray;
padding: 5px;
}
</style>
Student.vue
<template>
<div class="student">
<h2>学生姓名:{{ name }}</h2>
<h2>学生年龄:{{ age }}</h2>
<button @click="sendStudentName">将学生姓名交给School组件</button>
</div>
</template>
<script>
export default {
name: 'Student',
data () {
return {
name: '筱晶哥哥',
age: 18,
};
},
methods: {
sendStudentName () {
this.$bus.$emit('demo', this.name);
}
}
};
</script>
<style scoped>
.student {
background-color: pink;
padding: 5px;
margin-top: 30px;
}
</style>
School.vue
<template>
<div class="school">
<h2>学校名称:{{ name }}</h2>
<h2>学校地址:{{ address }}</h2>
</div>
</template>
<script>
export default {
name: 'School',
data () {
return {
name: '江苏师范大学',
address: '江苏省徐州市泉山区'
};
},
mounted () {
this.$bus.$on('demo', (data) => {
console.log('School组件接收到的数据:', data);
});
},
beforeDestroy () {
this.$bus.$off('demo');
}
};
</script>
<style scoped>
.school {
background-color: skyblue;
padding: 5px;
}
</style>
消息订阅和发布
概述
使用方式和全局事件总线类似。
包含以下的操作:
-
① 订阅消息:对应绑定事件监听。 -
② 发布消息:分发事件。 -
③ 取消消息订阅:解绑事件监听。
需要引入一个消息订阅和发布的第三方库:pubsub-js
。
使用
在线文档:https://github.com/mroderick/PubSubJS
下载:
yarn add pubsub-js
相关语法:
-
① 引入:
import PubSub from 'pubsub-js'
-
② 订阅消息:
PubSub.subscribe(‘msgName’, functon(msgName, data){ })
-
③ 发布消息,触发订阅的回调函数:
PubSub.publish(‘msgName’, data)
④ 取消消息的订阅:
PubSub.unsubscribe(token)
应用示例
示例:
目录结构:
|-- README.md
|-- babel.config.js
|-- package.json
|-- public
| |-- favicon.ico
| `-- index.html
|-- src
| |-- App.vue
| |-- assets
| | `-- logo.png
| |-- components
| | |-- School.vue
| | `-- Student.vue
| `-- main.js
|-- vue.config.js
`-- yarn.lock
安装pubsub-js
:
yarn add pubsub-js
main.js
import Vue from 'vue';
import App from './App.vue';
Vue.config.productionTip = false;
new Vue({
el: '#app',
render: h => h(App)
});
App.vue
<template>
<div class="app">
<h1>{{ msg }}</h1>
<School/>
<Student/>
</div>
</template>
<script>
import Student from './components/Student';
import School from './components/School';
export default {
name: 'App',
data () {
return { msg: '你好啊' };
},
components: {
Student,
School
}
};
</script>
<style lang="less">
.app {
background-color: gray;
padding: 5px;
}
</style>
School.vue
<template>
<div class="school">
<h2>学校名称:{{ name }}</h2>
<h2>学校地址:{{ address }}</h2>
</div>
</template>
<script>
import PubSub from 'pubsub-js';
export default {
name: 'School',
data () {
return {
name: '江苏师范大学',
address: '江苏省徐州市泉山区'
};
},
mounted () {
this.token = PubSub.subscribe('demo', (msgName, data) => {
console.log('School组件接收到的数据:', msgName, data);
});
},
beforeDestroy () {
PubSub.unsubscribe(this.token);
}
};
</script>
<style scoped>
.school {
background-color: skyblue;
padding: 5px;
}
</style>
Student.vue
<template>
<div class="student">
<h2>学生姓名:{{ name }}</h2>
<h2>学生年龄:{{ age }}</h2>
<button @click="sendStudentName">将学生姓名交给School组件</button>
</div>
</template>
<script>
import PubSub from 'pubsub-js';
export default {
name: 'Student',
data () {
return {
name: '筱晶哥哥',
age: 18,
};
},
methods: {
sendStudentName () {
PubSub.publish('demo', this.name);
}
}
};
</script>
<style scoped>
.student {
background-color: pink;
padding: 5px;
margin-top: 30px;
}
</style>
过渡和动画
概述
作用:在插入、更新或移除DOM元素的时候,在合适的时候给元素添加样式类名。

写法:
① 准备好样式:
-
元素进入的样式:
-
v-enter:进入的起点。 -
v-enter-active:进入过程中。 -
v-enter-to:进入的终点。 -
元素离开的样式:
-
v-leave:离开的重终点。 -
v-leave-active:离开过程中。 -
v-leave-to:离开的终点。
② 使用<transition></transition>
包裹要过渡的元素,并配置name
属性。
<transition :appear="true" name="hello">
<h1 v-show="isShow">你好啊!</h1>
</transition>
③ 备注:如果有多个元素需要过渡,则需要使用<transition-group></transition-group>
,并为每个元素指定key
值。
应用示例
示例:
目录结构:
|-- README.md
|-- babel.config.js
|-- package.json
|-- public
| |-- favicon.ico
| `-- index.html
|-- src
| |-- App.vue
| |-- assets
| | `-- logo.png
| |-- components
| | `-- Test.vue
| `-- main.js
|-- vue.config.js
`-- yarn.lock
main.js
import Vue from 'vue';
import App from './App.vue';
Vue.config.productionTip = false;
new Vue({
el: '#app',
render: h => h(App)
});
App.vue
<template>
<div>
<Test/>
</div>
</template>
<script>
import Test from './components/Test';
export default {
name: 'App',
components: {
Test
}
};
</script>
Test.vue
<template>
<div>
<button @click="isShow = !isShow">显示/隐藏</button>
<transition :appear="true" name="hello">
<h1 v-show="isShow">你好啊!</h1>
</transition>
</div>
</template>
<script>
export default {
name: 'Test',
data () {
return {
isShow: true
};
}
};
</script>
<style scoped>
h1 {
background-color: orange;
}
.hello-enter-active {
animation: demo 1s;
}
.hello-leave-active {
animation: demo 1s reverse;
}
@keyframes demo {
from {
transform: translateX(-100%);
}
to {
transform: translateX(0px);
}
}
</style>
示例:
目录结构:
|-- README.md
|-- babel.config.js
|-- package.json
|-- public
| |-- favicon.ico
| `-- index.html
|-- src
| |-- App.vue
| |-- assets
| | `-- logo.png
| |-- components
| | `-- Test.vue
| `-- main.js
|-- vue.config.js
`-- yarn.lock
main.js
import Vue from 'vue';
import App from './App.vue';
Vue.config.productionTip = false;
new Vue({
el: '#app',
render: h => h(App)
});
App.vue
<template>
<div>
<Test/>
</div>
</template>
<script>
import Test from './components/Test';
export default {
name: 'App',
components: {
Test
}
};
</script>
Test.vue
<template>
<div>
<button @click="isShow = !isShow">显示/隐藏</button>
<transition :appear="true" name="hello">
<h1 v-show="isShow">你好啊!</h1>
</transition>
</div>
</template>
<script>
export default {
name: 'Test',
data () {
return {
isShow: true
};
}
};
</script>
<style scoped>
h1 {
background-color: orange;
}
/* 进入的起点和离开的终点 */
.hello-enter, .hello-leave-to {
transform: translateX(-100%);
}
.hello-enter-active, .hello-leave-active {
transition: 0.5s linear;
}
/* 离开的起点和进入的终点 */
.hello-leave, .hello-enter-to {
transform: translateX(0);
}
</style>
示例:集成第三方动画
安装第三方动画:
yarn add animate.css
目录结构:
|-- README.md
|-- babel.config.js
|-- package.json
|-- public
| |-- favicon.ico
| `-- index.html
|-- src
| |-- App.vue
| |-- assets
| | `-- logo.png
| |-- components
| | `-- Test.vue
| `-- main.js
|-- vue.config.js
`-- yarn.lock
main.js
import Vue from 'vue';
import App from './App.vue';
Vue.config.productionTip = false;
new Vue({
el: '#app',
render: h => h(App)
});
App.vue
<template>
<div>
<Test/>
</div>
</template>
<script>
import Test from './components/Test';
export default {
name: 'App',
components: {
Test
}
};
</script>
Test.vue
<template>
<div>
<button @click="isShow = !isShow">显示/隐藏</button>
<transition :appear="true" enter-active-class="animate__swing" leave-active-class="animate__backOutUp"
name="animate__animated animate__bounce">
<h1 v-show="isShow">你好啊!</h1>
</transition>
</div>
</template>
<script>
import 'animate.css';
export default {
name: 'Test',
data () {
return {
isShow: true
};
}
};
</script>
<style scoped>
h1 {
background-color: orange;
}
</style>
vue ajax
vue项目中常用的2个Ajax库
vue-resource
vue插件,非官方库,vue1.x使用广泛,已经弃用。
axios
通用的ajax请求库,官方推荐,vue2.x使用广泛。
2 解决开发环境Ajax跨域问题
解决开发环境Ajax跨域的问题思路

① 使用Nginx。
② 借助webpack的devServer。
应用示例
示例:
目录结构:
|-- README.md
|-- babel.config.js
|-- package.json
|-- public
| |-- favicon.ico
| `-- index.html
|-- src
| |-- App.vue
| |-- assets
| | `-- logo.png
| |-- components
| `-- main.js
|-- vue.config.js
`-- yarn.lock
vue.config.js
module.exports = {
//关闭语法检查
lintOnSave: false,
//开启代理服务器
devServer: {
proxy: {
'/api1': {
target: 'http://localhost:5000',
ws: true, //如果要代理 websockets,配置这个参数
secure: false, // 如果是https接口,需要配置这个参数
changeOrigin: true, //用于控制请求头中的HOST值
pathRewrite: {
'^/api1': ''
}
},
'/api2': {
target: 'http://localhost:5001',
ws: true, //如果要代理 websockets,配置这个参数
secure: false, // 如果是https接口,需要配置这个参数
changeOrigin: true, //用于控制请求头中的HOST值
pathRewrite: {
'^/api2': ''
}
}
}
}
};
main.js
import Vue from 'vue';
import App from './App';
Vue.config.productionTip = false;
new Vue({
el: '#app',
render: h => h(App),
});
App.vue
<template>
<div>
<button @click="getStudents">获取学生信息</button>
<button @click="getCars">获取汽车信息</button>
</div>
</template>
<script>
import axios from 'axios';
export default {
name: 'App',
methods: {
async getStudents () {
try {
const result = await axios.get('/api1/students');
console.log(result.data);
} catch (e) {
console.log(e.message);
}
},
async getCars () {
try {
const result = await axios.get('/api2/cars');
console.log(result.data);
} catch (e) {
console.log(e.message);
}
}
},
};
</script>
插槽slot
概述
作用:让父组件可以向子组件指定位置插入html结构,也是一种组件间通信的方式,适用于父组件==>子组件
。
注意:插槽内容是在父组件中编译后,再传递给子组件的。
分类:
-
① 默认插槽:
<template>
<!-- 父组件 -->
<Category>
<div>HTML结构</div>
</Category>
</template>
<template>
<!-- 子组件 -->
<div>
<!-- 定义插槽 -->
<slot>插槽默认内容</slot>
</div>
<
</template>
-
② 具名插槽:
<template>
<!-- 父组件 -->
<Category>
<template slot="center">
<div>HTML结构</div>
</template>
</Category>
<Category>
<template v-slot:footer>
<div>HTML结构</div>
</template>
</Category>
</template>
<template>
<!-- 子组件 -->
<div class="category">
<h3>{{ title }}</h3>
<!-- 定义一个插槽(等着组件的使用者进行填充) -->
<slot name="center"></slot>
<slot name="footer"></slot>
</div>
</template>
-
③ 作用域插槽:
数据在组件自身,但是根据数据生成的结构需要组件的使用者来决定
(games数据在Category组件中,但是使用数据所遍历出来的结构由App组件来决定)。
<template>
<!-- 父组件 -->
<div class="container">
<Category title="游戏">
<template scope="{games}">
<!-- 生成的是ul列表 -->
<ul>
<li v-for="(item,index) in games" :key="index">{{ item }}</li>
</ul>
</template>
</Category>
</template>
<template>
<div class="category">
<h3>{{ title }}</h3>
<slot :games="games"></slot>
</div>
</template>
<script>
export default {
name: 'Category',
data () {
return {
games: ['红色警戒', '穿越火线', '劲舞团', '超级玛丽'],
};
},
props: {
title: String
}
};
</script>
应用示例
示例:默认插槽
目录结构:
|-- README.md
|-- babel.config.js
|-- package.json
|-- public
| |-- favicon.ico
| `-- index.html
|-- src
| |-- App.vue
| |-- assets
| | `-- logo.png
| |-- components
| | `-- Category.vue
| `-- main.js
|-- vue.config.js
`-- yarn.lock
main.js
import Vue from 'vue';
import App from './App';
Vue.config.productionTip = false;
new Vue({
el: '#app',
render: h => h(App),
});
App.vue
<template>
<div class="container">
<Category title="美食">
<img src="xxx.jpg">
</Category>
<Category title="游戏">
<ul>
<li v-for="(item,index) in games" :key="index">{{ item }}</li>
</ul>
</Category>
<Category title="电影">
<video controls src="xxx.mp4"></video>
</Category>
</div>
</template>
<script>
import Category from './components/Category';
export default {
name: 'App',
data () {
return {
foods: ['火锅', '烧烤', '小龙虾', '牛排'],
games: ['红色警戒', '穿越火线', '劲舞团', '超级玛丽'],
films: ['《教父》', '《拆弹专家》', '《你好,李焕英》', '《你好世界》'],
};
},
components: {
Category
},
};
</script>
<style>
.container {
display: flex;
justify-content: space-around;
}
img {
width: 100%;
}
video {
width: 100%;
}
</style>
Category.vue
<template>
<div class="category">
<h3>{{ title }}</h3>
<!-- 定义一个插槽(等着组件的使用者进行填充) -->
<slot>我是一些默认值,当使用者没有传递具体结构时,我会出现</slot>
</div>
</template>
<script>
export default {
name: 'Category',
data () {
return {};
},
props: {
title: String
}
};
</script>
<style scoped>
.category {
background-color: skyblue;
width: 200px;
height: 300px;
}
h3 {
text-align: center;
background-color: orange;
}
</style>
示例:具名插槽
目录结构:
|-- README.md
|-- babel.config.js
|-- package.json
|-- public
| |-- favicon.ico
| `-- index.html
|-- src
| |-- App.vue
| |-- assets
| | `-- logo.png
| |-- components
| | `-- Category.vue
| `-- main.js
|-- vue.config.js
`-- yarn.lock
main.js
import Vue from 'vue';
import App from './App';
Vue.config.productionTip = false;
new Vue({
el: '#app',
render: h => h(App),
});
App.vue
<template>
<div class="container">
<Category title="美食">
<img slot="center" src="xxx.jpg">
<a slot="footer" class="foot" href="#">更多美食</a>
</Category>
<Category title="游戏">
<ul slot="center">
<li v-for="(item,index) in games" :key="index">{{ item }}</li>
</ul>
<span slot="footer" class="foot">
<a href="#">单机游戏</a>
<a href="#">网络游戏</a>
</span>
</Category>
<Category title="电影">
<video slot="center" controls src="xxx.mp4"></video>
<template v-slot:footer>
<span class="foot">
<a href="#">经典</a>
<a href="#">热门</a>
<a href="#">推荐</a>
</span>
<h4>欢迎来到影院观看</h4>
</template>
</Category>
</div>
</template>
<script>
import Category from './components/Category';
export default {
name: 'App',
data () {
return {
foods: ['火锅', '烧烤', '小龙虾', '牛排'],
games: ['红色警戒', '穿越火线', '劲舞团', '超级玛丽'],
films: ['《教父》', '《拆弹专家》', '《你好,李焕英》', '《你好世界》'],
};
},
components: {
Category
},
};
</script>
<style>
.container, .foot {
display: flex;
justify-content: space-around;
}
h4 {
text-align: center;
}
img {
width: 100%;
}
video {
width: 100%;
}
</style>
Category.vue
<template>
<div class="category">
<h3>{{ title }}</h3>
<!-- 定义一个插槽(等着组件的使用者进行填充) -->
<slot name="center">我是一些默认值,当使用者没有传递具体结构时,我会出现1</slot>
<slot name="footer">我是一些默认值,当使用者没有传递具体结构时,我会出现2</slot>
</div>
</template>
<script>
export default {
name: 'Category',
data () {
return {};
},
props: {
title: String
}
};
</script>
<style scoped>
.category {
background-color: skyblue;
width: 200px;
height: 300px;
}
h3 {
text-align: center;
background-color: orange;
}
</style>
示例:作用域插槽
目录结构:
|-- README.md
|-- babel.config.js
|-- package.json
|-- public
| |-- favicon.ico
| `-- index.html
|-- src
| |-- App.vue
| |-- assets
| | `-- logo.png
| |-- components
| | `-- Category.vue
| `-- main.js
|-- vue.config.js
`-- yarn.lock
main.js
import Vue from 'vue';
import App from './App';
Vue.config.productionTip = false;
new Vue({
el: '#app',
render: h => h(App),
});
App.vue
<template>
<div class="container">
<Category title="游戏">
<template scope="{games}">
<ul>
<li v-for="(item,index) in games" :key="index">{{ item }}</li>
</ul>
</template>
</Category>
<Category title="游戏">
<template scope="{games}">
<ol>
<li v-for="(item,index) in games" :key="index">{{ item }}</li>
</ol>
</template>
</Category>
<Category title="游戏">
<template slot-scope="{games}">
<h4 v-for="(item,index) in games" :key="index">{{ item }}</h4>
</template>
</Category>
</div>
</template>
<script>
import Category from './components/Category';
export default {
name: 'App',
components: {
Category
},
};
</script>
<style>
.container {
display: flex;
justify-content: space-around;
}
h4 {
text-align: center;
}
img {
width: 100%;
}
video {
width: 100%;
}
</style>
Categroy.vue
<template>
<div class="category">
<h3>{{ title }}</h3>
<slot :games="games"></slot>
</div>
</template>
<script>
export default {
name: 'Category',
data () {
return {
games: ['红色警戒', '穿越火线', '劲舞团', '超级玛丽'],
};
},
props: {
title: String
}
};
</script>
<style scoped>
.category {
background-color: skyblue;
width: 200px;
height: 300px;
}
h3 {
text-align: center;
background-color: orange;
}
</style>
vuex
Vuex的简介
Vue是什么?
Vuex是专门在Vue中实现集中式状态(数据)管理的一个Vue插件,对Vue应用中多个组件的共享状态进行集中式的管理(读和写),也是一种组件间通信的方式,且适用于任意组件间的通信。
文档地址:https://vuex.vuejs.org/zh/
什么时候使用Vuex?
-
多个组件依赖于同一状态。
-
来自不同组件的行为需要变更同一状态。
-
多个组件要共享状态。
Vuex的工作原理

Vuex核心概念和API
核心概念和API
state
-
Vuex管理的状态对象。
-
state是唯一的。
-
示例:
const state = {count:0}
mutations
-
值是一个对想,包含多个直接更新state的函数(也可以称为方法)。
-
在actions中使用
context.commit('方法名',数据)
触发mutations中的方法。 -
mutations中方法的特点:不能写异步代码,只能单纯的操作state。
-
示例:
const mutations ={
INCREMENT (state, value) {
state.count += value;
},
}
actions
-
值是一个对象,包含多个响应用户动作的回调函数。
-
通过commit方法来触发mutations中对应函数的执行,间接的更新state。
-
actions中方法的特点:可以包含异步代码(定时器、Ajax等)。
-
示例:
const actions = { //准备actions,用于响应组件中的动作
increment (context, value) {
context.commit('INCREMENT', value);
},
decrement (context, value) {
context.commit('DECREMENT', value);
},
incrementIfOdd (context, value) {
const { count } = context.state;
if (count % 2 !== 0) {
context.commit('INCREMENT', value);
}
},
incrementAsync (context, value) {
setTimeout(() => {
context.commit('INCREMENT', value);
}, 2000);
}
},
getters
-
当state中的数据需要经过加工后再使用,可以使用getters进行加工。
-
值为一个对象,包含多个用于返回数据的函数。
-
使用:
$store.getters.xxx
-
示例:
const getters = { //准备getters,用于将state中的数据进行加工
bigCount (state) {
return state.count * 10;
}
}
四个map方法的使用
-
① mapState方法:用于帮助我们映射state中的数据为计算属性。
import { mapState } from 'vuex'
computed: {
/* 借助mapState生成计算属性,从state中读取数据。(对象写法) */
...mapState({ count: 'count', school: 'school', subject: 'subject' })
}
import { mapState } from 'vuex'
computed: {
/* 借助mapState生成计算属性,从state中读取数据。(数组写法) */
...mapState(['count', 'school', 'subject']),
}
-
② mapGetters方法:用于帮我们映射getters中的数据为计算属性。
import { mapGetters, mapState } from 'vuex';
computed: {
/* 借助mapGetters生成计算属性,从getters中读取数据。(对象写法) */
...mapGetters({bigCount:'bigCount'}),
},
import { mapGetters, mapState } from 'vuex';
computed: {
/* 借助mapGetters生成计算属性,从getters中读取数据。(数组写法) */
...mapGetters(['bigCount'])
},
-
③ mapActions方法:用于帮我们生成和 actions
对话的方法,即包含$storer.dispatch(xxx)
的函数。
import { mapActions } from 'vuex';
methods: {
/* 借助mapActions调用对应的方法,方法中会调用dispatch去联系action(对象写法) */
...mapActions({ incrementIfOdd: 'incrementIfOdd', incrementAsync: 'incrementAsync' }),
}
import { mapActions } from 'vuex';
methods: {
/* 借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(数组写法) */
...mapActions(['incrementIfOdd','incrementAsync']),
}
-
④ mapMutations方法:用于帮我们生成和mutations对话的方法,即包含 $store.commit(xxx)
的函数。
import { mapMutations } from 'vuex';
methods: {
/* 借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(对象写法) */
...mapMutations({ increment: 'INCREMENT', decrement: 'DECREMENT' }),
},
import { mapMutations } from 'vuex';
methods: {
/* 借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(数组写法) */
...mapMutations([increment,decrement]),
},
注意:mapActions和mapMutations使用的时候,如果需要传递参数需要在模板绑定事件时传递好参数,否则参数就是事件对象event
模块化+命名空间
-
目的:让代码更好维护,让多种数据分类更加明确。
-
store.js:
const countAbout = {
namespaced:true, //开启命名空间
state:{
sum:1
},
actions:{},
mutations:{},
getters:{
bigCount(state){
return state.sum * 10
}
}
}
const personAbout = {
namespaced:true, //开启命名空间
state:{},
actions:{},
mutations:{},
getters:{}
}
const store = new Vuex.Store({
module:{
countAbout,
personAbout
}
})
-
开启命名空间后,组件中读取state数据:
//方式一:自己直接读取
this.$store.state.personAbout.list
//方式二:借助mapSate读取
...mapState('countAbout',['count','school','subject'])
-
开启命名空间后,组件中读取getters数据:
//方式一:自己直接读取
this.$store.getters['personAbout/firstPersonName']
//方式二:借助mapGetters读取
...mapGetters('countAbout',[bigCount])
-
开启命名空间后,组件中调用dispatch:
//方式一:自己直接dispatch
this.$store.dispatch('personAbout/addPersonWang',person)
//方式二:借助mapActions
...mapActions('countAbout', { incrementIfOdd: 'incrementIfOdd', incrementAsync: 'incrementAsync' })
-
开启命名空间后,组件中调用commit:
//方式一:自己直接调用commit
this.$store.commit('personAbout/ADD_PERSON', person);
//方式二:借助mapMutations
...mapMutations('countAbout', { increment: 'INCREMENT', decrement: 'DECREMENT' }),
安装Vuex
安装Vuex命令:
yarn add vuex
应用示例
搭建Vuex环境
-
示例:
-
目录结构:
|-- README.md
|-- babel.config.js
|-- package.json
|-- public
| |-- favicon.ico
| `-- index.html
|-- src
| |-- App.vue
| |-- assets
| | `-- logo.png
| |-- components
| | `-- Count.vue
| |-- main.js
| `-- vuex
| `-- store.js
|-- vue.config.js
`-- yarn.lock
-
main.js
import Vue from 'vue';
import App from './App';
//引入store
import store from './vuex/store';
//关闭生产提示
Vue.config.productionTip = false;
new Vue({
el: '#app',
render: h => h(App),
store
});
-
App.vue
<template>
<div>
<Count/>
</div>
</template>
<script>
import Count from './components/Count';
export default {
name: 'App',
components: { Count },
};
</script>
-
vuex/store.js
//该文件用于创建Vuex中最为核心的store对象
//引入Vue
import Vue from 'vue';
//引入Vuex
import Vuex from 'vuex';
//使用Vuex插件
Vue.use(Vuex);
//准备actions,用于响应组件中的动作
const actions = {};
//准备mutations,用于操作数据(state)
const mutations = {};
//准备state,用于存储数据
const state = {};
//创建store并暴露store
export default new Vuex.Store({
state,
actions,
mutations,
});
-
components/Count.vue
<template>
<div>
<h2>当前求和为:{{ count }}</h2>
<select v-model.number="n">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button @click="increment">+</button>
<button @click="decrement">-</button>
<button @click="incrementIfOdd">奇数再加</button>
<button @click="incrementAsync">异步加</button>
</div>
</template>
<script>
export default {
name: 'Count',
data () {
return {
n: 1,//用户选择的数字
count: 0 //当前的和
};
},
methods: {
increment () {
this.count += this.n;
},
decrement () {
this.count -= this.n;
},
incrementIfOdd () {
if (this.count % 2 === 0) {
return;
}
this.count += this.n;
},
incrementAsync () {
setTimeout(() => {
this.count += this.n;
}, 1000);
}
},
mounted () {
console.log(this);
}
};
</script>
<style scoped>
button {
margin-left: 5px;
}
</style>
vuex-求和案例
-
示例:
-
目录结构:
|-- README.md
|-- babel.config.js
|-- package.json
|-- public
| |-- favicon.ico
| `-- index.html
|-- src
| |-- App.vue
| |-- assets
| | `-- logo.png
| |-- components
| | `-- Count.vue
| |-- main.js
| `-- vuex
| `-- store.js
|-- vue.config.js
`-- yarn.lock
-
main.js
import Vue from 'vue';
import App from './App';
//引入store
import store from './vuex/store';
//关闭生产提示
Vue.config.productionTip = false;
new Vue({
el: '#app',
render: h => h(App),
store
});
-
App.vue
<template>
<div>
<Count/>
</div>
</template>
<script>
import Count from './components/Count';
export default {
name: 'App',
components: { Count },
};
</script>
-
vuex/store.js
//该文件用于创建Vuex中最为核心的store对象
//引入Vue
import Vue from 'vue';
//引入Vuex
import Vuex from 'vuex';
//使用Vuex插件
Vue.use(Vuex);
//创建store并暴露store
export default new Vuex.Store({
state: { //准备state,用于存储数据
count: 0
},
actions: { //准备actions,用于响应组件中的动作
increment (context, value) {
context.commit('INCREMENT', value);
},
decrement (context, value) {
context.commit('DECREMENT', value);
},
incrementIfOdd (context, value) {
const { count } = context.state;
if (count % 2 !== 0) {
context.commit('INCREMENT', value);
}
},
incrementAsync (context, value) {
setTimeout(() => {
context.commit('INCREMENT', value);
}, 2000);
}
},
mutations: { //准备mutations,用于操作数据(state)
INCREMENT (state, value) {
state.count += value;
},
DECREMENT (state, value) {
state.count -= value;
}
},
});
-
Components/Count.vue
<template>
<div>
<h2>当前求和为:{{ $store.state.count }}</h2>
<select v-model.number="num">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button @click="increment">+</button>
<button @click="decrement">-</button>
<button @click="incrementIfOdd">奇数再加</button>
<button @click="incrementAsync">异步加</button>
</div>
</template>
<script>
export default {
name: 'Count',
data () {
return {
num: 1,//用户选择的数字
};
},
methods: {
increment () {
this.$store.dispatch('increment', this.num);
},
decrement () {
this.$store.dispatch('decrement', this.num);
},
incrementIfOdd () {
this.$store.dispatch('incrementIfOdd', this.num);
},
incrementAsync () {
this.$store.dispatch('incrementAsync', this.num);
}
},
mounted () {
console.log('Count', this);
}
};
</script>
<style scoped>
button {
margin-left: 5px;
}
</style>
vuex-求和案例-getters
-
示例:
-
目录结构:
|-- README.md
|-- babel.config.js
|-- package.json
|-- public
| |-- favicon.ico
| `-- index.html
|-- src
| |-- App.vue
| |-- assets
| | `-- logo.png
| |-- components
| | `-- Count.vue
| |-- main.js
| `-- vuex
| `-- store.js
|-- vue.config.js
`-- yarn.lock
-
main.js
import Vue from 'vue';
import App from './App';
//引入store
import store from './vuex/store';
//关闭生产提示
Vue.config.productionTip = false;
new Vue({
el: '#app',
render: h => h(App),
store
});
-
App.vue
<template>
<div>
<Count/>
</div>
</template>
<script>
import Count from './components/Count';
export default {
name: 'App',
components: { Count },
};
</script>
-
vuex.store.js
//该文件用于创建Vuex中最为核心的store对象
//引入Vue
import Vue from 'vue';
//引入Vuex
import Vuex from 'vuex';
//使用Vuex插件
Vue.use(Vuex);
//创建store并暴露store
export default new Vuex.Store({
state: { //准备state,用于存储数据
count: 0
},
actions: { //准备actions,用于响应组件中的动作
increment (context, value) {
context.commit('INCREMENT', value);
},
decrement (context, value) {
context.commit('DECREMENT', value);
},
incrementIfOdd (context, value) {
const { count } = context.state;
if (count % 2 !== 0) {
context.commit('INCREMENT', value);
}
},
incrementAsync (context, value) {
setTimeout(() => {
context.commit('INCREMENT', value);
}, 2000);
}
},
mutations: { //准备mutations,用于操作数据(state)
INCREMENT (state, value) {
state.count += value;
},
DECREMENT (state, value) {
state.count -= value;
}
},
getters: { //准备getters,用于将state中的数据进行加工
bigCount (state) {
return state.count * 10;
}
}
});
-
components/Count.vue
<template>
<div>
<h2>当前求和为:{{ $store.state.count }}</h2>
<h2>当前求和放大10倍后为:{{ $store.getters.bigCount }}</h2>
<select v-model.number="num">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button @click="increment">+</button>
<button @click="decrement">-</button>
<button @click="incrementIfOdd">奇数再加</button>
<button @click="incrementAsync">异步加</button>
</div>
</template>
<script>
export default {
name: 'Count',
data () {
return {
num: 1,//用户选择的数字
};
},
methods: {
increment () {
this.$store.dispatch('increment', this.num);
},
decrement () {
this.$store.dispatch('decrement', this.num);
},
incrementIfOdd () {
this.$store.dispatch('incrementIfOdd', this.num);
},
incrementAsync () {
this.$store.dispatch('incrementAsync', this.num);
}
},
mounted () {
console.log('Count', this);
}
};
</script>
<style scoped>
button {
margin-left: 5px;
}
</style>
vuex-求和案例-mapState和mapGetters
-
示例:
-
目录结构:
|-- README.md
|-- babel.config.js
|-- package.json
|-- public
| |-- favicon.ico
| `-- index.html
|-- src
| |-- App.vue
| |-- assets
| | `-- logo.png
| |-- components
| | `-- Count.vue
| |-- main.js
| `-- vuex
| `-- store.js
|-- vue.config.js
`-- yarn.lock
-
main.js
import Vue from 'vue';
import App from './App';
//引入store
import store from './vuex/store';
//关闭生产提示
Vue.config.productionTip = false;
new Vue({
el: '#app',
render: h => h(App),
store
});
-
App.vue
<template>
<div>
<Count/>
</div>
</template>
<script>
import Count from './components/Count';
export default {
name: 'App',
components: { Count },
};
</script>
-
vuex/store.js
//该文件用于创建Vuex中最为核心的store对象
//引入Vue
import Vue from 'vue';
//引入Vuex
import Vuex from 'vuex';
//使用Vuex插件
Vue.use(Vuex);
//创建store并暴露store
export default new Vuex.Store({
state: { //准备state,用于存储数据
count: 0,
school: '江苏师范大学',
subject: '前端'
},
actions: { //准备actions,用于响应组件中的动作
increment (context, value) {
context.commit('INCREMENT', value);
},
decrement (context, value) {
context.commit('DECREMENT', value);
},
incrementIfOdd (context, value) {
const { count } = context.state;
if (count % 2 !== 0) {
context.commit('INCREMENT', value);
}
},
incrementAsync (context, value) {
setTimeout(() => {
context.commit('INCREMENT', value);
}, 2000);
}
},
mutations: { //准备mutations,用于操作数据(state)
INCREMENT (state, value) {
state.count += value;
},
DECREMENT (state, value) {
state.count -= value;
}
},
getters: { //准备getters,用于将state中的数据进行加工
bigCount (state) {
return state.count * 10;
}
}
});
-
components/Count.vue
<template>
<div>
<h2>当前求和为:{{ count }}</h2>
<h2>当前求和放大10倍后为:{{ bigCount }}</h2>
<h2>学校名称:{{ school }}</h2>
<h2>学科:{{ subject }}</h2>
<select v-model.number="num">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button @click="increment">+</button>
<button @click="decrement">-</button>
<button @click="incrementIfOdd">奇数再加</button>
<button @click="incrementAsync">异步加</button>
</div>
</template>
<script>
import { mapGetters, mapState } from 'vuex';
export default {
name: 'Count',
data () {
return {
num: 1,//用户选择的数字
};
},
computed: {
/* 靠程序员自己去写计算属性 */
/* count () {
return this.$store.state.count;
},
school () {
return this.$store.state.school;
},
subject () {
return this.$store.state.subject;
},*/
/* 借助mapState生成计算属性,从state中读取数据。(对象写法) */
// ...mapState({ count: 'count', school: 'school', subject: 'subject' }),
/* 借助mapState生成计算属性,从state中读取数据。(数组写法) */
...mapState(['count', 'school', 'subject']),
// bigCount () {
// return this.$store.getters.bigCount;
// },
/* 借助mapGetters生成计算属性,从getters中读取数据。(对象写法) */
// ...mapGetters({bigCount:'bigCount'}),
/* 借助mapGetters生成计算属性,从getters中读取数据。(数组写法) */
...mapGetters(['bigCount'])
},
methods: {
increment () {
this.$store.dispatch('increment', this.num);
},
decrement () {
this.$store.dispatch('decrement', this.num);
},
incrementIfOdd () {
this.$store.dispatch('incrementIfOdd', this.num);
},
incrementAsync () {
this.$store.dispatch('incrementAsync', this.num);
}
},
mounted () {
console.log('Count', mapState({
count: 'count',
school: 'school',
subject: 'subject'
}));
}
};
</script>
<style scoped>
button {
margin-left: 5px;
}
</style>
vue-求和案例-mapActions和mapMutations
-
示例:
-
目录结构:
|-- README.md
|-- babel.config.js
|-- package.json
|-- public
| |-- favicon.ico
| `-- index.html
|-- src
| |-- App.vue
| |-- assets
| | `-- logo.png
| |-- components
| | `-- Count.vue
| |-- main.js
| `-- vuex
| `-- store.js
|-- vue.config.js
`-- yarn.lock
-
main.js
import Vue from 'vue';
import App from './App';
//引入store
import store from './vuex/store';
//关闭生产提示
Vue.config.productionTip = false;
new Vue({
el: '#app',
render: h => h(App),
store
});
-
App.vue
<template>
<div>
<Count/>
</div>
</template>
<script>
import Count from './components/Count';
export default {
name: 'App',
components: { Count },
};
</script>
-
vuex/store.js
//该文件用于创建Vuex中最为核心的store对象
//引入Vue
import Vue from 'vue';
//引入Vuex
import Vuex from 'vuex';
//使用Vuex插件
Vue.use(Vuex);
//创建store并暴露store
export default new Vuex.Store({
state: { //准备state,用于存储数据
count: 0,
school: '江苏师范大学',
subject: '前端'
},
actions: { //准备actions,用于响应组件中的动作
incrementIfOdd (context, value) {
const { count } = context.state;
if (count % 2 !== 0) {
context.commit('INCREMENT', value);
}
},
incrementAsync (context, value) {
setTimeout(() => {
context.commit('INCREMENT', value);
}, 2000);
}
},
mutations: { //准备mutations,用于操作数据(state)
INCREMENT (state, value) {
state.count += value;
},
DECREMENT (state, value) {
state.count -= value;
}
},
getters: { //准备getters,用于将state中的数据进行加工
bigCount (state) {
return state.count * 10;
}
}
});
-
components/Count.vue
<template>
<div>
<h2>当前求和为:{{ count }}</h2>
<h2>当前求和放大10倍后为:{{ bigCount }}</h2>
<h2>学校名称:{{ school }}</h2>
<h2>学科:{{ subject }}</h2>
<select v-model.number="num">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button @click="increment(num)">+</button>
<button @click="decrement(num)">-</button>
<button @click="incrementIfOdd(num)">奇数再加</button>
<button @click="incrementAsync(num)">异步加</button>
</div>
</template>
<script>
import { mapActions, mapGetters, mapMutations, mapState } from 'vuex';
export default {
name: 'Count',
data () {
return {
num: 1,//用户选择的数字
};
},
computed: {
/* 借助mapState生成计算属性,从state中读取数据。(对象写法) */
// ...mapState({ count: 'count', school: 'school', subject: 'subject' }),
/* 借助mapState生成计算属性,从state中读取数据。(数组写法) */
...mapState(['count', 'school', 'subject']),
/* 借助mapGetters生成计算属性,从getters中读取数据。(对象写法) */
// ...mapGetters({bigCount:'bigCount'}),
/* 借助mapGetters生成计算属性,从getters中读取数据。(数组写法) */
...mapGetters(['bigCount'])
},
methods: {
/*
increment () {
this.$store.commit('INCREMENT', this.num);
},
decrement () {
this.$store.commit('DECREMENT', this.num);
},
*/
/* 借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(对象写法) */
...mapMutations({ increment: 'INCREMENT', decrement: 'DECREMENT' }),
/* 借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(数组写法) */
// ...mapMutations([increment,decrement]),
/*
incrementIfOdd () {
this.$store.dispatch('incrementIfOdd', this.num);
},
incrementAsync () {
this.$store.dispatch('incrementAsync', this.num);
},
*/
/* 借助mapActions调用对应的方法,方法中会调用dispatch去联系action(对象写法) */
...mapActions({ incrementIfOdd: 'incrementIfOdd', incrementAsync: 'incrementAsync' }),
/* 借助mapActions调用对应的方法,方法中会调用dispatch去联系action(数组写法) */
// ...mapActions(['incrementIfOdd','incrementAsync'])
},
mounted () {
mapState({ count: '' });
}
};
</script>
<style scoped>
button {
margin-left: 5px;
}
</style>
vuex-求和案例-多组件共享
-
示例:
-
目录结构:
|-- README.md
|-- babel.config.js
|-- package.json
|-- public
| |-- favicon.ico
| `-- index.html
|-- src
| |-- App.vue
| |-- assets
| | `-- logo.png
| |-- components
| | |-- Count.vue
| | `-- Person.vue
| |-- main.js
| `-- vuex
| `-- store.js
|-- vue.config.js
`-- yarn.lock
-
main.js
import Vue from 'vue';
import App from './App';
//引入store
import store from './vuex/store';
//关闭生产提示
Vue.config.productionTip = false;
new Vue({
el: '#app',
render: h => h(App),
store
});
-
App.vue
<template>
<div>
<Count/>
<hr>
<Person/>
</div>
</template>
<script>
import Count from './components/Count';
import Person from './components/Person';
export default {
name: 'App',
components: { Count, Person },
};
</script>
-
vuex/store.js
//该文件用于创建Vuex中最为核心的store对象
//引入Vue
import Vue from 'vue';
//引入Vuex
import Vuex from 'vuex';
//使用Vuex插件
Vue.use(Vuex);
//创建store并暴露store
export default new Vuex.Store({
state: { //准备state,用于存储数据
count: 0,
school: '江苏师范大学',
subject: '前端',
personList: [
{ id: '001', name: '筱晶哥哥' }
]
},
actions: { //准备actions,用于响应组件中的动作
incrementIfOdd (context, value) {
const { count } = context.state;
if (count % 2 !== 0) {
context.commit('INCREMENT', value);
}
},
incrementAsync (context, value) {
setTimeout(() => {
context.commit('INCREMENT', value);
}, 2000);
}
},
mutations: { //准备mutations,用于操作数据(state)
INCREMENT (state, value) {
state.count += value;
},
DECREMENT (state, value) {
state.count -= value;
},
ADD_PERSON (state, value) {
state.personList.unshift(value);
}
},
getters: { //准备getters,用于将state中的数据进行加工
bigCount (state) {
return state.count * 10;
}
}
});
-
components/Count.vue
<template>
<div>
<h2>当前求和为:{{ count }}</h2>
<h2>当前求和放大10倍后为:{{ bigCount }}</h2>
<h2>学校名称:{{ school }}</h2>
<h2>学科:{{ subject }}</h2>
<h3 style="color: red">Person组件的总人数是:{{ personList.length }}</h3>
<select v-model.number="num">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button @click="increment(num)">+</button>
<button @click="decrement(num)">-</button>
<button @click="incrementIfOdd(num)">奇数再加</button>
<button @click="incrementAsync(num)">异步加</button>
</div>
</template>
<script>
import { mapActions, mapGetters, mapMutations, mapState } from 'vuex';
export default {
name: 'Count',
data () {
return {
num: 1,//用户选择的数字
};
},
computed: {
...mapState(['count', 'school', 'subject', 'personList']),
...mapGetters(['bigCount'])
},
methods: {
/* 借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(对象写法) */
...mapMutations({ increment: 'INCREMENT', decrement: 'DECREMENT' }),
/* 借助mapActions调用对应的方法,方法中会调用dispatch去联系action(对象写法) */
...mapActions({ incrementIfOdd: 'incrementIfOdd', incrementAsync: 'incrementAsync' }),
},
mounted () {
mapState({ count: '' });
}
};
</script>
<style scoped>
button {
margin-left: 5px;
}
</style>
-
components/Person.vue
<template>
<div>
<h1>人员列表</h1>
<h3 style="color: skyblue">Count组件的求和是:{{ count }}</h3>
<input v-model.trim="name" placeholder="请输入名字" type="text">
<button @click="add">添加</button>
<ul>
<li v-for="p in personList" :key="p.id">{{ p.name }}</li>
</ul>
</div>
</template>
<script>
import { nanoid } from 'nanoid';
export default {
name: 'Person',
data () {
return {
name: ''
};
},
computed: {
personList () {
return this.$store.state.personList;
},
count () {
return this.$store.state.count;
}
},
methods: {
add () {
let person = { id: nanoid(), name: this.name };
this.$store.commit('ADD_PERSON', person);
this.name = '';
}
}
};
</script>
<style scoped>
</style>
vuex-求和案例-namespaced
-
示例:
-
目录结构:
|-- README.md
|-- babel.config.js
|-- package.json
|-- public
| |-- favicon.ico
| `-- index.html
|-- src
| |-- App.vue
| |-- assets
| | `-- logo.png
| |-- components
| | |-- Count.vue
| | `-- Person.vue
| |-- main.js
| `-- vuex
| |-- count.js
| |-- person.js
| `-- store.js
|-- vue.config.js
`-- yarn.lock
-
main.js
import Vue from 'vue';
import App from './App';
//引入store
import store from './vuex/store';
//关闭生产提示
Vue.config.productionTip = false;
new Vue({
el: '#app',
render: h => h(App),
store
});
-
App.vue
<template>
<div>
<Count/>
<hr>
<Person/>
</div>
</template>
<script>
import Count from './components/Count';
import Person from './components/Person';
export default {
name: 'App',
components: { Count, Person },
};
</script>
-
vuex/count.js
const countOptions = {
namespaced: true,
state: {
count: 0,
school: '江苏师范大学',
subject: '前端',
},
actions: {
incrementIfOdd (context, value) {
const { count } = context.state;
if (count % 2 !== 0) {
context.commit('INCREMENT', value);
}
},
incrementAsync (context, value) {
setTimeout(() => {
context.commit('INCREMENT', value);
}, 2000);
}
},
mutations: {
INCREMENT (state, value) {
state.count += value;
},
DECREMENT (state, value) {
state.count -= value;
},
},
getters: {
bigCount (state) {
return state.count * 10;
}
}
};
export default countOptions;
-
vuex/person.js
const personOptions = {
namespaced: true,
state: {
personList: [
{ id: '001', name: '筱晶哥哥' }
]
},
actions: {
addPersonWang (context, value) {
if (value.name.indexOf('王') === 0) {
context.commit('ADD_PERSON', value);
} else {
alert('添加的姓名不是以王开头');
}
}
},
mutations: {
ADD_PERSON (state, value) {
state.personList.unshift(value);
}
},
getters: {
firstPersonName (state) {
return state.personList[0].name;
}
}
};
export default personOptions;
-
vuex/store.js
//该文件用于创建Vuex中最为核心的store对象
//引入Vue
import Vue from 'vue';
//引入Vuex
import Vuex from 'vuex';
//求和相关配置
import countOptions from './count';
//人员管理相关配置
import personOptions from './person';
//使用Vuex插件
Vue.use(Vuex);
//创建store并暴露store
export default new Vuex.Store({
modules: {
countAbout: countOptions,
personAbout: personOptions
}
});
-
components/Count.vue
<template>
<div>
<h2>当前求和为:{{ count }}</h2>
<h2>当前求和放大10倍后为:{{ bigCount }}</h2>
<h2>学校名称:{{ school }}</h2>
<h2>学科:{{ subject }}</h2>
<h3 style="color: red">Person组件的总人数是:{{ personList.length }}</h3>
<select v-model.number="num">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button @click="increment(num)">+</button>
<button @click="decrement(num)">-</button>
<button @click="incrementIfOdd(num)">奇数再加</button>
<button @click="incrementAsync(num)">异步加</button>
</div>
</template>
<script>
import { mapActions, mapGetters, mapMutations, mapState } from 'vuex';
export default {
name: 'Count',
data () {
return {
num: 1,//用户选择的数字
};
},
computed: {
...mapState('countAbout', ['count', 'school', 'subject']),
...mapState('personAbout', ['personList']),
...mapGetters('countAbout', ['bigCount'])
},
methods: {
/* 借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(对象写法) */
...mapMutations('countAbout', { increment: 'INCREMENT', decrement: 'DECREMENT' }),
/* 借助mapActions调用对应的方法,方法中会调用dispatch去联系action(对象写法) */
...mapActions('countAbout', { incrementIfOdd: 'incrementIfOdd', incrementAsync: 'incrementAsync' }),
},
mounted () {
console.log(this);
}
};
</script>
<style scoped>
button {
margin-left: 5px;
}
</style>
-
components/Person.vue
<template>
<div>
<h1>人员列表</h1>
<h3 style="color: skyblue">Count组件的求和是:{{ count }}</h3>
<h3>列表中第一个人的名字是:{{ firstPersonName }}</h3>
<input v-model.trim="name" placeholder="请输入名字" type="text">
<button @click="add">添加</button>
<button @click="addPersonWang">添加一个姓王的人</button>
<ul>
<li v-for="p in personList" :key="p.id">{{ p.name }}</li>
</ul>
</div>
</template>
<script>
import { nanoid } from 'nanoid';
export default {
name: 'Person',
data () {
return {
name: ''
};
},
computed: {
personList () {
return this.$store.state.personAbout.personList;
},
count () {
return this.$store.state.countAbout.count;
},
firstPersonName () {
return this.$store.getters['personAbout/firstPersonName'];
}
},
methods: {
add () {
let person = { id: nanoid(), name: this.name };
this.$store.commit('personAbout/ADD_PERSON', person);
this.name = '';
},
addPersonWang () {
let person = { id: nanoid(), name: this.name };
this.$store.dispatch('personAbout/addPersonWang', person);
this.name = '';
}
},
mounted () {
console.log(this.$store);
}
};
</script>
<style scoped>
</style>
原文始发于微信公众号(程序员阿晶):Vue高级部分(一)
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/19855.html