Vue入门
属性动态绑定,内容插值语法
VScode-Vue插件
live server 插件,模拟服务器启动开发前端代码
Vue 3 Snippets 代码提示插件
Volar Vue的开发提示插件
其他插件
bootcdn 搜索js插件类库
Draw.io Integration vscode流程图插件
vue/cli脚手架下载使用
npm install -g @vue/cli
常用 npm install -i @vue/cli
vue创建项目
vue create 项目名称
nanoid 生成uuid小库
npm i nanoid
引入发布订阅库
npm i pubsub-js
chrome添加Vue开发者工具
https://v2.cn.vuejs.org/v2/guide/installation.html#Vue-Devtools
Vue Devtools页签,下跳转git获取插件工具
工具报错
npm ERR! code E406
npm ERR! 406 Not Acceptable - GET http://maven.repo.cmc.tools.huawei.com/artifactory/api/npm/sz-npm-public/yallist/-/yallist-2.1.2.tgz
源问题:非显示非自身源,直接删除package.json 重新绑定自身源地址
伪造数据:插件地址https://fakerjs.dev/guide/
webpack命令行---配置less支持
npm view webpack versions 查看webpack的版本
npm view less-loader versions
安装less-loader 9
npm i less-loader@9
web开发网站
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript
组件化的编码流程
1.实现静态组件: 抽取组件,使用组件实现静态页面效果
2.展示动态数据:
2.1 数据的类型, 名称是什么?
2.2 数据保存在哪个组件
3.交互---从绑定事件监听开发
小功能开发
总结
1. 组件化编码流程:
(1) 拆分静态组件: 组件要哪找功能点拆分,命名不要与html元素冲突
(2) 实现动态组件: 考虑好数据的存放位置,数据是一个组件在用, 还是一些组件在用:
1) 一个组件在用: 放在组件自身
2) 一些组件在用: 放在他们共同的父组件上(状态提升)
(3) 实现交互: 从绑定事件开始
2. props适用于:
(1) 父组件 ===>子组件 通信
(2) 子组件 ===>父组件 通信 (要求父先给子一个函数)
3. 使用v-model时要切记: v-model绑定的值不能是props传过来的值,因为props是不可以修改的
4. props传过来的若是对象类型的值, 修改睡醒中属性时Vue不会报错,单不推荐
基础概念
01 基础
虚拟DOM
vue将数据生成虚拟DOM,存在内存中,然后再讲虚拟DOM,在页面展示为真实DOM
新增数据,通过diff算法,获取是否新增虚拟DOM
基础语法了解
ES6语法规范
ES6模块化
包管理器
原型,原型链
数组常用方法
axios
peomise
...
总结
1.要想使用Vue 必须new 一个Vue实例,切要传入一个配置对象
2.容器对象依然符合html语法,只不过引入了Vue的特殊语法(例如 双大括号)
3.root容器里面的代码,称为Vue模板
4.vue实例是一一对应的
5.真实开发中vue实例只有一个,并且需要配合组件一起使用
6.{{xxxx}} 是需要写js表达式,且xxxx中可以自动读取到data中所有属性
7.一但data中数据发生改变,模板中用到的数据都会发生改变
注意区分
js表达式能生成一个值
js语句不能生成一个值[if()]
02 语法
插值语法
<h1>你好 {{name}}</h1>
指令语法
v-bind数据绑定
<a v-bind:href='url'>跳转百度</a>
v-bind可以简写为:
<a :href='url'>跳转百度</a>
v-bind是单向数据绑定
v-model数据双向绑定
双向绑定<input type="text" v-model:value='name'>
双向绑定 只能应用在表单类元素上(及输入类元素,及需要有value值)
总结
1.插值语法用于指定标签体内容
2.指令语法用于解析标签(包括,标签属性,标签内容,绑定事件....),加了指令语法,则指令语法后面都会被当做js表达式
3.绑定:
单向绑定:v-bind是单向数据绑定,数据只能从data到页面
双向绑定:v-model是双向绑定,但是:
1.双向绑定一般都应用在表单类元素上(如:input select..)
2.v-model:value 可以改写为v-model, 因为v-model默认手机value值
el和data组件
el的两种写法
const x=new Vue({
//el:'#root', // el的第一种写法
data:{
name:'zhangsan'
}
});
x.$mount('#root'); // el的第二种写法 mount挂载到模板上
data的两种写法
const x=new Vue({
//el:'#root',
// data:{
// name:'zhangsan'
// }
// data:function(){
// console.log(this) // this代表的是当前Vue对象
// return {
// name:'zhangsan'
// }
// }
data(){
return {
name:'zhangsan'
}
}
});
总结
由Vue所管理的函数不要使用箭头函数,因为箭头函数的调用对象是window不是Vue了
MVVM模型
M:模型Model:对应data中的数据
V:视图(View):模板
VM:视图模型(ViewModel) 对应Vue实例对象
总结
Vue 通常赋值给变量vm
1.data中的属性最终会展示在vm的属性上
2.vm上的属性全都都可以获取使用,vm原型上的属性,也可以使用
数据代理
defineProperty 对象添加属性
Object.defineProperty(person,'age',{
value:18
});
defineProperty定义的属性不可被枚举(及不能被遍历获取)
Object.defineProperty(person,'age',{
value:18,
enumerable:true,
writable:true,
configurable:true
});
enumerable:true,控制台可以枚举,但是在控制台不可修改属性
配置writable:true,则控制台可以修改对应属性,但是不可以被删除
配置configurable:true 则控制台可以删除对应属性
配置get/set属性, 设置number属性可以被赋值和获取,没有get/set方法,person属性一旦初始化之后,就不可
被通过参数形式修改
let number =18;
let person={
naem:'张三',
sex:'男',
};
Object.defineProperty(person,'age',{
get(){
console.log('有人读取age属性')
return number;
},
set(value){
console.log('有人改了age属性,且就是',value)
number=value;
}
});
总结
1.Vue做数据代理
通过vm对象来代理data对象中属性的操作性(读/写)
2.Vue中数据代理好处
更加方便的操作data中的数据
3.基本原理
通过Object.defineProperty()把data所有属性加载vm上
为每一个加在vm上的属性,添加一个getter/setter方法
在getter/setter内部去操作(读/写)data中对应的属性
4._data表为Vue数据劫持
指令
v-bind数据绑定,简写:
v-model数据双向绑定 v-mode.value简写v-mode
v-on:click 绑定点击事件 简写@click
<button @click="showInfo1">点我提示信息</button> <!-- 简写形式 -->
</br><button @click="showInfo2($event,66)">点我提示信息</button>
showInfo2(event,number) {
console.log(number,event); // 传参数和监听事件
}
总结
事件的基本使用:
1.使用v-on:xxx 或@:xxx绑定事件,其中xxx为事件名
2.事件的回调需要配置在methods对象中,最终会在vm上
3.methods中配置的函数,不要使用箭头函数,否则this就不是vm对象了
4.methods中配置的函数,都是Vue所管理的函数, this的指向都是vm或组件函数
5.@click =demo和@click =demo(event)效果是一致的,都可以传递事件
事件修饰符
<a href="http://www.baidu.com" @click.prevent="showInfo"> 点我提示信息</a>
总结
Vue中的事件修饰符
1.prevent: 阻止默认事件 (常用)
2.stop: 阻止事件冒泡 (常用)
3.once: 事件只触发一次 (常用)
4.capture: 使用事件捕获模式
5.self: 只有event.target是当前操作的元素时才能触发时间
6.passive: 事件的默认行为立即执行, 无需等待事件回调函数执行
事件修饰符可以连续写
@click.stop.prevent
键盘事件
@keydown 按下触发事件
@keyup 按下去再起来时触发事件(常用)
event.keyCode 获取键盘按键编码
<input type="text" placeholder="请按下回车键显示输入" @keyup.enter="showInfo"/>
methods:{
showInfo(e){
//if(e.keyCode!==13) return;
console.log(e.keyCode);
}
}
@keyup.enter中.enter就可以替换if(e.keyCode!==13) return
键盘常用事件别名
1.键盘事件别名:
回车: enter
删除: delete (捕获"删除"和"回退"键)
空格: space
换行: tab (特殊:必须姐姐keydown使用)
上: up
下: down
左: left
右: right
3. 系统修饰建(特殊按键) 说明:ctrl alt shift meta(windows键)
(1)配合keyup键使用,需按下特殊按键同时,再按下其他键.虽后释放其他按键,事件才会触发
(2)配合keydown使用,无特殊要求
系统修饰建也可以结合具体按键来组合过滤
键盘事件定义别名方法
Vue.config.keyCodes.hidwww=13
计算属性
计算属性配置-computed
computed:{
// 完整写法
fullName:{
// todo 读取时机
get(){
console.log("dsfdf");
return this.firstName + '-'+this.secondName;
}
// set 根据实际情况提供
set(value){
// 修改 firstName,secondName
}
}
// 简写 只有在不使用set方法时才能使用简写
fullName(){
console.log("dsfdf");
return this.firstName + '-'+this.secondName;
}
}
<body>
<div id="root">
姓: <input type="text" v-model="firstName"><br>
名: <input type="text" v-model="secondName"><br>
全名: <span>{{fullName()}}</span>
</div>
</body>
<script text="text/javascript">
Vue.config.productionTip = false;
new Vue({
el:'#root',
data:{
firstName:'张',
secondName:'三'
},
methods:{
fullName(){
return this.firstName+'-'+this.secondName
}
}
})
</script>
总结
1.定义: 要用的属性不存在, 要通过已有属性计算出来
2.原理: 底层实现是通过Object.defineproperty方法提供的getter/setter方法
3.get函数什么时候调用
(1).初次读取时会执行一次
(2).当所依赖的数据发生改变时,会被调用
4.优势: 与methods实现相比, 内部有缓存机制(复用).效率更高,调试方便
5.备注:
1.计算属性最终会在vm上,直接读取
2.如果计算属性需要修改,则需要些set方法,且需要修改计算属性所依赖的属性
监视属性 watch
watch:{
isHot:{
immediate:true, // 初始时让handler调用一下
handler(newvalue,oldvalue){
console.log('ishot被改变了,',newvalue,oldvalue)
}
}
}
immediate初始时让handler调用一下
handler获取修改前后的值,
也可以配置在vm上
vm.$watch('isHot', {
immediate: true, // 初始时让handler执行一下
handler(newvalue, oldvalue) {
console.log('ishot被改变了,', newvalue, oldvalue)
}
})
<body>
<div id="root">
姓<input type="text" v-model="firstName"><br>
名<input type="text" v-model="secondName"><br>
全名 <span>{{fullName}}</span>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false;
const vm = new Vue({
el: '#root',
data: {
isHot: true,
firstName:'张',
secondName:'三',
fullName:'张-三'
},
methods: {
changeTem() {
this.isHot = !this.isHot;
}
},
watch: {
firstName(val){
this.fullName=val+'-'+this.secondName;
},
secondName(val){
this.fullName=this.firstName+'-'+val;
}
}
})
</script>
总结
监视属性:
1. 当被监视的属性变化时,回调函数自动调用,进行关联操作
2. 监视属性必须存在时才能进行监视
3. 监视的两种方法
(1): new Vue时传入watch配置
(2): vm.$watch配置
深度监视
深度监视:
(1). Vue中的watch默认不检测对象内部值的变化
(2). 配置deep:true可以检测对象内部值的变化(多层)
备注:
(1): Vue自身可以检测对象内部值的改变,但是Vue提供的watch默认不可以
(2): 使用watch时根据对象具体结构,决定是否使用深度监视
watch与计算属性对比
同步修改时用计算属性,需要异步操作是使用watch监视属性(因为计算属性不能开启异步计算)
小tip: this属性调用说明
使用this时,在当前作用域没有呢找到this指向,则上升到外层作用域直到找到this指向
总结
computed和watch之间的区别:
1: computed能完成的功能,watch都能完成
2: watch能完成的功能,computed不一定能完成,例如:watch可以实现异步操作
两个重要的点:
1: 所有Vue管理的函数最好写成普通函数,这样this的指向才能是vm 或者 组件实例对象
2: 所有不被Vue管理的函数(定时器的回调函数,ajax的回调函数,Promise回调函数),最好写成箭头函数.这样的this 才能指向vm或者组件实例对象
绑定样式
<body>
<div id="root">
<!-- 绑定class样式 --字符串写法.适用于: 样式的类名不确定, 需要动态 绑定 -->
<div class="basic" :class="mood" @click="changeMood">{{name}}</div>
<!-- 绑定class样式 --数组写法.适用于: 要绑定的名字不确定,样式也不确定 -->
<div class="basic" :class="arr">{{name}}</div>
<!-- 绑定class样式 --对象写法. 适用于: 要绑定的样式数量确定, 名字也不确定 -->
<div class="basic" :class="classObj">{{name}}</div>
<!-- 绑定style样式 --对象写法 -->
<div class="basic" :style="styleObj" >{{name}} </div>
<!-- 绑定style样式 --数组写法 -->
<div class="basic" :style="[styleObj,styleObj2]" >{{name}} </div>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false;
new Vue({
el:'#root',
data:{
name:'张三',
mood:'normal',
arr:['guid1','guid2','guid3'],
classObj:{
guid1:false,
guid2:false
},
styleObj:{
fontSize:'40px',
color:'red',
backgroundColor:'green'
},
styleObj2:{
backgroundColor:'orange'
}
},
methods: {
changeMood(){
//this.mood = 'happy'
const arr = ['happy','normal','sad']
const index=Math.floor(Math.random()*3);
this.mood=arr[index];
}
},
})
</script>
<body>
<div id="root">
<h2 v-show="true"> 欢迎 {{name}} {{n}}</h2>
<button @click="n++">点我加一</button>
<!-- <h2 v-if="false"> 欢迎 {{name}}</h2> -->
<h2 v-if="n===1">一</h2>
<h2 v-else-if="n===2">二</h2>
<h2 v-else-if="n===3">三</h2>
<h2 v-else>else</h2>
<!-- template 只能和v-if结合使用不能合v-show结合使用,且template在生成页面时会去掉 -->
<template v-if="n===4">
<h2 >四</h2>
<h2 >四</h2>
<h2 >四</h2>
</template>
</div>
</body>
总结
条件渲染"
1. v-if
写法:
(1). v-if="表达式"
(2). v-else-if="表达式"
(3). v-else="表达式"
适用于,切换频率比较低的场景
特点: 不展示的dom元素直接移除掉
注意: v-if 可以和v-else-if v-else一起使用,但要求结构不能被打断
2. v-show
写法: v-show="表达式"
使用: 切换场景频繁
特点: 不展示的DOM元素未被移除.仅仅是使用样式隐藏
3. 备注: 使用v-if 元素使无法获取到,v-show元素肯定能获取到
基本列表
v-for遍历
<body>
<div id="root">
<!-- 遍历数组 -->
<h2>人员列表</h2>
<ul>
<!-- <li v-for="person in persons" :key="person.id">
{{person.name}} - {{person.age}}
</li> -->
<li v-for="(person,index) in persons" :key="person.id">
{{person.name}} - {{person.age}}
</li>
</ul>
<!-- 遍历对象 -->
<h2>汽车信息</h2>
<ul>
<li v-for="(value,key) in car" :key="key">
{{key}} - {{value}}
</li>
</ul>
<!-- 遍历字符串 -->
<h2>测试遍历字符串</h2>
<ul>
<li v-for="(char,index) in str" :key="index">
{{index}} - {{char}}
</li>
</ul>
<!-- 遍历指定次数 -->
<h2>测试遍历指定次数</h2>
<ul>
<li v-for="(number,index) in 5" :key="index">
{{index}} - {{number}}
</li>
</ul>
</div>
</body>
<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:'奥迪',
price:'70万',
color:'黑色'
},
str:'hello'
}
})
</script>
总结
v-for指令:
1. 用于展示列表数据
2. 语法: v-for="(item, index) in xxx" :key="yyy"
v-for key属性
<body>
<div id="root">
<!-- 遍历数组 -->
<h2>人员列表</h2>
<button @click="addPerson">添加一个数据</button>
<ul>
<li v-for="(person,index) in persons" :key="person.id">
{{person.name}} - {{person.age}}
<input type="text">
</li>
</ul>
</div>
</body>
<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},
]
},
methods: {
addPerson(){
const p={id:'004',name:'老刘',age:40};
this.persons.unshift(p);
}
},
})
总结
开发中key如何使用:
1. 最好使用每条数据的唯一标示作为key.比如id,身份证号,学号等唯一值
2. 如果不存在对数据的逆序操作,逆序删除等破坏顺序操作,仅用于简单展示,使用index作为key,没有问题
过滤搜索
<script type="text/javascript">
Vue.config.productionTip = false;
new Vue({
el: '#root',
data: {
keyword: '',
persons: [
{ id: '001', name: '马冬梅', age: 18, sex: '女' },
{ id: '002', name: '周冬雨', age: 19, sex: '女' },
{ id: '003', name: '周杰伦', age: 20, sex: '男' },
{ id: '003', name: '温兆伦', age: 22, sex: '男' },
],
// 用watch实现
// filPersons: []
},
// 用watch实现
// watch: {
// 字符串使用indexOf方法 判断是否包含''(空串) 结果为0 非-1
//存在问题就是初始化时.数据为空
// keyword(val){
// this.filPersons= this.persons.filter(p=>{
// return p.name.indexOf(val) !==-1
// })
// }
// keyword: {
// immediate: true,
// handler(val) {
// this.filPersons = this.persons.filter(p => {
// return p.name.indexOf(val) !== -1
// })
// }
// }
// }
// computed实现
computed:{
filPersons(){
return this.persons.filter(p=>{
return p.name.indexOf(this.keyword) !==-1;
})
}
}
})
</script>
排序
<script type="text/javascript">
Vue.config.productionTip = false;
new Vue({
el: '#root',
data: {
keyword: '',
sortType: 0,// 0表示原顺序,1降序,2升序
persons: [
{ id: '001', name: '马冬梅', age: 18, sex: '女' },
{ id: '002', name: '周冬雨', age: 19, sex: '女' },
{ id: '003', name: '周杰伦', age: 20, sex: '男' },
{ id: '003', name: '温兆伦', age: 22, sex: '男' },
],
},
// computed实现
computed: {
filPersons() {
const arr= this.persons.filter(p => {
return p.name.indexOf(this.keyword) !== -1;
})
if(this.sortType){
arr.sort((a,b)=>{
return this.sortType ===1?b.age-a.age:a.age-b.age;
})
}
return arr;
}
}
})
</script>
检测实现原理
Vue检测数据,就是靠setter方法,检测数据变化,刷新虚拟DOM
Vue后添加的数据不会自动匹配getter/setter,需使用api实现添加getter/setter ===>Vue.set(vm._data.student,'sex','男') 简写形式 vm.$set(vm.student,'sex','男')
但是Vue.set不能直接给vm的data中添加属性
Vue中数组修改可以被监视到的方法有: push 尾部插入
pop 尾部弹出
shift 头部删除
unshift 头部插入
splice 指定下标插入
sort 数组排序
reverse 数组翻转
也可以使用Vue.set(vm._data.student,index,'男') 简写形式 vm.$set(vm.student,index,'男'),修改数组内数去实现同步
<body>
<div id="root">
<!-- <h2>校长:{{leader}}</h2> -->
<h2> 学生名字: {{student.name}}</h2>
<button @click="addSex"> 添加性别</button>
<button @click="student.age.rAge++"> 年龄++</button>
<button @click="addFriend"> 添加一个朋友</button>
<button @click="updateFirstFriendName"> 添加一个朋友</button>
<button @click="addHobby"> 添加一个爱好</button>
<button @click="updatehobby"> 修改一个爱好</button>
<h2 v-if="student.sex"> 性别: {{student.sex}}</h2>
<h2> 学生年龄: 真实年龄-{{student.age.rAge}}, 对外年龄-{{student.age.sAge}}</h2>
<h2> 爱好: </h2>
<ul>
<li v-for="(h,index) in student.hobby" :key="index">
{{h}}
</li>
</ul>
<h2> 学生的朋友们: </h2>
<ul>
<li v-for="(f,index) in student.friends" :key="index">
{{f.name}} -- {{f.age}}
</li>
</ul>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false;
const vm=new Vue({
el: '#root',
data: {
student: {
name: 'tom',
age: {
rAge: 40,
sAge: 29,
},
friends: [
{ name: 'jeery', age: 15 },
{ name: 'tony', age: 30 }
],
hobby:['抽烟','喝酒','烫头']
}
},
methods: {
addSex(){
// 为非vm对象,和根对象(data _data)添加属性
// Vue.set(this.student,'sex','男')
this.$set(this.student,'sex','男')
},
addFriend(){
// 数组添加新的内容
this.student.friends.unshift({ name: 'Tom', age: 17})
},
updateFirstFriendName(){
this.student.friends[0].name='张三'
},
addHobby(){
this.student.hobby.push('学习')
},
updatehobby(){
this.student.hobby.splice(0,1,'开车');
// Vue.set(this.student.hobby,0,'开车1');
// this.$set(this.student.hobby,0,'开车2')
}
},
})
function Observable(obj) {
const keys = Object.keys(obj);
keys.forEach(k => {
Object.defineProperty(this, k, {
get() {
return obj[k]
},
set(val) {
console.log('被修改了')
obj[k] = val;
}
})
})
}
</script>
总结
1. Vue会监视data层级下所有数据的变化
2. 如何监视对象中的数据:
通过setter方法实现,且要在new Vue()时就传入要监视的对象
(1). 对象中后添加属性,Vue默认不做响应式处理
(2). 如果需要给后添加的属性,添加响应式,请使用如下API
Vue.set(target, propertyName/index, value)
vm.$set(target, propertyName/index, value)
3. 如何监视数组中的数据
通过包装数组更新元素的方法实现,本质就是做了两件事
(1). 调用原生的方法对数据进行更新
(2). 重新解析模板,更新页面
4. 在Vue修改数组中某个元素,一定要用如下方法:
1. 使用这些API: push(),pop(),shift(),unshift(),sort(),reverse()
2. Vue.set() 或 vm.$set()
特别注意: Vue.set() 和 vm.set() 不能给vm 和 vm的根 数据对象添加属性
数据劫持
就是Vue为数据操作添加了getter/setter方法
表单提交
<body>
<div id="root">
<form @submit.prevent="demo">
<label for="account">账号: </label>
<!-- 去掉前后空格 v-model.trim -->
<input type="text" id="account" v-model.trim="account">
密码: <input type="password" v-model="password">
<br>
<!-- type number 与v-model.number结合使用 -->
年龄: <input type="number" v-model.number="age">
<br><br>
性别:
男 <input type="radio" name="gender" v-model="sex" value="male">
女 <input type="radio" name="gender" v-model="sex" value="female">
<br><br>
爱好:
学习<input type="checkbox" v-model="hobby" value="study">
打游戏<input type="checkbox" v-model="hobby" value="game">
吃饭<input type="checkbox" v-model="hobby" value="eat">
<br><br>
所属校区: <select v-model="city">
<option value="">请选择校区</option>
<option value="beijing">北京</option>
<option value="shanghai">上海</option>
<option value="shenzhen">深圳</option>
<option value="wuhan">武汉</option>
</select>
<br><br>
其他信息:<br>
<!-- v-model.lazy 延迟收集信息 -->
<textarea cols="30" rows="10" v-model.lazy="other"></textarea>
<br>
<input type="checkbox" v-model="agree">
阅读并接受<a href="http://www.baidu.com"><用户协议></a>
<br>
<button>提交</button>
</form>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false;
new Vue({
el:'#root',
data:{
account:'',
password:'',
age:'',
sex:'female',
hobby:[], // hobby的初始值,影响checkbox的类型绑定
city:'beijing',
other:'',
agree:''
},
methods: {
demo(){
console.log(this._data)
}
},
})
</script>
总结
收集表单数据:
若:<input type="text"> 则v-model收集的是value的值,用户输入的是value的值
若:<input type="radio"> 则v-model收集的是value的值,且要给标签配置value值
若:<input type="checkbox">
1. 没有配置input的value属性, 那么收集的就是checked(勾选 or 未勾选 是布尔值)
2. 配置input的value属性
(1): v-model的初始值是非数组,那么收集的就是checked(勾选 or 未勾选 是布尔值)
(2): v-model的初始值是数组, 那么收集的就是value组成的数组
备注:v-model的三个修饰符
1. v-model.trim 去除字符串前后空格
2. v-model.number 映射输入为数值型,需结合 type=number
3. v-model.lazy 延迟收集数据,
过滤器
<body>
<div id="root">
<h2>时间格式化</h2>
<!-- 计算属性实现 -->
<h3>现在时间:{{feTime}}</h3>
<!-- methods实现 -->
<h3>现在时间:{{getFeTime()}}</h3>
<!-- 过滤器实现 -->
<h3>现在时间: {{time | timeFormater()}}</h3>
<!-- 多参数传递 -->
<h3>现在时间: {{time | timeFormater('YYYY-MM-DD') }}</h3>
<!-- 多参数传递,多管道传值 -->
<h3>现在时间: {{time | timeFormater('YYYY-MM-DD') | mySlice}}</h3>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false;
// 配置全局过滤器
Vue.filter('mySlice',function(value){
return value.slice(0,4);
})
new Vue({
el: '#root',
data: {
time: 1688636495221
},
computed:{
feTime(){
return dayjs(this.time).format('YYYY-MM-DD HH:mm:ss')
}
},
methods: {
getFeTime(){
return dayjs(this.time).format('YYYY-MM-DD HH:mm:ss');
}
},
// 局部过滤器
filters:{
timeFormater(value,format='YYYY-MM-DD HH:mm:ss'){
return dayjs(value).format(format);
} ,
mySlice(value){
return value.slice(0,4);
}
}
})
</script>
总结
过滤器:
定义: 对要显示的数据进行特定格式化后再显示(适用于一些简单的逻辑处理),过于复杂的使用计算属性,methods
语法:
1. 注册过滤器: Vue.filter(name,callback) 或 new Vue(filter:{})
2. 使用过滤器: {{xxx | 过滤器名}} 或 v-bind:属性 ='xxx | 过滤器名'
备注:
1. 过滤器也可以接收额外参数,多个过滤器可以串联使用
2. 过滤器并没有改变原本的数据,是产生新的对应数据
内置指令
v-text
1.作用: 向其所在的节点中渲染文本内容
2.与插值语法区别:v-text会替换掉节点中的内容, {{xxx}} 则不会
v-html
v-html指令:
1. 作用, 向指定节点中渲染包含html结构的内容
2. 与插值语法区别:
(1). v-html会替换掉节点中所有的内容, {{xxx}}则不会
(2). v-html可以识别html结构
3.严重问题: v-html有安全漏洞问题(xss!!!!!!!)
(1). 在网站上动态渲染任何html是非常危险的,容易导师XSS攻击
(2). 一定要在可信的内容上使用v-html, 永远不要用在用户内容上
安全防护
xss:跨站脚本,解决方案是在服务端设置cookie为HttpOnly
安全携带HttpOnly的cookie
$.ajax({
url: 'http://example.com/api',
type: 'GET',
xhrFields: {
withCredentials: true
},
success: function(data) {
console.log(data);
}
});
hrFields`属性设置了`withCredentials`为`true`,表示请求需要携带cookie。如果服务器端设置了`HttpOnly`属性,那么这个cookie就会被安全地传输。
CSRF:跨站请求伪造
手动添加请求头:Referer
手动添加请求头:X-CSRF-Token 后跟token
v-cloak
<style>
[v-cloak]{
display: none;
}
</style>
<body>
<div id="root">
<div v-cloak>你好, {{name}}</div>
</div>
</body>
总结
v-cloak指令(没有值):
(1). 本质是一个特殊的属性, Vue实例创建完毕并接管容器后, 会删除v-cloak属性
(2). 使用css配合v-cloak可以解决网速慢时,页面展示出{{xxx}}的问题
v-once
<div id="root">
<div v-cloak>你好, {{name}}</div>
<h2 v-once>初始化的值是: {{n}}</h2>
<h2>当前的值是: {{n}}</h2>
<!-- <button @click="add">点我n+1</button> -->
<button @click="n++">点我n+1</button>
</div>
总结
v-once指令:
1. v-once所在节点,初始化动态渲染之后,就视为静态内容
2. 以后数据的变化不会引起v-once所在节点的数据更新,可用于优化性能
v-pre
总结
v-pre指令:
1. 跳过其所在节点的编译过程
2. 可利用它跳过没有使用相关指令语法,没有使用插值语法的节点, 会加快编译
自定义指令
需求
1. 定义一个v-big指令, 和v-text类似,但会把绑定的数值放大10倍
2. 定义一个v-fbind指令, 但可以让其所绑定的input元素默认获取焦点
<body>
<div id="root">
<h2>{{myname}}</h2>
<h2>当前的n值: <span v-text="n"></span></h2>
<h2>放大10倍后的n值是: <span v-big="n"></span></h2>
<button @click="n++"> 到我n++</button>
<hr>
<input type="text" v-bind:value="n">
<hr>
<input type="text" v-fbind:value="n">
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false;
new Vue({
el:'#root',
data:{
n:'2',
myname:'张三'
},
directives:{
// big函数何时被调用 1. 指令与元素绑定成功时被调用,2. 指令被重新解析时,被调用
big(element,binding){
// console.log(element instanceof HTMLElement)
// console.log(element,binding)
// console.log('big')
element.innerText = binding.value * 10;
},
// fbind(element,binding){
// element.value=binding.value;
// element.focus();// 失效原因是: 编译的模板的input框,在首次编译时,只是保存在内存中没有展示在页面上,所以无法获取焦点
// }
// 出现在directtives 指令的this对象都是window
fbind:{
// 指令与元素成功绑定时 调用
bind(element,binding){
element.value=binding.value;
},
// 指令所在元素插入页面时 调用
inserted(element,binding){
element.focus();
},
// 指令所在元素更新时 调用
update(element,binding){
element.value=binding.value;
}
}
}
})
</script>
自定义全局指令
// 全局自定义指令 字符串形式
Vue.directive('f-bind',{
// 指令与元素成功绑定时 调用
bind(element,binding){
element.value=binding.value;
},
// 指令所在元素插入页面时 调用
inserted(element,binding){
element.focus();
},
// 指令所在元素更新时 调用
update(element,binding){
element.value=binding.value;
}
});
// 全局自定义指令 函数形式
Vue.directive('big',function(element,binding){
element.innerText = binding.value * 10;
},)
总结
自定义指令总结:
一. 定义语法:
(1). 局部指令:
new Vue({directive:{指令名称:配置对象}})
或
new Vue({directives{指令名称:回调函数})
(2). 全局指令:
Vue.directive(指令名,配置对象) 或 Vue.directive(指令名,回调函数)
二 配置对象中常用的3个回调
(1). bind: 指令与元素成功绑定时调用
(2). inserted: 指令所在元素被插入页面时被调用
(3). update: 指令所在模板结构再重新解析时调用
三 备注:
(1). 指令定义是不加v- 使用时要加v-
(2). 指令,名如果是多个单子, 要使用kabab-case命名方式,不要使用camelCase命名
生命周期
mounted
<body>
<div id="root">
<!-- 重名可以使用简写 -->
<!-- <h2 :style="{opacity: opacity}">欢迎学习</h2> -->
<h2 :style="{opacity}">欢迎学习</h2>
</div>
</body>
<script type="text/javascript">
const vm = new Vue({
el: '#root',
data: {
opacity: 1
},
methods: {
},
// vue完成模板解析,并把真实模板挂载到页面时调用
mounted(){
setInterval(() => {
vm.opacity -= 0.01
if (vm.opacity <= 0) vm.opacity = 1
}, 16)
}
})
// 不推荐使用方式
// setInterval(() => {
// vm.opacity -= 0.01
// if (vm.opacity <= 0) vm.opacity = 1
// }, 16)
</script>
声明周期函数
<body>
<div id="root">
<h2 :style="{opacity}">欢迎学习</h2>
<h2>当前值是: {{n}}</h2>
<button @click="add">点我n+1</button>
<button @click="bye">点我销毁vm</button>
</div>
</body>
<script type="text/javascript">
const vm = new Vue({
el: '#root',
data: {
opacity: 1,
n: 0
},
methods: {
add() {
console.log('add')
this.n += 1
},
// vm销毁
bye(){
console.log('bye')
this.$destroy() // 开发很少使用
}
},
// vue完成模板解析,并把真实模板挂载到页面时调用
mounted() {
setInterval(() => {
vm.opacity -= 0.01
if (vm.opacity <= 0) vm.opacity = 1
}, 16)
},
// 数据代理和事件监听创建之前
beforeCreate() {
console.log('beforeCreate')
},
created() {
console.log('created')
},
// 数据代理和事件监听创建之后
beforeMount() {
console.log('beforeMount')
},
mounted() {
console.log('mounted',this.$el)
},
// 此时页面和声明周期数据尚未同步
beforeUpdate() {
console.log('beforeUpdate')
},
updated() {
console.log('updated')
this.n=99
},
// 此阶段可以访问获取数据,但是对数据的修改不会触发页面更新
beforeDestroy() {
console.log('beforeDestroy')
},
destroyed() {
console.log('destroyed')
},
})
</script>
总结
声明周期:
1. 又名: 生命周期回调函数, 生命周期函数, 生命周期钩子
2. 是什么: Vue在关键时刻帮我调用的一些特殊函数
3. 生命周期函数名字不可更改,但函数内容是程序员根据需求编写
4. 声明周期函数的this指向vm 或 组件实例对象
常用生命周期钩子:
1. mounted: 发送ajax请求,启动定时器,绑定自定义事件,订阅消息等[初始化操作]
2. beforeDestroy: 清除定时器,解绑自定义事件,取消订阅消息等[收尾工作]
关于销毁Vue实例:
1.销毁后,借助Vue开发者工具看不到任何信息
2.销毁后自定义事件会失效,但原生DOM事件依然有效
3.一版不会在beforDestroy上操作数据,因为即使操作数据,也不会触发页面更新
组件
组件:实现应该中局部功能代码和资源的集合
非单文件组件: 一个文件中包含n个组件
单文件组件: 一个文件中只包含1个组件
非单文件组件
<body>
<div id="root">
<hello></hello>
<hr>
<school></school>
<hr>
<student></student>
</div>
<div id="root2">
<hr>
<hello></hello>
</div>
</body>
<script type="text/javascript">
const school = Vue.extend({
template: `
<div>
<h2>学校名称: {{schoolName}}</h2>
<h2>学校地址: {{address}}</h2>
<button @click='show'>点我提示学校名</button>
</div>
`,
data() {
return {
schoolName: '崂山',
address: '崂山蛇草水'
}
},
methods: {
show(){
alert(this.schoolName)
}
},
})
const student = Vue.extend({
template:`
<div>
<h2>学生名称: {{name}}</h2>
<h2>年龄: {{age}}</h2>
</div>`,
data() {
return {
name: '张三',
age: '18'
}
}
})
const hello=Vue.extend({
template:`
<div>
<h2>你好 {{name}}</h2>
</div>
`,
data(){
return {name:'tom'}
}
})
Vue.component('hello',hello)
// 创建Vue
new Vue({
el: '#root',
components: {
school,
student
}
})
new Vue({
el:'#root2'
})
</script>
总结
Vue中创建组件的三大步骤:
一. 定义组件(创建组件)
二. 注册组件
三. 使用组件(写组件标签)
一. 如何定义一个组件
使用Vue.extend(option)创建, 其中option和new Vue(option)时传入的那个option几乎一样,但也有区别:
区别如下:
1. el不要写, 为什么? 最终所有的组件都要经过一个vm管理, 由vm中的el决定服务与那个容器
2. data必须写成函数. 为什么? 避免组件复用时, 数据存在引用关系
备注: 使用template可以配置组件结构
二. 如何注册组件:
1. 局部注册: 拿new Vue的时候传入components选项
2. 全局注册: 拿Vue.component('组件名',组件)
三. 编写组件标签;
<school></school>
pit 注意事项
几个注意点:
1.关于组件名:
一个单词组成:
第一种写法首字母小写: school
第二种写法首字母大写: School
多个单词组成:
第一种写法(kebab-case命名): my-school
第二种写法(CamelCase命名): MySchool (需要Vue脚手架支持)
备注:
(1). 组件名称尽可能回避HTML中已有的元素名称, 例如: h2 H2都不行
(2). 可以使用name项去指定组件在开发者工具中的名字
2. 关于组件标签
第一种写法: <school></school>
第二种写法: <school/>
备注: 不使用脚手架时,自闭和写法会有丢失标签问题
3.一个简写方式
const school = Vue.extend(option)
可以简写为
const school = option
<body>
<div id="root">
<h1>{{msg}}</h1>
<school></school>
<hr>
<student-msg></student-msg>
<hr>
<student-msg-single></student-msg-single>
</div>
</body>
<script type="text/javascript">
// 定义组件
const school = Vue.extend({
template: `
<div>
<h2>学校名称: {{name}}</h2>
<h2>学校地址: {{address}}</h2>
</div>
`,
data() {
return {
name: '崂山',
address: '崂山蛇草水'
}
}
})
const studentMsg = Vue.extend({
name:'student',
template: `
<div>
<h2>学生名称: {{studentName}}</h2>
<h2>学生年龄: {{studentAge}}</h2>
</div>
`,
data() {
return {
studentName: '崂山',
studentAge: '崂山蛇草水'
}
}
})
const studentMsgSingle ={
name:'student',
template: `
<div>
<h2>简写</h2>
<h2>学生名称: {{studentName}}</h2>
<h2>学生年龄: {{studentAge}}</h2>
</div>
`,
data() {
return {
studentName: '崂山',
studentAge: '崂山蛇草水'
}
}
}
new Vue({
el: '#root',
data:{
msg:'欢迎'
},
components: {
school,
'student-msg':studentMsg,
'student-msg-single':studentMsgSingle
}
})
</script>
组件嵌套
<body>
<div id="root">
</div>
</body>
<script type="text/javascript">
// 定义组件
const studentMsg = Vue.extend({
name: 'student',
template: `
<div>
<h2>学生名称: {{studentName}}</h2>
<h2>学生年龄: {{studentAge}}</h2>
</div>
`,
data() {
return {
studentName: '崂山',
studentAge: '崂山蛇草水'
}
}
})
const school = Vue.extend({
template: `
<div>
<h2>学校名称: {{name}}</h2>
<h2>学校地址: {{address}}</h2>
<hr>
<student-msg></student-msg>
</div>
`,
data() {
return {
name: '崂山',
address: '崂山蛇草水'
}
},
components: {
'student-msg': studentMsg,
}
})
const hello = {
template: `<h2>欢迎: {{name}}</h2>`,
data() {
return {
name: '罗峰'
}
}
}
const app = {
template: `<div>
<hello></hello>
<school></school>
</div>`,
components: {
school,
hello
}
}
new Vue({
template:`<app></app>`,
el: '#root',
// 注册组件
components: {
app
}
})
</script>
component
关于VueComponent:
1. school组件本质是一个名为VueComponent的构造函数, 且不是程序员自己定义的, 是Vue.extend生成的
2. 我们只需要写<school/>或<school><school/>,Vue解析模板时会帮我创建school组建的实例对象
即Vue帮我们执行的: new VueComponent(options)
3. 特别注意: ,每次调用Vue.extend, 返回的都是全新的VueComponent
4. 关于this的指向
(1). 组件配置中:
data函数,methods中的函数,watch中的函数component中的函数,它们中的this均指向VueComponent实例
(2). new Vue(option)配置中
data函数,methods中的函数,watch中的函数component中的函数,它们中的this均指向Vue实例
5. VueComponent的实例对象,以后统称为vc(也可以称之为: 组件实例对象)
Vue实例对象,以后统称为vm
内置关系
1. 一个重要的内置关系: VueComponent.prototype.__proto__ === Vue.prototype
实例的原型属性,永远指向自己缔造者的原型属性
2. 为什么要有这个关系: 让组件实例对象可以访问到Vue原型上的属性,方法等
<script type="text/javascript">
// 定义一个构造函数
function Demo(){
this.a=1
this.b=2
}
// 创建一个Demo的实例对象
const d=new Demo();
console.log(Demo.prototype) // 显式原型属性 (只有函数才有显式原型属性)
console.log(d.__proto__) // 隐式原型属性 (只有对象才有隐式原型属性)
Demo.prototype.x=99
console.log(d.__proto__.x)
console.log(d.x)
</script>
单文件组件
1. webpack配置编译环境
2. 使用Vue官方的脚手架,配置编译还款
Vue起名规则
.vue文件起名规则
1. school.vue
2. School.vue
3. MySchool.vue
组件定义
组件需要包含:html,css,js等内容
html 组件结构
css 组件样式
js 组件交互
package.json详解
"scripts": {
"serve": "vue-cli-service serve", // 本地服务器运行
"build": "vue-cli-service build", // 构建打包
"lint": "vue-cli-service lint" // 语法检查
},
项目结构
asset 放静态文件
总结
vue.js 与vue.runtime.xxx.js的区别
(1) vue.js是完整版的Vue, 包含: 核心功能, 模板解决器
(2) vue.runtime.xxx.js
2. 因为vue.runtime.xxx.js没有模板解析器,所以不能使用template配置项, 需要使用render函数中通过createElement去定义具体内容
脚手架使用
导出webpack配置信息
vue inspect > output.js
不可更改名称的文件:
文件夹 public src assets
页面 index.html
js文件 main.js
修改Vue默认配置方法: 指导地址在Vue CLI中--配置参考--pages选项卡中
配置语法检测: lintOnSave
ref 获取真实的dom或 组件的实例对象
<div>
<h1 v-text="msg" ref="title"></h1>
<SchoolMsg ref="src"/>
<button @click="showDom">点我输出上方的DOM元素</button>
</div>
export default {
name:'app',
components:{SchoolMsg},
data(){
return {msg:'欢迎'}
},
methods: {
showDom(){
console.log(this.$refs.title) //获取真实的dom元素
console.log(this.$refs.src) //获取组件实例对象
}
},
}
总结
ref属性:
1. 被用来给元素或子组件注册引用信息(id的替代)
2. 背应用到html标签上获取的是真实的DOM元素,应用在组件标签上获取的是组件实例对象
3. 使用方式:
打标识: <h1 ref="xxx">....</h1> 或 <School ref="xxx"/>
获取: this.$refs.xxx
props属性 动态传值
StudentMsg.vue
<template>
<div class="demo">
<h1>{{ msg }}</h1>
<h2>学生名称: {{ name }}</h2>
<h2>学生性别: {{ gender }}</h2>
<h2>学生年龄: {{ myAge }}</h2>
<h2>学生年龄: {{ age }}</h2>
<button @click="modifyAge">尝试修改年龄</button>
</div>
</template>
<script>
export default {
name: 'StudentMsg',
data() {
return {
msg: '我是一个学生',
// 修改通过props传过来的数据方法
myAge: this.age
}
},
// props: ['name', 'gender', 'age'] //简单接收
// 接收的同时对类型限制
// props:{
// name:String,
// age:Number,
// gender:String
// }
// 接收的同时对数据进行类型限制+默认值指定+是否必传限制
props:{
name:{
type:String, // name的类型为字符串
required:true, // name是必传
},
age:{
type:Number,
default:99 // 非必传则使用 并赋值默认值
},
gender:{
type:String,
required:true
}
},
methods:{
modifyAge(){
this.myAge ++
}
}
}
</script>
App.vue
<div>
<!-- 未加冒号意味着是字符串 加了冒号(v-bind)意味着传递冒号中的表达式 -->
<!-- <StudentMsg name="张三" age="18" gender="男" /> -->
<StudentMsg name="张三" :age="18" gender="男" />
<hr>
<StudentMsg name="王麻子" :age="20" gender="女" />
<hr>
<StudentMsg name="李九" gender="男" />
</div>
总结
配置项props
功能: 让组件接收外部外部传过来的数据
(1) 传递数据:
<StudentMsg name="张三" :age="18" gender="男" />
(2) 接收数据:
第一种方式(只接收):
props:['name']
第二种方式(限制类型):
props:{
name: String
}
第三种方式(限制类型,限制必要性,指定默认值):
props:{
name:{
type:String, //类型
required: true //是否必要
default: '老王' //默认值(不会和required一起使用)
}
}
备注: props是只读的, Vue底层会监测props的修改,如果进行了修改,就会发出警告,若业务需求确实需要修改,那么请复制props的内容到data中一份,然后去修改data中的数据.
mixin(混入)
功能: 可以把多个组件共用的配置提取成一个混入对象
使用方式:
第一步定义混入 例如:
{
data(){....}
methods:{...}
....
}
第二步使用混入 例如:
(1): 全局混入: Vue.mixin(xxx)
(2): 局部混入: mixins:[xxx]
新建公共js mixin.js
定义:
export const mixin={
methods: {
showName(){
alert(this.name)
}
},
mounted() {
console.log('你好啊')
},
}
export const mixin2={
data(){
return {
x:100,
y:99
}
}
}
StudentMsg.vue
引入:
import {mixin,mixin2} from '../mixin'
export default {
name: 'StudentMsg',
data() {
return {
msg: '我是一个学生',
name: '张三',
gender: '男'
}
},
// methods: {
// showName(){
// alert
// }
// }
mixins:[mixin,mixin2]
}
插件使用 Vue.use()
功能: 用于增强Vue
本质: 包含install方法的一个对象, install的第一个参数Vue, 第二个以后参数是插件使用者传递的参数
定义插件:
对象.install = function(Vue, option) {
// 设置全局的过滤器
Vue.filter(....)
}
使用插件: Vue.use()
style --scoped 防止局部冲突
scoped:使得组件中定义的重名的样式都可以在自己的作用域内生效.
<style scoped>
.demo {
background-color: cadetblue;
}
</style>
浏览器的本地存储
webStorage
1. 存储内容大小一般支持5M左右(不同浏览器可能还不一样)
2. 浏览器通过Window.sessionStorage或Window.localStorage属性来实现本地存储机制
3. 相关API:
1. localStorage.setItem('key', 'value') 接收键值,添加到存储中
2. localStorage.getItem('key') 返回key对应的值
3. localStorage.removeItem('key') 删除key
4. localStorage.clear() 清空所有数据
=====================================================================
1. sessionStorage.setItem('key', 'value') 接收键值,添加到存储中
2. sessionStorage.getItem('key') 返回key对应的值
3. sessionStorage.removeItem('key') 删除key
4. sessionStorage.clear() 清空所有数据
4. 备注:
1. sessionStorage存储的内容会随浏览器窗口关闭而消失
2. localStorage存储的内容, 需要手动清除才会消失
3. localStorage.getItem('key')/sessionStorage.getItem('key')如果对应key的walue回去不到则返回null
4. JSON.parse(null)的结果任然为null
组件的自定义事件
<!-- 自定义事件 -->
<!-- v-on:atmy="demo" 表示给组件Student实例对象绑定一个atmy事件.在atmy事件触发时会执行demo函数 -->
<!-- 自定义事件触发原则,给谁绑定的事件,找谁触发 -->
<Student v-on:atmy="getStudentName"></Student>
// 定义回调函数
methods:{
getSchoolName(name){
console.log('App 收到了学校名: ',name)
},
getStudentName(name){
console.log('App 收到了学生名: ',name)
}
}
// 使用回调函数 this.$emit('自定义事件名称',参数 (对象,参数 ...参数名))
methods: {
sendStudentName(){
// $emit(事件名称) 手动触发事件
this.$emit('atmy',this.studentName)
}
},
总结
组件的自定义事件:
1. 一种组件间通讯的方式, 适用于: 子组件 ===> 父组件
2. 使用场景: A 是父组件, B是子组件, B想给A传数据, 那么就要在A中给B绑定自定义事件(事件的回调在父A中)
3. 绑定自定义事件:
1. 第一种方式, 在父组件中: <Student v-on:atmy="getStudentName" @demo="m1" />
2. 第二种方式, 在父组件中:
<Student ref="student"/>
....
mounted() {
this.$refs.student.$on('atmy',this.getStudentName)
}
3. 若想让自定义事件只能触发一次,可以使用once修饰符,或 $once方法
4. 触发自定义事件: this.$emit('atmy',数据)
5. 解绑自定义事件$this.$off('atmy')
6. 组件上也可以绑定原生的DOM事件, 需要使用native 修饰符
7. 注意: 通过 this.$refs.xxx.$on('atmy',数据)绑定自定义事件时,回调要么配置在methods中,要么在箭头函数中,否则this会出问题
全局事件总线
开发经验总结
安装全局事件总线$bus
new Vue({
render: h => h(App),
beforeCreate() {
Vue.prototype.$bus=this //绑定到vm上 安装全局事件总线
},
}).$mount('#app')
注意:在对应组件上添加方法在方法结束之前销毁自定义事件
总结
全局事件总线(GlobalEventBus)
1. 一种组件间通讯的方式,用于任意组件间的通信
2. 安装全局事件总结:
new Vue({
render: h => h(App),
beforeCreate() {
Vue.prototype.$bus=this //绑定到vm上 安装全局事件总线
},
}).$mount('#app')
3. 使用事件总线:
1.接收数据: A组件想接收数据, 则在A组件中给$bus绑定自定义事件,事件的回调停留在A组件吱声
mounted() {
this.$bus.$on('hello', data => {
console.log('我是School组件,收到了数据:', data)
})
},
2. 提供数据: this.$bus.$emit('事件名称',数据)
4. 最好在beforeDestory钩子中,用$off方法解绑组件所用到的事件
消息订阅与发布
需借助第三方工具 pubsub-js实现发布订阅
引入发布订阅
引入开源库--pubsub-js
npm i pubsub-js
总结
1. 一种组件间的通讯方式, 适用于任意间组件通讯
2. 适用步骤:
1.安装pubsub: npm i pubsub-js
2.引入: import pubsub from 'pubsub-js'
3.接收数据: A组件想接收数据, 则在A组件中订阅消息,订阅的回调在A组件自身
// 方式二
methods(){
demo(data)()
}
....
mounted() {
this.pubId=pubsub.subscribe('hello',this.demo)
}
// 方式一
mounted() {
this.pubId=pubsub.subscribe('hello',(msgName,...data)=>{
console.log(this) // 此处引用的方法非Vue自身方法,不使用箭头函数导致无this函数指向
})
},
4. 提供数据: pubsub.publish('事件名称',数据)
5. 最好在beforeDestroy 钩子中,pubsub.unsubscribe(this.pubId)取消订阅
nextTick
1. 语法: this.$nextTick(回调函数)
2. 作用: 在下一次DOM更新结束后执行其指定回调
3. 什么时候用: 当改变数据后,要基于更新后的新DOM进行木写操作时,要在nextTick所指定的回调函数中执行
Vue样式--成型的css
Animate.css
npm i animate.css
总结
Vue封装的过度与动画
1. 作用: 在插入, 更新或移除DOM元素时, 在合适的时候给元素添加样式类名
2.
3. 写法:
1.准备好样式:
元素进入的样式:
1. v-enter: 进入的起点
2. v-enter-active: 进入过程中
3. v-enter-to: 进入的终点
元素离开的样式:
1. v-leave: 离开的起点
2. v-leave-active: 离开的过程中
3. v-leave-to: 离开的终点
2.使用<transition>包裹要过度的元素,并配置name属性:
<transition name="hello" appear>
<h1 v-show="isShow">你好啊</h1>
</transition>
3. 备注: 若使用多个元素需要过度, 则需要使用: <transition-group> 且每个元素要绑定key值
axios使用与安装
npm i axios
脚手架配置代理服务器
vue.config.js
配置代理端口
devServer: {
proxy: 'http://localhost:4000'
}
此种配置问题,只能配置一个代理,且在public文件夹下游的文件,会影响请求转发,(直接获取pulblic下的文件)
没有匹配到静态文件的请求,会代理到代理服务器
配置代理地址:https://cli.vuejs.org/zh/config/#devserver-proxy 页签: devServer.proxy
项目开发
使用github练习
公开地址:https://api.github.com/search/users?q=xxx
页面需求开发分析
1.初始化展示:
2.加载中展示:
3.加载数据成功展示:
4.加载失败展示:
使用vue-resource插件
npm i vue-resource
插槽solt
插槽的作用:
1. 作用: 让父组件可以向子组件指定位置插入html结构,也是一种组件间通讯的方式,适用于父组件===>子组件
2. 分类: 默认插槽, 具名插槽, 作用域插槽
1. 默认插槽
父组件:
<Category title="美食" :listData="food">
<img slot src="/favicon.ico">
</Category>
子组件
<slot> 我是一个插槽等待填充</slot>
2. 具名组件
<Category title="美食" :listData="food">
<img slot='food' src="/favicon.ico">
</Category>
或
<template v-slot:games>
<div class="foot">
<a href="http://www.baidu.com" target="_blank">经典</a>
</div>
<h4>欢迎前来观看</h4>
</template>
子组件
<slot name="food"> 我是一个插槽等待填充</slot>
3.作用域插槽(也可以有名字)
理解: 数据在组件自身, 但是根据需要生成的结构需要组件的使用者决定
父组件
<Category title="游戏">
<template slot-scope="shangss">
<ul>
<li v-for="(item, index) in shangss.games" :key="index">{{ item }}</li>
</ul>
</template>
</Category>
子组件
<slot :games="games"></slot>
....
data() {
return {
games: ['火锅games', '烧烤games', '小龙虾games', '牛排games'],
}
}
vuex
-
概念: 专门在Vue中实现集中式状态(数据)管理的一个Vue插件, 对vue应用中多个组件的共享状态进行集中式管理(读/写), 也是组件间通讯的方式, 且适用于任意组件间通选
-
什么时候用vuex
- 多个组件依赖于同一个状态
- 来自不同的组件的行为需要变更同一个状态
通俗讲就是共享数据
vuex使用:
1.npm i vuex@3 (2022年3月Vue3成为Vue默认版本)(Vue2只能用vuex的3版本 Vue3只能用vuex的4版本)
2.Vue.use(Vuex)
3.store
4.vc要能看到store
store中map用法
1.mapState方法: 用于将state中的属性生成对应的计算属性
// 此处方法相当于上面方法,从state中取数据 对象写法
// ...mapState({sum:'sum',school:'school',subject:'subject'}),
// 数组写法,需要计算属性和his.$store.state.sum中sum 名字相同
...mapState(['sum','school','subject']),
2. mapGettersfangs: 用于将getter中的方法生成对应计算属性
// 对象写法
// ...mapGetters({addAll:'addAll'}),
// 数组写法
...mapGetters(['addAll']
3. mapActions方法: 用于生成actions对话中的方法,即包含 $store.disoatch(xxx)方法
// 对象写法
...mapActions({incrementOdd:'addLM',incrementWait:'addLMW'})
// 数组写法
...
4. mapMutations方法: 用于生成mutations中的方法,即包含$store.commit(xxx)
备注: mapActions和mapMutations 使用时,需要传递参数,在模板中需要传递参数,否则就会传递默认的事件event
模块化编码
https://api.uixsj.cn/hitokoto/get?type=spcial
路由vue-route
一组key:value对应的关系,需要路由管理器管理
vue-router:vue的一个插件库, 专门用来实现SPA应用(单页Web应用(single page web application ))
引入vue-router: npm i vue-router
路由的理解
1. 路由:1. 一个路由就是一组映射关系(key-value)
2. key为路径, value可能是function 或 component
2. 路由分类:
1. 后端路由:
1) 理解: value 是function, 用于处理客户端提交的请求
2) 工作过程: 服务端接收一个请求, 根据请求路径找到匹配的函数来处理请求, 返回响应数据
2. 前端路由:
1) 理解: value是component,用于展示页面内容
2) 工作过程: 当浏览器的路径改变时,对应的组件就会展示
vue-router
vue2只能使用vue-router3
npm i vue-router@3
编写路由的配置
创建router/index.js文件夹
// 该文件用于创建整个路由器
import VueRouter from "vue-router";
// 引入组件
import About from '../components/About'
import Home from '../components/Home'
// 创建并暴露一个路由器
export default new VueRouter({
routes:[
{
path:'/about',
component:About
},
{
path:'/home',
component:Home
}
]
})
实现切换
// router-link实现页面切换 active-class 指定切换时添加的class
<router-link class="list-group-item " active-class="active" to="/about">About</router-link>
// 指定视图解析位置
<router-view></router-view>
由路由切换的组件为路由组件,存放pages文件中, 其他为一版组件放入components文件
几个注意点:
1. 路由组件通常存放在pages文件夹, 一般组件存放在components文件夹
2. 通过切换, '隐藏'了路由组件, 默认是被销毁掉的, 需要时再次挂载
3. 每个组件都有自己的$route, 存储的是自己路由信息
4. 整个应用只有一个$router, 可以通过组件的$router属性获取到
多级路由
1. 配置路由规则, 使用childen配置项
routes:[
{
path:'/home',
component:Home,
children:[
{
path:'news', // 此处不写斜杠,vue会自动添加
component:News
},
{
path:'message',
component:Message
},
]
}
2. 路由时需携带完整路径(
<router-link class="list-group-item " active-class="active" to="/Home/News">News</router-link>
)
路由传递参数query
1.传递参数方法:
<!-- 跳转理由并携带query参数 to的字符串写法-->
<!-- <router-link :to="`/home/message/detail?id=${m.id}&title=${m.title}`">{{ m.title }}</router-link> -->
<!-- to 的对象写法 -->
<router-link :to="{
path:'/home/message/detail',
query:{
id:m.id,
title:m.title
}
}">
2. 接收参数
$route.query.key
命名路由
简化路由过长问题,及在路由 index中给路由命名
routes: [
{
name:'guanyu',
path: '/about',
component: About
},
{
path: '/home',
component: Home,
children: [
{
path: 'news',
component: News
},
{
path: 'message',
component: Message,
children: [{
name:'xiangqing',
path: 'detail',
component: Detail,
}
]
},
]
}
]
2. 使用
<router-link :to="{
name:'xiangqing', // name 可以直接替换path中的路径
// path:'/home/message/detail',
query:{
id:m.id,
title:m.title
}
}"
路由传递参数param
路由的params参数,声明路由的时候要声明接收参数
routes: [
{
name:'guanyu',
path: '/about',
component: About
},
{
path: '/home',
component: Home,
children: [
{
path: 'news',
component: News
},
{
path: 'message',
component: Message,
children: [{
name:'xiangqing',
path: 'detail/id/title',// param 写法
// path: 'detail', // query写法
component: Detail,
}
]
},
]
}
]
2. 传递参数
<li v-for="m in messageList" :key="m.id">
<!-- 跳转理由并携带query参数 to的字符串写法-->
<!-- <router-link :to="`/home/message/detail/${m.id}/${m.title}`">{{ m.title }}</router-link> -->
<!-- to 的对象写法 -->
<router-link :to="{
name:'xiangqing', // params写法只能用name
params:{
id:m.id,
title:m.title
}
}">{{ m.title }}</router-link>
</li>
3. 注意: 使用params传递参数,router-link必须使用name命名路径
路由的props的配置
{
path: 'message',
component: Message,
children: [{
name:'xiangqing',
// path: 'detail/id/title',// param 写法
path: 'detail',// props 写法
// path: 'detail', // query写法
component: Detail,
// props的第一种写法,会把props以key-value形式在props传给Detail
// props:{a:1,b:hello}
// props第二种写法, 把params参数以 props传给Detail
// props:true
// props第三种写法 必须结合query使用
// props($route){ 使用结构赋值的连续写法
props({query:{id,title}}){
return {
// id:$route.query.id,
// title:$route.query.title
id,title
}
}
}
]
},
路由对历史记录的影响
router-link 的replace属性
1. 作用: 控制路由跳转时操作历史记录的模式
2. 浏览器历史记录有两种写入模式,分别是push和replace模式, push是追加历史记录, replace是替换历史记录, 路由默认使用push
3. 如何开启replace模式: <router-link replace ....>
编程式路由
1. 作用: 不借助 <router-link> 实现路由跳转,让路由更加灵活
2. 具体编码: 在methods中实现
methods: {
pushShow(m) {
// 浏览器不保存历史记录
this.$router.push({
name: 'xiangqing',
query: {
id: m.id,
title: m.title
}
})
},
replaceShow(m) {
// 浏览器保存历史记录
this.$router.replace({
name: 'xiangqing',
query: {
id: m.id,
title: m.title
}
})
}
}
this.$router.back() 后退
this.$router.forward() 前进
this.$router.go(-3) 前进(正数)/后退(负数)指定步数,(步数多少有限制)
缓存路由组件
1. 作用: 让不展示的路由组件保持存货,不销毁
2. 具体编码:
<!-- 缓存组件信息 组件名-->
<keep-alive include="[News,Message]">
<router-view></router-view>
</keep-alive>
两个新的生命周期钩子
1. 作用: 路由组件所独有两个钩子,用于捕获路由组件的激活状态\
2. 具体名字:
1 activated路由组件被激活时触发
2 deactivated 路由组件失活时触发
路由守卫(路由权限)
1. 作用: 对路由进行权限控制
2. 分类: 全局守卫, 独享守卫, 组件内守卫
全局守卫
// 全局前置路由守卫 --每次路由切换之前被调用,和初始化的时候被调用
router.beforeEach((to,from,next)=>{
console.log(to,from);
// if(to.path === '/home/news' || to.path==='/home/message'){
if(to.meta.isAuth){ // 对权限进行控制
if(localStorage.getItem('school') === 'school'){
next();
}else{
alert('需要权限')
}
}else{
next();
}
})
// 后置路由守卫
router.afterEach((to, from) =>{
document.title = to.meta.title || '后台'
})
独享路由守卫
独享路由守卫只有前置路由守卫,可以和全局的后置路由守卫结合使用,
{
name: 'xinwe',
path: 'news',
component: News,
meta: { isAuth: true },
// 独享路由守卫
beforeEnter(to, from, next) {
// if(to.path === '/home/news' || to.path==='/home/message'){
if (to.meta.isAuth) { // 对权限进行控制
if (localStorage.getItem('school') === 'school') {
next();
} else {
alert('需要权限')
}
} else {
next();
}
}
},
路由中 # 为hash
hash值最大的特点,是不会作为路径的一部分交给后端服务器
配置是否展示#
const router = new VueRouter({
// 更改路径hash规则
mode:'history',
routes: []
})
安装express测试服务器
新建文件,
npm init
npm i express
node server
// 解决 路径模式为history时路由跳转问题
引入 connect-history-api-fallback 插件
npm i connect-history-api-fallback
服务器js 代码
const express = require('express')
const app = express()
app.get('/person',(req,res) =>{
res.send({
name:'tom',
age:18
})
})
app.listen(5005,(err)=>{
if(!err)console.log('服务器启动成功');
})
Z总结
1. 对于一个url来说, 什么是hash值? --- # 及其后面的内容就是hash值
2. hash值不会包含HTTP请求中, 及: hash值不会带给服务器
3. hash模式:
1. 地址中永远带有 # 号,不美观
2. 若以后地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法
3. 兼容性好
4. history模式
1. 地址干净, 美观
2. 兼容性和hash模式有差别
3. 应用部署上线需要后端人员支持,解决刷新页面时服务端404问题
UI组件库
移动端常用UI组件
1. Vant
2. Cube UI
3. Mint UI
PC端常用UI组件
1. Element UI (饿了吗)
2. IView UI
(Ant Design Vue) 蚂蚁出品
引入 ElementUI 具体参考Element官方文档
npm i element-ui
按需引入:先引入插件 -D为开发引入,及只在开发中使用
npm install babel-plugin-component -D
配置中
["es2015", { "modules": false }] ===> ["@babel/preset-env", { "modules": false }]