参考博客
组件化编程
用来实现局部功能的代码和资源的集合
const student = Vue.extend({
template:`
<div> //此处的div必须存在,需要放置一个根节点
<h2>学生姓名:{{studentName}}</h2>
<h2>学生年龄:{{age}}</h2>
</div>
`,
data(){
return {
studentName:'JOJO',
age:20
}
}
})
2.局部注册组件
new Vue({
el:'#root',
data:{
msg:'你好,JOJO!'
},
components:{
xuesheng:student
}
})
全局注册
Vue.component("student",student)
3.使用组件
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>基本使用</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<h1>{{msg}}</h1>
<!-- 第三步:编写组件标签 -->
<student></student>
<!-- 此处若使用的是,xuesheng作为组件key,那么将会是如下形式 -->
<xuesheng></xuesheng>
</div>
</body>
</html>
注意组件的命名规范:
关于组件名:
当组件名由一个单词组成时: 第一种写法(首字母小写):school 第二种写法(首字母大写):School
当组件名由多个单词组成时: 第一种写法(kebab-case命名):my-school 第二种写法(CamelCase命名):MySchool (这种写法需要Vue CLI 脚手架支持) 备注: 组件名尽可能回避HTML中已有的元素名称,例如:h2、H2都不行 可以使用Vue.extend()中的name属性项指定组件在开发者工具中呈现的名字 关于组件标签: 第一种写法: <school></school> 第二种写法:<school/> 备注: 不使用脚手架时,<school/> 会导致后续组件不能渲染
const student = Vue.extend({
template:`
<div>
<h2>学生名称:{{name}}</h2>
<h2>学生年龄:{{age}}</h2>
</div>
`,
data(){
return {
name:'JOJO',
age:20
}
}
})
const school = Vue.extend({
template:`
<div>
<h2>学校名称:{{name}}</h2>
<h2>学校地址:{{address}}</h2>
<student></student> //此处使用子组件
</div>
`,
components:{
student
},
data(){
return {
name:'尚硅谷',
address:'北京'
}
}
})
new Vue({
el:'#root',
components:{
school
}
})
<template>
<div id="demo">
</div>
</template>
<script>
export default {
name:'组件名称,通常为文件名',
data() {
return {
}
},
methods: {
},
}
</script>
<style>
#demo{
background: orange;
}
</style>
1.编写子组件 School.vue:
<template>
<div id='Demo'>
<h2>学校名称:{{name}}</h2>
<h2>学校地址:{{address}}</h2>
<button @click="showName">点我提示学校名</button>
</div>
</template>
<script>
export default {
name:'School',
data() {
return {
name:'尚硅谷',
address:'北京'
}
},
methods: {
showName(){
alert(this.name)
}
},
}
</script>
<style>
#Demo{
background: orange;
}
</style>
2.编写全局组件管理父组件 App.vue
<template>
<div>
<School></School>
</div>
</template>
<script>
import School from './School.vue'
export default {
name:'App',
components:{
School
}
}
</script>
3.编写js,用于引入总组件 main.js
import App from './App.vue'
new Vue({
template:`<App></App>`,
el:'#root',
components:{App}
})
4.编写html展示组件内容
index.html
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>单文件组件练习</title>
</head>
<body>
<div id="root"></div>
<script src="../../js/vue.js"></script>
<script src="./main.js"></script>
</body>
</html>
执行过程: 打开index.html–>加载vue.js–>加载自己编写的main.js–>在main.js中会引入app.vue这个组件–>app.vue组件又会引入School.vue组件——>渲染数据
注:以上流程并不能实际运行,因为import 等关键字,并不能被浏览器解析,需要通过脚手架进行编译,才能被浏览器识别解析
Vue CLI脚手架
Vue 脚手架是 Vue 官方提供的标准化开发工具(开发平台)
- 安装步骤:(前提是已经安装了node)
1.cmd 中执行 配置 npm 淘宝镜像,加速下载
npm config set registry http://registry.npm.taobao.org
2.全局安装@vue/cli
npm install -g @vue/cli
- 使用步骤:
1.在需要创建项目的目录下,进入cmd,,使用如下命令创建项目文件
vue create 项目名称
2.选择vue的使用版本,通过上下键切换,回车确定,此处使用版本2 3.在项目文件目录下(D:\IDEXiangMu\Vue\vue-for ),进入cmd,执行如下命令启动项目
npm run serve
4,在cmd窗口中执行Ctrl+C暂停项目 5.编译打包,执行命令后出现的dist文件夹就是编译后的文件
npm run build
6.全局检查语法,一般不用
npm run lint
脚手架结构说明
.文件目录 ├── node_modules ├── public │ ├── favicon.ico: 页签图标 │ └── index.html: 主页面 ├── src │ ├── assets: 存放静态资源,比如图片,视频 │ │ └── logo.png │ │── component: 存放自定义的组件 │ │ └── HelloWorld.vue │ │── App.vue: 汇总所有组件 │ └── main.js: 整个项目的入口文件,执行启动命令后首先执行的就是该文件 ├── .gitignore: git版本管制忽略的配置,及哪些文件不需要接受Git的管理,在此配置 ├── babel.config.js: babel的配置文件,不需要管 ├── package.json: 应用包配置文件 ,配置应用名称,版本等信息 ├── README.md: 应用描述文件 ├── vue.config.js: 配置脚手架的文件,对脚手架的配置,可以添加到此处 └── package-lock.json: 包版本控制文件,不需要管
文件解析
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({
render: h => h(App),
}).$mount('#app')
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
</body>
</html>
vue.config.js 官方配置参考
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
lintOnSave:false
})
ref属性(id选中器)
被用来给元素或子组件注册引用信息(等同于id属性) 应用在html标签上获取的是真实DOM元素,应用在组件标签上获取的是组件实例对象(vc)
使用对比:
<template>
<div id="app">
<h1 id="title_one">{{msg}}</h1>
<h1 ref="title">{{msg}}</h1>
<!-- 自定义组件school -->
<school ref="sch" />
<button @click="show" ref="btn">点我输出ref</button>
</div>
</template>
<script>
import school from './components/school.vue'
export default {
name: 'App',
components: {
school
},
data() {
return {
msg: "这是app组件"
}
},
methods: {
show() {
console.log("ref信息--》", this.$refs.title)
console.log("ref信息--》", this.$refs.sch)
console.log("ref信息--》", this.$refs.btn)
console.log("ref信息2--》", document.getElementById("title_one"))
}
}
}
</script>
使用方式:
ref的使用方式:this.$refs.名称
id的使用方式:document.getElementById("名称")
注:当ref属性条件到自定义的组件时,那么获取到的将会是VueComponent对象,比如上例中的school
props配置项(组件的数据传递)
让组件接收外部传过来的数据
例: 自定义组件Student.vue
<template>
<div>
<h1>{{msg}}</h1>
<h2>学生姓名:{{name}}</h2>
<h2>学生性别:{{sex}}</h2>
<!--此处使用本组件中变量-->
<h2>学生年龄:{{myAge}}</h2>
<button @click="add" ref="btn">点我增加年龄</button>
</div>
</template>
<script>
export default {
name:'Student',
data() {
return {
msg:"我~~",
myAge:this.age
}
},
props:{
name:{
type:String,
required:true,
},
age:{
type:Number,
default:99
},
sex:{
type:String,
required:true
}
},
methods:{
add(){
this.myAge++
}
}
0}
</script>
使用:
<!------此处传递的参数必须用引号,默认传递的数据类型都是String,对于其他类型需要通过v-bind的形式,vue才会进行判断,例如此处的age需要的类型是Number,如果不使用v-bind那么就会认为是一个字符串,就会报错-------->
<Student name="张三" :age="18" sex="男"/>
注:vue中,不能对通过组件传递过来的参数进行直接操作,需要通过赋值,之后在进行操作,例如上文当中的age,需要将age 赋值到myAge当中,在对myAge进行操作,否则会报错,对于非String类型的参数,在传递时应该使用v-bind的形式,例如上文中的 :age
mixin混入(多个组件使用共有的方法,数据,配置……)
可以把多个组件共用的配置提取成一个混入对象
export const add_minx = {
data(){
return {
x:100,
y:100
}
},
methods:{
add(){
alert(this.name)
}
},
}
<template>
<div>
<h2>学生姓名:{{name}}</h2>
<h2>学生性别:{{sex}}</h2>
<button @click="add" ref="btn">点我</button>
</div>
</template>
<script>
import {add_minx} from '../mixin.js'
export default {
name:'Student',
data() {
return {
name:"我~~",
sex:"女"
}
},
mixins:[add_minx]
}
</script>
School.vue
<template>
<div>
<h2>学校名称:{{name}}</h2>
<h2>学校地址:{{addres}}</h2>
<button @click="add" ref="btn">点我</button>
</div>
</template>
<script>
import {add_minx} from '../mixin.js'
export default {
name:'School',
data() {
return {
name:"我是学校",
addres:"我是地址"
}
},
mixins:[add_minx]
}
</script>
plugin插件(自定插件,及插件使用)
新建plugin.js,在文件中自定一个插件,作用为,添加一个全局过滤器,给vm,vc添加一个方法
export default {
install(Vue){
Vue.filter('mySlice',function(value){
return value.slice(0,4)
})
Vue.prototype.hello = ()=>{alert('你好啊')}
}
}
使用: 1.main.js中开启插件
import Vue from 'vue'
import App from './App.vue'
import plugin from './plugin'
Vue.config.productionTip = false
Vue.use(plugin)
new Vue({
el:"#app",
render: h => h(App)
})
2.在School.vue中使用插件
<template>
<div>
<!--使用插件中的过滤器-->
<h2>学校姓名:{{name | mySlice}}</h2>
<h2>学校地址:{{address}}</h2>
</div>
</template>
<script>
export default {
name:'School',
data() {
return {
name:'尚硅谷atguigu',
address:'北京'
}
},
methods:{
test() {
this.hello()
}
}
}
</script>
scoped样式(多组件防止,样式名重复,样式冲突)
当多个组件同时被引入时,可能存在样式名重复,比如组件A和组件B都定义了 .demo{}名字相同的样式,scoped 就是让样式在局部生效,防止冲突
使用:
<!--表示当前样式只作用域当前组件-->
<style scoped>
.demo{
color: aliceblue;
}
</style>
浏览器的本地存储
- Window.localStorage(可以简写为localStorage)
<script>
let person = {name:"JOJO",age:20}
localStorage.setItem('person',JSON.stringify(person))
Window.localStorage.setItem('person',JSON.stringify(person))
const person = localStorage.getItem('person')
console.log(JSON.parse(person))
localStorage.removeItem('person')
localStorage.clear()
</script>
- Window.sessionStorage(可以简写为sessionStorage)
<script>
let person = {name:"JOJO",age:20}
sessionStorage.setItem('person',JSON.stringify(person))
Window.sessionStorage.setItem('person',JSON.stringify(person))
const person = sessionStorage.getItem('person')
console.log(JSON.parse(person))
sessionStorage.removeItem('person')
sessionStorage.clear()
</script>
Window.sessionStorage 存储的内容会随着浏览器窗口关闭而消失 Window.localStorage 存储的内容,需要手动清除才会消失
自定义事件(组件的数组传递)
<template>
<div id="app">
<!--永久绑定自定义事件-->
<!-- <Student v-on:myEvent="myFunction" />-->
<!--绑定自定义事件,但只执行一次-->
<Student v-on:myEvent.once="myFunction" />
<!--等同于-->
<Student @myEvent.once="myFunction" />
</div>
</template>
<script>
import Student from './components/Student.vue'
export default {
name: 'App',
components: {
Student
},
methods:{
myFunction(name){
console.log("自定义事件的监听方法被调用",name)
}
}
}
</script>
触发事件: Student.vue
<template>
<div>
<h2>学生姓名:{{name}}</h2>
<h2>学生性别:{{sex}}</h2>
<button @click="sendStudentName" ref="btn">点我</button>
</div>
</template>
<script>
export default {
name:'Student',
data() {
return {
name:"我~~",
sex:"女"
}
},
methods:{
sendStudentName(){
this.$emit("myEvent",this.name)
}
}
}
</script>
<template>
<div id="app">
<!--通过函数进行绑定-->
<Student ref="stu" />
</div>
</template>
<script>
import Student from './components/Student.vue'
export default {
name: 'App',
components: {
Student
},
methods:{
myFunction(name){
console.log("自定义事件的监听方法被调用",name)
}
},
mounted(){
this.$refs.stu.$on("myEvent",this.myFunction)
this.$refs.stu.$once("myEvent",this.myFunction)
}
}
</script>
触发事件:(同上)
自定义事件的销毁
this.$off('事件名称')
this.$off(['事件名称1','事件名称2'])
this.$off()
自定义组件绑定原生事件
<!--给自定义组件添加原生事件-->
<Student @click.native="myFunction" />
全局事件总线(组件的数据传递)
全局事件总线是一种可以在任意组件间通信的方式,本质上就是一个对象。
main.js
new Vue({
render: h => h(App),
beforeCreate(){
Vue.prototype.$bus=this;
}
}).$mount('#app')
1.接收数据:A组件想接收数据,则在A组件中给$bus绑定自定义事件,事件的回调留在A组件自身 例: Student.vue
<script>
export default {
name: 'Student',
methods:{
demo(name) {
console.log("我是学生,我的学校名称是", name)
}
},
mounted() {
this.$bus.$on("sendSchoolName", this.demo)
},
beforeDestroy() {
this.$bus.$off("sendSchoolName")
}
}
</script>
2.提供数据:B组件给A组件提供数组,触犯A中定义的事件 例:School.vue
<script>
export default {
name: 'School',
methods:{
sendSchooleName(){
this.$bus.$emit("sendSchoolName",this.name);
}
}
}
</script>
注:应当在beforeDestroy钩子中,用$off去解绑当前组件所用到的事件
消息的发布与订阅(组件的数据传递)
- 安装pubsub(在项目路径下的cmd中执行如下命令)
npm i pubsub-js
import pubsub from 'pubsub-js'
- 接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身
例:Student.vue
<script>
import pubsub from 'pubsub-js'
export default {
name: 'Student',
methods: {
demo(msgName, data) {
console.log("我是学生,我的学校名称是", data)
}
},
mounted() {
this.pubId = pubsub.subscribe('demo', this.demo)
},
beforeDestroy() {
pubsub.unsubscribe(this.pubId)
}
}
</script>
<script>
import pubsub from 'pubsub-js'
export default {
name: 'School',
methods:{
sendSchooleName(){
pubsub.publish('demo',this.name)
}
}
}
</script>
$nextTick(回调函数)
$nextTick(回调函数)可以将回调延迟到下次 DOM 更新循环之后执行,比如对一个元素进行操作,当代码执行到此处,元素还没有被渲染,那么可以使用该函数,进行回调操作
this.$nextTick(function(){
this.$refs.inputEdit.focus()
})
Axios异步通信(接口调用)
npm install axios
axios(
{
method: 'post',
url: '/data.json',
baseURL: 'http://localhost:63342/vue-first/demo',
data: {
firstName: 'Fred',
lastName: 'Flintstone'
},
transformRequest: [function (data, headers) {
return data;
}],
transformResponse: [function (data) {
return data;
}],
params: {
ID: 12345
},
timeout: 1000,
withCredentials: false,
responseType: 'json',
maxContentLength: 2000,
}
).then(
response => {
console.log("数据结果", response.data)
},
error => {
console.log("请求失败了,失败信息", error)
}
);
Axios请求跨域
@CrossOrigin(origins = "*")
例:
@GetMapping(value = "/getData")
@CrossOrigin(origins = "*")
public String getData()
{
return null;
}
2.vue CLI 配置代理
- 服务器单击版
在vue.config.js中添加如下配置:
const {
defineConfig
} = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
lintOnSave: false,
devServer: {
proxy:'http://localhost:8002'
}
})
使用方式:
import axios from 'axios'
export default {
name: 'App',
methods: {
getMsg() {
axios.get("http://localhost:8080/getData").then(
response => {
console.log("数据", response.data)
},
error => {
console.log("请求失败了", error)
}
)
}
}
}
- 微服务版
在vue.config.js中添加如下配置:
const {
defineConfig
} = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
lintOnSave: false,
devServer: {
proxy: {
'/stuApi': {
target: 'http://localhost:8002',
pathRewrite: {
'^/stuApi': ''
},
ws: true,
changeOrigin: true
},
'/carApi': {
target: 'http://localhost:8001',
pathRewrite: {
'^/carApi': ''
},
ws: true,
changeOrigin: true
}
}
}
})
使用方式:
import axios from 'axios'
export default {
name: 'App',
methods: {
getSchoolMsg() {
axios({
method: 'get',
url: 'http://localhost:8080/stuApi/getData',
responseType: 'json',
}
).then(
response => {
console.log("数据2222", response.data)
},
error => {
console.log("请求失败了222", error)
}
);
},
getMsg() {
axios.get("http://localhost:8080/carApi/getData").then(
response => {
console.log("数据", response.data)
},
error => {
console.log("请求失败了", error)
}
)
}
}
}
插槽(自定义组件中编写html)
总结:
- 默认插槽:适用于只需要使用一个插槽的情况(不太推荐)
- 具名插槽:适用于需要使用多个插槽的情况
- 作用域插槽:适用于数据在组件本身,外部应用需要使用组件自己的数据,但是渲染数据的结构不同时使用
1.默认插槽
<template>
<div class="category">
<!-- 此处的 slot 就是定义了一个插槽,当其他组件使用本组件时,就可以在 slot 的位置添加html代码-->
<slot>此处是默认值,当使用者没有传递具体结构时,出现</slot>
</div>
</template>
<template>
<div class="container">
<!--Category 为自定义的组件,当前组件内编写的html代码就会展示到 Category 组件中标注slot插槽的位置-->
<Category >
<img src="https://s3.ax1x.com/2021/01/16/srJlq0.jpg" alt="">
</Category>
<Category >
<ul>
<li v-for="(g,index) in games" :key="index">{{g}}</li>
</ul>
</Category>
<Category >
<video controls src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"></video>
</Category>
</div>
</template>
<script>
import Category from './components/Category'
export default {
name:'App',
components:{Category},
data() {
return {
games:['植物大战僵尸','红色警戒','空洞骑士','王国']
}
},
}
</script>
注:当使用自定义组件时,如果不对组件中的插槽值进行填充,那么组件就会展示slot标签中的内容 2.具名插槽
<template>
<div class="category">
<!--此处定义了一个名为 center 的插槽-->
<slot name="center">此处是默认值,当使用者没有传递具体结构时,出现</slot>
<slot name="footer">此处是默认值,当使用者没有传递具体结构时,出现</slot>
</div>
</template>
<template>
<div class="container">
<Category >
<!--指定使用名为center的插槽-->
<img slot="center" src="https://s3.ax1x.com/2021/01/16/srJlq0.jpg" alt="">
<a slot="footer" href="http://www.atguigu.com">更多美食</a>
</Category>
<Category >
<ul slot="center">
<li v-for="(g,index) in games" :key="index">{{g}}</li>
</ul>
<!--当前这个div下的html代码都会使用名为footer的这个插槽,但是会额外增加一个div的结构-->
<div class="foot" slot="footer">
<a href="http://www.atguigu.com">单机游戏</a>
<a href="http://www.atguigu.com">网络游戏</a>
</div>
</Category>
<Category >
<video slot="center" controls src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"></video>
<!--当前这个template 下的html代码都会使用名为footer的这个插槽,且不会增减额外结构-->
<template v-slot:footer>
<div class="foot">
<a href="http://www.atguigu.com">经典</a>
<a href="http://www.atguigu.com">热门</a>
<a href="http://www.atguigu.com">推荐</a>
</div>
<h4>欢迎前来观影</h4>
</template>
</Category>
</div>
</template>
<script>
import Category from './components/Category'
export default {
name:'App',
components:{Category},
data() {
return {
games:['植物大战僵尸','红色警戒','空洞骑士','王国']
}
},
}
</script>
3.作用域插槽 (数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定)
<template>
<div class="category">
<h3>{{title}}分类</h3>
<!-- 定义一个插槽 ,当谁使用这个插槽时,就给他传一个数据 games-->
<slot :games="games">我是一些默认值,当使用者没有传递具体结构时,我会出现1</slot>
</div>
</template>
<script>
export default {
name:'Category',
props:['title'],
data() {
return {
games:['植物大战僵尸','红色警戒','空洞骑士','王国']
}
},
}
</script>
<template>
<div class="container">
<Category title="游戏" >
<!--此处使用scope定义一个data,用于接收插槽传过来的数据,-->
<template scope="data">
<ul>
<!-- 此处的 data.games 就是插槽当中的传递过来的数据,这个 games 是插槽当中定义的,插槽当中写的是什么那就是什么 -->
<li v-for="(g,index) in data.games" :key="index">{{g}}</li>
</ul>
</template>
</Category>
<Category title="游戏" >
<!-- 也可以使用{games}这样的语法,等同于data.games -->
<template scope="{games}">
<ol>
<li v-for="(g,index) in games" :key="index">{{g}}</li>
</ol>
</template>
</Category>
<Category title="游戏" >
<!-- 也可以使用solt-scope= "{games}"这样的语法,等同于scope="{games}" -->
<template solt-scope= "{games}">
<h4 v-for="(g,index) in games" :key="index">{{g}}</h4>
</template>
</Category>
</div>
</template>
<script>
import Category from './components/Category'
export default {
name:'App',
components:{Category}
}
</script>
Vuex(多组件操作同一数据)
专门在 Vue 中实现集中式数据管理的一个 Vue 插件,对 vue 应用中多个组件的共享数据进行集中式的管理(读/写),也是一种组件间通信的方式,且适用于任意组件间通信
组件中读取vuex中的数据:$store.state.自定义的key 组件中读取vuex中getters的数据:$store.getters.自定义的key (当state中的数据需要经过加工后再使用时,可以使用getters加工)
组件中修改vuex中的数据:this.$store.dispatch('action中的方法名',数据) 或 this.$store.commit('mutations中的方法名',数据)
注:若调用过程的业务逻辑简单,比如下文使用案例中数据求和的add(),那么就可以越过actions,即不写dispatch,直接编写commit,但是像案例中的addOdd(),因为逻辑较为复杂,比如真是的业务场景可能会请求接口等操作,那么就应该使用dispatch,在actions中编写业务逻辑,或请求接口等操作
Vuex的基本使用
npm i vuex
npm i vuex@3
- 搭建环境
1.新建src/vuex/store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const actions = {
}
const mutations = {
}
const getters = {
}
const state = {}
export default new Vuex.Store({
actions,
mutations,
state,
getters
})
2.在main.js 中导入store.js
import Vue from 'vue'
import App from './App.vue'
import store from "./vuex/store.js"
Vue.config.productionTip = false
new Vue({
render: h => h(App),
store ,
}).$mount('#app')
- 使用案例
store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const actions = {
oddJIa(context,value){
if(context.state.sum % 2){
context.commit('JIA',value)
}
}
}
const mutations = {
JIA(state,value){
console.log(state,value);
state.sum+=value;
}
}
const getters = {
bigSum(){
return state.sum * 10
}
}
const state = {
sum:0
}
export default new Vuex.Store({
actions,
mutations,
state,
getters
})
自定义组件中调用:
<template>
<div class="category">
<!--读取数据-->
当前求和:{{$store.state.sum}}
<h3>当前求和的10倍为:{{$store.getters.bigSum}}</h3>
<select v-model="n">
<option :value="1">1</option>
<option :value="2">2</option>
<option :value="3">3</option>
</select>
<button @click="add">+</button>
<button @click="addOdd">奇数再加</button>
</div>
</template>
<script>
export default {
name:'Count',
data(){
return {
n:1,
sum:0
}
},
methods:{
add(){
this.$store.commit("JIA",this.n);
},
addOdd(){
this.$store.dispatch("oddJIa",this.n);
}
}
}
</script>
使用mapState,mapGetters, mapActions,mapMutations简化调用方式
1.引入mapState,mapGetters
import {mapState,mapGetters,mapMutations,mapActions} from 'vuex'
2.使用
<template>
<div class="category">
<!--读取数据-->
当前求和:{{sum}}
<h3>当前求和的10倍为:{{bigSum}}</h3>
<select v-model="n">
<option :value="1">1</option>
<option :value="2">2</option>
<option :value="3">3</option>
</select>
<button @click="add(n)">+</button>
<button @click="addOdd(n)">奇数再加</button>
</div>
</template>
<script>
import {mapState,mapGetters,mapMutations,mapActions} from 'vuex'
export default {
name:'Count',
data(){
return {
n:1
}
},
methods:{
...mapMutations({add:'JIA'}),
...mapActions({addOdd:'oddJIa'})
},
computed:{
...mapState(["sum"]),
...mapState({mySum:'sum'}),
...mapGetters(["bigSum"])
}
}
</script>
Vuex多组件数据共享
- 定义Student.vue读取通过Count.vue改边过的数值
<template>
<div class="category">
我是学生组件,我拿到了当前求和数值:{{sum}}
</div>
</template>
<script>
import {mapState,mapGetters,mapMutations,mapActions} from 'vuex'
export default {
name:'Student',
computed:{
...mapState(["sum"])
}
}
</script>
-在App.vue中引入Student.vue
<template>
<div class="container">
<Count/>
<hr>
<Student/>
</div>
</template>
<script>
import Count from './components/Count'
import Student from './components/Student'
export default {
name:'App',
components:{Count,Student}
}
</script>
- 效果
Vuex模块化,对共享数据进行分类(推荐使用)
- 安装Vuex
- 创建
src/vuex/store.js
import Vue from 'vue'
import Vuex from 'vuex'
import countOptions from './count.js'
import personOptions from './person.js'
Vue.use(Vuex)
export default new Vuex.Store({
modules:{
countOptions,
personOptions
}
})
export default{
namespaced:true,
actions:{
addOdd(context,value){
console.log("actions中的addOdd被调用了")
if(context.state.sum % 2){
context.commit('ADD',value)
}
},
addWait(context,value){
console.log("actions中的addWait被调用了")
setTimeout(()=>{
context.commit('ADD',value)
},500)
}
},
mutations:{
ADD(state,value){
state.sum += value
},
SUBTRACT(state,value){
state.sum -= value
}
},
state:{
sum:0,
name:'111',
school:'尚硅谷',
},
getters:{
bigSum(state){
return state.sum * 10
}
}
}
import axios from "axios"
import { nanoid } from "nanoid"
export default{
namespaced:true,
actions:{
addPersonWang(context,value){
if(value.name.indexOf('王') === 0){
context.commit('ADD_PERSON',value)
}else{
alert('添加的人必须姓王!')
}
},
addPersonServer(context){
axios.get('http://api.uixsj.cn/hitokoto/get?type=social').then(
response => {
context.commit('ADD_PERSON',{id:nanoid(),name:response.data})
},
error => {
alert(error.message)
}
)
}
},
mutations:{
ADD_PERSON(state,value){
console.log('mutations中的ADD_PERSON被调用了')
state.personList.unshift(value)
}
},
state:{
personList:[
{id:'001',name:'JOJO'}
]
},
getters:{
firstPersonName(state){
return state.personList[0].name
}
}
}
- 创建
src/components/Count.vue
<template>
<div>
<h1>当前求和为:{{sum}}</h1>
<h3>当前求和的10倍为:{{bigSum}}</h3>
<h3>我是{{name}},我在{{school}}学习</h3>
<h3 style="color:red">Person组件的总人数是:{{personList.length}}</h3>
<select v-model.number="n">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button @click="increment(n)">+</button>
<button @click="decrement(n)">-</button>
<button @click="incrementOdd(n)">当前求和为奇数再加</button>
<button @click="incrementWait(n)">等一等再加</button>
</div>
</template>
<script>
import {mapState,mapGetters,mapMutations,mapActions} from 'vuex'
export default {
name:'Count',
data() {
return {
n:1,
}
},
methods: {
...mapMutations('countOptions',{increment:'ADD',decrement:'SUBTRACT'}),
...mapActions('countOptions',{incrementOdd:'addOdd',incrementWait:'addWait'})
},
computed:{
...mapState('countOptions',['sum','school','name']),
...mapGetters('countOptions',['bigSum']),
...mapState('personOptions',['personList'])
}
}
</script>
<style>
button{
margin-left: 5px;
}
</style>
- 创建
src/components/Person.vue
<template>
<div>
<h1>人员列表</h1>
<h3 style="color:red">Count组件求和为:{{sum}}</h3>
<h3>列表中第一个人的名字是:{{firstPersonName}}</h3>
<input type="text" placeholder="请输入名字" v-model="name">
<button @click="add">添加</button>
<button @click="addWang">添加一个姓王的人</button>
<button @click="addPerson">随机添加一个人</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.personOptions.personList
},
sum(){
return this.$store.state.personOptions.sum
},
firstPersonName(){
return this.$store.getters['personOptions/firstPersonName']
}
},
methods: {
add(){
const personObj = {id:nanoid(),name:this.name}
this.$store.commit('personOptions/ADD_PERSON',personObj)
this.name = ''
},
addWang(){
const personObj = {id:nanoid(),name:this.name}
this.$store.dispatch('personOptions/addPersonWang',personObj)
this.name = ''
},
addPerson(){
this.$store.dispatch('personOptions/addPersonServer')
}
},
}
</script>
App.vue
<template>
<div class="container">
<Count/>
<hr>
<Person/>
</div>
</template>
<script>
import Count from './components/Count'
import Person from './components/Person'
export default {
name:'App',
components:{Count,Person}
}
</script>
Vue Router路由管理器(单页面应用)
基本使用: 1.安装vue-router
npm i vue-router
2.编写router配置项,新建src/router/index.js 文件
import VueRouter from "vue-router";
import xxx from '../components/xxx'
import xxx2 from '../components/xxx2 '
export default new VueRouter({
routes:[
{
path:'/xxx',
component:xxx
},
{
path:'/xxx2',
component:xxx2
}
]
})
3.在main.js中应用插件:
import VueRouter from 'vue-router'
import router from './router/index.js'
Vue.config.productionTip = false
Vue.use(VueRouter)
new Vue({
el:"#app",
render: h => h(App),
router
})
4.在App.vue组件中使用路由跳转组件
<!-- Vue中借助router-link标签实现路由的切换,
此处的属性to中配置的是在src/router/index.js中配置的组件路由路径,
active-class中配置的是,当选中当前路由后,导航栏执行的样式
-->
<router-link class="list-group-item" active-class="active" to="/about"> About</router-link>
<br/>
<router-link class="list-group-item" active-class="active" to="/home">Home</router-link>
<!-- 指定通过路由跳转的组件的呈现位置 -->
<router-view></router-view>
注:路由组件通常存放在src/pages 文件夹,一般组件通常存放在src/components 文件夹,通过切换,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载,每个组件都有自己的 $route属性,里面存储着自己的路由信息,整个应用只有一个router,可以通过组件的$router属性获取到
使用步骤案例:
npm i vue-router@3
npm i vue-router
- 配置路由
新建src/router/index.js 文件
import VueRouter from "vue-router";
import Home from '../components/Home'
import About from '../components/About'
export default new VueRouter({
routes:[
{
path:'/about',
component:About
},
{
path:'/home',
component:Home
}
]
})
import Vue from 'vue'
import App from './App.vue'
import VueRouter from 'vue-router'
import router from './router/index.js'
Vue.config.productionTip = false
Vue.use(VueRouter)
new Vue({
el:"#app",
render: h => h(App),
router
})
- 自定义组件
src/components/Home.vue
<template>
<h2>我是Home组件的内容</h2>
</template>
<script>
export default {
name:'Home'
}
</script>
src/components/About.vue
<template>
<h2>我是About组件的内容</h2>
</template>
<script>
export default {
name:'About'
}
</script>
<template>
<div>
<div class="row">
<div class="col-xs-offset-2 col-xs-8">
<div class="page-header"><h2>Vue Router Demo</h2></div>
</div>
</div>
<div class="row">
<div class="col-xs-2 col-xs-offset-2">
<div class="list-group">
<!-- 原始html中我们使用a标签实现页面跳转 -->
<!-- <a class="list-group-item active" href="./about.html">About</a>
<a class="list-group-item" href="./home.html">Home</a> -->
<!-- Vue中借助router-link标签实现路由的切换,
此处的属性to中配置的是在src/router/index.js中配置的组件路由路径,
active-class中配置的是,当选中当前路由后,导航栏执行的样式 -->
<router-link class="list-group-item" active-class="active" to="/about">
About
</router-link>
<br/>
<router-link class="list-group-item" active-class="active" to="/home">
Home
</router-link>
</div>
</div>
<div class="col-xs-6">
<div class="panel">
<div class="panel-body">
<!-- 指定通过路由跳转的组件的呈现位置 -->
<router-view></router-view>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name:'App',
}
</script>
路由嵌套(一个组件中跳转另一个子组件)
- 配置路由规则
\src\router\index.js
import VueRouter from "vue-router";
import xxx from '../components/xxx'
import xxx2 from '../components/xxx2 '
export default new VueRouter({
routes:[
{
path:'/xxx',
component:xxx
},
{
path:'/xxx2',
component:xxx2
children:[
{
path:'xx22',
component:xx22
},
{
path:'message',
component:Message
}
]
}
]
})
<router-link to="/xx2/xx22">News</router-link>
<router-link to="/xx2/xx222">News</router-link>
路由跳转传递参数
1.传递query参数(推荐)
<!-- 跳转并携带query参数,to的字符串写法 -->
<router-link :to="/home/message/detail?id=666&title=你好">跳转</router-link>
<!-- 跳转并携带query参数,to的对象写法 -->
<router-link :to="{
path:'/home/message/detail',
query:{
id:666,
title:'你好'
}
}">跳转</router-link>
$route.query.id
$route.query.title
例:
<ul>
<li>消息编号:{{$route.query.id}}</li>
<li>消息标题:{{$route.query.title}}</li>
</ul>
- 通过query传递参数,可以结合props配置,让路由组件更方便的收到参数
{
path:'/home/message/detail',
component:Detail,
props(route){
return {
id:route.query.id,
title:route.query.title
}
}
}
例:
<template>
<ul>
<li>消息编号:{{id}}</li>
<li>消息标题:{{title}}</li>
</ul>
</template>
<script>
export default {
name:'Detail',
props:['id','title']
}
</script>
2.传递params参数
{
path:'/home',
component:Home,
children:[
{
path:'news',
component:News
},
{
component:Message,
children:[
{
name:'xiangqing',
path:'detail/:id/:title',
component:Detail
}
]
}
]
}
<!-- 跳转并携带params参数,to的字符串写法 -->
<router-link :to="/home/message/detail/666/你好">跳转</router-link>
<!-- 跳转并携带params参数,to的对象写法 -->
<router-link
:to="{
name:'xiangqing',
params:{
id:666,
title:'你好'
}
}"
>跳转</router-link>
$route.params.id
$route.params.title
例:
<ul>
<li>消息编号:{{$route.params.id}}</li>
<li>消息标题:{{$route.params.title}}</li>
</ul>
注:路由携带params参数时,若使用to的对象写法,则不能使用path配置项,必须使用name配置!
命名路由(简化跳转路径)
{
path:'/demo',
component:Demo,
children:[
{
path:'test',
component:Test,
children:[
{
name:'hello'
path:'welcome',
component:Hello,
}
]
}
]
}
<!--简化前,需要写完整的路径 -->
<router-link to="/demo/test/welcome">跳转</router-link>
<!--简化后,直接通过名字跳转 -->
<router-link :to="{name:'hello'}">跳转</router-link>
<!--简化写法配合传递参数 -->
<router-link
:to="{
name:'hello',
query:{
id:666,
title:'你好'
}
}"
>跳转</router-link>
控制路由跳转时操作浏览器的后退前进记录
浏览器的历史记录有两种写入方式:push和replace,其中push是追加历史记录,replace是替换当前记录。路由跳转时候默认为push方式
使用:
<router-link replace ...>News</router-link>
编程式路由导航(定时跳转,自动跳转)
使用router-link标签进行跳转,最终router-link都会解析成一个a标签,但是有的时候并不想代码结构中出现这个a标签,所以为了不破坏代码结构,就可以使用变成编程式路由导航从而实现这个功能,或者,有的时候我们希望可以实现,定时跳转,或者自动跳转,那么就可以实现这种方式
this.$router.push({
name:'xiangqing',
params:{
id:xxx,
title:xxx
}
})
this.$router.replace({
name:'xiangqing',
params:{
id:xxx,
title:xxx
}
})
this.$router.forward()
this.$router.back()
this.$router.go(1)
例:
<template>
<div>
<ul>
<li v-for="m in messageList" :key="m.id">
<!--通过h1标签进行跳转-->
<h1 @click="showPush(m)">
{{m.title}} - push查看
</h1>
<h1 @click="showReplace(m)">
{{m.title}} - >replace查看
</h1>
</li>
</ul>
<hr/>
<router-view></router-view>
</div>
</template>
<script>
export default {
name:'News',
data(){
return{
messageList:[
{id:'001',title:'消息001'},
{id:'002',title:'消息002'},
{id:'003',title:'消息003'}
]
}
},
methods:{
showPush(m){
this.$router.push({
name:'xiangqing',
query:{
id:m.id,
title:m.title
}
})
},
showReplace(m){
this.$router.replace({
name:'xiangqing',
query:{
id:m.id,
title:m.title
}
})
}
}
}
</script>
缓存路由组件(通过路由跳转到其他页面后,保存原页面数据)
因为router在进行路由跳转时,每次都会销毁原来的组件,比如通过A组件跳转到B组件,那么跳转的B组件之后,A组件就会被销毁,所以假如说,原本在A组件当中填写的数据,当跳转到B之后,数据就不存在了,那么就可以使用缓存路由组件的这种方式,避免这种情况的发生
<keep-alive include="News">
<router-view></router-view>
</keep-alive>
<keep-alive :include="['News','Message']">
<router-view></router-view>
</keep-alive>
路由守卫(路由跳转时,进行过滤)
以下配置均在src/router/index.js 文件中进行:
import VueRouter from "vue-router";
const router = new VueRouter({
routes:[
{ name:'xinwen',
path:'news',
component:News,
meta:{
isAuth:true,
title:'我是网页的小标题'
}
}
]
})
router.beforeEach((to,from,next)=>{
console.log('beforeEach',to,from)
if(to.meta.isAuth){
if(localStorage.getItem('username') === '管理员'){
next()
}else{
alert('你不是管理员,暂无权限查看')
}
}else{
next()
}
})
router.afterEach((to,from) => {
console.log('afterEach',to,from)
if(to.meta.title){
document.title = to.meta.title
}else{
document.title = '我是默认的网页标题'
}
})
export default router
import VueRouter from "vue-router";
const router = new VueRouter({
routes:[
{ name:'xinwen',
path:'news',
component:News,
meta:{
isAuth:true,
title:'我是网页的小标题'
},
beforeEnter(to,from,next){
if(localStorage.getItem('username') === '管理员'){
next()
}else{
alert('你不是管理员,暂无权限查看')
}
}else{
next()
}
}
}
]
})
export default router
- 组件内部守卫
在自定义组件src/pages/About.vue 中:
<template>
<h2>我是About组件的内容</h2>
</template>
<script>
export default {
name:'About',
beforeRouteEnter (to, from, next) {
if(localStorage.getItem('username') === '管理员'){
next()
}else{
alert('你不是管理员,暂无权限查看')
}
}else{
next()
}
},
beforeRouteLeave (to, from, next) {
console.log('About--beforeRouteLeave',to,from)
next()
}
}
</script>
路由器的两种工作模式
const router = new VueRouter({
mode:"history"
routes:[...]
})
解决在history模式下,页面404问题
Vue UI组件库
ElementUI官网
使用vue+elementui搭建的后台管理系统模板
ElementUI全局引入
npm i element-ui -S
import Vue from 'vue'
import App from './App.vue'
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.config.productionTip = false
Vue.use(ElementUI)
new Vue({
el:"#app",
render: h => h(App),
})
src/App.vue
<template>
<div>
<br>
<el-row>
<el-button icon="el-icon-search" circle></el-button>
<el-button type="primary" icon="el-icon-edit" circle></el-button>
<el-button type="success" icon="el-icon-check" circle></el-button>
<el-button type="info" icon="el-icon-message" circle></el-button>
<el-button type="warning" icon="el-icon-star-off" circle></el-button>
<el-button type="danger" icon="el-icon-delete" circle></el-button>
</el-row>
</div>
</template>
<script>
export default {
name:'App',
}
</script>
ElementUI按需引入
- 安装 babel-plugin-component
npm install babel-plugin-component -D
module.exports = {
presets: [
'@vue/app',
["@babel/preset-env", { "modules": false }]
],
plugins: [
[
"component",
{
"libraryName": "element-ui",
"styleLibraryName": "theme-chalk"
}
]
]
}
import Vue from 'vue'
import App from './App.vue'
import { Button,Row } from 'element-ui'
Vue.config.productionTip = false
Vue.component(Button.name, Button);
Vue.component(Row.name, Row);
new Vue({
el:"#app",
render: h => h(App),
})
|