参考博客
用来实现局部功能的代码和资源的集合
//第一步:创建student组件
const student = Vue.extend({
template:`
//此处的div必须存在,需要放置一个根节点
学生姓名:{{studentName}}
学生年龄:{{age}}
`,
data(){
return {
studentName:'JOJO',
age:20
}
}
})
2.局部注册组件
//创建vm
new Vue({
el:'#root',
data:{
msg:'你好,JOJO!'
},
//第二步:注册组件(局部注册)
components:{
xuesheng:student //这里的xuesheng为组件的key,及第三步使用的标签名称,student为第一步中创建的组件名称,也可以按照如下方式简写
//student 直接写组件名称,那么,标签也将会是这个
}
})
全局注册
//全局注册组件,第一个参数为组件标签名,即组件key,第二个参数为创建的组件实例
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属性项指定组件在开发者工具中呈现的名字
关于组件标签:
第一种写法:
第二种写法:
备注: 不使用脚手架时,
会导致后续组件不能渲染
组件嵌套
定义子组件
//定义student组件
const student = Vue.extend({
template:`
学生名称:{{name}}
学生年龄:{{age}}
`,
data(){
return {
name:'JOJO',
age:20
}
}
})
//定义school组件
const school = Vue.extend({
template:`
学校名称:{{name}}
学校地址:{{address}}
//此处使用子组件
`,
components:{
student//将student子组件注册到自己当中
},
data(){
return {
name:'尚硅谷',
address:'北京'
}
}
})
//创建vm
new Vue({
el:'#root',
components:{
school
}
})
//在template编写html代码,此处的template标签只是为了区分模块代码,并不具有实际意义,等代码编译后,该标签将不会再存在,需要注意的是,template标签下必须存在一个根节点,即div
<template>
<div id="demo">
</div>
</template>
//在script标签中编写当前组价的js代码,即vue代码
<script>
//此处的 export default为es6语法,作用为将当前组件抛出,便于其他组件导入
export default {
name:'组件名称,通常为文件名',
data() {
return {
}
},
methods: {
},
}
/**
注:
此处的代码实际就是如下代码,只不过Vue.extend可以省略
export default Vue.extend({
template:`
`,
data(){
return {
name:'111'
}
}
methods: {
add(){
alert(this)
}
},
})
**/
</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 为es6语法,作用是将步骤1中通过 export default 抛出的组件进行导入
import School from './School.vue'
//相当于App.vue是所有组件的父组件,由他来管理所有组件,程序到时候只需要引入这一个就可以了,所以他也需要抛出去,便于其他地方引入
export default {
name:'App',
components:{
School
}
}
</script>
3.编写js,用于引入总组件
main.js
//将管理所有组件的app组件导入
import App from './App.vue'
new Vue({
template:` `,
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 脚手架是 Vue 官方提供的标准化开发工具(开发平台)
npm config set registry http://registry.npm.taobao.org
2.全局安装@vue/cli
npm install -g @vue/cli
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: 包版本控制文件,不需要管
文件解析
// main.js是整个项目文件的入口,当执行npm run serve命令后首先执行的就是改文件
//引入Vue
import Vue from 'vue'
//引入app组件,他是所有组件的父组件
import App from './App.vue'
//关闭vue的生产提示
Vue.config.productionTip = false
//创建vue实例对象,vm
new Vue({
//将APP组件放入容器中,即注册组件
render: h => h(App),
}).$mount('#app')
/**
* 以上代码相当于:
*
new Vue({
el:'#app'
render: h => h(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
})
被用来给元素或子组件注册引用信息(等同于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
让组件接收外部传过来的数据
例:
自定义组件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//vue不能对传递过来的参数直接操作,所以需要将传过来的参数age赋值到本组件当中,进行操作
}
},
// 简单声明接收,不对传递过来的参数类型做限制
// props:['name','age','sex']
// 接收的同时对数据进行类型限制
/* props:{
name:String, //表示当前name参数必须是Sting类型
age:Number, //表示当前age参数必须是Number类型
sex:String //表示当前sex参数必须是Sting类型
} */
// 接收的同时对数据进行类型限制 + 指定默认值 + 限制必要性
props:{
name:{
type:String, //表示当前name参数必须为String类型,且必须传递
required:true,
},
age:{
type:Number,//表示当前age参数必须为Number类型,不是必须传递,若不传则默认值为99
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
可以把多个组件共用的配置提取成一个混入对象
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>
//引入自定义的mixin文件,使用该文件中的add方法
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>
//引入自定义的mixin文件,使用该文件中的add方法
import {add_minx} from '../mixin.js'
export default {
name:'School',
data() {
return {
name:"我是学校",
addres:"我是地址"
}
},
mixins:[add_minx]
}
</script>
新建plugin.js,在文件中自定一个插件,作用为,添加一个全局过滤器,给vm,vc添加一个方法
export default {
install(Vue){
//全局过滤器
Vue.filter('mySlice',function(value){
return value.slice(0,4)
})
//给Vue原型上添加一个方法(vm和vc就都能用了)
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>
当多个组件同时被引入时,可能存在样式名重复,比如组件A和组件B都定义了 .demo{}名字相同的样式,scoped 就是让样式在局部生效,防止冲突
使用:
<!--表示当前样式只作用域当前组件-->
<style scoped>
.demo{
color: aliceblue;
}
</style>
<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>
<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(){
//此处触发事件,myEvent指的是时间名称类似于onClick,此处的this.name表示对于事件监听的函数传递的参数
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(){
//为ref属性名为stu的组件,绑定一个名为myEvent的事件,事件触发后执行myFunction方法
this.$refs.stu.$on("myEvent",this.myFunction)
//以上这种绑定方式灵活性更高,比如,需要等待5秒后才为该组件绑定事件,或者,调用接口后再绑定事件
//表示当前事件绑定后只能执行一次,一次过后就会失效
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; //配置全局事件总线,以实现组件通信,此处的 $bus 是自定义的,
}
}).$mount('#app')
1.接收数据:A组件想接收数据,则在A组件中给$bus绑定自定义事件,事件的回调留在A组件自身
例: Student.vue
<script>
export default {
name: 'Student',
methods:{
demo(name) {
console.log("我是学生,我的学校名称是", name)
}
},
mounted() {//组件挂载完毕后执行
//参数1:事件名称,参数2:事件被触发后的回调方法
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(){
//将学校名称传递给sendSchoolName事件
this.$bus.$emit("sendSchoolName",this.name);
}
}
}
</script>
注:应当在beforeDestroy钩子中,用$off去解绑当前组件所用到的事件
npm i pubsub-js
import pubsub from 'pubsub-js'
<script>
//引入pubsub
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>
//引入pubsub
import pubsub from 'pubsub-js'
export default {
name: 'School',
methods:{
sendSchooleName(){
//将学校名称传递给schoolName事件
pubsub.publish('demo',this.name) //发布消息
}
}
}
</script>
$nextTick(回调函数)可以将回调延迟到下次 DOM 更新循环之后执行,比如对一个元素进行操作,当代码执行到此处,元素还没有被渲染,那么可以使用该函数,进行回调操作
//等模板解析完成之后再执行
this.$nextTick(function(){
//编写逻辑
//此处是,将ref为inputEdit的input元素让其获取焦点
this.$refs.inputEdit.focus()
})
npm install axios
//发起请求
axios(
{
method: 'post', //请求方式,默认get
url: '/data.json', //请求地址
// 如果url不是绝对路径,那么会将baseURL和url拼接作为请求的接口地址
// 用来区分不同环境,建议使用
baseURL: 'http://localhost:63342/vue-first/demo',
//请求参数只有当请求方法为'PUT', 'POST',和'PATCH'时可用
data: {
firstName: 'Fred',
lastName: 'Flintstone'
},
// 用于请求之前对请求数据进行操作
// 只用当请求方法为‘PUT’,‘POST’和‘PATCH’时可用
// 最后一个函数需return出相应数据
// 可以修改headers
transformRequest: [function (data, headers) {
// 可以对data做任何操作
return data;
}],
// 用于对相应数据进行处理
// 它会通过then或者catch
transformResponse: [function (data) {
// 可以对data做任何操作
return data;
}],
// 必须是一个纯对象或者 URL参数对象
params: {
ID: 12345
},
// 请求超时时间(毫秒)
timeout: 1000,
// 是否携带cookie信息
withCredentials: false, // default
// 响应格式
// 可选项 'arraybuffer', 'blob', 'document', 'json', 'text', 'stream'
responseType: 'json', // 默认值是json
// 设置http响应内容的最大长度
maxContentLength: 2000,
}
//将请求回来的数据绑定给info
).then(
response => { //此处接收响应回来的数据
console.log("数据结果", response.data)
},
error => {
console.log("请求失败了,失败信息", error)
}
);
@CrossOrigin(origins = "*")
例:
@GetMapping(value = "/getData")
//解决接口调用跨域
@CrossOrigin(origins = "*")
public String getData()
{
return null;
}
2.vue CLI 配置代理
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() {//此处使用vue cli 代理,所以直接写localhost:8080
axios.get("http://localhost:8080/getData").then(
response => { //此处接收响应回来的数据
console.log("数据", response.data)
},
error => {
console.log("请求失败了", error)
}
)
}
}
}
const {
defineConfig
} = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
//关闭语法检查
lintOnSave: false,
//开启代理服务器(方式二)
devServer: {
proxy: {
'/stuApi': { //自定义一个前缀,只要使用stuApi这个前缀,那么,都会代理到target中配置的服务器上
target: 'http://localhost:8002',
//因为添加了前缀,所以访问路径会从http://localhost:8001/data变成http://localhost:8001/stuApi/data
//但是服务器并不存在/stuApi/data的资源,所以通过pathRewrite配置正则表达式,将/stuApi这个前缀去掉
pathRewrite: {
'^/stuApi': '' //将/stuApi的前缀替换为空字符串
},
ws: true, //用于支持websocket,默认值为true
changeOrigin: true //用于控制请求头中的host值,默认值为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', //此处使用Vue cli代理,所以直接使用localhost:8080
url: 'http://localhost:8080/stuApi/getData',
responseType: 'json', // 默认值是json
}
).then(
response => { //此处接收响应回来的数据
console.log("数据2222", response.data)
},
error => {
console.log("请求失败了222", error)
}
);
},
getMsg() {//此处使用Vue cli代理,所以直接使用localhost:8080
axios.get("http://localhost:8080/carApi/getData").then(
response => { //此处接收响应回来的数据
console.log("数据", response.data)
},
error => {
console.log("请求失败了", error)
}
)
}
}
}
总结:
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>
专门在 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
src/vuex/store.js
//引入Vue核心库
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//应用Vuex插件
Vue.use(Vuex)
//准备actions对象——响应组件中用户的动作、处理业务逻辑
const actions = { //此处编写需要对数据处理的逻辑,比如调用接口,或者逻辑判断
//此处编写使用 this.$store.dispatch()方法时的配置
}
//准备mutations对象——修改state中的数据
const mutations = {//当state中的数据需要经过加工后再使用时,可以使用getters加工
}
//准备getters对象——用于将state中的数据进行加工
const getters = {
}
//准备state对象——保存具体的数据
const state = {}
//创建并暴露store
export default new Vuex.Store({
actions,
mutations,
state,
getters
})
2.在main.js
中导入store.js
import Vue from 'vue'
import App from './App.vue'
//引入store文件
import store from "./vuex/store.js"
Vue.config.productionTip = false
new Vue({
render: h => h(App),
store , //应用store
}).$mount('#app')
//引入Vue核心库
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//应用Vuex插件
Vue.use(Vuex)
//准备actions对象——响应组件中用户的动作、处理业务逻辑
const actions = {//此处编写需要对数据处理的逻辑,比如调用接口,或者逻辑判断
//此处编写使用 this.$store.dispatch()方法时的配置
oddJIa(context,value){
//判断当前的和,是否为奇数
if(context.state.sum % 2){
//调用下方mutations中的JIA方法
context.commit('JIA',value)
}
}
}
//准备mutations对象——修改state中的数据
const mutations = {//此处编写对数据的直接操作
//自己定义一个加法,此处的方法名尽量大写,便于与actions中的方法名进行区分
JIA(state,value){
console.log(state,value);
state.sum+=value;
}
}
//准备getters对象——用于将state中的数据进行加工
const getters = {
bigSum(){
return state.sum * 10
}
}
//准备state对象——保存具体的数据
const state = {
//初始化数据
sum:0
}
//创建并暴露store
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(){
//调用store.js中的mutations下的JIA方法
this.$store.commit("JIA",this.n);
},
addOdd(){
//调用store.js中的actions下的oddJIa方法
this.$store.dispatch("oddJIa",this.n);
}
}
}
</script>
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简化this.$store.commit的这个调用方式,
* 同理,add为本组件中定义的方法,JIA为store.js中mutations对象下定义的方法,
* 使用这种写法需要注意,本组件中add方法需要传递参数,否则会出现 Nan,
* 如果,组件中的方法名和store.js中的方法名相同,那么就可以使用数组简写的形式,如下
* 简写形式:
...mapMutations({JIA:'JIA',JIAN:'JIAN'})
*/
...mapMutations({add:'JIA'}),
/**
* 此处使用 mapActions简化 this.$store.dispatch的这个调用方式
* 简写形式:
* ...mapActions({oddJIa:'oddJIa',oddJIa:'addWait'})
*/
...mapActions({addOdd:'oddJIa'})
},
computed:{
/**
* 此处的...(三个点)表示的是将mapState返回的属性,合并到computed中去
此处的sum为store.js中state对象下的属性,这里使用了简写的形式,所以,
template标签中渲染数据的key必须和这里的sum保持一致,也必须和store.js中state对象下的属性sum保持一致,
都使用相同的key
*/
...mapState(["sum"]),
//若不一致,也可以使用这种形式,mySum为本组件中即将渲染的变量
...mapState({mySum:'sum'}),
/**
* mapGetters的作用等同于mapState,但他的作用是获取getters中配置的内容,
并且key也应该和store.js中getters对象下的属性
保持一致
*/
...mapGetters(["bigSum"])
}
}
</script>
<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>
src/vuex/store.js
//引入Vue核心库
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//引入自定义的count.js数据文件
import countOptions from './count.js'
//引入person.js数据文件
import personOptions from './person.js'
//应用Vuex插件
Vue.use(Vuex)
//创建并暴露store
export default new Vuex.Store({
modules:{
countOptions,//ziding
personOptions
}
})
src/vuex/count.js
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
}
}
}
src/vuex/person.js
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>
基本使用:
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',//定义跳转,About组件的路由(路径)
component:xxx //注册自定义的组件
},
{
path:'/xxx2',//定义跳转,Home组件的路由(路径)
component:xxx2 //注册自定义的组件
}
]
})
3.在main.js中应用插件:
//引入VueRouter插件
import VueRouter from 'vue-router'
//引入自定义的src/touter/index.js
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',//定义跳转,About组件的路由(路径)
component:About //注册自定义的组件
},
{
path:'/home',//定义跳转,Home组件的路由(路径)
component:Home//注册自定义的组件
}
]
})
src/main.js
import Vue from 'vue'
import App from './App.vue'
//引入VueRouter插件
import VueRouter from 'vue-router'
//引入自定义的src/touter/index.js
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',//定义跳转,About组件的路由(路径)
component:xxx //注册自定义的组件
},
{
path:'/xxx2',//定义跳转,Home组件的路由(路径)
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>
{
path:'/home/message/detail',
component:Detail,
//第一种写法:props值为对象,该对象中所有的key-value的组合最终都会通过props传给Detail组件
// props:{a:900}
//第二种写法:props值为布尔值,布尔值为true,则把路由收到的所有params参数通过props传给Detail组件
// props:true
//第三种写法:props值为函数,该函数返回的对象中每一组key-value都会通过props传给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', //使用占位符声明接收params参数
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){
//使用push方法进行跳转会在浏览器上留下本次跳转的记录。所以点击浏览器的回退,就会回退到上一个页面
this.$router.push({
name:'xiangqing',
query:{
id:m.id,
title:m.title
}
})
},
showReplace(m){
//使用replace方法跳转,不会在浏览器上留下跳转记录,所以点击回退,就会回退到上上一个页面
this.$router.replace({
name:'xiangqing',
query:{
id:m.id,
title:m.title
}
})
}
}
}
</script>
因为router在进行路由跳转时,每次都会销毁原来的组件,比如通过A组件跳转到B组件,那么跳转的B组件之后,A组件就会被销毁,所以假如说,原本在A组件当中填写的数据,当跳转到B之后,数据就不存在了,那么就可以使用缓存路由组件的这种方式,避免这种情况的发生
//缓存一个路由组件,
<keep-alive include="News"> //include中写想要缓存的组件名称,不写表示缓存全部组件
<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:{//这个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)
//判断当前组件中是否配置了title
if(to.meta.title){
//将当前网页展示的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:{//这个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>
hash模式:
优点:兼容性较好
缺点:地址中永远带着#号,不美观
若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法
例:
history模式:
优点:地址干净,美观
缺点:兼容性和hash模式相比略差,部署上线后不能刷新和输入url,出现404的问题
例:
模式切换:
//创建一个路由器
const router = new VueRouter({
mode:"history"//切换到history模式,默认是hash模式,所以,默认无需配置
routes:[...]
})
解决在history模式下,页面404问题
ElementUI官网
使用vue+elementui搭建的后台管理系统模板
npm i element-ui -S
src/main.js
import Vue from 'vue'
import App from './App.vue'
//引入ElementUI组件库
import ElementUI from 'element-ui';
//引入ElementUI全部样式
import 'element-ui/lib/theme-chalk/index.css';
Vue.config.productionTip = false
//使用ElementUI
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>
npm install babel-plugin-component -D
module.exports = {
presets: [
'@vue/app',
["@babel/preset-env", { "modules": false }]
],
plugins: [
[
"component",
{
"libraryName": "element-ui",
"styleLibraryName": "theme-chalk"
}
]
]
}
src/main.js
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);
/* 或写为
* Vue.use(Button)
* Vue.use(Row)
*/
new Vue({
el:"#app",
render: h => h(App),
})