初识Vue
- 引入Vue.js文件
- 想让Vue工作,必须创建一个Vue实例,且要传入一个配置对象。
- root容器里的代码依然能符合html规范,只不过混入了一些特殊的Vue语法。
- root容器里的代码被称为【Vue】模板。
- Vue实例和容器是一一对应的。
- 真实开发中只有一个Vue实例,并且会配合着组件一起使用。
- 中的xxx要写js表达式,且xxx可以自动读取到data中的所有属性。
- 一旦data中的数据发生变化,那么页面中用到该数据的地方也会自动更新。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <div class="root"> <h1>Hello,{{name}}</h1> </div> <div class="root"> <h1>Hello,{{name}}</h1> </div> <script> Vue.config.productionTip = false;
new Vue({ el: '.root', data: { name: '小红', age: 22 } }); </script>
|
Vue模板语法
Vue模板语法有两大类:
1.插值语法:
功能:用于解析标签体内容
写法:{ {xxx} },可以直接读取到data内的所有属性
2.指令语法:
功能:用于解析标签(包括:标签属性、标签体内容、绑定事件等等)
写法:v-bind:href=”url”或简写为:href=”url”,引号中可以直接读取到data中的所有属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| <div id="root"> <h1>插值语法</h1> <h3>你好,{{name}}</h3> <hr> <h1>指令语法</h1> <a v-bind:href="url">点我去我的博客</a> <a :href="url2">点击去我的好基友的博客</a> </div> <script> Vue.config.productionTip = false new Vue({ el: '#root', data: { name: 'jack', url: 'https://yuxiaohong.cn/', url2: 'https://jaggerr.cn' } }) </script>
|
数据绑定
单向数据绑定
<input type=”text” v-bind:value=”name”>
双向数据绑定
<input type=”text” v-model:value=”name”>
el和data的两种写法
data与el的两种写法:
1.el有两种写法:
(1).new Vue的时候配置el属性
(2).先创建Vue实例,随后再通过v.$mount(‘#root)指定el的值
2.data有两种写法:
(1).对象式
(2).函数式
目前两种写法都可以,但是以后学到组件的时候,data必须使用函数式,否则会报错
3.一个重要的原则,一定不要写箭头函数,一旦写了箭头函数,this就不再是Vue的实例了,而变成了全局的Window
el的两种写法
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <script> const v = new Vue({ el: '#root', data: { name: '小红' } }) console.log(v); setTimeout(() => { v.$mount('#root') },2000) </script>
|
data的两种写法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <script> new Vue({ el: '#root',
data:function(){ return{ name:'小红' } } }) </script>
|
MVVM模型
1.M:模型(Model):data中的数据
2.V:视图(View):模板代码
3.VM:视图模型(ViewModel):Vue实例
观察发现:
1.data中所有的属性,最后都出现在了vm身上
2.vm身上所有的属性及Vue原型上所有的属性,在Vue模板中都可以直接使用
数据模型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| <script> let number = 18 let person = { name:'张三', sex:'男', age:number } Object.defineProperty(person,'age',{ get:function(){ return number }, set:function(value){ console.log('有人修改了age属性,其值是',value); number = value } }) console.log(Object.keys(person)); console.log(person); </script>
|
数据代理
1.Vue中的数据代理:
通过vm对象来代理data对象中属性的操作(读\写)。
2.Vue中数据代理的好处:
更加方便的操作data中的数据
3.基本原理:
通过Object.defineProperty()把data对象中所有属性添加到vm上。
为每一个添加到vm上的属性,都指定一个getter和setter。
在getter,setter内部去操作(读\写)data中相应的属性。
数据代理(通过obj2去访问和修改obj中的属性值):
数据代理图示
Vue中的数据代理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <div id="root"> <h2>学校名称:{{name}}</h2> <h2>学校地址:{{address}}</h2> </div> <script> Vue.config.productionTip = false const vm = new Vue({ el:'#root', data:{ name:'GEO', address:'石家庄裕华区' } }) </script>
|
事件处理
事件的基本使用:
1.使用v-on:xxx或者@xxx绑定事件,xxx是事件名。
2.事件的回调需要配置在methods对象中,最终会在vm上。
3.methods中配置的函数,不要用箭头函数。
4.methods中配置的函数,都是Vue管理的函数,this指向是vm或组件实例对象。
5.@click=”demo”和@click=”demo($event)”效果一致,但后者可以在括号中进行额外的传参。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| <div id="root"> <h2>欢迎来{{name}}主页</h2> <button @click="showInfo1">点我提示信息(不传参)</button> <button @click="showInfo2(66,$event)">点我提示信息(传参)</button> </div> <script> const vm = new Vue({ el:'#root', data:{ name:'GEO' }, methods:{ showInfo1(event){ alert('你好!'); console.log(event.target); console.log(event.target.innerText); console.log(vm === this); }, showInfo2(number,event){ console.log(number,event); alert('你好!!!'); console.log(event.target); console.log(event.target.innerText); console.log(vm === this); } } }) </script>
|
点击不传参按钮:
点击传参按钮:
事件修饰符
Vue中的事件修饰符:
1.prevent:阻止默认事件(常用)
2.stop:阻止事件冒泡
3.once:事件只触发一次
4.capture:使用事件的捕获模式
5.self:只有event.target是当前操作的元素时才触发事件
6.passive:事件的默认行为立即执行,无需等待回调函数执行完毕
最后,修饰符是可以连着写的,如:.stop.passive
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
| <!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>Document</title> </head> <script src="../../JS/vue.js"></script> <style> *{ margin-top: 20px; } .demo1{ background-color: aqua; } .box1{ padding:5px; background-color: aqua; } .box2{ padding: 5px; background-color: gold; } .list{ width: 200px; height: 200px; background-color: silver; overflow: auto; } li{ height: 100px; } </style> <body> <div id="root"> <h1>欢迎来到{{name}}</h1>
<a href="https://yuxiaohong.cn" @click.prevent='showInfo'>点我去我的博客</a> <div class="demo1" @click="showInfo"> <button @click.stop="showInfo">点我提示信息</button> </div> <button @click.once="showInfo">点我提示信息</button> <div class="box1" @click.capture="showMsg(1)"> div1 <div class="box2" @click="showMsg(2)"> div2 </div> </div>
<div class="demo1" @click.self="showInfo"> <button @click="showInfo">点我提示信息</button> </div> <ul @wheel.passive="demo" class="list"> <li>1</li> <li>2</li> <li>3</li> <li>4</li> </ul> </div> </body> <script> const vm = new Vue({ el:'#root', data:{ name:'河北地质大学' }, methods:{ showInfo(e){ console.log(e); console.log(e.target); }, showMsg(msg){ console.log(msg); }, demo(){ for (let i = 0; i < 100000; i++) { console.log('@'); } } } }) </script> </html>
|
键盘事件
1.Vue中常用的按键别名:
回车:enter
删除:delete
退出:esc
空格:space
换行:tab(特殊,需配合keydown使用,因为tab本身有切换焦点的功能)
上:up
下:down
左:left
右:right
2.Vue中未提供别名的按键,可以使用按键原始的key值去绑定,像大小写键,要写成caps-lock这种形式
3.系统修饰键:ctrl、alt、shift、meta(win)
(1).配合keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发。
(2).配合keydown使用:正常触发事件。
4.也可以使用keyCode去指定具体的按键(不推荐)
5.Vue.config.keyCodes.自动以键名=键码,可以定制按键别名
6.如果想用组合按键,用点进行链接,如:ctrl.y(就是需要同时按下ctrl键和y键)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| <!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>Document</title> </head> <script src="../../JS/vue.js"></script> <body> <div id="root"> <input type="text" placeholder="按下回车提示输入" @keyup.caps-lock="showInfo"> </div> </body> <script> const vm = new Vue({ el: '#root', data: {
}, methods: { showInfo(e){ console.log(e.target.value); } } }) </script> </html>
|
计算属性
姓名案例_插值语法实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <div id="root"> 姓:<input type="text" v-model:value="firstName"><br><br> 名:<input type="text" v-model:value="lastName"><br><br> 全名:<span>{{firstName.slice(0,3)}}-{{lastName}}</span> </div> <script> new Vue({ el: '#root', data: { firstName: '张', lastName: '三' }, methods: {
} }) </script>
|
展示效果:
姓名案例_methods实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <div id="root"> 姓:<input type="text" v-model:value="firstName"><br><br> 名:<input type="text" v-model:value="lastName"><br><br> 全名:<span>{{fullName()}}</span> </div> <script> new Vue({ el: '#root', data: { firstName: '张', lastName: '三' }, methods: { fullName () { let name = '' name += this.firstName.slice(0, 3) + '-' + this.lastName return name } } }) </script>
|
姓名案例_计算属性的实现
计算属性:
1.定义:要用的属性不存在,需要通过已有属性(不能是变量)计算得来。
2.原理:底层借助了Object.defineproperty方法提供的getter和setter。
3.get函数什么时候执行:
(1).初次读取时会执行一次。
(2).当依赖的数据发生改变时会再次被调用。
4.优势:与methods相比,内部有缓存机制(复用),效率更高,调试方便。
5.备注:
(1).计算属性最终会出现在vm上,直接读取使用即可。
(2).如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| <div id="root"> 姓:<input type="text" v-model:value="firstName"><br><br> 名:<input type="text" v-model:value="lastName"><br><br> 全名:<span>{{fullName}}</span> </div> <script> const vm = new Vue({ el: '#root', data: { firstName: '张', lastName: '三' }, computed: { fullName: { get () { console.log('get被调用了'); return this.firstName.slice(0, 3) + '-' + this.lastName }, set (value) { console.log('set被调用了'); const arr = value.split('-'); this.firstName = arr[0]; this.lastName = arr[1]; } } } }) </script>
|
计算属性的简写形式
当需求只用读取数据,而不需要修改时,则可以使用简写方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <div id="root"> 姓:<input type="text" v-model:value="firstName"><br><br> 名:<input type="text" v-model:value="lastName"><br><br> 全名:<span>{{fullName}}</span> </div> <script> const vm = new Vue({ el: '#root', data: { firstName: '张', lastName: '三' }, computed: { fullName() { consloe.log('get被调用了'); return this.firstName + '-' + this.lastName; } } } })
|
监视属性
天气案例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <div id="root"> <h2>今天天气很{{info}}</h2> <button @click="changeWeather">切换天气</button> </div> <script> const vm = new Vue({ el: '#root', data: { isHot: true }, methods: { changeWeather () { this.isHot = !this.isHot } }, computed: { info () { return this.isHot ? '炎热' : '凉爽' } } }) </script>
|
效果展示:
监视属性
监视属性(watch):
1.当监视属性发生变化时,回调函数自动调用,进行相关操作。
2.监视的属性必须存在,才能进行监视。
3.监视的两种写法:
(1).new Vue时传入watch配置
(2).通过vm.$watch监视
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| <div id="root"> <h2>今天天气很{{info}}</h2> <button @click="changeWeather">切换天气</button> </div> <script> const vm = new Vue({ el: '#root', data: { isHot: true }, methods: { changeWeather () { this.isHot = !this.isHot } }, computed: { info () { return this.isHot ? '炎热' : '凉爽' } },
}) vm.$watch('isHot', { immediate: true, handler (newValue, oldValue) { console.log('isHot被修改了。', newValue, oldValue); } }) </script>
|
深度监视
1.Vue中的watch默认不监测对象内部值的改变。
2.配合deep:true可以监测对象内部值的改变。
备注:
1.Vue自身可以监测对象内部值的改变,但Vue提供的watch默认不可以。
2.使用watch时可以根据数据的具体结构,决定是否采用深度监视
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| <div id="root"> <h2>今天天气很{{info}}</h2> <button @click="changeWeather">切换天气</button> <hr> <h3>a的值是{{numbers.a}}</h3> <button @click="numbers.a++">点我让a+1</button> <h3>b的值是{{numbers.b}}</h3> <button @click="numbers.b++">点我让b+1</button> </div> <script> const vm = new Vue({ el: '#root', data: { isHot: true, numbers: { a: 1, b: 1 } }, methods: { changeWeather () { this.isHot = !this.isHot } }, computed: { info () { return this.isHot ? '炎热' : '凉爽' } }, watch: { isHot: { handler (newValue, oldValue) { console.log('isHot被修改了。', newValue, oldValue); } }, numbers: { deep: true, handler () { console.log('numbers发生改变了'); } }, isHot(newValue,OldValue){ console.log('isHot被修改了。',newValue,OldValue); } } vm.$watch('isHot', function(newValue,OldValue){ console.log('isHot被修改了。',newValue,OldValue); }) }) </script>
|
使用Vue脚手架
初始化Vue脚手架
说明
1.Vue脚手架是Vue官方提供的标准化开发工具(开发平台)
2.最新的版本是4.x。
3.文档:https://cli.vuejs.org/zh/
具体步骤
第一步(仅第一次执行):全局安装@vue/cli
npm install -g @vue/cli
如果下载速度太慢可以换到华为镜像:
npm config set registry=https://mirrors.huaweicloud.com/repository/npm/
第二步:切换到要创建项目的目录,然后使用命令创建项目
vue creat xxxx
第三步:启动项目
npm run serve
文件结构
nide_module文件夹:存放脚手架相关文件
src文件夹下的assets文件夹:通常放网站的静态资源
src文件夹下有个main.js:是整个项目的入口文件(Vue实例对象)
src文件夹下有个App.vue:统合所有组件
src文件夹下的components文件夹:存储各个组件
.gitignore:规定哪些文件不需在推送至远程仓库
babel.config.js:babel的配置文件(ES6转ES5)
package.json:包的说明书
package-lock.json:包版本控制文件
README.md:对整个工程或项目的一个描述
ref属性
在原生JS中,我们通过操作DOM来获得元素节点的信息,因为在Vue中要避免亲自操纵DOM,所以Vue提供了一个属性
ref,通过在标签中添加该属性,从而可以直接获得该元素节点的信息,而不必再操作DOM
ref属性
1.被用来给元素或子组件注册引用信息(id的替代者)
2.应用在html标签上获取的是真实DOM元素,应用在组件标签上是组件实例对象(vc)
3.使用方式:
打标识:<h1 ref=”xxx”>…..</h1> 或 <School ref=”xxx”></School>
获取:this.$refs.xxx
1 2 3 4 5
| <h1 ref="title">666</h1>
<script> console.log(this.$refs.title) </script>
|
props属性
功能:让组件接受外部传过来的数据
(1)传递数据:
<Demo name =”xxx”/>
(2)接收数据:
第一种方式(只接收):
props:[‘name’]
第二种方式(限制类型):
props:{
name:String
}
第三种方式(限制类型、限制必要性、指定默认值):
props:{
name:{
type:String, //类型
required:true, //必要性
default:’小红’ //默认值
}
}
备注:props是只读的,Vue底层会检测你对props的修改,如果进行了修改,就会发出警告,若业务需求确实需要修改,那么请复制props的内容到data中一份,然后去修改data中的数据
mixin属性
功能:可以把多个组件共用的配置提取成一个混入对象
使用方式:
第一步定义混合:
{
data(){…},
methods(){…}
…
}
第二步使用混入:
(1)全局混入:Vue.mixin(xxx)
(2)局部混入:mixins:[‘xxx’]
插件
功能:用于增强Vue
本质:包含install方法的一个对象,install的第一个参数是Vue,第二个以后的参数是插件使用者传递的数据
定义插件:
对象.install = function(Vue,options){
//1.添加全局过滤器
Vue.filter(…)
//2.添加全局指令
Vue.directive(…)
//3.配置全局混入
Vue.mixin(…)
//4.添加实例方法
Vue.prototype.$myMethod = function(){…}
Vue.prototype.$myProperty = xxxx
}
使用插件:Vue.use()
scoped样式
作用:让样式仅在局部生效,防止命名冲突
写法:<style scoped>
总结TodoList案例
组件化编码流程
1.拆分静态组件:组件要按照功能点拆分。命名不要与html元素冲突。
2.实现动态组件:考虑好数据存放位置,数据是一个组件在用,还是一些组件在用。
一个组件在用:放在组件自身即可
一些组件在用:放在他们共同的父组件上(状态提升)
3.实现交互:从绑定事件开始。
props适用于
1.父组件 ===> 子组件 通信
2.子组件 ===> 父组件 通信(要求父先给子一个函数)
使用v-model时要切记:v-model绑定的值不能是props传过来的值,因为props是不可以修改的
props传过来的若是对象类型的值,修改对象中的属性时Vue不会报错,但不推荐这样做
webStorage
1.存储内容一般支持5MB左右(不同浏览器可能不同)
2.浏览器通过window.Storage和window.sessionStorage属性来实现本地存储机制
3.相关API
1 2 3 4 5 6 7 8 9 10 11
| 该方法接受一个键和值作为参数,会把键值对添加到存储中,如果键名存在,则更新其对应的值 xxxxxStorage.setItem('key','value')
该方法接受一个键名作为参数,返回键名所对应的值 xxxxxStorage.getItem('person)
该方法接受一个键名作为参数,并把该键名从存储中删除 xxxxxStorage.removeItem('key')
该方法会清空存储中的所有数据 xxxxxStorage.clear()
|
4.备注:
(1).SessionStorage存储的内容会随着浏览器窗口的关闭而消失。
(2).LocalStorage存储的内容,需要手动清除才会消失。
(3).xxxxxStorage.getItem(xxx)如果xxx对应的value获取不到,那么getItem的返回值是null。
(4).JSON.parse(null)的结果依然是null。
组件的自定义事件
1.一种组件间通信的方式,适用于 子组件===>父组件
2.使用场景:A是父组件,B是子组件,B想给A传数据,那么就要在A中给B绑定自定义事件(事件的回调在App中)
3.绑定自定义事件:
(1).第一种方式:在父组件中:
1 2 3 4 5 6 7
| <Demo ref="demo"></Demo> ..... <script> mounted(){ this.$refs.xxx.$on('atguigu',this.test) } </script>
|
(2).第二种方式,在父组件中:
1 2 3 4 5 6
| <Demo ref="demo"></Demo> <script> mounted(){ this.$refs.xxx.$on('atguigu',this.test) }) </script>
|
(3).若想让自定义事件只触发一次,可以使用once修饰符,或$once方法。
4.触发自定义事件:this.$emit(‘atguigu’,数据)
5.解绑自定义事件:this.$off(‘atguigu’)
6.组件上也可以绑定原生DOM事件,但必须使用native修饰符
7.注意:通过thios.$refs.xxx.$on(‘atguigu’,回调)绑定自定义事件时,回调要么配置在methods中,要么用箭头函数,否则this的指向会出现问题
全局事件总线(GlobalEventBus)
1.一种组件间的通信方式,适用于任意组件间的通信。
2.安装全局事件总线:
1 2 3 4 5 6
| new Vue({ ... beforeCreate(){ Vue.prototype.$bus = this } })
|
3.使用事件总线:
(1).接收数据:A组件想接受数据,则在A组建中给$bus绑定自定义事件,事件的回调留在A组件自身
1 2 3 4 5 6 7
| methods:{ demo(data){...} } ... mounted(){ this.$bus.$on('xxx',this.demo) }
|
(2).提供数据:this.$bus.$emit(‘xxx’,数据)
4.最好在beforeDestroy钩子中,用$off解绑当前组件所用到的事件。
消息订阅与发布(pubsub)
1.一种组件间通信的方式,适用于任意组件间通信。
2.引入:import pubsub from ‘pubsub-js’
3.接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身。
1 2 3 4 5 6 7
| methods(){ demo(data){...} } ... mounted(){ this.pid = pubsub.subscribe('xxx',this.demo) }
|
4.提供数据:pubsub.publish(‘xxx’,数据)
5.最好在beforeDestroy钩子中,用pubsub.unsubscribe(pid)去取消订阅
nextTick
1.语法:this.$nextTick(回调函数)
2.作用:在下一次DOM节点更新结束后执行其指定的回调
3.什么时候要用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行
Vue脚手架配置代理
方法一
在vue.config.js中添加如下配置:
1 2 3
| devServer:{ proxy:"http://localhost:5000" }
|
说明:
1.优点:配置简单,请求资源时直接发送给前端(8080)即可。
2.缺点:不能配置多个代理,不能灵活控制请求是否走代理。
3.工作方式:若按照上述配置代理,当请求了前端不存在IDE资源时,那么该请求会转发给服务器(优先匹配前端资源)
方法二
编写vue.config.js配置具体代理规则:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| module.exports={ devServer:{ proxy:{ '/api1':{ target:'http://localhost:5000', changeOrigin:true, pathRewrite:{'^/api1':''} }, '/api2':{ target:'http://localhost:5001', changeOrigin:true, pathRewrite:{'^/api2':''} } } } }
|
1.优点:可以配置多个代理,且可以灵活控制请求是否走代理。
2.缺点:配置略微繁琐,请求资源是必须加前缀。
Slot插槽
1.作用:让父组件可以向子组件指定位置插入html结构,也是一种组件间通信的方式,适用于 父组件===>子组件
2.分类:默认插槽、具名插槽、作用域插槽
3.使用方式:
(1).默认插槽:
1 2 3 4 5 6 7 8 9 10
| <Category> <div>html结构</div> </Category>
<template> <div> <slot>插槽默认内容</slot> </div> </template>
|
(2).具名插槽
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <Category> <template slot="center"> <div>html结构1</div> </template> <template slot="footer"> <div>html结构2</div> </template> </Category>
<template> <div> <slot name={center}>插槽默认内容</slot> <slot name={footer}>插槽默认内容</slot> </div> </template>
|
(3).作用域插槽:
①.理解:数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定。
②.具体编码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| <CateGory> <template scope="scopeData"> <ul> <li v-for="item in scopeData.games" :key="item">{{item}</li> </ul> </template> </CateGory>
<template> <div> // 将数据传递给父组件 <slot :games="games"></slot> </div> </template> <script> export default{ name:'Category', props:['title'], data(){ return{ games:['AEPX','LOL','PUBG','R6S'] } } </script>
|
Vuex
vuex是什么
1.概念:专门在Vue中实现集中式状态(数据)管理的一个插件,对vue应用中多个组件的共享状态进行集中式的管理(读\写),且适用于任意组件间的通信。
什么时候使用Vuex
1.多个组件依赖于同一状态。
2.来自不同组件的行为需要变更同一状态。
搭建vuex环境
1.创建文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const actions={}
const mutations={}
const state ={}
export default new Vuex.Store({ actions, mutations, state })
|
2.在main.js文件中创建vm时传入store配置项
1 2 3 4 5 6 7 8 9
| import store from '@/vuex/store.js'
new Vue({ el:'#app', render:h=>h(App), store })
|
基本使用
1.初始化数据、配置actions、mutations,操作文件store.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const actions={ jia(context,value){ console.log('actions中的jia被调用了',context,value) context.commit('JIA',value) }, }
const mutations={ JIA(state,value){ console.log('mutations中的JIA被调用了',state,value) } }
const state={ sum:0 }
export default new Vuex.Store({ actions, mutations, state })
|
2.组件中读取vuex中的数据:$store.state.sum
3.组件中修改vuex中的数据:$store.dispatch(‘actions中的方法名’,数据)或$store.commit(‘mutations中的方法名’,数据)
若没有网络请求或其他业务逻辑,组件中也可以越过actions,即不写dispatch,直接编写commit
getters的使用
1.概念:当state中的数据需要经过加工后再使用时,可以使用getters进行加工。
2.store.js中追加getters配置
1 2 3 4 5 6 7 8 9 10 11 12 13
| const getters={ bigSum(){ return state.sum*10 } }
export default new Vuex.Store({ actions, mutations, state, getters })
|
3.组件中读取数据:$store.getters.bigSum
四个map方法的使用
1.mapState方法:用于帮助我们映射state中的数据为计算属性
1 2 3 4 5 6 7
| computed:{ ...mapState({sum:'sum',school:'school',subject:'subject'}), ...mapState(['sum','school','subject']) }
|
2.mapGetters方法:用于帮助我们映射getters中的数据为计算属性
1 2 3 4 5
| ...mapGetters(bigSum:'bigSum'),
...mapGetters(['bigSum'])
|
3.mapActions方法:用于帮助我们生成actions对话的方法,即:包含$state.dispatch(xxx)的函数
1 2 3 4 5 6 7
| methods:{ ...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'}), ...mapActions(['jiaOdd','jiaWait']) }
|
4.mapMutations方法:用于帮助我们生成与mutations对话的方法,即:包含$store.commit(xxx)的函数
1 2 3 4 5 6 7
| methods:{ ...mapMutations({increment:'JIA',decrement:'JIAN'}), ...mapMutations(['JIA','JIAN']) }
|
模块化+命名空间
1.目的:让代码更好维护,让多种数据分类更加明确
2.修改store.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| const countOptions={ namespaced:true, state:{x:1}, mutations:{}, actions:{}, getters:{ bigSum(state){ return state.sum*10 } } }
const personOptions={ namespaced:trus, state:{}, mutations:{}, actions:{} }
const store = new Vuex.Store({ modules:{ countOptions, personOptions } })
|
3.开启命名空间后,组件中读取state数据:
1 2 3 4
| this.$store.state.personOptions.personList
...mapState('countOptions',['sum','school','subject'])
|
4.开启命名空间后,组件中读取getters数据:
1 2 3 4
| this.$store.getters['personOptions/firstPersonName',
...mapGetters('countOptions',['bigSum'])
|
5.开启命名空间后,组件中调用dispatch:
1 2 3 4
| this.$store.dispatch('personOptions/addPersonWang','person'),
...mapActoons('countOptions',{increment:'jiaOdd',inrenmentWait:'jiaWait'})
|
6.开启命名空间后,组件中调用commit:
1 2 3 4
| this.$store.commit('personOptions/ADDPERSON',person),
...mapMutations('countOptions',{increment:'JIA',decrement:'JIAN'})
|
路由
简介:
1.Vue中的路由应用在单页面应用,即只有一个html文件,在导航区切换标签时,页面不会整体刷新也不会打开新的页面,只会在特地区域刷新新的组件内容。
对vue-router的理解:
是vue的一个插件库,专门实现SPA应用(单页面应用)
对SPA应用的理解:
(1)单页Web应用(single page web application)
(2)整个应用只有一个完整的页面
(3)点击页面中的导航链接不会刷新页面,只会做页面的局部刷新
(4)数据需要通过ajax请求获取
路由的理解:
(1)一个路由就是一组映射关系(key-value)
(2)key为路径,value可能是function或component
路由分类:
(1)后端路由:value是function,用于处理客户端提交的请求。
服务器接收到一个请求时,根据请求路径找到匹配的函数来处理请求,返回响应数据。
(2)前端路由:value是component,用于展示页面内容。
当浏览器的路径改变时,对应的组件就会显示。
基本使用
1.安装vue-router,命令:npm i vue-router
2.应用插件:Vue.use(VueRouter)
3.编写router配置项:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import VueRouter from 'vue-router'
import About from '../components/About' import Home from '../components/Home'
const router = new VueRouter({ routes:[ { path:'/about', component:About }, { path:'/home', component:Home } ] })
export default router
|
4.实现切换:(active-class可配置高亮样式)
1
| <router-link active-class="active" to="/about">About</router-link>
|
5.指定展示位置:
1
| <router-view></router-view>
|
几个注意点
1.路由组件通常存放在pages文件夹,一般组件通常存放在components文件夹。
2.通过切换,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载。
3.每个组件都有自己的$route属性,里面存储着自己的路由信息。
4.整个应用只有一个router,可以通过组件的$router属性获取到。
嵌套(多级)路由
1.配置路由规则,使用children配置项:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| routes:[ { path:'/about', component:About }, { path:'/home', component:Home, children:[ { path:'news', component:News }, { path:'message', component:Message } ] } ]
|
2.跳转:要写完整路径
1
| <router-link to="/home/news">News</router-link>
|
路由的Query参数
1.传递参数:
1 2 3 4 5 6 7 8 9 10 11 12
| <router-link :to="/home/message/detail?id=666&title=你好">跳转</router-linl>
<router-link :to="{ path:'home/message/detail', query:{ id:p.id, title:p.title } }"> 跳转</router-link>
|
2.接受参数
1 2
| $route.query.id $route.query.title
|
命名路由
1.作用:可以简化路由的跳转
2.如何使用
(1)给路由命名
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| { path:'/demo', component:Demo, children:[ { path:'test', component:Test, children:[ { name:'hello' path:'welcome' component:Hello } ] } ] }
|
(2)简化跳转
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <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>
|
路由的params参数
1.配置路由,声明接收params参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| { path:'/home', component:Home, children:[ { path:'news', component:News }, { component:Message, children:[ { name:'xiangqing', path:'detali/:id/:title', component:Detail } ] } ] }
|
2.传递参数
1 2 3 4 5 6 7 8 9 10 11 12 13
| <router-link :to="/home/message/detail/666/你好">跳转</router-link>
<router-link :to="{ name:'xiangqing', params:{ id:666, title:'你好' } }" >跳转</router-link>
|
特别注意:路由携带params参数时,若使用to的对象写法,则不能使用path配置项,必须使用name配置
3.接收参数:
1 2
| $route.params.id $route.params.title
|
路由的props配置
作用:让路由组件更方便收到参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| { name:'xiangqing', path:'detail/:id', component:Detail, props: { a: 1, b: 'hello' }
props: true
props($route) { return { id: $route.query.id, title: $route.query.title } } }
|
<router-link>的两种属性
1.作用:控制路由跳转时操作浏览器历史记录的模式
2.浏览器的历史记录有两种写入方式:分别为push和replace,push是追加历史记录,replace是替换当前历史记录,路由跳转的时候默认为oush
区别:push模式中,浏览器可以回退到上一个页面,replace不能回退
3.如何开启replace模式:
1
| <router-link replace>News</router-link>
|
编程式路由导航
1.作用:不借助<router-link>实现路由跳转,让路由跳转更加灵活。
2.具体编码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| this.$router.push({ name:'xiangqing', params:{ id:xxx, title:xxx } })
this.$router.replace({ name:'xiangqing', params:{ id:xxx, title:xxx } })
this.$router.back() this.$router.forward() this.$router.go()
|
缓存路由组件
1.作用:让不展示的路由组件保持挂载,不被销毁。
2.具体编码:
1 2 3 4 5 6 7 8 9
| <keep-alive include="NewS"> <router-link></router-link> </keep-alive>
<keep-alive :include="['NewS','MessagE']"> <router-view></router-view> </keep-alive>
|
两个新的生命周期钩子
1.作用:理由组件所独有的两个钩子,用于捕获路由组件的激活状态。
2.具体名字:
activated路由组件被激活时触发
deactivated路由组件失活时触发
路由守卫
1.作用:对路由进行权限控制
2.分类:全局守卫、独享守卫、组件内守卫
3.全局守卫:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| router.beforeEach((to,from,next)=>{ if(to.meta.isAuth){ if(localStorage.getItem('name')==='xiaohong'){ next() } else{ alert('无权查看') } } else{ next() } })
router.afterEach((to,from)=>{ if(to.meta.title){ document.title=to.meta.title } else{ document.title='系统' } })
beforeEnter:(to,from,next)=>{ if(to.meta.isAuth){ if(localStorage.getItem('name')==='xiaohong'){ next() } else{ alert('无权查看') } } else{ next() } }
|
路由器的两种工作模式
1.对于一个url来说什么事hash值————#及其后面的内容
2.hash值不会包含在HTTP请求中,即:hash值不会带给服务器
3.hash模式:
(1)地址中永远带着#,不美观。
(2)若以后将地址通过第三方App分享,若App校验严格,则地址会被标记为不合法
(3)兼容性高较好
4.history模式:
(1)地址干净美观
(2)兼容性和hash模式相比略差
(3)应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题
扩展:自己创建一个简易的服务器
前提:需要导包:express和connect-history-api-fallback(用于解决history模式下刷新404问题,hash模式不受影响)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| const express = require('express')
const history = require('connect-history-api-fallback')
const app = express() app.use(history()) app.use(express.static(__dirname + '/static'))
app.get('/person', (req, res) => { res.send({ name: 'tom', age: 18 }) })
app.listen(5005, (err) => { if (!err) console.log('服务器启动成功了') })
|
ElementUI
快速上手
1.下载包:npm i element-ui
2.引入包:
1 2 3 4 5
| import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
|
3.直接从ElementUI官网复制所需代码
按需引入
1.下载包:npm install babel-plugin-component -D