数据驱动视图
优点:vue监听数据的变化,进而自动渲染刷新页面
缺点:数据驱动视图是单向的(数据变化—>刷新页面)
双向数据绑定
在网页中,表单负责采集数据,ajax负责提交数据
M:Model 页面渲染数据所依赖的数据源,对应vue中的data 数据
V:View 视图,当前页面所渲染的DOM结构,对应页面DOM结构
VM:ViewModel 是MVVM的核心,表示vue的实例对象
Object.defineProperty(obj, prop, descriptor)
参数1: obj 要定义属性的对象
参数2:prop 要定义或修改的属性的名称或 Symbol
参数3:descriptor 要定义或修改的属性值
返回值:被传递给函数的对象
<script>
let person = {
name:"dudu",
age:4,
}
Object.defineProperty(person,'gender',{
value:'女'
})
console.log(person) //{name: 'dudu', age: 4, gender: '女'}
</script>
let person = {
name:"dudu",
age:4,
}
Object.defineProperty(person,'gender',{
value:'女',
enumerable:true, //是否可枚举
})
console.log(Object.keys(person)) //['name', 'age','gender'] console.log(Object.keys(person)) //['name', 'age']
<script>
let number = 19
let person = {
name:'嘟嘟',
sex:'女'
}
Object.defineProperty(person,'age',{
// 当读取age属性时会调用
// 必须有返回值,返回值就是age的值
get() {
console.log("有人在读取age属性")
return number
},
set(val) {
console.log("age属性被修改了!")
number = val
}
})
</script>
let obj1 = {x:100}
let obj2 = {y:200}
// 通过obj2操作obj1中的x
Object.defineProperty(obj2,'x',{
get() {
return obj1.x
},
set(val) {
obj1.x = val
}
})
通过Object.defineProperty()把data对象中的所有属性添加到vm上。
为每一个添加到vm上的属性,指定一个getter、setter
在getter、setter内部去操作(读、写)data对应的属性
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>vue基本用法title>
<script src="./lib/vue-2.6.14.js">script>
head>
<body>
<div id="app">{{username}}div>
<script>
const vm = new Vue({
// el是固定用法,表示当前vm实例要控制的区域,值是一个选择器
el:'#app',
// data对象就是要渲染到页面的数据
data:{
username:'嘟嘟'
}
})
script>
body>
html>
安装的参考链接
注:安装好后需要重启谷歌才生效
作用:用于辅助开发者渲染DOM元素的文本内容
<body>
<div id="app">
<p>
姓名:
<span v-text="username">span>
p>
<p>
性别:{{gender}}
p>
<div v-html="info">div>
div>
<script>
const vm = new Vue({
// el是固定用法,表示当前vm实例要控制的区域,值是一个选择器
el:'#app',
// data对象就是要渲染到页面的数据
data:{
username:'嘟嘟',
gender:"女",
info:`vue.js学习
`
}
})
script>
body>
动态绑定属性值
<input type="text" :placeholder="tips">
在vue的模板渲染语法中除了支持绑定数据值,还可以支持JavaScript表达式的运算,但一般不推荐在HTML中写js表达式
<div>{{age+1}}</div>
:class=“xxx” xxx可以是字符串、对象、数组。
字符串写法适用于:类名不确定,要动态获取。
对象写法适用于:要绑定多个样式,个数不确定,名字也不确定。
数组写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用。
:style="{fontSize: xxx}“其中xxx是动态值。
:style=”[a,b]"其中a、b是样式对象。
和事件处理函数methods搭配使用,@定义的方法要写在methods中
在绑定事件处理函数时可以进行传参
<body>
<div id="app">
<p>值:{{num}}p>
<button @click="addNum(5)">加法button>
<button @click="subNum">减法button>
div>
<script>
const vm = new Vue({
// el是固定用法,表示当前vm实例要控制的区域,值是一个选择器
el:'#app',
// data对象就是要渲染到页面的数据
data:{
num:0
},
methods: {
addNum(n) {
this.num += n
},
subNum() {
this.num--
}
}
})
script>
body>
注:vue2 中的methods方法中this指向的是vm实例
<body>
<div id="app">
<p>值:{{num}}p>
<button @click="addNum">加法button>
div>
<script>
const vm = new Vue({
// el是固定用法,表示当前vm实例要控制的区域,值是一个选择器
el:'#app',
// data对象就是要渲染到页面的数据
data:{
num:0
},
methods: {
addNum(e) {
this.num += 1
// // 判断奇偶
if(this.num % 2 ===0){
// e.target 获取当前操作的DOM
e.target.style.backgroundColor = "skyBlue"
e.target.style.color = "#fff"
}else {
e.target.style.backgroundColor = ""
e.target.style.color = "#000"
}
}
}
})
script>
body>
<button @click="addNum(2,$event)">加法</button>
<a href="https://www.baidu.com/" @click.prevent="showInfo">百度a>
vue中的事件修饰符
用于监听详细的键盘事件
<body>
<div id="app">
<input type="text" @keyup.esc="cleanInput" @keyup.enter="enterFun">
div>
<script>
const vm = new Vue({
el:'#app',
data: {
},
methods: {
cleanInput(e) {
// 清空输入
e.target.value = ''
},
enterFun(e) {
console.log(`输入的值为"${e.target.value}"`)
}
}
})
script>
body>
作用:在不操作DOM的前提下,快速捕获表单数据
<body>
<div id="app">
<p>用户名:{{username}}p>
<input type="text" v-model="username">
<p><input type="text" :value="username">p>
div>
<script>
const vm = new Vue({
el:'#app',
data: {
username:"嘟嘟"
}
})
script>
body>
input输入框
type=“radio”
type=“checkbox”
type=“text”
…
textarea
select
控制元素的显示和隐藏
v-if可以和v-else-if、v-else搭配使用
<div v-if="type === 'A'">优秀div>
<div v-else-if="type === 'B'">良好div>
<div v-else-if="type === 'C'">及格div>
<div v-else>差div>
v-for=“被循环的每一项 in 待循环数组”
或者
v-for=“被循环的每一项 of 待循环数组”
<body>
<script src="./lib/vue-2.6.14.js">script>
<div id="app">
<table class="table table-borderd table-hover">
<thead>
<th>索引th>
<th>IDth>
<th>姓名th>
thead>
<tbody>
<tr v-for="(item,index) of list" :key="item.id">
<td>{{index}}td>
<td>{{item.id}}td>
<td>{{item.name}}td>
tr>
tbody>
table>
div>
<script>
const vm = new Vue({
el:'#app',
data: {
list: [{
id:1,
name:"张三"
},{
id:2,
name:"李四"
},{
id:3,
name:"王二"
},{
id:4,
name:"麻子"
}]
}
})
script>
body>
虚拟DOM中key的作用:
key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】, 随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下:
对比规则:
(1).旧虚拟DOM中找到了与新虚拟DOM相同的key:
①.若虚拟DOM中内容没变, 直接使用之前的真实DOM!
②.若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM。
(2). 旧虚拟DOM中未找到与新虚拟DOM相同的key
创建新的真实DOM,随后渲染到到页面。
用index作为key可能会引发的问题
1) 若对数据进行:逆序添加、逆序删除等破坏顺序操作:会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。
2) 如果结构中还包含输入类的DOM:会产生错误DOM更新 ==> 界面有问题。
开发中如何选择key?:
1)最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。
2)如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的。
<body>
<div id="app">
<div class="card">
<div class="card-header">
添加品牌
div>
<div class="card-body">
<form>
<div class="form-row align-items-center">
<div class="col-auto">
<div class="input-group mb-2">
<div class="input-group-prepend">
<div class="input-group-text">品牌名称div>
div>
<input type="text" class="form-control" placeholder="请输入品牌名称" v-model.trim="addName">
div>
div>
<div class="col-auto">
<button type="submit" class="btn btn-primary mb-2" @click.prevent="addRow">添加button>
div>
div>
form>
div>
div>
<table class="table table-bordered table-hover table-striped">
<thead>
<tr>
<th scope="col">#th>
<th scope="col">品牌名称th>
<th scope="col">状态th>
<th scope="col">创建时间th>
<th scope="col">操作th>
tr>
thead>
<tbody>
<tr v-for="item of list" :key="item.id">
<td>{{item.id}}td>
<td>{{item.name}}td>
<td>
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" :id="item.id" v-model="item.status">
<label class="custom-control-label" :for="item.id" v-if="item.status">已启用label>
<label class="custom-control-label" :for="item.id" v-else>已禁用label>
div>
td>
<td>{{item.time}}td>
<td>
<a href="javascript:;" @click="delRow(item.id)">删除a>
td>
tr>
tbody>
table>
div>
<script>
const vm = new Vue({
el:"#app",
data:{
addName:'', //要添加的品牌名
list:[
{
id:1,
name:'宝马',
status:true,
time: new Date()
},{
id:2,
name:'奔驰',
status:false,
time: new Date()
},{
id:3,
name:'奥迪',
status:false,
time: new Date()
},{
id:4,
name:'红旗',
status:true,
time: new Date()
}
]
},
methods: {
// 删除
delRow(id) {
// 过滤掉id不等于要删除的元素id,然后重新赋值给list
this.list = this.list.filter(item => item.id !== id)
},
// 添加
addRow() {
let keyword = this.addName
if(keyword === "") {
alert("您没有输入任何内容!!")
return
}else {
let len = this.list.length + 1
let item = {
id:len,
name:keyword,
status:true,
time:new Date()
}
// push()在数组最后添加元素
this.list.push(item)
this.addName = ''
}
}
},
})
script>
body>
注: 过滤器只适用于vue2 ,vue3中已删除过滤器
<body>
<div id="app">
<p>{{message | capi}}p>
div>
<script>
const vm = new Vue({
el:'#app',
data: {
message:"hello!你好呀~"
},
methods: {
},
// 声明过滤器
// 过滤器本质为一个函数
filters:{
// val指向的是message的值
capi(val) {
// 实现首字母大写
// charAt()从字符串中取出对应索引的字符
const first = val.charAt(0).toUpperCase()
const other = val.slice(1)
// 过滤器一定要有返回值
return first + other
}
}
})
script>
body>
Day.js官网
<script>
// 声明格式化时间的全局过滤器
Vue.filter('dateFormat',(time) => {
const timeformat = dayjs(time).format('YYYY-MM-DD HH:mm:ss')
return timeformat
})
script>
// 使用全局过滤器
<p>{{item.time | dateFormat}}p>
监视data数据变化,接收两个参数,第一个参数是新值,第二个参数是旧值
<body>
<div id="app">
<p>
用户名:<input type="text" v-model="userName">
p>
div>
<script src="./lib/vue-2.6.14.js">script>
<script>
var vm = new Vue({
el:"#app",
data:{
userName:""
},
// 侦听器
watch:{
// 方法格式的侦听器
userName(newVal,oldVal) {
console.log(`新值:${newVal}`)
console.log(`旧值:${oldVal}`)
}
}
})
script>
body>
// 对象格式的侦听器
userName: {
immediate:true,
handler(newVal,oldVal) {
console.log(`新值:${newVal}`)
}
}
<body>
<div id="app">
<p>
用户名:<input type="text" v-model="info.userName">
p>
div>
<script src="./lib/vue-2.6.14.js">script>
<script>
var vm = new Vue({
el:"#app",
data:{
info: {
userName:"嘟嘟"
}
},
// 侦听器
watch:{
// 深度侦听
info: {
immediate:true,
handler(newVal,oldVal) {
console.log(`${newVal}`)
},
deep:true, //开启深度监听
}
}
})
script>
body>
'info.userName'(newVal){
console.log(`新值:${newVal}`)
}
本质是一个属性,是通过一系列运算得到的属性
计算属性可用在 模板结构 或者 methods 方法中
<body>
<div id="app">
<p>r:<input type="text" v-model="r">p>
<p>g:<input type="text" v-model="g">p>
<p>b:<input type="text" v-model="b">p>
<div class="box" :style="{backgroundColor:rgb}">
{{rgb}}
div>
div>
<script src="./lib/vue-2.6.14.js">script>
<script>
var vm = new Vue({
el:'#app',
data:{
r:0,
g:0,
b:0
},
// 就算属性
computed: {
rgb() {
// 动态生成rgb字符串
return `rgb(${this.r},${this.g},${this.b})`
}
}
})
script>
body>
两个小原则
基本语法
axios({
// 请求方式:get、post
method:'',
// 请求地址
url:'',
// get时的参数
params:{},
// post的参数
data:{}
}).then(()=>{})
可以用async、await 代替then
cnpm install -g @vue/cli
winpty vue.cmd create 项目名
node_modules:项目依赖
public:一般放置静态资源(如:图片),webpack打包时会原封不动的打包到dist文件夹
src:源代码文件夹
assets:放置静态资源(一般是多个组件公用的静态资源),webpack在打包时会把assets文件夹当做一个模块,打包到js文件中
components:放置非路由组件、全局组件
pages/views:放置路由组件(需创建)
api:存放axios相关文件(需自己创建)
store: 存放vuex相关文件(需自己创建)
router:存放路由的配置文件(需创建)
mock:存放模拟数据相关的(需创建)
App.vue:唯一的根组件
main.js:入口文件,整个程序中最先执行的文件
gitignore git的忽略文件,一般不动
babel.config.js:babel相关的配置文件,一般不动
package.json:包管理配置文件,记录项目信息(项目怎么运行,有哪些依赖),类似于项目的“身份证”
package-lock.json:可以删除,缓存性的文件
readme.md:说明性文件
通过 main.js 把 App.vue 渲染到 index.html 指定区域中
其中:
main.js代码
// 导入vue的包,得到Vue构造函数
import Vue from 'vue'
// 导入根组件,
import App from './App.vue'
Vue.config.productionTip = false
// 创建vue实例
new Vue({
// 把render指定的组件渲染到HTML页面中
render: h => h(App),
}).$mount('#app') //$mount()相当于el
组件化开发:根据封装的思想,把页面上可复用的UI结构封装为组件,从而方便项目的开发和维护
vue中的组件后缀名为 .vue
在组件中,this指向当前组件的实例对象
组件的template中只能有一个根节点
import Left from '@/components/left.vue'
import Right from '@/components/right.vue'
export default {
name: 'App',
components: {
Left,
Right
}
}
<Left>Left>
<right>right>
// 导入文件时是否携带文件的扩展名
"path-autocomplete.extensionOnImport": true,
// 配置@的路径提示
"path-autocomplete.pathMappings": {
"@":"${folder}/src"
},
注:如果不起作用,可能是因为同一个文件夹下有多个项目
在main.js入口文件中,通过 Vue.component(参数1,参数2) 方法注册
参数1:字符串格式,表示组件的“注册名”
参数2:需要被全局注册的组件
// 引入三级联动组件
import TypeNav from '@/components/TypeNav'
// 注册为全局组件
/**
* 第一个参数:全局组件的名字
* 第二个参数:哪一个组件
*/
Vue.component('nav',TypeNav)
props是组建的自定义属性,在封装通用组件时可以提高组件的复用性。
export default {
props:['自定义属性1','自定义属性2',...]
}
props: {
自定义属性1: {
// default设置默认值
default:0,
// type定义类型
type:所需的基本数据类型,
// 是否是必填项
required:false //默认是false
}
}
<template>
<div>
和:{{sum}}
<button @click="add">+button>
div>
template>
<script>
export default {
name:'',
props:['init'],
data () {
return {
sum:this.init
};
},
methods: {
add() {
this.sum++
}
},
}
script>
<style lang='scss' scoped>
style>
// 使用props的值
<count :init="9">count>
props: {
自定义属性1: {
// default设置默认值
default:0
}
}
props: {
init: {
default:0,
type:Number
}
},
<count init="9">count>
<count :init="9">count>
<style lang='scss' scoped>
style>
子组件通过自定义事件向父组件传值
<template>
<div id="app">
<test :message="msg" @changeParent="getSonData">test>
<hr>
<div>
<p>父组件接收子组件传过来的值:{{countFromSon}}p>
div>
div>
template>
<script>
import Test from '@/components/test.vue'
export default {
name: 'App',
data() {
return {
msg:"hello,我是父组件中的数据!",
// 接收子组件的值
countFromSon:0
}
},
components: {
Test
},
methods: {
getSonData(val) {
this.countFromSon = val
}
},
}
script>
<style lang="scss">
style>
<template>
<div>
<div>来自父组件的值:{{message}}div>
<hr>
<div>
<p>{{sonMsg}}:{{count}}p>
<button @click="add">+button>
div>
div>
template>
<script>
export default {
name:'',
props:{
message: {
type:String
}
},
data () {
return {
sonMsg:"我是子组件的数据!",
count:0
};
},
methods: {
add() {
this.count++
this.$emit('changeParent',this.count)
}
},
}
script>
<style lang='scss' scoped>
style>
vue2中兄弟组件通过EventBus
从数组中查找某个元素用some,可以通过return true来终止循环
const ARR = ['红红','蓝蓝','白白','灰灰','绿绿','紫紫']
// 查找'白白'对应的索引
ARR.some((item,index) => {
console.log("查找了1次")
if(item === '白白') {
console.log(index)
return true
}
})
判断数组中的每一项是否都满足条件,如果全部都满足返回true,只要有一项不满足则返回false
const arr = [
{id:1,name:"西瓜",status:true},
{id:2,name:"草莓",status:true},
{id:3,name:"哈密瓜",status:true}
]
// 判断每一项的status是否为true
const result = arr.every(item => item.status)
console.log(result) //true
将每一次循环的结果进行累加,返回最后累加的结果
语法:
reduce( (累加的结果,当前循环项) => {},初始值)
前一次循环累加的结果会作为下一次的初始值,直到循环结束,返回最终累加结果
示例:
const arr = [
{id:1,name:"西瓜",status:true,price:10,count:2},
{id:2,name:"草莓",status:false,price:30,count:3},
{id:3,name:"哈密瓜",status:true,price:20,count:5}
]
// 计算数组中status为true的水果的总价
const newArr = arr.filter( item => item.status)
const res = newArr.reduce((total,item) => {
return total += item.price * item.count
},0)
console.log(res) // 120
单一原则
”判定范围,一个组件负责一个功能,如果需要更多功能,需要拆分为更小的组件<script>
export default {
name:'',
props:['info'],
data () {
return {
msg:"hello!"
};
},
methods: {
show() {
console.log("methods方法")
}
},
// beforeCreate
// 此时props、data、methods还没有被创建,是不可使用的
beforeCreate() {
console.log("beforeCreate此时拿不到任何数据")
},
// created
// 此时props、data、methods已经被创建好,但还不可以操作DOM
// 该阶段常用来发起ajax请求
created() {
console.log(this.info)
console.log(this.msg)
this.show()
},
// beforeMount
beforeMount() {
console.log("beforeMount,此时DOM还没有渲染")
},
// mounted
mounted() {
console.log("mounted!此时DOM已经渲染完成")
console.log(this.$el)
},
// beforeUpdate
// 当组件的数据发生变化时才会触发
beforeUpdate() {
console.log("beforeUpdate!数据发生了变化")
},
// updated
// 根据最新的数据,重新渲染dom
// 如果要在数据发生变化后操作最新的DOM要在updated中实现
updated() {
},
// beforeDestroy
// 将要销毁组件
beforeDestroy() {
},
// destroyed
// 组件销毁完成
destroyed() {
},
}
script>
vue中几乎不需要操作DOM,只需要维护好数据。如果一定要操作DOM可以通过ref实现。
每个vue组件的实例上都包含一个 $refs对象,里面存储着对应的DOM元素或者组件的引用。默认情况下,组件的 $refs 指向一个空对象。
注:$开头的都是vue内置成员,都是程序员可用的
<div class="root_box" ref="rootBox">
<h2>这是根组件h2>
<button @click="changeBtn">切换背景颜色button>
div>
methods: {
changeBtn() {
this.$refs.rootBox.style.backgroundColor = "pink"
}
},
methods: {
// ...
example() {
// 修改数据
this.message = 'changed'
// DOM 尚未更新
this.$nextTick(function() {
// DOM 现在更新了
// `this` 被绑定到当前实例
this.doSomethingElse()
})
}
}
<keep-alive>
<component :is="componentId">component>
keep-alive>
<keep-alive exclude="Right">
<component :is="componentId">component>
keep-alive>
name:'LeftOwn',
<keep-alive include="LeftOwn">
<component :is="componentId">component>
keep-alive>
<子组件名>
需要的HTML结构
子组件名>
<template>
<div>
<slot>插槽默认内容...当父组件没有传递内容时,就会显示默认值slot>
div>
template>
<slot name="slotName">默认内容slot>
<div slot="slotName">插槽内容div>
<slot name="slotName">slot>
<left>
<template v-slot:slotName>
插槽内容
template>
left>
<slot name="slotName" :msg="要传的插槽内容!">slot>
<template slot-scope="scopeData">
<p>
{{scopeData.msg}}
p>
template>
<category title="游戏">
<template slot-scope="{msg}">
<h4 >{{msg}}h4>
template>
category>
在每个组件中,可以通过directives节点声明私有自定义指令,如果通过v-自定义指令名进行使用
<h1 v-color>根组件h1>
// 私有自定义指令
directives: {
color: {
// 一旦绑定到元素就会触发bind方法
// el代表所绑定的DOM
bind(el) {
el.style.color="red"
}
}
}
<h3 v-color="color">自定义指令1h3>
<h3 v-color="'pink'">自定义指令2h3>
// 私有自定义指令
directives: {
color: {
// 一旦绑定到元素就会触发bind方法
// el代表所绑定的DOM
bind(el,binding) {
el.style.color= binding.value
}
}
}
directives: {
color(el,binding) {
el.style.color= binding.value
}
}
//main.js
// 参数1:字符串,name表示全局自定义指令的名字
// 参数2:对象,用来接收指令的参数值
Vue.directive('name',(el,binding) => {
})
用于集中式状态管理,可以进行数据的读写操作和多组件数据共享,相当于一个大型仓库,可以进行任意组件的通信
理解:vuex是一个餐厅(store)
vue Component 为客人,客人点菜(store.dispatch)
actions 为服务员,获取到客人点的菜,然后提交给后厨(store.commit)
mutations 为后厨,根据服务员点的菜,进行处理
state 为点的菜
cnpm i vuex@3
import Vue from 'vue'
// 引入Vuex
import Vuex from 'vuex'
// 使用vuex
Vue.use(Vuex)
// 创建actions,用于响应组件中的动作
const actions = {}
// 创建mutations,用于操作数据(state)
const mutations = {}
// 创建state,用于存储数据
const state = {}
// 创建并向外暴露(导出)store
export default new Vuex.Store({
actions,
mutations,
state
})
// 引入store
import store from '@/store'
new Vue({
store, // 注册store
render: h => h(App),
}).$mount('#app')
如果是在main.js中使用vuex,此时会报如下错误:
错误原因:会先执行所有的import,在store/index.js中vuex还没有被创建
const state = {
sum:0,
}
this.$store.dispatch(‘方法名’,要传的数据)
addOdd() {
this.$store.dispatch('oddAdd',this.num)
},
increase() {
this.$store.commit('ADD',this.num)
},
add ( {commit} ,val)
第二个参数:接收到dispatch所传的值
actions中 可以处理业务逻辑
const actions = {
// 第一个参数:上下文
// 第二个参数:所传的值
oddAdd(context,val) {
if(context.state.sum % 2) {
context.commit('ADD',val)
}
}
}
// 创建mutations,用于操作数据(state)
const mutations = {
ADD(state,val) {
console.log(val)
state.sum += val
}
}
const getters = {
xxx(state) {
return xxxxxx
}
}
export default new Vuex.Store({
....
getters
})
$store.getters.xxx
import {mapState,mapGetters} from ‘vuex’
把state中的数据映射为计算属性
…mapState({key1:‘value1’,key2:'value2…}),
value要和state中的key名保持一致
value和key都是字符串,key可以不加引号,value必须加引号
computed: {
...mapState({sum:'sum',school:'school',con:'con'}),
}
…mapState([‘xxx’,‘xxx’,‘xxx’]),
多个内容用逗号分隔,且xxx要与state中的key名保持一致
computed: {
...mapState(['sum','school','con']),
}
把getters中的数据映射为计算属性,语法和mapState一致
computed: {
...mapGetters(['bigSum'])
}
computed: {
...mapGetters({bigSum:'bigSum'})
}
生成与actions对话的方法,即生成包含 $store.dispatch(xxx) 的函数
语法和mapState一致,但是需要在调用方法时传参
...mapActions({addOdd:'oddAdd'})
<button @click="addOdd(num)">和为奇数再加button>
...mapActions(['oddAdd'])
生成与mutations对话的方法,即生成包含 $store.commit(xxx) 的函数
语法和mapState一致,但是需要在调用方法时传参
...mapMutations({increase:'ADD',substraction:'SUB'}),
<button @click="increase(num)">+button>
<button @click="substraction(num)">-button>
...mapMutations(['ADD','SUB'])
const state = {
...
}
const mutations = {
...
}
// getters:计算属性,项目中主要用于简化仓库中的数据
const getters = {
...
}
const actions = {
...
}
export default {
state,
mutations,
getters,
actions
}
import Vue from 'vue'
import Vuex from 'vuex'
// 使用一次vuex
Vue.use(Vuex)
// 引入小仓库
import Home from './Home'
import Search from './Search'
// 对外暴露store的一个实例
export default new Vuex.Store({
// 实现vuex仓库模块式开发存储数据
modules: {
Home,
Search
}
})
注: 项目不大时,可以直接在store/index.js中分模块
const countAbout = {
namespaced:true,//开启命名空间
state:{x:1},
mutations: { ... },
actions: { ... },
getters: {
bigSum(state){
return state.sum * 10
}
}
}
const personAbout = {
namespaced:true,//开启命名空间
state:{ ... },
mutations: { ... },
actions: { ... }
}
const store = new Vuex.Store({
modules: {
countAbout,
personAbout
}
})
// 直接读取
this.$store.state.模块名.要读取的state数据
// mapState读取
...mapState('模块名',['xxx','xxx','xxx']),
// 直接读取
this.$store.getters['模块名/要读取的getters']
// mapState读取
...mapGetters('模块名',['xxx'])
// 直接dispatch
this.$store.dispatch('模块名/actions方法名',数据)
// 借助mapActions
...mapActions('模块名',{xxx:'xxxx',xxx:'xxxxx'})
// 直接commit
this.$store.commit('模块名/xxx',数据)
// mapMutations
...mapMutations('模块名',{xxx:'xxxxx'...})
vue的一个插件库,用来实现SPA应用
cnpm i vue-router@3 --save
import Vue from 'vue'
// 引入路由
import VueRouter from 'vue-router'
// 使用
Vue.use(VueRouter)
// 引入组件
import Home from '@/views/Home.vue'
import About from '@/views/About.vue'
// 向外导出
export default new VueRouter({
routes: [
{
path:'/home',
component:Home
},
{
path:'/about',
component:About
},
]
})
// 引入路由
import router from '@/router'
new Vue({
router, //注册路由
render: h => h(App),
}).$mount('#app')
备注:
<router-link to="/路径">xxxxxrouter-link>
<router-view>router-view>
routes: [
{
path:'/home',
component:Home,
children:[
{
path:'news',
component:New
},
{
path:'messages',
component:Message,
children:[
{
path:'detail',
component:Detail
}
]
},
]
},
]
<router-link to='/home/news'>News</router-link>
<router-view>router-view>
<router-link :to="`/home/messages/detail?id=${item.id}&title=${item.title}`">{{item.title}}router-link>
<p>标题:{{$route.query.title}}p>
{
path:'detail/:id/:title',
component:Detail
}
<router-link :to="`/home/messages/detail/${item.id}/${item.title}`">{{item.title}}</router-link>
```javascript
// 方法一:通过字符串直接传参
// params形式
this.$router.push('/search/'+this.searchValue)
// query形式
this.$router.push('/search?key='+this.searchValue)
// params+query
this.$router.push('/search/'+this.searchValue+'?key='+this.searchValue.toUpperCase())
this.$router.push(`/search/${this.searchValue}?k=${this.searchValue.toUpperCase()}`)
特别注意:路由携带params参数时,若使用to的对象写法,则不能使用path配置项,必须使用name配置!
// 通过name命名路由
name:'search',
path:'/search/:keyword'
// 方法三:对象的写法(常用的),要对路由命名(name)
this.$router.push({
name:'search',
params:{
keyword:this.searchValue
},
query:{
key:this.searchValue.toUpperCase()
}
})
routes: [
{
name:'about',
path:'/about',
component:About
},
]
<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>
props: {
id:'0001',
title:'标题'
}
props:['id','title'],
<p>标题:{{title}}p>
props($route) {
return {
id:$route.query.id,
title:$route.query.title
}
}
props({query}) {
return {
id:query.id,
title:query.title
}
}
props({query:{id,title}}) {
return {
id:id,
title:title
}
}
<router-link to="路径">xxxxxrouter-link>
<router-link replace .......>xxxrouter-link>
this.$router.push({
name:'message',// 和路由配置中的name保持一致
query: {
id:item.id,
title:item.title
}
})
keep-alive
实现<keep-alive include="需要缓存的组件的名字(name)">
<router-view>router-view>
keep-alive>
<keep-alive :include="[name1,name2,....]">
<router-view>router-view>
keep-alive>
activated
路由组件被激活时触发。deactivated
路由组件失活时触发作用:对路由进行权限控制
分类:全局守卫、独享守卫、组件内守卫
const router = new VueRouter({
routes: [.....]
})
// 全局前置路由守卫
router.beforeEach((to,from,next)=>{
console.log('beforeEach',to,from)
// meta:路由元信息,需要在routes中配置
if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制
if(localStorage.getItem('school') === 'atguigu'){ //权限控制的具体规则
next() //放行
}else{
alert('暂无权限查看')
// next({name:'guanyu'})
}
}else{
next() //放行
}
})
export default router
//全局后置守卫:初始化时执行、每次路由切换后执行
router.afterEach((to,from)=>{
console.log('afterEach',to,from)
if(to.meta.title){
document.title = to.meta.title //修改网页的title
}else{
document.title = 'vue_test'
}
})
beforeEnter(to,from,next){
console.log('beforeEnter',to,from)
if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制
if(localStorage.getItem('school') === 'atguigu'){
next()
}else{
alert('暂无权限查看')
// next({name:'guanyu'})
}
}else{
next()
}
}
//进入守卫:通过路由规则,进入该组件时被调用
beforeRouteEnter (to, from, next) {
},
//离开守卫:通过路由规则,离开该组件时被调用
beforeRouteLeave (to, from, next) {
}
#
及其后面的内容就是hash值mode: ‘history’