文章目录
- Vue速成手册
- 0 前言
- 1 基础篇
- 2 组件化开发
- 3 vue脚手架
- 4 脚手架内组件通讯
- 5 vuex
- 6 vue-router
- 7 vue ui组件库
- 8 LocalStorage 和 SessionStorage
- 常见问题总结:
Vue速成手册
0 前言
本文使用的是Vue2.6.14
- 环境如下:
C:\Users\z004abwh>node -v
v16.14.2
C:\Users\z004abwh>npm -v
8.5.0
-
IDE
- Vscode
- 插件Vetur、ES6、HTML和CSS等基础插件
-
调试工具
vue devtools
两种安装方式:
-
第一种:github下载zip包,
https://github.com/vuejs/devtools#vue-devtools
最新版本的调试工具要用yarn命令
npm install -g yarn
解压从github上下载的vue-devtools,进入目录
执行:yarn install
执行:yarn run build
最后把打包的结果导入到浏览器的扩展程序中
-
第二种:直接在一个空目录中使用npm i vue-devtools(亲测可用)
在vue-devtools文件夹下:shells>chrome>manifest.json,将配置里的persistent的值修改为true;
打开chrome,进入扩展程序管理界面(chrome://extensions/),加载已解压的扩展程序。选择vue-devtools>shells>chrome。加载完成就会出现以下的Vue.js devtools扩展程序。然后重启浏览器,再打开vue项目页面就可以在chrome的Devtools(就是console的那一栏,一般都排在最后面的。)找到Vue面板了。
-
1 基础篇
项目的目录结构:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YzZauMtX-1663494649201)(D:\markdown\Vue速成手册.assets\image-20220720104855387.png)]
1.1 Hello world
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--创建一个容器-->
<div id="app">
<!--插值表达式-->
{{ message }}
</div>
<script src="./vue.js"></script>
<script>
<!-- 实例化一个Vue对象-->
const vm = new Vue({
// 挂在一个容器
el: "#app",
// 创建组件内的数据
data: {
message: "Hello Vue 2.6.14"
}
})
</script>
</body>
</html>
浏览器调试窗口预留:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1EJGdfUx-1663494649202)(D:\markdown\Vue速成手册.assets\image-20220720104954011.png)]
1.1.1 小结
- 想让Vue工作,必须要创建一个Vue实例,Vue构造函数接收一个唯一参数就是配置对象。
- app这个div内依然遵守html规范,只是扩展了一些Vue语法
- id为app这个div被称为Vue的模板
- Vue实例和容器是一一对应的
- 真实开发中,一个项目一般只会有一个Vue实例,Vue实例配合组件一起使用
- data中的数据改变,模板中使用data的位置会自动更新
1.2 模板语法
vue中的模板语法分为两大类
- 插值语法
- 用于解析标签内容
- 写法:{{ xxx }}, xxx是js表达式,可以直接读取到data中的所有区域
1.3 v-bind数据绑定
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>v-bind数据绑定</title>
</head>
<body>
<div id="app">
<!-- 一般写法-->
<input type="text" v-bind:value="message"> <br><br>
<!-- 简写方法-->
<input type="text" :value="message">
</div>
<script src="./vue.js"></script>
<script>
const vm = new Vue({
el: "#app",
data: {
message: "数据绑定"
}
})
</script>
</body>
</html>
1.4 v-model双向数据绑定
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>v-bind数据绑定</title>
</head>
<body>
<div id="app">
<!-- 一般写法-->
<input type="text" v-model:value="message"> <br><br>
</div>
<script src="./vue.js"></script>
<script>
const vm = new Vue({
el: "#app",
data: {
message: "数据绑定"
}
})
</script>
</body>
</html>
1.5 el的两种写法
-
第一种写法:el做为Vue构造函数的参数对象中的一个key,其值是一个符合css选择器的一个容器
const vm = new Vue({ el: '#app' })
-
第二种写法:先创建Vue实例,最后通过vm.$mount(‘#app’) 挂载对象
const vm = new Vue({}) vm.$mount('#app')
1.6 data的两种写法
-
对象形式
const vm = new Vue({ el: '#app', data: { message: "hello" } })
-
函数形式(在组件中必须是函数形式的)
const vm = new Vue({ el: '#app', // 正常写法 data: function(){ return { message: "hello world" } } // 简写形式 data(){ return { message: "hello world" } } // 错误写法 data: ()=>{ console.log(this) # 这个this指向的是Window return{message: 'Hello'} } })
由Vue管理的函数,一定不要写箭头函数,否则this指向就不再是Vue实例了
1.7 MVVM 模型
-
M-Model 指data中的数据
-
V-View 指模板
-
VM-ViewModel 视图模型,Vue实例
-
vm对象
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-knIl6gzX-1663494649202)(D:\markdown\Vue速成手册.assets\image-20220720120634191.png)]
vm对象里面的属性和其原型对象上面的属性都可以在模板中直接使用。
1.8 数据代理的本质
1.8.1 JS基础知识复习
Object对象是原型链的尽头,也可以说是所有对象的超类,在Object对象上有一个defineProperty方法。下面看下这个方法的具体使用流程:
// 定义一个临时变量,初始化为空
let temp = ''
// 定义一个对象,设置两个属性分别是name和age
let obj = {
name: '张三',
age: 18
}
// 调用Object的defineProperty方法
// 1 参数1: 是需要监听的对象
// 2 参数2: 是需要监听的对象里面的某一个属性
// 3 参数3: 用于定制get方法和set方法,get方法的返回值是obj.xxx的结果值,set方法接收一个参数,这个参数是obj.xxx = yyy 中的yyy
Object.defineProperty(obj, 'name', {
get(){
console.log('obj的name属性被读取了')
return '小乌龟'
},
set(value){
console.log('obj的name属性被改写了')
temp = value
}
})
// 改变name属性的值
obj.name = '张无忌' // 输出: obj的name属性被改写了
// 临时变量被赋值
console.log(temp) // 输出: 张无忌
// 获取对象属性值方法一
console.log(obj['name']) // 输出: obj的name属性被读取了 小乌龟
// 获取对象属性值方法二
console.log(obj.name) // 输出: obj的name属性被读取了 小乌龟
- 一个对象代理另一个对象的读和写
let obj1 = {
name: '张三',
age: 19
}
let obj2 = {
name: '里斯',
age: 24
}
Object.defineProperty(obj1, 'name', {
get(){
return obj2.name
},
set(value){
obj2.name = value
}
})
Object.defineProperty(obj1, 'age', {
get(){
return obj2.age
},
set(value){
obj2.age = value
}
})
/*
测试代码
obj1.name
'里斯'
obj1.age
24
*/
1.8.2 vue中数据代理的解读
vue中的数据代理是通过vm对象来代理data对象中属性的操作
使用vm对象代理data对象的好处是更方便,在模板中可以直接操作data中的数据,在vm实例中可以通过this.xxx直接获取到xxx属性。
new Vue({…})创建vm实例成功后,会把data中的值赋值到vm的_data属性下,vm.xxx本质上会调用vm._ _ data.xxx,_ data.xxx又会调用defineProperty的get()方法,进行实现使用vm对象代理data对象的行为。
- 通过object.defineProperty()把data中所有属性添加到vm上
- 为每一个添加到vm上的属性都指定一个setter和getter
- 在getter、setter内部去操作data中对象的属性
- vue将data中的数据拷贝一份到_data属性中,又将_data里面的属性提到Vue实例中,通过defineProperty实现数据代理,通过getter、setter操作属性,进而操作_data中的属性,_ _ data对data进行数据劫持,实现响应式。
1.9 处理事件
1.9.1 基础用法
-
以点击事件为例演示Vue中如何绑定事件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>绑定事件练习</title> </head> <body> <div id="app"> <!-- v-on普通写法--> <button v-on:click="test">绑定事件练习</button> <!-- v-on简单写法--> <button @click="test">绑定事件简单写法</button> <!-- 参数传递@click="sendParams"等价于@click="sendParams($event)" $event保存着绑定的事件--> <button @click="sendParams">绑定事件默认传参</button> <!-- 如果有多个参数--> <button @click="sendMultiParams($event, 123)">绑定事件传递多个参数</button> </div> <script src="./vue.js"></script> <script> const vm = new Vue({ el: '#app', data: { msg: 'hello' }, methods: { test() { alert(this.msg) }, sendParams(e) { console.log(e) // 输出点击事件 }, sendMultiParams(e, x) { console.log(e) // 输出点击事件 console.log(x) // 输出123 } } }) </script> </body> </html>
-
简单总结一下:
-
绑定事件的正常写法: v-on:event = “callback”; 绑定事件的简单写法:@event = “callback”
-
@click=“callback” 等价于 @click=“callback($event)” ,如果回调不需要传其他参数那么前面的两种写法都可以
-
如果回调需要用到事件对象和其他参数,那必须这样写:@click=“callback($event, args)”
-
回调函数定义在methods上
-
-
-
methods函数中的this指向问题
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>methods中this的指向问题</title> </head> <body> <div id="app"> <button @click="getMsg">获取data中的msg</button> <button @click="getMsg2" style="background-color: red">获取data中的msg</button> </div> <script src="./vue.js"></script> <script> msg = '全局我最大' const vm = new Vue({ el: '#app', data: { msg: '嘻嘻哈哈好开心' }, methods: { getMsg: function () { console.log(this) // 输出 : Vue实例对象 也就是vm console.log(this.msg) // 输出 : 嘻嘻哈哈好开心 }, getMsg2: () => { console.log(this) // 输出:Window 全局对象 console.log(this.msg) // 输出: 全局我最大 } } }) </script> </body> </html>
- 简单总结:
- methods中定义的函数最终会出现在vm对象或者组件实例对象中
- methods中的函数必须使用普通的函数定义方式,不可以使用箭头函数,如果使用箭头函数,会改变函数内this的指向
- 简单总结:
1.9.2 常用事件修饰符
- prevent 阻止默认事件
- stop 阻止事件冒泡
- once 事件只触发一次
- capture 使用事件的捕获模式
- self 只有event.target是当前操作的元素才触发事件
- passtive 事件的默认行为立即执行,无需等待事件回调执行完毕
事件描述符可以连续写
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>事件修饰符</title>
<!-- 引入Vue -->
<script type="text/javascript" src="../js/vue.js"></script>
<style>
* {margin-top: 20px;}
.demo1 {height: 50px;background-color: skyblue;}
.box1 {padding: 5px;background-color: skyblue;}
.box2 {padding: 5px;background-color: white;}
.list {width: 200px;height: 200px;background-color: skyblue;overflow: auto;}
li {height: 100px;}
</style>
</head>
<body>
<div id="root">
<h2>欢迎来到{{ name }}学习</h2>
<!-- 阻止默认事件(常用) -->
<a href="http://www.atguigu.com" @click.prevent="showInfo">点我提示信息</a>
<!-- 阻止事件冒泡(常用) -->
<div class="demo1" @click="showInfo">
<button @click.stop="showInfo">点我提示信息</button>
<!-- 修饰符可以连续写 -->
<!-- <a href="http://www.qq.com" @click.prevent.stop="showInfo">点我提示</a> -->
</div>
<!-- 事件只触发一次(常用) -->
<button @click.once="showInfo">点我提示信息</button>
<!-- 使用事件的捕获模式 -->
<div class="box1" @click.capture="showMsg(1)">
div1
<div class="box2" @click="showMsg(2)">
div2
</div>
</div>
<!-- 只有event.target是当前操作的元素时才触发事件; -->
<div class="demo1" @click.self="showInfo">
<button @click="showInfo">点我提示信息</button>
</div>
<!-- 事件的默认行为立即执行,无需等待事件回调执行完毕; -->
<!-- scroll是滚动条滚动,passsive没有影响 -->
<!-- wheel是鼠标滚轮滚动,passive有影响 -->
<ul @wheel.passive="demo" class="list">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
</div>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el: '#root',
data: {
name: '尚硅谷'
},
methods: {
showInfo(e) {
alert('同学你好!')
// console.log(e.target)
},
showMsg(msg) {
console.log(msg)
},
demo() {
for (let i = 0; i < 100000; i++) {
console.log('#')
}
console.log('累坏了')
}
}
})
</script>
</body>
</html>
1.9.3 监听键盘事件
-
Vue定制的常用按键别名
- enter 回车
- delete 删除
- esc 退出
- space 空格
- up
- down
- left
- right
- tab 必须配合keydown使用
-
自定义按键别名
Vue.config.KeyCodes.自定义键名 = 键码
-
案例
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>键盘事件</title> <!-- 引入Vue --> <script type="text/javascript" src="../js/vue.js"></script> </head> <body> <div id="root"> <h2>欢迎打开{{name}}笔记</h2> <input type="text" placeholder="按下回车提示输入" @keyup.enter="showInfo"><br/> <input type="text" placeholder="按下tab提示输入" @keydown.tab="showInfo"><br/> <input type="text" placeholder="按下回车提示输入" @keydown.huiche="showInfo"><br/> </div> <script type="text/javascript"> Vue.config.productionTip = false // 阻止 vue 在启动时生成生产提示。 Vue.config.keyCodes.huiche = 13 // 定义了一个别名按键 new Vue({ el: '#root', data: { name: 'cess' }, methods: { showInfo(e) { // console.log(e.key,e.keyCode) console.log(e.target.value) } }, }) </script> </body> </html>
1.10 计算属性
1.10.1 一个小例子引入计算属性
-
使用插值表达式实现:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>计算属性引入小例子</title> </head> <body> <div id="app"> first name: <input type="text" v-model:value="firstName"/> <br> last name: <input type="text" v-model:value="lastName"/> <h3 style="color: red">full name: {{ firstName + '-' + lastName }}</h3> </div> <script src="./vue.js"></script> <script> Vue.config.productionTip = false const vm = new Vue({ el: '#app', data: { firstName: '张', lastName: '三', }, }) </script> </body> </html>
-
使用methods实现:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>计算属性引入小例子</title> </head> <body> <div id="app"> first name: <input type="text" v-model:value="firstName"/> <br> last name: <input type="text" v-model:value="lastName"/> <h3 style="color: red">full name: {{ getFullName() }}</h3> </div> <script src="./vue.js"></script> <script> Vue.config.productionTip = false const vm = new Vue({ el: '#app', data: { firstName: '张', lastName: '三', }, methods: { getFullName() { console.log('@getFullName被执行了。。。') return this.firstName + this.lastName }, } }) </script> </body> </html>
-
使用计算属性写法
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>计算属性引入小例子</title> </head> <body> <div id="app"> first name: <input type="text" v-model:value="firstName"/> <br> last name: <input type="text" v-model:value="lastName"/> <h3 style="color: green">full name: {{ fullName }}</h3> </div> <script src="./vue.js"></script> <script> Vue.config.productionTip = false const vm = new Vue({ el: '#app', data: { firstName: '张', lastName: '三', }, computed: { // 完整写法 fullName: { get() { return this.firstName + '-' + this.lastName }, set(value) { let arr = value.split('-') this.firstName = arr[0] this.lastName = arr[1] } } // 如果只是获取数据,那么可以直接使用简写方式 fullName(){ return this.firstName + '-' + this.lastName } } }) </script> </body> </html>
1.10.2 计算属性小结
计算属性一般用于通过已知属性计算获取到的值,例如上面的例子。姓名都是已经的属性。连接起来获取全名
计算属性的原理也是利用Object.defineProperty()方法提供的getter和setter
get函数初次读取时执行一次,数据变化时执行一次
与methods相比,计算属性有缓存机制,数据不变化就不会更新
计算属性会出现在vm实例对象上,可以直接在模板中像data中的数据一样使用
如果需要改变计算属性,并响应式的更新页面,那么需要设计set方法,如果不需要修改计算属性和其相关的属性,那么使用简写格式是最好用的。
1.11 侦听属性watch用法
watch监视属性用于监视属性的变化,当属性发生变化时,回调函数自动被调用。
监视的属性必须存在才能被监视,可以是data里面的属性也可以是计算属性
监视属性同样具有两种写法:
- 第一种是在配置对象中直接传入
- 第二种是使用vm实例的$watch函数
1.11.1 watch基本用法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>watch用法</title>
</head>
<body>
<div id="app">
<h3>好好 {{ msg }}</h3>
<button @click="changeStatus">切换状态</button>
</div>
<script src="./vue.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
flag: true,
msg: ''
},
methods: {
changeStatus() {
this.flag ? this.msg = '学习' : this.msg = '玩耍'
this.flag = !this.flag
}
},
// 监视属性,key必须是watch,vue中已经写死,value是一个对象
watch: {
// 监视的属性,可以是data也可以是计算属性,当被监视的属性发生变化时,handler函数会被调用
msg: {
// 内部钩子函数,接收两个值,分别是修改后的值和修改前的值
handler(newVal, oldVal) {
console.log('msg被修改了', newVal, oldVal)
}
}
}
})
</script>
</body>
</html>
-
计算属性的简写方式
/*前面省略*/ watch: { msg(newVal, oldVal){ console.log('msg被修改了', newVal, oldVal) } } /*后面省略*/
-
如果想加载时就执行一次,那么需要配置immediate属性
/*前面省略*/ watch: { msg: { immediate: true, //加载完成就触发一次 handler(newVal, oldVal) { console.log('msg被修改了', newVal, oldVal) } } } /*后面省略*/
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HFuLA8wM-1663494649203)(D:\markdown\Vue速成手册.assets\image-20220721110751321.png)]
加载时候就执行了一次监听属性的回调函数,这时候未修改时候的值是undefined。
1.11.2 监视复杂对象
在js中对象和数组属于复杂对象,复杂对象的值是一个指针,指向的是堆中的地址,修改复杂对象内部的值并不会改变对象本身,所以也就不会触发监视属性。
-
监视复杂对象的错误写法
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>watch深度监视案例</title> </head> <body> <div id="app"> <h1>个人信息</h1> <input type="text" :value="person.name"> <input type="text" :value="person.age"/> <button @click="changeName">修改姓名</button> <button @click="changeAge">修改年龄</button> </div> <script src="./vue.js"></script> <script> const vm = new Vue({ el: '#app', data: { person: { name: '张三', age: 12 } }, methods: { changeName() { this.person.name = 'lisi' }, changeAge() { this.person.age = 99 }, }, watch: { person: { handler(newValue, oldValue) { console.log('person被修改了' + '新值是:' + newValue + '老值是:' + oldValue) } } } }) </script> </body> </html>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ju1c6FXJ-1663494649204)(D:\markdown\Vue速成手册.assets\image-20220721112249335.png)]
无论怎么点击按钮,handler回调都不会执行。
-
监视复杂对象的正确写法1
watch: { 'person.name': { handler(newValue, oldValue) { console.log('person.name被修改了' + '新值是:' + newValue + '老值是:' + oldValue) } }, 'person.age': { handler(newValue, oldValue) { console.log('person.age被修改了' + '新值是:' + newValue + '老值是:' + oldValue) } } }
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o2Pn9EES-1663494649204)(D:\markdown\Vue速成手册.assets\image-20220721112442174.png)]
-
监视复杂对象的正确写法2
watch: { person: { deep: true, handler(newValue, oldValue) { console.log('person.name被修改了' + '新值是:' + newValue.name + '老值是:' + oldValue.name) console.log('person.age被修改了' + '新值是:' + newValue.age + '老值是:' + oldValue.age) } },
deep属性可以监听到复杂对象内部的数据
1.12 计算属性与侦听属性的区别
1.12.1 小实例讲解计算属性与侦听属性
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ffnji6u6-1663494649204)(D:\markdown\Vue速成手册.assets\image-20220721114031722.png)]
-
使用计算属性实现:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>计算属性与侦听属性的用法比对</title> </head> <body> <div id="app"> <input type="text" v-model:value="firstName"> <br> <input type="text" v-model:value="lastName"> <h3>{{ fullName }}</h3> </div> <script src="./vue.js"></script> <script> const vm = new Vue({ el: '#app', data: { firstName: '张', lastName: '三' }, computed: { fullName(){ return this.firstName + '-' + this.lastName } } }) </script> </body> </html>
这里的代码不做过多的解释,很基础的实现过程。
-
使用侦听属性实现:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>计算属性与侦听属性的用法比对</title> </head> <body> <div id="app"> <input type="text" v-model:value="firstName"> <br> <input type="text" v-model:value="lastName"> <h3>{{ fullName }}</h3> </div> <script src="./vue.js"></script> <script> const vm = new Vue({ el: '#app', data: { firstName: '张', lastName: '三', fullName: '张-三' }, // computed: { // fullName(){ // return this.firstName + '-' + this.lastName // } // } watch: { firstName(newVal){ this.fullName = newVal + this.lastName }, lastName(newVal){ this.fullName = this.firstName + newVal } } }) </script> </body> </html>
1.12.2 更新一下需求
我现在的需求是,如果名字更新那么全名立即更新,如果是姓更新了,等一秒钟再更新全名,更新需求后发现用计算属性很难实现了
-
用侦听属性实现:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>计算属性与侦听属性的用法比对</title> </head> <body> <div id="app"> <input type="text" v-model:value="firstName"> <br> <input type="text" v-model:value="lastName"> <h3>{{ fullName }}</h3> </div> <script src="./vue.js"></script> <script> const vm = new Vue({ el: '#app', data: { firstName: '张', lastName: '三', fullName: '张-三' }, // computed: { // fullName(){ // return this.firstName + '-' + this.lastName // } // } watch: { firstName(newVal) { setTimeout(() => { this.fullName = newVal + this.lastName }, 1000) }, lastName(newVal) { this.fullName = this.firstName + newVal } } }) </script> </body> </html>
1.12.3 小结
-
计算属性和侦听属性的对比
-
计算属性
计算属性能完成的功能,侦听属性都可以完成
-
侦听属性
侦听属性能够完成的功能,计算属性不一定能完成,比如实现一些异步操作,在一个属性修改1s后执行一个动作。
-
-
关于this指向的小总结,极其重要
- 所有Vue管理的函数,最好写成普通函数,这样this指向的是vm或者是组件实例对象
- 所有不被Vue管理的函数,比如定时器函数,ajax回调函数,promise回调函数等,最好写成箭头函数,这样this的指向还是vm或者组件实例对象。
1.13 使用v-bind绑定样式
通过v-bind绑定任意dom元素的class属性,通过对class属性赋值
绑定样式的写法:
:class = 'xxx' // xxx可以是字符串、数组、对象
- 字符串写法用于:类名不确定、要动态获取
- 数组写法用于:要绑定多个样式,个数也不确定,名字也不确定
- 对象写法用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用
也可以直接绑定dom元素的style属性
:style="[a,b]" // 其中a,b是样式对象
:style="{fontSize: xxx}" 其中xxx是动态值
示例代码:
<style>
.basic {width: 300px;height: 50px;border: 1px solid black;}
.happy {border: 3px solid red;background-color: rgba(255, 255, 0, 0.644);
background: linear-gradient(30deg, yellow, pink, orange, yellow);}
.sad {border: 4px dashed rgb(2, 197, 2);background-color: skyblue;}
.normal {background-color: #bfa;}
.atguigu1 {background-color: yellowgreen;}
.atguigu2 {font-size: 20px;text-shadow: 2px 2px 10px red;}
.atguigu3 {border-radius: 20px;}
</style>
<div id="root">
<!-- 绑定class样式--字符串写法,适用于:样式的类名不确定,需要动态指定 -->
<div class="basic" :class="mood" @click="changeMood">{{name}}</div><br/><br/>
<!-- 绑定class样式--数组写法,适用于:要绑定的样式个数不确定、名字也不确定 -->
<div class="basic" :class="classArr">{{name}}</div><br/><br/>
<!-- 绑定class样式--对象写法,适用于:要绑定的样式个数确定、名字也确定,但要动态决定用不用 -->
<div class="basic" :class="classObj">{{name}}</div><br/><br/>
<!-- 绑定style样式--对象写法 -->
<div class="basic" :style="styleObj">{{name}}</div><br/><br/>
<!-- 绑定style样式--数组写法 -->
<div class="basic" :style="styleArr">{{name}}</div>
</div>
<script type="text/javascript">
Vue.config.productionTip = false
const vm = new Vue({
el: '#root',
data: {
name: '尚硅谷',
mood: 'normal',
classArr: ['atguigu1', 'atguigu2', 'atguigu3'],
classObj: {
atguigu1: false,
atguigu2: false,
},
styleObj: {
fontSize: '40px',
color: 'red',
},
styleObj2: {
backgroundColor: 'orange'
},
styleArr: [
{
fontSize: '40px',
color: 'blue',
},
{
backgroundColor: 'gray'
}
]
},
methods: {
changeMood() {
const arr = ['happy', 'sad', 'normal']
const index = Math.floor(Math.random() * 3)
this.mood = arr[index]
}
},
})
</script>
1.14 条件渲染
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>条件渲染</title>
</head>
<body>
<div id="app">
<h1>打分系统</h1>
<label for="number">分数:</label>
<input type="text" v-model:value="number" id="number">
<button @click="getRandomNumber">获取分数</button>
<br>
<hr>
<p>等级:</p>
<p v-if="number>=80">A</p>
<p v-else-if="number<80 && number>=60">B</p>
<p v-else-if="number<60 && number>=40">C</p>
<p v-else>D</p>
</div>
<script src="./vue.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
number: 0,
},
methods: {
getRandomNumber() {
this.number = Math.round(Math.random(1, 100) * 100)
}
}
})
</script>
</body>
</html>
-
小结
-
写法
v-if = "表达式" v-else-if = "表达式" v-else
-
v-if 与v-show的区别
v-if 会直接操作dom元素,直接删除dom元素,v-if用于切换不频繁的场景 v-show 不会直接操作dom元素,v-show操作的是display属性,不会直接操作dom元素,用于频繁切换的场景
-
如果需要使用template标签对整块结构进行操作,那么只支持v-if,不支持v-show
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>v-if与v-show</title> </head> <body> <div id="app"> <!--在template标签内智能使用v-if 不能使用v-show --> <template v-if="isShow"> <p>静夜思</p> <br> <p>窗前明月光</p> <p>疑是地上霜</p> <p>举头望明月</p> <p>低头思故乡</p> </template> <button @click="show"> 显示/隐藏 </button> </div> <script src="./vue.js"></script> <script> const vm = new Vue({ el: '#app', data: { isShow: true }, methods: { show(){ this.isShow = !this.isShow } } }) </script> </body> </html>
-
1.15 v-for
v-for用于遍历数组、对象、字符串
-
语法:
<li v-for=(item, index) of items :key="index"></li>
-
示例代码
<title>基本列表</title> <script type="text/javascript" src="../js/vue.js"></script> <div id="root"> <!-- 遍历数组 --> <h3>人员列表(遍历数组)</h3> <ul> <li v-for="(p,index) of persons" :key="index">{{ p.name }}-{{ p.age }}</li> </ul> <!-- 遍历对象 --> <h3>汽车信息(遍历对象)</h3> <ul>e <li v-for="(value,k) of car" :key="k">{{ k }}-{{ value }}</li> </ul> <!-- 遍历字符串 --> <h3>测试遍历字符串(用得少)</h3> <ul> <li v-for="(char,index) of str" :key="index">{{ char }}-{{ index }}</li> </ul> <!-- 遍历指定次数 --> <h3>测试遍历指定次数(用得少)</h3> <ul> <li v-for="(number,index) of 5" :key="index">{{ index }}-{{ number }}</li> </ul> </div> <script type="text/javascript"> Vue.config.productionTip = false new Vue({ el: '#root', data: { persons: [ { id: '001', name: '张三', age: 18 }, { id: '002', name: '李四', age: 19 }, { id: '003', name: '王五', age: 20 } ], car: { name: '奥迪A8', price: '70万', color: '黑色' }, str: 'hello' } }) </script>
1.15.x key原理 diff算法 vm.$set() api的用法
待补充
1.16 收集表单数据
-
表单类型
- text :v-model收集value值
- radio:value值
- checkbox :
- 没有配置value属性, 那么收集的是checked属性(勾选与未勾选,是布尔值)
- 如果配置value属性:
- v-model的初始值是非数组,那么收集的是checked属性(勾选或者未勾选,布尔型)
- v-model的初始值是数组,那么收集的就是value组成的数组
-
v-model 修饰符
- lazy 失去焦点后再收集数据
- number 输入字符串转换成有效的数字
- trim 输入首尾空格过滤
-
收集表单示例
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>使用v-model收集表单数据</title> </head> <body> <div id="root"> <form @submit.prevent="demo"> 账号:<input type="text" v-model.trim="userInfo.account"> <br/><br/> 密码:<input type="password" v-model="userInfo.password"> <br/><br/> 年龄:<input type="number" v-model.number="userInfo.age"> <br/><br/> 性别: 男<input type="radio" name="sex" v-model="userInfo.sex" value="male"> 女<input type="radio" name="sex" v-model="userInfo.sex" value="female"> <br/><br/> 爱好: 学习<input type="checkbox" v-model="userInfo.hobby" value="study"> 打游戏<input type="checkbox" v-model="userInfo.hobby" value="game"> 吃饭<input type="checkbox" v-model="userInfo.hobby" value="eat"> <br/><br/> 所属校区 <select v-model="userInfo.city"> <option value="">请选择校区</option> <option value="beijing">北京</option> <option value="shanghai">上海</option> <option value="shenzhen">深圳</option> <option value="wuhan">成都</option> </select> <br/><br/> 其他信息: <textarea v-model.lazy="userInfo.other"></textarea> <br/><br/> <input type="checkbox" v-model="userInfo.agree">阅读并接受 <a href="https://www.yuque.com/cessstudy">《用户协议》</a> <button>提交</button> </form> </div> <script src="./vue.js"></script> <script type="text/javascript"> Vue.config.productionTip = false new Vue({ el: '#root', data: { userInfo: { account: '', password: '', age: 18, sex: 'female', hobby: [], city: 'beijing', other: '', agree: '' } }, methods: { demo() { console.log(JSON.stringify(this.userInfo)) } } }) </script> </body> </html>
1.17 自定义过滤器(vue3将移除)
对需要再模板中显示的数据进行特定格式化后再显示,适合做一些简单的逻辑
-
注册过滤器
-
全局过滤器
Vue.filter(name, callback) 全局过滤器
-
局部过滤器
new Vue({ filters: { name: callback } })
-
-
使用过滤器
{{ xxx | 过滤器名 }} v-bind:属性名 = ”xxx | 过滤器名“
过滤器可以接收额外参数,多个过滤器可以串联
并不是改变源数据,而是产生新的数据用于模板的展示
-
代码示例
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>过滤器</title> <script type="text/javascript" src="./vue.js"></script> <script src="https://cdn.bootcdn.net/ajax/libs/dayjs/1.10.6/dayjs.min.js"></script> </head> <body> <div id="root"> <h2>时间</h2> <h3>当前时间戳:{{time}}</h3> <h3>转换后时间:{{time | timeFormater()}}</h3> <h3>转换后时间:{{time | timeFormater('YYYY-MM-DD HH:mm:ss')}}</h3> <h3>截取年月日:{{time | timeFormater() | mySlice}}</h3> </div> </body> <script type="text/javascript"> Vue.config.productionTip = false // 全局过滤器 Vue.filter('mySlice',function(value){ return value.slice(0,11) }) new Vue({ el:'#root', data:{ time:1626750147901, }, // 局部过滤器 filters:{ timeFormater(value, str="YYYY年MM月DD日 HH:mm:ss"){ return dayjs(value).format(str) } } }) </script> </html>
1.18 vue内置指令小结
-
v-bind
-
v-model
-
v-for
-
v-on
-
v-show
-
v-if
-
v-else-if
-
v-else
-
v-text
- 向其所在的节点中渲染文本内容
- 与插值语法的区别,v-text会直接替换掉dom元素中的其他内容,插值表达式可以拼接,更加灵活
-
v-html
- v-html会解析html元素,可能造成xss攻击
- v-html与插值表达式的区别也是前者可以替换掉dom中的内容
- v-html与v-text的区别是前者可以解析html元素,后者不可以
-
v-cloak
-
使用css配和v-cloak可以解决网速慢时页面展示出{{ xxx }} 的问题
-
示例代码
<title>v-cloak指令</title> <style> [v-cloak] { display:none; } </style> <div id="root"> <h2 v-cloak>{{ name }}</h2> </div> // 够延迟5秒收到vue.js <script type="text/javascript" src="http://localhost:8080/resource/5s/vue.js"></script> <script type="text/javascript"> console.log(1) Vue.config.productionTip = false new Vue({ el:'#root', data:{name:'cess'} }) </script>
-
-
v-once
- v-once所在的dom节点只在第一次动态渲染后,就视为静态内容了
- 用于优化性能
-
v-pre
- 跳过v-pre修饰的dom节点,不参与编译过程
- 加快编译过程
1.19 自定义Vue指令
待补充
1.20 vue生命周期与钩子函数
这是vue基础的最后一个内容,也是vue中最重要的内容。
1.20.1 小实例引入vue生命周期
需求是这样的,我想打开网页就能看到文字具有动态效果。
不使用vue周期函数的实现代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>练习生命周期</title>
</head>
<body>
<div id="app">
<h1 :style="{opacity: opacity}">欢迎就入魔鬼训练营</h1>
</div>
<script src="./vue.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
opacity: 1
},
})
setInterval(function () {
if (vm.opacity>0) {
vm.opacity -= 0.01
} else {
vm.opacity = 1
}
}, 16)
</script>
</body>
</html>
- 缺点:
- 每次打开界面都会创建一个定时器,定时器数量会越来越多,造成浏览器卡死。
- vue外部使用vm也是不推荐的
使用vue钩子的实现代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>练习生命周期</title>
</head>
<body>
<div id="app">
<h1 :style="{opacity: opacity}">欢迎就入魔鬼训练营</h1>
</div>
<script src="./vue.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
opacity: 1
},
mounted(){
this.interval = setInterval(()=>{
if (vm.opacity > 0) {
vm.opacity -= 0.01
} else {
vm.opacity = 1
}
}, 16)
},
beforeDestroy(){
clearInterval(this.interval)
}
})
</script>
</body>
</html>
1.20.2 小结
常用的生命周期函数:
- mounted发送ajax请求,启动定时器,绑定自定义事件,订阅消息等初始化操作
- beforeDestroy清除定时器、解绑自定义事件,取消订阅消息等收尾工作
关于销毁Vue实例:
- 销毁后借助Vue开发者工具看不到任何信息
- 销毁后自定义事件会失效,但原生dom事件依然有效
- 一般不会在beforedestroy操作数据,因为即便操作数据,也不会再触发更新流程了
2 组件化开发
2.1 理解组件化
2.1.1 概念理解
- 模块
- 在前端工程中模块一般指定一个.js文件,可以在js文件引入其他模块暴露的信息,模块是一个名词,用于描述一个单独的js文件
- 模块化
- 使用多文件js编写的项目就可以称为模块化的项目,模块化是一个形容词,用来形容项目
- 组件(用于实现局部功能的代码和资源的集合)
- 组件用于描述前端项目中的一块内容,组件具有一定的可复用性,类似于函数,可以通过传递参数的不同输出不同的结果
- 组件化
- 组件化同样是一个形容词,用于形容项目
2.1.2 模块化编写项目的缺点
在没有类似vue这种前端框架之前,我们都是用模块化编写的前端项目的,模块化开发的缺点是依赖关系混乱,不好维护,代码复用性不高,具有很高的耦合性,代码冗余。
组件化编程具有低耦合,代码复用率高等特点:
2.2 非单文件组件
-
定义
一个文件中包含有n个组件
2.3 单文件组件
-
定义
一个文件中只有1个组件
2.4 组件的基本使用
使用组件的三大步骤
1定义组件
2注册组件
3使用组件
2.4.1 定义组件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>创建组件</title>
</head>
<body>
<div id="app">
<!--在容器内使用组件-->
<h1>{{ title }}</h1>
<person></person>
<hr>
<dog></dog>
</div>
<template id="cdog">
<div>
<h1>狗详细信息</h1>
<label for="dog-name"></label>
<input type="text" v-model:value="name" id="dog-name"> <br>
<label for="dog-sex"></label>
<input type="text" v-model:value="sex" id="dog-sex">
<button @click="showDogInfo">显示狗的信息</button>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
// 第一步: 创建组件
const person = Vue.extend({
template: `
<div>
<label for="name"></label>
<input id="name" type="text" v-model:value="name"> <br>
<label for="age"></label>
<input id="age" type="text" v-model:value="age">
<button @click="showInfo">显示人员信息</button>
</div>
`,
data() {
return {
name: '张三',
age: 19
}
},
methods: {
showInfo() {
alert(this.name + '-' + this.age)
}
}
})
const dog = Vue.extend(
{
template: '#cdog',
data() {
return {
name: '大黄',
sex: '小公狗'
}
},
methods: {
showDogInfo() {
alert(this.name + '-' + this.sex)
}
}
}
)
// 第二步:注册组件,全局注册,对所有的vm都有效
Vue.component('person', person)
const vm = new Vue({
el: '#app',
data: {
title: '人员详细信息'
},
// 局部注册组件,只能在当前vm的容器内使用
components: {
dog,
}
})
</script>
</body>
</html>
-
对上面的案例简单总结:
-
定义组件
普通写法
const component-name = Vue.extend({ data(){ return { xxxx: 'yyyy' } } })
简单写法
const component-name = { data(){}, methods: {} }
Vue.extend传入的配置项于new Vue() 传入的配置项基本相同,有两点需要注意:
Vue.extend中不能有el,因为所有的组件最终都会挂载到vm上面
Vue.extend中的data必须写成函数形式的,不可以写成对象形式的
-
注册组件
-
全局注册
Vue.component('组件名', 组件) //例如: Vue.component('person', person) // 组件名是最终在模板中使用的标签名
-
局部注册
new Vue({ ''' components: { 组件名: 组件 } }) // 一般组件名与组件都会定义成一样的名字,所以在局部注册的时候一般会使用简写形式 new Vue({ ’‘’ components: { 组件, } })
-
-
使用组件
-
用法1
<组件名></组件名>
-
用法2 (需要脚手架支持,否则会有问题)
<组件名/>
-
-
-
注意事项
-
关于组件名
-
如果是一个单词组成
- 第一种写法(首字母小写):person
- 第二种写法(首字母大写):Person
-
如果是多个单词组成
- 第一种写法(kebab-case命名): person-info
- 第二种写法(CamelCase命名): PersonInfo
-
特别注意
-
组件名要尽可能回避html标签
-
可以在定义组件时候使用name配置项指定组件在开发者工具中呈现的名字
const cat = { name: 'xiao-zhu-pei-qi', template: '#ccat', data(){ return { msg: '我是一只小花猫' } } }
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OyDDRgvZ-1663494649205)(D:\markdown\Vue速成手册.assets\image-20220722133451776.png)]
-
-
-
2.5 组件的嵌套用法
vm可以理解为所有组件的父组件,一个组件内部可以挂多个组件,组件是可以嵌套的,类似与数据结构中的树,vm类似于根节点
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>组件嵌套</title>
</head>
<body>
<div id="app">
<app></app>
</div>
<script src="../js/vue.js"></script>
<script>
const student = {
name: 'student',
data() {
return {
name: '张三',
age: 11
}
},
template: `
<div>
<h3>学生信息展示</h3>
<p>学生姓名: {{ name }}</p>
<p>学生年龄: {{ age }}</p>
</div>
`,
}
const school = {
name: 'school',
data() {
return {
name: '家里蹲',
address: '蹲家里'
}
},
template: `
<div>
<h2>学校信息展示</h2>
<p>学校名称:{{ name }}</p>
<p>学校地址:{{ address }}</p>
<hr>
<student></student>
</div>
`,
components: {
student,
}
}
const app = {
name: 'app',
template: `
<div>
<h1>{{ title }}</h1>
<school></school>
</div>
`,
data() {
return {
title: 'app标题'
}
},
components: {
school
}
}
const vm = new Vue({
el: '#app',
components: {
app
}
})
</script>
</body>
</html>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o4aug9T0-1663494649206)(D:\markdown\Vue速成手册.assets\image-20220722142336299.png)]
2.6 关于VueComponent
通过const school = Vue.extend({options}),会定义一个组件,school本质上是一个VueComponent构造函数
当我们在模板中写 或者,Vue解析时会创建school的实例对象,Vue自动调用了new Component(options)
每次调用Vue.extend,都会返回一个全新的VueComponent,不同的组件是不同的对象
关于this指向:
组件配置中data函数、methods中的函数、watch中的函数、computed中的函数他们的this均是VueComponent实例对象
new Vue(options) 配置中:data函数或者对象、 methods中的函数、watch中的函数、computed中的函数他们的this均是Vue实例对象
VueComponent实例对象简称vc, Vue实例对象简称vm
组件实例对象vc可以访问到Vue原型上面的属性和方法
**VueComponent.prototype. _ _ proto _ _ === Vue.prototype **
2.6.1 简单说一下prototype和 _ _ proto _ _
_ _ proto _ _ 是对象特有的,负责对象的属性方法的查找,如果对象自身没有需要的属性或者方法,_ _ proto _ _会指向对象构造函数的原型对象, _ _ proto _ _的终点是null
prototype 是函数特有的,但是因为js中函数也是一种特殊的对象,所以在函数中也有 _ _ proto _ _ 这是js原型结构中很难理解的地方
2.7 单文件组件
单文件组件就是一个文件只有一个组件,一个文件只完成局部的功能和包装局部的静态资源,vue中单文件组件的文件后缀是.vue
定义vue中单文件组件的常规目录结构结构:
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 2022/7/22 14:49 114 app.vue
-a---- 2022/7/22 14:49 132 index.html
-a---- 2022/7/22 14:48 0 main.js
-a---- 2022/7/22 14:49 117 school.vue
-a---- 2022/7/22 14:49 118 student.vue
-
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:'UESTC', address:'成都' } }, methods: { showName(){ alert(this.name) } }, } </script> <style> #Demo{ background: orange; } </style>
-
student.vue
<template> <div> <h2>学生姓名:{{name}}</h2> <h2>学生年龄:{{age}}</h2> </div> </template> <script> export default { name:'Student', data() { return { name:'cess', age:20 } }, } </script>
-
App.vue
<template> <div> <School></School> <Student></Student> </div> </template> <script> import School from './School.vue' import Student from './Student.vue' export default { name:'App', components:{ School, Student } } </script>
-
main.js
import App from './App.vue' new Vue({ template:`<App></App>`, el:'#root', components:{App} })
-
index.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>
3 vue脚手架
3.1 初始化脚手架
3.1.1 基本介绍
vue-cli(vue脚手架)是vue官方提供的标准化开发平台
目前最新 的版本是5.x
3.1.2 使用步骤
-
配置淘宝镜像cnpm (这一步不是必须的,主要为了安装扩展包时候加速的)
npm config set register http://registry.npm.taobao.org
-
全局安装@vue/cli
npm install -g @vue/cli
-
切换到需要创建项目的目录,创建一个空的vue项目
vue create xxx // xxx是项目名称
-
选择使用的vue版本
选择vue2,等待下载安装依赖包,这里需要联网
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DAbFV7wu-1663494649206)(D:\markdown\Vue速成手册.assets\image-20220722150357812.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ab37CKoo-1663494649207)(D:\markdown\Vue速成手册.assets\image-20220722150416187.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-slE6bzIt-1663494649207)(D:\markdown\Vue速成手册.assets\image-20220722150523086.png)]
表示成功创建了项目。
-
启动项目
npm run serve // 注意这里是serve 不是server
-
打包项目
npm run build // 用于生产环境的打包
-
停止运行的测试服务器
Ctrl+C
vue脚手架隐藏了所有webpack相关的配置,若想查看具体的webpack配置,请执行vue inspect > output.js
output.js只能用于查看配置项,在output.js中修改配置并不能改变vue的配置项
3.2 脚手架项目文件结构
.文件目录
├── node_modules
├── public
│ ├── favicon.ico: 页签图标
│ └── index.html: 主页面
├── src
│ ├── assets: 存放静态资源
│ │ └── logo.png
│ │── component: 存放组件
│ │ └── HelloWorld.vue
│ │── App.vue: 汇总所有组件
│ └── main.js: 入口文件
├── .gitignore: git版本管制忽略的配置
├── babel.config.js: babel的配置文件
├── package.json: 应用包配置文件
├── README.md: 应用描述文件
└── package-lock.json: 包版本控制文件
3.3 修改配置项
使用vue.config.js 可以对脚手架进行个性化定制
module.exports = {
pages: {
index: {
entry: 'src/index/main.js' // 入口
}
},
lintOnSave: false // 关闭语法检查
}
配置参考链接:
https://cli.vuejs.org/zh/config/#devserver-proxy
3.4 main.js 文件解读
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({
render: h => h(App),
}).$mount('#app')
3.4.1 不同版本的vue
选择runtime版本的vue的目的是为了减少生产环境的空间,提高效率
默认情况下,vue脚手架使用的vue是runtime版本
-
vue.js与vue.runtime.xxx.js的区别
vue.js是完整的Vue,包含核心功能+模板解析器
vue.runtime.xxx.js,只包含核心功能,没有模板解析器
vue.runtime.xxx.js中没有模板解析器,所以不能使用template配置项,需要使用render函数接收到的createElement函数去执行具体内容
3.4.2 render函数
// render函数的完整形式:
render(createElement){
return createElement(App)
}
4 脚手架内组件通讯
4.1 ref属性
ref用来给元素或子组件注册引用信息,类似于id
将ref应用在html标签上获取的是真实的DOM元素;应用在组件标签上获取的是组件实例对象vc
-
设置ref标识
<h1 ref="h1"> 组件间通讯 </h1> <School ref="school"></School>
-
获取ref
this.$refs.xxx 获取ref this.$refs 返回一个对象
-
App.vue
<template> <div> <div class="first-menu"> <h1 ref="h1"> 组件间通讯 </h1> <h3>ref属性</h3> <button @click="testTag">测试标签</button> <button @click="testCpn">测试组件</button> <hr> </div> <div class="content"> <School ref="school"></School> </div> </div> </template> <script> import School from "@/components/School"; export default { name: 'App', components: { School }, methods: { testTag() { console.log(this.$refs.h1) // 获取到的是真实的DOM,this.$refs.h1.innerHTML 获取到标签内的文本 }, testCpn() { console.log(this.$refs.school) // 获取到的是组件实例对象,this.$refs.school.name 获取到的是组件实例对象的name } } } </script> <style> .active { background-color: cornflowerblue; } .content { position: absolute; left: 200px; } </style>
-
School.vue
<template> <div> <h4 ref="info">学校信息</h4> <p>学校名称: {{ name }}</p> <p>学校地址:{{ addr }} </p> </div> </template> <script> export default { name: "School", data(){ return { name: '清华大学', addr: '北京' } }, } </script> <style scoped> </style>
4.2 props属性
props 让组件接收外部传来的数据
-
传递数据
<School name='清华大学' :age="122"></school>
注意:这里的:age 是 v-bind:age的缩写,通过v-bind,122是一个整数类型
-
接收数据
-
方式一:
props: ['name', 'age']
-
方式二(限制接收到变量的类型):
props: { name: String, age: Number }
-
方式三 (限制类型,限制必要性,设定默认值)
props:{ name: { type: String, // 限制类型 required: true, // 必须传递 default: 'kobe' // 默认值 } }
-
注意:
props是只读的,vue底层会监视props属性中的值,如果对其进行修改,不会报错,但是会提示,如果需要修改props中的值,请将props复制到data中进行修改。
-
demo
-
普通接收
- App.vue
<template> <div> <div class="first-menu"> <h1 ref="h1"> 组件间通讯 </h1> <h3>props属性</h3> <hr> </div> <div class="content"> <School ref="school" name="清华大学" addr="北京"></School> <School ref="school" name="武汉大学" addr="武汉"></School> <School ref="school" name="厦门大学" addr="厦门"></School> </div> </div> </template> <script> import School from "@/components/School"; export default { name: 'App', components: { School }, } </script> <style> .active { background-color: cornflowerblue; } .content { position: absolute; left: 200px; } </style>
-
School.vue
<template> <div> <h4 ref="info">学校信息</h4> <p>学校名称: {{ name }}</p> <p>学校地址:{{ addr }} </p> <hr> </div> </template> <script> export default { name: "School", data() { return {} }, props: ['name', 'addr'] // 直接接收属性值 } </script> <style scoped> </style>
-
限制变量类型接收
<template> <div> <h4 ref="info">学校信息</h4> <p>学校名称: {{ name }}</p> <p>学校地址:{{ addr }} </p> <hr> </div> </template> <script> export default { name: "School", data() { return {} }, props: { name: String, addr: Number // 如果传递的不是数字型,报下方错误 } } </script> <style scoped> </style> // Vue warn]: Invalid prop: type check failed for prop "addr". Expected Number with value NaN, got String with value "北京".
-
限制类型、必要性、默认值
<template> <div> <h4 ref="info">学校信息</h4> <p>学校名称: {{ name }}</p> <p>学校地址:{{ addr }} </p> <hr> </div> </template> <script> export default { name: "School", data() { return {} }, props: { name: { type: String, required: true, default: '家里蹲大学' }, addr: { type: String, required: true, default: 'home' } } } </script> <style scoped> </style>
-
4.3 mixin 属性
可以把多个组件共用的配置提取成一个混入mixin对象.
-
定义mixin
const xxx = { // 公用数据 data(){....}, // 公用方法 methods: {}, // 钩子函数 created() {} }
-
使用mixin
-
全局使用混入
Vue.mixin(xxx)
-
局部使用混入
mixins: ['xxx', ]
-
-
demo
-
src/mixin/index.js
const mixin = { data() { return { 'login': 'xxxx' } }, methods: { Login() { alert('登录视图') }, Logout() { alert('登出视图') } }, created() { alert('mixin 创建组件钩子函数') } } export default mixin
-
src/App.vue (局部导入)
<template> <div> 登录用户: {{ login }} <button @click="Login">Login</button> <button @click="Logout">Logout</button> </div> </template> <script> import mixin from '@/mixins' export default { name: 'App', mixins: [mixin, ] // 局部mixin } </script> <style> .active { background-color: cornflowerblue; } .content { position: absolute; left: 200px; } </style>
-
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.use(ElementUI) // 全局使用ElementUI Vue.config.productionTip = false import mixin from '@/mixins' Vue.mixin(mixin) // 全局导入 new Vue({ render: h => h(App), }).$mount('#app')
-
4.4 plugin 属性
用于提升、扩展Vue的功能
-
本质
包含install方法的一个对象,install的第一个参数是Vue,第二个的参数是插件使用者传递的数据
-
使用插件
Vue.use()
-
demo
-
src/plugin/index.js
export default { install(Vue, x,y,z){ console.log(x,y,z) // 全局过滤器 Vue.filter('myslice', function(value){return value.slice(0,4)}) // 定义全局指令 Vue.directive('fbind', { // 指令与元素成功绑定时 bind(element, binding) {element.value = binding.value} }, // 指令所在元素被插入页面时 inserted(element, binding){element.focus()} // 指令所在的模板被重新解析时 update(element, binding){element.value = binding.value} ) } // 定义混入 Vue.mixin({ data(){ return { x: 100, y:200 } } }) // 给Vue原型上添加一个方法(vm和vc可以用) Vue.prototype.hello = ()=>{alert('hello plugins!')} }
-
src/main.js
Vue.use(Vue, 1,2,3) // 应用插件
-
4.5 scoped 属性
## 4.6 自定义事件
父组件传递数据到子组件,使用props
==**自定义事件是从子组件传递数据到父组件**==
子组件想要给父组件传递数据,要在父组件中给子组件定义自定义事件,事件回调在父组件中
- 语法
- 父组件
```vue
<template>
<children @customEvent="callback"> </children> // 或者 v-on:customEvent="callback"
</template>
<script>
methods: {
callback(...params){
// params 接收来自子组件中的数据
}
}
</script>
```
- 子组件
```vue
<script>
methods: {
alertCustomEvent(){
this.$emit('customEvent', param1, param2, ...)
// 参数1 是父组件为子组件设定的自定义事件名
// 参数2~参数n 是子组件为父组件传递的数据,最终被父组件的回调函数接收并处理。
}
}
</script>
```
```js
this.$emit('customEvent') // 解绑一个自定义事件
this.$emit(['event1', 'event2']) // 解绑多个自定义事件
this.$destroy() // 销毁子组件,绑定其身上的自定义事件随着全部失效
```
- 父组件 方式2
```vue
<template>
<div>
<div class="top">
<bookHeader ref="bookHeader"></bookHeader>
</div>
</div>
</template>
<script>
methods: {
addBookCallback(obj){
this.bookList.push(obj)
}
},
mounted() {
this.$refs.bookHeader.$on('addBook', this.addBookCallback)
// 如果想让自定义事件只执行一次,将$on转换成$once即可
}
</script>
```
- 父组件无论使用方式1还是方式2,子组件的使用方式均相同。
- 组件绑定原生事件
在原生事件后面加上==**.native**== 否则原生事件也会被当做是自定义事件。
```js
<template>
<div>
<div class="top">
<bookHeader @click.native="sayHello"></bookHeader>
</div>
</div>
</template>
4.7 全局事件总线
4.7.1 定义
一种可以在任意组件之间通信的方式,不限于父子组件之间,本质上就是一个对象
- 所有的组件都必须能够看到这个对象
- 这个对象必须能够使用$on、$emit、$off、方法去绑定、触发和解绑事件
4.7.2 使用步骤
-
定义全局事件总线
new Vue({ ... beforeCreate() { Vue.prototype.$bus = this // 安装全局事件总线, $bus就是当前的vm } ... })
-
使用全局事件总线
-
接收数据
接收数据的组件,使用$bus.$on定义自定义数据,回调函数留在接收数据的组件中.
-
提供数据
提供数据的组件使用$bus.$emit()触发自定义事件
-
-
解绑当前组件中用到的所有事件,beforeDestroy()
-
Demo
-
接收数据的组件
<template> <div> <div class="top"> <bookHeader></bookHeader> </div> <div class="middle"> <bookList :books="bookList"></bookList> </div> <div class="bottom"> <bookFooter></bookFooter> </div> </div> </template> <script> import bookHeader from "@/components/bookHeader"; import bookList from "@/components/bookList"; import bookFooter from "@/components/bookFooter"; export default { name: 'App', data() { return { bookList: [ {id: 1, name: '三国演艺'} ] } }, components: { bookHeader, bookList, bookFooter }, methods: { addBookCallback(obj) { // 哪个组件接收数据,回调函数就写在哪个组件内 this.bookList.push(obj) }, sayHello() { console.log('say hello') } }, mounted() { this.$bus.$on('addBook', this.addBookCallback) //绑定事件 }, beforeDestroy() { this.$bus.$off('addBook') // 解绑事件 } } </script> <style scoped> </style>
发送数据的组件
<template> <div> <el-input v-model="input" placeholder="请输入内容" @change="addBook"></el-input> </div> </template> <script> export default { name: "bookHeader", data() { return { input: '' } }, methods: { addBook() { this.$bus.$emit('addBook', { // 与自定义组件不同的是必须使用this.$bus触发事件 id: 2, name: this.input }) } } } </script> <style scoped> </style>
-
4.7.3 小结
全局事件总线相比较自定义事件总线更加灵活。
4.8 发布与订阅
用的较少,先不展开,后面有时间在搞。
5 vuex
5.1 搭建vuex环境
-
安装
npm i vuex@3 vuex目前最新版本是4版本,4版本必须使用vue3,如果使用vue2版本,必须安装vuex3,否则安装会报错
-
配置
在src文件下创建store目录,在目录内创建index.js
看图说话:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IludkSgi-1663494649207)(D:\markdown\Vue速成手册.assets\image-20220802231636436.png)]
// 导入Vue import Vue from 'vue' // 导入Vuex import Vuex from 'vuex' // 注册插件 Vue.use(Vuex) const actions = { // xxx(context, value) { context.commit('ADD', value) }, } const mutations = { ADD(state, value) { state.sum += value }, } const state = { sum: 10 } export default new Vuex.Store({ actions, mutations, state })
5.2 getter
5.3 模块化
6 vue-router
6.1 安装
当前最新的vue-router版本是V4.x,V4.x版本的vue-router适用于vue3,如果使用vue2版本,请使用vue-router3.x版本
npm i vue-router@3
cnpm i vue-router@3 # 使用淘宝镜像安装,可以加速
6.2 配置
-
src/router/index.js
import Vue from 'vue' import VueRouter from 'vue-router' import About from '../components/About' import Home from '../components/Home' Vue.use(VueRouter) export default new VueRouter({ routes: [ { path: '/about', component: About }, { path: '/home', component: Home } ] })
-
src/main.js
import Vue from 'vue' import App from './App.vue' import router from './router' Vue.config.productionTip = false new Vue({ render: h => h(App), router }).$mount('#app')
-
src/components/App.vue
<template> <div> <div id="app"> 跳转标签 <router-link active-class="active" to="/about">About</router-link> <router-link active-class="active" to="/home">Home</router-link> </div> <div> 组件内容展示区 <router-view></router-view> </div> </div> </template> <script> export default { name: 'App', } </script> <style> .active { background-color: cornflowerblue; } </style>
-
src/components/About.vue
-
src/components/Home.vue
6.3 路由传参query
假设有一个图书管理系统,首页显示所有图书的列表,当点击每本图书后,显示图书的详细视图,点击不同的图书显示不同的详细视图,类似这种需求就需要给路由传参。
vue-router中路由传参有两种写法,分别是字符串写法和对象写法。
src/router/index.js
import Vue from "vue"
import VueRouter from 'vue-router'
import Detail from "@/components/Detail";
Vue.use(VueRouter)
export default new VueRouter({
routes: [
{
path: '/books',
component: Detail
}
]
})
src/App.vue
<template>
<div>
<div id="app">
<h1>图书管理系统</h1>
<table border="1px">
<thead>
<th>ID</th>
<th>书名</th>
<th>出版社</th>
</thead>
<tbody>
<tr v-for="book in bookList" :key="book.id">
<th>{{ book.id }}</th>
<th>
<router-link :to="`/books?id=${book.id}&name=${book.name}&publish=${book.publish}`">
{{ book.name }}
</router-link>
</th>
<th>{{ book.publish }}</th>
</tr>
</tbody>
</table>
</div>
<hr>
<div style="background-color: cornflowerblue">
<router-view></router-view>
</div>
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {
bookList: [
{id: '001', name: '天龙八部', publish: '中国出版社'},
{id: '002', name: '西游记', publish: '中国出版社'},
{id: '003', name: '水浒传', publish: '中国出版社'},
{id: '004', name: '金瓶梅', publish: '中国出版社'},
{id: '005', name: '神雕侠侣', publish: '中国出版社'},
{id: '006', name: '天下第一', publish: '中国出版社'},
]
}
}
}
</script>
<style>
.active {
background-color: cornflowerblue;
}
</style>
src/components/Detail.vue
<template>
<div>
<h1>书籍详细信息</h1>
<h2>书籍名称:{{ $route.query.name }}</h2>
<h2>书籍出版社:{{ $route.query.publish }}</h2>
</div>
</template>
<script>
export default {
name: "Detail"
}
</script>
<style scoped>
</style>
-
路由传参方式1:
<router-link :to="`/books?id=${id}&name=${name}&publish=${publish}`">跳转</router-link>
在组件中使用$route.query.name 等获取路由中传递的参数
-
路由传参方式2:
<router-link :to="{ path: '/books', query: { id: book.id, name: book.name, publish: book.publish } }"> {{ book.name }} </router-link>
在组件中使用$route.query.name 等获取路由中传递的参数
6.4 命令路由
为了简化多级路由的写法,可以在定义路由时添加一个name属性。
src/router/index.js
routes: [
{
name: 'detail',
path: '/books',
component: Detail
}
]
src/App.vue
-
不需要传参,to属性使用字符串拼接写法和对象写法都可以
字符串写法
<router-link :to="{name: 'detail'}">跳转</router-link>
对象写法
<router-link :to="{ name:'detail'}"> <router-link>
-
需要传参,to属性必须使用对象写法
<router-link :to="{ name:'detail', query: { id: '01', name: '水浒传', publish: '中国出版社' } }"> <router-link>
6.5 路由传参params
query传参是在跳转时传递参数,类似get请求传参,将参数通过?链接到url后面,通过&符号链接多个key-value.
http://127.0.0.1:8000/books?id=1&name=水浒传
id和name就会传递给/books路由对应组件的$router.query中。
params在定义路由的时候就要确定要传递的参数,在path中使用占位符声明接收params参数,如果使用params传参,to的对象写法不能使用path属性,必须使用name属性。
src/router/index.js
import Vue from "vue"
import VueRouter from 'vue-router'
import Detail from "@/components/Detail";
Vue.use(VueRouter)
export default new VueRouter({
routes: [
{
name: 'detail',
path: '/books/:id/:name/:publish', // 使用:占位符对需要传递的参数占位
component: Detail
}
]
})
src/App.vue
'''
<router-link :to="`/books/${book.id}/${book.name}/${book.publish}`">
{{ book.name }}
</router-link>
'''
再次重申,如果to使用对象写法,必须使用命名路由,不能使用path
'''
<router-link :to="{
name: 'detail',
params: {
id: book.id,
name: book.name,
publish: book.publish
}
}">{{ book.name }}</router-link>
'''
src/component/Detail.vue
使用$route.params.xxx接收路由中的params参数
<template>
<div>
<h1>书籍详细信息</h1>
<h2>书籍ID:{{ $route.params.id }}</h2>
<h2>书籍名称:{{ $route.params.name }}</h2>
<h2>书籍出版社:{{ $route.params.publish }}</h2>
</div>
</template>
<script>
export default {
name: "Detail"
}
</script>
<style scoped>
</style>
6.6 路由props属性
props可以让路由更方便接收到参数:
{
name:'detail',
path:'books/:id/:name/:publish',
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
}
}
}
src/route/index.js
import Vue from "vue"
import VueRouter from 'vue-router'
import Detail from "@/components/Detail";
Vue.use(VueRouter)
export default new VueRouter({
routes: [
{
name: 'detail',
path: '/books/:id/:name/:publish',
component: Detail,
// 写法1:对象写法
// props: {
// hello: 'world'
// }
// 写法2: 布尔值
//props: true
// 写法3: 函数写法
props($route){
return {
xixi:$route.params.id,
haha:$route.params.name,
xxoo: $route.params.publish
}
}
}
]
})
/src/components/Detail.vue
<template>
<div>
<h1>书籍详细信息</h1>
<h2>书籍ID:{{ xixi }}</h2>
<h2>书籍名称:{{ haha }}</h2>
<h2>书籍出版社:{{ xxoo }}</h2>
<!-- <h2>我来自路由中的props:{{ hello }}</h2>-->
</div>
</template>
<script>
export default {
name: "Detail",
// 写法1:对应路由中props的对象写法
// props: ['hello']
// 写法2:对应路由中props的布尔值写法,如果为true,params中传递的参数都将到组件的props中
// props: ['id','name','publish']
// 写法3:函数写法
props: ['xixi', 'haha', 'xxoo']
}
</script>
<style scoped>
</style>
6.7 编程式路由导航
6.7.1 浏览器缓存
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JXxXgPPA-1663494649207)(D:\markdown\Vue速成手册.assets\image-20220917172112078.png)]
在浏览器左上角一般都会有两个箭头,分别表示“向前"和"向后".
浏览器的路由缓存类似于一个栈,有两种存储地址的方式:
-
replace 方式:
<router-link :replace=true>Name</router-link> // 简写方式 <router-link replace>Name</router-link>
-
push方式(默认):
6.7.2 编程式路由组件(不使用router-link实现路由跳转)
不借助实现路由跳转,让路由跳转更加灵活
this.$router.push({})
this.$router.replace({})
this.$router.forward() // 前进
this.$router.back() // 后退
this.$router.go(n) // 可前进也可后退,正数为前进,负数为后退
push和replace里面的参数和to相同:
this.$router.push({
name: 'router name'
})
- Test.vue
<template>
<div>
<h2>Test push&replace</h2>
<div>
<router-link v-for="book in bookInfo" :key="book.id" :to="{name:'xiangxi', params:{id:book.id, title:book.title}}">
{{ book.title }}
</router-link>
</div>
</div>
</template>
<script>
export default {
name: "Test",
data() {
return {
bookInfo: [
{id: 1001, title: '三国演义'},
{id: 1002, title: '水浒传'},
{id: 1003, title: '西游记'},
{id: 1004, title: '红楼梦'},
]
}
},
methods: {
showPush(book){
this.$router.push({
name: 'xiangxi',
params: {
id: book.id,
title: book.title
}
})
},
showReplace(book){
this.$router.replace({
name: 'xiangxi',
params: {
id: book.id,
title: book.title
}
})
}
}
}
</script>
<style scoped>
</style>
- Xiangxi.vue
<template>
<div>
<h1>图书详情信息</h1>
<p>图书编号: {{ id }}</p>
<p>图书名称: {{ title }}</p>
</div>
</template>
<script>
export default {
name: "Xiangxi",
data() {
return {
'id': this.$route.params.id,
'title': this.$route.params.title
}
}
}
</script>
<style scoped>
</style>
6.8 缓存路由组件
默认情况下:路由组件被切换后,会被vue销毁,所以在该组件中的数据都将被清除。
keep-alive 标签可以将组件缓存,不被销毁,所以在路由切换后能够保存组件中的数据。
-
缓存一个组件
include 属性中填写组件名
<keep-alive include="Python"> <router-view></router-view> </keep-alive>
-
缓存多个组件
include属性中填写组件列表
注意:缓存一个组件的时候,include前面没有冒号:,缓存多个组件的时候,include前面有冒号:
<keep-alive :include="['Python', 'Java']"> <router-view></router-view> </keep-alive>
-
缓存所有组件 不写include属性就代表缓存所有
<keep-alive> <router-view></router-view> </keep-alive>
6.9 路由相关的钩子函数
-
activated
路由组件被激活时触发
-
deactivated
路由组件失活时触发
<template>
<div>
<h1>Python学科</h1>
<p><span :style="{opacity}">人生苦短,我用Python</span>
<input type="text" v-model="msg">
</p>
</div>
</template>
<script>
export default {
name: "Python",
data() {
return {
msg: 'python:',
opacity: 0,
}
},
activated() {
this.timer = setInterval(() => {
if (this.opacity <= 1) {
this.opacity += 0.01
} else {
this.opacity = 0
}
}, 16)
},
deactivated() {
clearInterval(this.timer)
}
}
</script>
<style scoped>
</style>
6.10 路由守卫
对路由权限进行控制
路由配置中通过:meta配置源信息
6.10.1 全局守卫
对所有组件都起作用
-
全局前置守卫:初始化时,每次路由切换前执行
router.beforeEach((to, from, next) => { if (to.meta.requireAuth) { let c = localStorage.getItem('class') if (c === 'v') { next() } else { alert('无权访问') next(false) } } else { next() } })
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dYvPKB9A-1663494649208)(D:\markdown\Vue速成手册.assets\image-20220918093757914.png)]
-
全局后置守卫:初始化时,每次路由切换后执行
router.afterEach((to, from)=>{ document.title = to.meta.title })
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Cyq5sdyx-1663494649208)(D:\markdown\Vue速成手册.assets\image-20220918093838110.png)]
-
小结
to,from,next三个参数都是必要的 to:即将要进入的目标 路由对象 from:当前导航正要离开的路由 next:一定要调用该方法来处理这个钩子,如果不写next()或者next(false),页面路由不会跳转,也就是页面被阻止在当前页面了 to,from是一个对象,就是 routes[] 数组里面配置的某个具体的路由对象, 比如:to.path, to,name, to.meta 或 from.path, from.name, from.meta 【path,name,meta】这些字段都是自己在路由里面定义的字段,这样就可以开始写逻辑了。 next(): 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。 next(false): 中断当前的导航。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。 next('/') 或者 next({ path: '/' }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向 next 传递任意位置对象,且允许设置诸如 replace: true、name: 'home' 之类的选项以及任何用在 router-link 的 to prop 或 router.push 中的选项。 next(error): (2.4.0+) 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError() 注册过的回调。
6.10.2 独享守卫
对某个组件起作用,你可以在路由配置上直接定义 beforeEnter
守卫:
参数与全局前置守卫相同:
{
path: '/class',
name: 'class',
meta: {
requireAuth: true,
title: 'Class'
},
component: Class,
children: [
{
path: 'python',
name: 'python',
component: Python,
beforeEnter: (to, from, next)=>{
if (localStorage.getItem('class') === 'v1'){
next()
}else{
alert('无权访问')
next(false)
}
}
},
]
}
6.10.3 组件内守卫
-
通过路由匹配后,进入路由组件时守卫
beforeRouteEnter((to, from, next)=>{})
-
离开路由组件时守卫
beforeRouteLeave((to, from, next)=>{})
6.10.4 路由的两种模式
-
hash
地址中有#号 地址中#号后面的值不会出现在http请求中 兼容性好 不美观 app检验严格的情况会被标记为不合法
-
history
地址干净、美观 兼容性比hash差 可能会出现404报错,因为路径中的数据会被发送到后端
-
设置模式
const router = new VueRouter({ mode: 'history' or 'hash' })
7 vue ui组件库
1 安装element-ui
cnpm i element-ui -S
2 全局配置
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.use(ElementUI) // 全局使用ElementUI
new Vue({
render: h => h(App),
}).$mount('#app')
src/App.vue
<template>
<div>
<div id="app">
<el-row>
<el-button type="primary" icon="el-icon-edit circle" @click="sayHello"></el-button>
</el-row>
</div>
</div>
</template>
<script>
export default {
name: 'App',
components: {},
methods: {
sayHello(){alert('say hello!')}
}
}
</script>
<style>
</style>
3 部分配置
1 安装babel-plugin-component
npm i babel-plugin-component -D
2 修改babel-config-js
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset',
["@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),
})
一般如果对性能要求不是太苛刻的话都会选择全局配置
8 LocalStorage 和 SessionStorage
8.1 LocalStorage(本地存储)
-
setItem
localStorage.setItem('k1', 'v1')
-
getItem
localStorage.getItem('k1') 'v1'
-
length 属性
localStorage.length 1
-
getItem
localStorage.getItem('k2') 'v2'
-
clear 清楚所有存储
localStorage.clear()
-
localStorage.removeItem
localStorage.removeItem('k1')
8.2 SessionStorage(会话存储)
-
API与localStorage相同
- setItem(key, value) - getItem(key) - removeItem(key) - length - clear() 清空所有的会话存储
常见问题总结:
1 Component name “Class” should always be multi-word vue/multi-word-component-names
-
解决方式1:
在vue.config.js中添加关闭语言检查功能
const {defineConfig} = require('@vue/cli-service') module.exports = defineConfig({ transpileDependencies: true, lintOnSave: false /*关闭语法检查*/ })
-
解决方式2:
按照要求修改组件名
2 路由文件与main文件分模块时报如下错误:Error in beforeCreate hook: “TypeError: this._router.init is not a function”
问题原因: 导出和导入模块的方式不对
正确的处理方式:
格式一 变量形式 --暴露
export default {
router
}
--接收
import {router} from './router'
格式二 直接暴露
export default router
接收
import router from './router'