导论
- 官网:https://cn.vuejs.org/
搭建 Vue 开发环境
1、安装 Vue.js devtools
- 老版本的vue网址:https://v2.cn.vuejs.org/v2/guide/installation.html
2、安装开发者调试工具
注意:此时会出现
Download the Vue Devtools extension for a better development experience:https://github.com/vuejs/vue-devtools
提示
- 将下载的 Vue.js devtools 拖入chome的拓展程序中,并在详情里面勾选“允许访问文件地址”
3、关闭了那个烦人的提示
- 进入 vue.js 设置,就可以了
productionTip: false
一、Vue 核心
入门程序
1、想让vue工作,就必须创建一个vue实例,且要传入一个配置对象。
2、root容器里的代码依然符合html规范,只不过混入了一些特殊的vue语法
3、root容器里的代码被称为【vue模板】
4、vue实力和容器时一一对应的
5、真是开发中只有一个vue实力,并且回配合着组件一起使用
6、{{xxx}}中的xxx要谢js表达式,且xx可以自动读取data中的所有属性
7、一旦data中的数据发生改变,那么页面中用该数据的地方也会自动更新
<div id="root">
<h1>Hello {{name.toUpperCase()}}</h1>
</div>
<script>
new Vue({
el: '#root',
data: {
name: "Robber"
}
})
</script>
模板语法
1、插值语法:用于解析标签体内容
2、指令语法:适用解析标签
插值语法
<div id="root">
<h1>Hello {{name}}</h1>
</div>
<script>
// Vue.config.productionTip = false;
new Vue({
el: '#root',
data: {
name: "Robber"
}
})
</script>
指令语法
<div id="div">
<h1>指令表达式</h1>
<hr>
<a v-bind:href="url">拉克斯基的离开房间撒地方</a>
</div>
<script>
new Vue({
el: '#div',
data: {
url: 'http://www.baidu.com'
}
})
</script>
数据绑定
单向数据绑定
-
语法:v-bind:href =“xxx” 或简写为 :href
-
特点:数据只能从 data 流向页面
双向数据绑定
-
语法:v-mode:value=“xxx” 或简写为 v-model=“xxx”
-
特点:数据不仅能从 data 流向页面,还能从页面流向 data
<div id="root">
<!-- 1、常规写法 -->
单向数据绑定<input type="text" v-bind:value="name"><br>
双向数据绑定<input type="text" v-model:value="name">
<hr>
<!-- 2、简写 -->
单向数据绑定<input type="text" :value="name"><br>
双向数据绑定<input type="text" v-model="name">
</div>
<script>
new Vue({
el: '#root',
data: {
name: 'Robber'
}
})
</script>
data 与 el 的两种写法
1、el 有两种写法
1)new Vue时候配置el属性
2)先创建vue实例,随后再通过vm.$mount(‘#root’)z指定el的数值
2、data有两种写法
1)对象式
2)函数时
如何选择:目前哪种写法都可以,以后学习到组件时,data必须使用函数式,否则回报错
3、一个重要的原则:
由vue管理的函数,一定不要谢箭头函数,this就不再时vue实例了。
el 两种写法
//第一种
let vue = new Vue({
el: '#asdf',
data: {
name: 'Robber'
}
})
//第二种
let vue = new Vue({
data: {
name: 'Robber'
}
})
vue.$mount('#asdf');
data 两种写法
//函数式
let vue = new Vue({
data() {
return {
name: 'Robber'
}
}
})
vue.$mount('#asdf');
//对象式
let vue = new Vue({
data: {
name: 'Robber'
}
})
vue.$mount('#asdf');
MVVM 模型
1、M:模型(Model):data中的数据
2、V:视图(View):模板代码
3、VM:试图模型(ViewModel):Vue实例
观察发现:
1. data中所有的属性,最后都出现再vm身上
2. vm身上所有属性 以及 vue原型上所有属性,在Vue模板中可以直接使用
Object.defineProperty
- 该方法可以动态的修改对象的属性数值
let number = 18;
let person = {
name: 'Robber',
sex: '男'
}
Object.defineProperty(person, 'age', {
get() {
console.log('有人读取');
return number;
},
set(value) {
number = value;
}
})
数据代理
- 可以通过一个对象代理obj对象
<script>
let obj = { x: 100 }
let obj2 = { y: 100 }
Object.defineProperty(obj2, 'x', {
get() {
return obj.x;
},
set(value) {
obj.x = value;
}
})
</script>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eKcO8cIB-1678617517273)(D:\01_Software\03-markdownImages\image-20230201100331824.png)]
事件绑定
1、使用v-on:xxx 或 @xxx 绑定事件,其中xxx式期间名
2、事件的回调需要配置再methods对象中,最终会在vm上
3、methods中配置的函数,不要用箭头函数,否则this就不是vm了
4、methods中配置的函数,都是被vue所管理的函数,this的指向式vm 或 组件实例对象
5、@click=“demo” 和 @click=“demo{$(event)}” 效果一样,但后者可以传参
<div id="box">
<h1>事件处理</h1>
<hr>
<button v-on:click="fun1">{{functionName}}</button>
<button @click="fun2($event,66)">哈哈哈哈</button>
</div>
<script>
const vm = new Vue({
el: '#box',
data: {
functionName: '第一个函数'
},
methods: {
fun1() {
alert('fun1');
},
fun2(event,data) {
alert('fun2');
},
}
})
</script>
点击事件
1.prevent:阻止默认事件
2.stop:阻止事件冒泡
3.once:事件只触发一次
4.capture:使用事件的捕获模式
5.self:只有event.target式当前操作的元素时才会触发事件(可以阻止冒泡)
6.passive:事件的默认行为立即执行,无需等待事件回调执行完毕
<div id="box">
<h1>事件修饰符</h1>
<hr>
<!-- prevent 阻止默认事件 -->
<a @click.prevent="tiaozhuan" href="http://www.baidu.com">跳转链接</a>
<hr>
<!-- stop 阻止事件冒泡 -->
<div @click="div1">div1
<div @click.stop="div2">div2</div>
</div>
<!-- once:事件只触发一次 -->
<button @click.once="tiaozhuan">
只能触发一次
</button>
</div>
<script>
new Vue({
el: '#box',
data: {
},
methods: {
tiaozhuan() {
alert('asdfsadf');
}, div1() {
console.log('div1');
},
div2() {
console.log('div2');
}
}
})
</script>
键盘事件
1、Vue中常用的按键别名:
- 回车 => enter
- 删除 => delete
- 退出 => esc
- 空格 => space
- 换行 => tab
- 上 => up
- 下 => down
- 左 => left
- 右 => right
2、Vue未提供别名的按键,可以使用按键原始的key值取绑定,但注意要转为kebab-case
3、系统修饰键(用法特殊):ctrl、alt、shift、meta
1)配合keyup使用:按下修饰键的同时,再按下其他键 随后释放其他键,事件才会被触发
2)配合keydown使用:正常触发事件
4、也可以使用keyCode去指定具体的按键(不推荐)
5、Vue.config.keyCodes.自定义键名 = 键码,可以去定制按键别名
<div id="root">
<h1>欢迎你</h1>
<hr>
<input type="text" placeholder="输入文字" @keyup.enter="fun">
</div>
<script>
new Vue({
el: '#root',
data: {
},
methods: {
fun(e) {
alert(e.target.value);
}
}
})
</script>
计算属性与监听
插值计算
-
定义:要用的属性不存在,要通过已有属性计算机的来。
-
原理:底层借助了Object.defineproperty方法提供的getter和setter方法
-
get函数什么时候执行?
1)初次读取时会执行一次
2)当依赖的数据发生改变时会被再次调用
-
优势:与methods实现相比。内部由缓存机制(复用),效率高调试方便。
-
备注:
1)计算属性最终会出现在vm上,直接读取使用即可
2)如果计算属性要被修改,那必须谢set函数取响应修改,且set中要引起计算时依赖的数据发生变化
插值算法
<div id="root">
姓:<input type="text" v-model:value="firstName"><br>
名:<input type="text" v-model:value="lastName"><br>
全名:<span>{{hahah()}}</span>
</div>
<script>
const vue = new Vue({
el: '#root',
data: {
firstName: '张',
lastName: '三'
},
methods: {
hahah() {
return this.firstName + this.lastName;
}
}
})
</script>
计算机属性
<div id="root">
姓:<input type="text" v-model:value="firstName"><br>
名:<input type="text" v-model:value="lastName"><br>
全名:<span>{{fullName}}</span>
</div>
<script>
const vue = new Vue({
el: '#root',
data: {
firstName: '张',
lastName: '三'
},
computed: {
fullName: {
get() {
console.log('get方法被触发')
return this.firstName + '-' + this.lastName
},
set(value) {
var arr = value.split('-');
this.firstName = arr[0];
this.lastName = arr[1];
}
}
}
})
</script>
简写方法
- 当该fullName属性只有get方法时,就可以直接使用简写的形式,形式跟函数一样返回值则是该属性的数值
computed: {
// 1、完整写法
// fullName: {
// get() {
// console.log('get方法被触发')
// return this.firstName + '-' + this.lastName
// },
// set(value) {
// var arr = value.split('-');
// this.firstName = arr[0];
// this.lastName = arr[1];
// }
// }
//2、简写
fullName() {
console.log('get方法被调用');
return this.firstName + '-' + this.lastName;
}
}
监听属性
-
当被监视的属性变化时,回调函数自动调用,进行相关操作
-
监视的属性必须存在,才能进行监听
-
监视的两种写法:
1)new Vue时传入watch配置
2)通过vm.$watch监视
<div id="box">
<h1>今天天气{{tianqi()}}</h1>
<button @click="isHost = !isHost">切换天气</button>
</div>
<script>
new Vue({
el: '#box',
data: {
isHost: true
},
methods: {
tianqi() {
return this.isHost ? '炎热' : '凉爽';
}
},
})
</script>
通过watch监视
const vue = new Vue({
el: '#box',
data: {
isHost: true
},
methods: {
tianqi() {
return this.isHost ? '炎热' : '凉爽';
}
},
watch: {
isHost: {
immediate: true, // 初始化让handler调用一次
handler(newValue, oldValue) {
console.log('isHot被修改', newValue, oldValue);
}
}
}
})
第二种方式
vue.$watch('isHost', {
immediate: true, // 初始化让handler调用一次
handler(newValue, oldValue) {
console.log('isHot被修改', newValue, oldValue);
}
})
深度监视
- Vue中的watch默认不检测对象内布置的改变(一层)
- 配置deep:true可以检测对象内布置改变(多层)
备注
1.Vue自身可以检测对象内布置的改变,单Vue提供的watch默认不可以!
2.使用watch时根据数据的具体解构,决定大哥是否采用深度监视。
<div id="box">
<h1>看这里{{numbers.a}}</h1>
<button @click="numbers.a++">a+1</button>
</div>
<script>
const vue = new Vue({
el: '#box',
data: {
numbers: {
a: 100,
b: 200
}
},
watch: {
numbers: {
handler() {
console.log('numbers被修改');
}
}
}
})
</script>
深度监视的简写
const vue = new Vue({
el: '#box',
data: {
isHost: true
},
methods: {
tianqi() {
return this.isHost ? '炎热' : '凉爽'
},
change() {
this.isHost = !this.isHost
}
},
watch: {
isHost() {
console.log('isHost被修改');
}
}
})
第二种形式
vue.$watch('isHost', {
deep: true,
immediate: true,
handler() {
console.log("asdfkjasdflj");
}
})
computed和watch的区别
- computed能完成的功能,watch都能完成
- watch能完成的,computed不一定能完成,例如:watch可以进行异步操作
两个重要的小原则:
- 所有被vue管理的函数,最好使用普通函数,这样this的指向才是vm 或者是组件实例对象
- 所有不被vue所管理的函数(定时器的回调函数、ajax的回调函数等),这样this的指向才是vm或者时组件实例对象
修改名字案例
<div id="root">
姓:<input type="text" v-model:value="firstName"><br>
名:<input type="text" v-model:value="lastName"><br>
全名:<span>{{fullName}}</span>
</div>
<script>
const vue = new Vue({
el: '#root',
data: {
firstName: '张',
lastName: '三',
fullName: '张-三'
},
watch: {
firstName(value) {
console.log('asldfjslakdjf ');
this.fullName = value + '-' + this.lastName;
},
lastName(value) {
this.fullName = this.firstName + '-' + value;
}
}
})
</script>
class 与 style 绑定
- 字符串的写法,适用于:样式的类名不确定,需要动态指定。
- 数组写法,适用于:要绑定的样式个数不确定、名字不确定
- 对象写法,适用于:要绑定的样式个数确定、名字也确定,但要动态决定要不要
<div id="root">
<!-- 1. 字符串的写法,适用于:样式的类名不确定,需要动态指定。 -->
<div class="style1" :class="change" @click="hhh">
aslkdjflsadjf
</div>
<!-- 2. 数组写法,适用于:要绑定的样式个数不确定、名字不确定 -->
<div class="style1" :class="classArr">
aslkdjflsadjf
</div>
<!-- 3. 对象写法,适用于:要绑定的样式个数确定、名字也确定,但要动态决定要不要 -->
<div class="style1" :class="classTemp">
asdfkjhasdkfjhasjkdfhjk
</div>
</div>
<script>
new Vue({
el: '#root',
data: {
change: '',
classArr: ['style1', 'style2', 'style3'],
classTemp: {
style2: true,
style3: true
}
},
methods: {
hhh() {
this.change = 'style2';
}
},
})
</script>
条件渲染
1、v-if
1)v-if=“表达式”
2)v-else-if=“表达式”
3)v-else=“表达式”
适用于:切换频率较低的场景
特点:不展示的DOM元素被移除。
注意:v-if可以和v-eles-if、v-else一起使用,但要求解构不能被"破坏"
2、v-show
写法:v-show=“表达式”
适用于:切换频率较高的场景
特点:不展示的DOM元素未被移除,仅仅式使用样式隐藏掉
3、备注:使用v-if时,元素可能无法获得到,而使用v-show一定可以获取到
<div id="root">
<h1>条件渲染</h1>
<hr>
<button @click="fun">n+1</button>
<h1>{{n}}</h1>
<div v-show="n === 1">哈哈哈哈</div>
<div v-if="n === 1">n = 1</div>
<div v-else-if="n === 1">n = 2</div>
<div v-else-if="n === 2">n = 3</div>
<div v-else>n = 0</div>
</div>
<script>
const vue = new Vue({
el: '#root',
data: {
n: 0
},
methods: {
fun() {
this.n = this.n + 1;
}
},
})
</script>
列表渲染
- v-for指令
- 用于展示列表数据
- 语法:v-for=“(item, index) in xxx” :key=“yyy”
- 可遍历:数组、对象、字符串、指定次数
<div id="root">
<ul>
<!-- 1、遍历数组 -->
<li v-for="(person,index) in persons" :key="person.id">
{{index}} : {{person}}
</li>
<!-- 2、遍历对象 -->
<li v-for="c,index in car" :key="index">
{{index}} :{{c}}
</li>
</ul>
</div>
<script>
new Vue({
el: '#root',
data: {
persons: [
{ id: 1, name: "Robber" },
{ id: 2, name: "Luck" },
{ id: 3, name: "Arr" }
],
car: {
name: "奥迪A8",
price: 80000
}
}
})
</script>
面试题:react、vue的key有什么作用?
1、虚拟DOM中key的作用:
- key是虚拟DOM对象的标识,当数据发生变化时,vue会根据【新数据】生成【新的虚拟DOM】,随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下:
2、对比规则:
(1)旧虚拟DOM中找到了与新虚拟DOM相同的key:
a:若虚拟DOM中内容没有变,直接使用之前的内容DOM!
b:若虚拟DOM中内容变了,则生成新的真实DOM,随后替换页面中之前的真实DOM
(2)旧虚拟DOM中未找到与新虚拟DOM相同的key
创建新的真是DOM,随后渲染到页面
3、用index作为key可能会引发的问题:
(1)若对数据进行:逆序提娜佳、逆序删除等破坏顺序操作:
会产生没有必要的真实DOM更i性能 ===> 界面没有问题,但效率底
(2)如果结构中还包括输入类的DOM:
会产生错误DOM更新 ==> 界面有问题
4、开发中如何选择key?
(1)最好使用每条数据的唯一标识作为key,比如id、手机好、身份证号、学号等唯一值
(2)如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅对于选软列表用于展示,使用index作为key是没有问题的
列表过滤
<div id="root">
<h1>computed</h1>
<hr>
<input type="text" placeholder="请输入" v-model="keyWord">
<ul>
<li v-for="person in fillPerson">
{{person.name}}
</li>
</ul>
</div>
<script>
new Vue({
el: '#root',
data: {
keyWord: '',
persons: [
{ name: '李信' },
{ name: '韩信' },
{ name: '蔡文姬' },
{ name: '虞姬' },
{ name: '甄姬' }
]
},
computed: {
fillPerson() {
return this.persons.filter((item) => {
return item.name.indexOf(this.keyWord) !== -1;
})
}
}
})
</script>
列表排序
<div id="root">
<h1>computed</h1>
<hr>
<input type="text" placeholder="请输入" v-model="keyWord">
<button @click="sortType=1">升序数组</button>
<button @click="sortType=2">降序数组</button>
<button @click="sortType=0">原数组</button>
<ul>
<li v-for="person in fillPerson">
{{person.name}}-{{person.age}}
</li>
</ul>
</div>
<script>
new Vue({
el: '#root',
data: {
sortType: 0, // 1为升序 2为降序
keyWord: '',
persons: [
{ name: '李信', age: 19 },
{ name: '韩信', age: 20 },
{ name: '蔡文姬', age: 23 },
{ name: '虞姬', age: 11 },
{ name: '甄姬', age: 23 }
]
},
computed: {
fillPerson() {
let temp = this.persons.filter((item) => {
return item.name.indexOf(this.keyWord) !== -1;
})
if (this.sortType) {
temp.sort((p1, p2) => {
return this.sortType === 1 ? p1.age - p2.age : p2.age - p1.age;
})
}
return temp;
}
}
})
</script>
vue原理
问题的提出
- 问题:为什么通过整体替换对象就不可行
- 原因:Vue 将被侦听的数组的变更方法进行了包裹,所以它们也将会触发视图更新。这些被包裹过的方法包括push()、shift()、unshift()、splice()、sort()
- 解决:
- 1、通过vue包装的push来修改数组中的元素,如果不调用vue包装数组中的方法则不会被vue管理
- 2、也可以通过Vue.set()方法
<script>
new Vue({
el: '#root',
data: {
persons: [
{ name: 'Robber', age: 19 },
{ name: '海盗', age: 33 }
]
},
methods: {
change() {
//1、可行,此时对象中的属性是存在get、set方法
this.persons[0].age = 22;
this.persons[0].name = '哈哈哈哈';
//2、不可行
this.persons[0] = { name: 'hahahah', age: 123 }
}
},
})
</script>
vue检测数据的原理
<script>
let data = {
name: '海盗',
address: '上海'
}
let obs = new Observer(data)
let vm = {};
vm._data = data = obs
function Observer(obj) {
//汇总所有的属性
const arr = Object.keys(obj);
arr.forEach((k) => {
console.log(k);
Object.defineProperty(this, k, {
get() {
return obj[k];
},
set(value) {
obj[k] = value;
}
})
})
}
</script>
Vue.set()方法
- 如果后面添加的属性需要响应式的属性就必须使用该方法,并且该方法只能用在 _data ** 中的对象**添加属性
- target:要更改的数据源
- key:要更改的具体数据
- value:重新赋的值
<div id="root">
<button @click="addSex">添加性别</button>
<h1>学生信息</h1>
<h2>学生姓名:{{student.name}}</h2>
<h2>学生性别:{{student.sex}}</h2>
</div>
<script>
let vm = new Vue({
el: '#root',
data: {
student: {
name: 'Robber',
sex: ''
}
},
methods: {
addSex() {
this._data.student.sex = '男'
}
},
})
</script>
Vue监视数据的原理
1、vue会监视data中所有层次的数据
2、如何检测对象中的数据?
- 通过setter实现监视,且要再new Vue时就传入要检测的数据
1)对象中吼追加的属性,Vue默认不做响应式处理
2)如需给后添加的属性做响应式,清使用如下API:
a)Vue.set(target, propertyName/index, value
b)Vue.$set(target, propertyName/index, value)
3、如何检测数组中的数据?
- 通过包装数组更新元素的方法实现,本质就是做了两件事:
1)调用原生对应的方法对数组进行更新。
2)重新解析模板,进而更新页面
4、再Vue修改数组中的某个元素一定要用如下方法:
1)使用这些API:push()、shift()、unshift()、splice()、sort()
2)Vue.set() 或 vm.$set()
特别注意:Vue.set() 和 vm.$set() 不能给vm 或 vm的根数据 添加属性!
<div id="box">
<button @click="student.age++">年龄加一</button>
<button @click="addSex">添加性别属性,默认值为:男</button>
<button @click="addFriend">再列表首位添加一个朋友</button>
<button @click="updateFirst">修改第一个朋友的名字为:张三</button>
<button @click="addHobby">添加一个爱好</button>
<button @click="updateFirstHobby">修改第一个爱好:开车</button>
<hr>
<h1>学生信息</h1>
<hr>
<h2>{{student.name}}</h2>
<h2>{{student.age}}</h2>
<h2 v-if="student.sex">{{student.sex}}</h2>
<ul>
<li v-for="h in student.hobby">
{{h}}
</li>
</ul>
<h2>这是我的朋友</h2>
<ul>
<li v-for="f in student.friend">
{{f.name}}-{{f.age}}
</li>
</ul>
</div>
<script>
new Vue({
el: '#box',
data: {
student: {
name: 'Robber',
age: 20,
hobby: ['抽烟', '喝酒', '烫头'],
friend: [
{ name: "luck", age: 22 },
{ name: "jack", age: 10 },
{ name: "lyq", age: 20 }
]
},
},
methods: {
addSex() {
Vue.set(this.student, 'sex', '男')
},
addFriend() {
this.student.friend.unshift({ name: "hahahah", age: 222 })
},
updateFirst() {
this.student.friend[0].name = '张三'
},
addHobby() {
this.student.hobby.push('洗澡')
},
updateFirstHobby() {
// this.student.hobby.splice(0, 1, '开车')
Vue.set(this.student.hobby, 0, '开车')
}
},
})
</script>
收集表单数据
1、若,则v-model收集的时value值,用户输入的就是value值
2、若,则v-model收集的时value值,且要给标签配置value值。
3、若
1)没有配置input的value属性,那么收集的就是checked(勾选 or 未勾选,是布尔值)
2)配置input的value属性:
a)v-model 的初始值是非数组,那么收集的就是checked(勾选 or 未勾选,是布尔值)
b)v-model 的初始值是数组,那么收集的就是value组成的数组
备注:v-model的三个修饰符:
lazy:失去焦点再收集数据
number:输入字符串转为有校的数字
trim:输入收尾空格过滤
<div id="root">
<form action="">
账号<input type="text" v-model="account"><br><br>
密码<input type="text" v-model="password"><br><br>
年龄<input type="text" v-model="age"><br><br>
性别:男 <input type="radio" name="sex" v-model="sex" value="male">
女<input type="radio" name="sex" v-model="sex" value="female"><br><br>
爱好:
学习<input type="checkbox" name="" id="" value="study" v-model="hobby">
打游戏<input type="checkbox" name="" id="" value="play" v-model="hobby">
吃饭<input type="checkbox" name="" id="" value="eat" v-model="hobby"><br><br>
所属校区
<select name="" id="" v-model="address">
<option value="">请选择</option>
<option value="beijing">北京</option>
<option value="shanghai">上海</option>
<option value="shenzhen">深圳</option>
<option value="">广州</option>
</select><br><br>
其他信息:<textarea v-model="other"></textarea><br><br>
<input type="checkbox" name="" id="">阅读病接受
<a href="">《用户协议》</a><button @click.prevent="">提交</button>
</form>
</div>
<script>
new Vue({
el: '#root',
data: {
account: '',
password: '',
age: '',
sex: '',
hobby: [],
address: '',
other: ''
}
})
</script>
过滤器
-
定义:对要显示的数据进行特定格式化后再提示(适用于一些简单逻辑的处理)
-
语法:
- 注册过滤器:Vue.filter(name.callback) 或 new Vue{filters:{}}
- 使用过滤器:{{ xxx | 过滤器名}} 或 v-bind:属性 = “xxx | 过滤器名”
-
备注:
1.过滤器也可以接受额外参数、多个过滤器也可以串联
2.并没有改变原本的数据,是产生信的对应的数据
<div id="box">
<h1>过滤器</h1>
<hr>
<!-- computed方式 -->
<h2>{{nowTime}}</h2>
<!-- methods形式 -->
<h2>{{nowtime1()}}</h2>
<!-- 过滤器 -->
<h2>{{time |filter1}}</h2>
<!-- 串联过滤器 -->
<h2>{{time | filter1("YYYY-MM-DD") | mySlice}}</h2>
<!-- 全局过滤器 -->
<h2>{{time | filter2}}</h2>
</div>
<script>
//1、全局过滤器
Vue.filter('filter2', function (value) {
return dayjs(this.time).format('YYYY-MM-DD HH:mm:ss')
})
// 2、局部过滤器
const vm = new Vue({
el: "#box",
data: {
time: 1675345179721
},
computed: {
nowTime() {
return dayjs(this.time).format('YYYY-MM-DD HH:mm:ss')
}
},
methods: {
nowtime1() {
return dayjs(this.time).format('YYYY-MM-DD HH:mm:ss')
}
},
filters: {
filter1(time, str = "YYYY-MM-DD HH:mm:ss") {
return dayjs(this.time).format(str)
},
mySlice(value) {
return value.slice(0, 4)
}
}
})
</script>
内置指令
我们学过指令:
- v-bind:单项绑定解析表达式,可简写:xxx
- v-model:双向绑定数据
- v-for:遍历数组/对象/字符串
- v-on:绑定事件监听,可简写为@
- v-if:条件渲染(动态控制节点是否存在)
- v-else:条件渲染(动态控制节点是否存在)
- v-show:条件渲染(动态控制结点是否展示)
v-text指令
1、作用:向其所在的节点中渲染文本内容
2、与插值语法的区别:v-text会替换掉节点中的内容,{{xx}}则不会
v-html指令
1、作用:向指定节点中渲染包含html结构的内容
2、与插值语法的区别:
1)v-html会替换节点中所有内容,{{xx}}则不会
2)v-html可以识别的html结构
3、严重注意:v-html由安全性问题!!!
1)在网站上动态渲染人一HTML是非常微信啊的,容易导致XSS攻击
2)一定要在可信的内容上使用v-html,永远不要在用户提交的内容上!
v-cloak指令
1、本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性。
2、使用css配合v-cloak可以解决网速慢时页面展示出{{xxx}}的问题
CSS
[v-cloak] {
display: none;
}
HTML
<div id="box">
<h1>v-cloak指令</h1>
<h2 v-cloak>{{name}}</h2>
</div>
<script>
new Vue({
el: '#box',
data: {
name: 'Robber'
}
})
</script>
v-once
1、v-once所在节点在初次动态渲染后,就视为静态内容了。
2、以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能
<div id="box">
<h1>v-once</h1>
<button @click="n++">n+1</button>
<h2 v-once>初始值:{{n}}</h2>
<h2>n加一:{{n}}</h2>
</div>
<script>
new Vue({
el: '#box',
data: {
n: 1
}
})
</script>
v-pre
1、跳过其所在节点的编译过程。
2、可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译
自定义指令
一、定义语法
1)局部指令:
directives: {
bing(element, binding) {
console.log(element);
console.log(binding);
element.innerText = binding.value * 10
},
fbing: {
bind(element, binding) {
element.value = binding.value
},
inserted(element, binding) {
},
update(element, binding) {
this.n = binding.value * 100
element.value = binding.value
}
}
}
2)全局指令:
vm.directive('bing', function (element, binding) {
element.innerText = binding.value * 10
})
二、配置对象中常用的3个回调
1)bind:指令元素成功绑定时回调
2)inserted:指令所在元素被插入页面时调用
3)update:指令所在模板结构被重新解析调用
三、备注
1)指令定义不加v-,但使用时要加v-;
2)指令名如果是多个单词,要使用user-name名命方式,不要用userName
//1、需求1:定义一个v-big指令,和v-text功能类似,但会把绑定的数值放大10倍
//2、需求2:定义一个v-fbind指令,和v-bind功能类似,但可以让其所绑定的input元素默认获取焦点
<div id="box">
<h1>自定义属性</h1>
<h2>初始值:{{n}}</h2>
<h2>自定义指令n的数值:<span v-bing="n">{{n}}</span></h2>
<button @click="n++">点击n+1</button>
<input type="text" v-fbing="n">
</div>
const vm = new Vue({
el: '#box',
data: {
n: 1
},
directives: {
bing(element, binding) {
console.log(element);
console.log(binding);
element.innerText = binding.value * 10
},
fbing: {
bind(element, binding) {
element.value = binding.value
},
inserted(element, binding) {
},
update(element, binding) {
this.n = binding.value * 100
element.value = binding.value
}
}
}
})
//全局配置
vm.directive('bing', function (element, binding) {
element.innerText = binding.value * 10
})
Vue生命周期
一、常用的生命周期钩子:
1、mounted:发送ajax请求、启动定时器、绑定自定义事件、订阅消息等【初始化操作】
2、beforeDestroy:清楚定时器、解绑自定义事件、取消订阅消息等【收尾工作】
二、关于销毁Vue实例
1、销毁后借助Vue开发者工具看不到任何消息
2、销毁后自定义事件会时效,但原生DOM事件依然有效
3、一般不会再beforeDestroy操作数据,因为即使操作数据,也不会再触发更新流程了
三、vm的生命周期
将要创建 ===> 调用beforeCreate函数
创建完毕 ===> 调用created函数
将要挂载 ===> 调用beforeMount函数
挂载完毕 ===> 调用mounted函数
将要更新 ===> 调用beforeUpdate函数
更新完毕 ===> 调用update函数
将要销毁 ===> 调用beforeDestroy函数
销毁完毕 ===> 调用destroyed函数
<div id="box">
<h1>生命周期</h1>
<h2 :style="{opacity}">透明度</h2>
<button @click="destroy">销毁vm</button>
</div>
<script>
new Vue({
el: '#box',
data: {
opacity: 0.5
},
methods: {
destroy() {
this.$destroy()
}
},
mounted() {
this.timer = setInterval(() => {
this.opacity -= 0.1
if (this.opacity <= 0) this.opacity = 1
}, 100);
},
beforeDestroy() {
console.log("销毁前工作");
clearTimeout(this.timer);
},
})
</script>
二、Vue 组件化编程
非单文件组件
Vue使用组件的三大步骤:
一、定义组件(创建组件)
二、注册组件
三、使用组件(写组件标签)
一、如何定义一个组件?
- 使用Vue.extend(option)创建,其中options和new Vue(options)时传入的那个options几乎一样,但也有点区别:
- el不要写,为什么? 最终所有的组件都要经过一个vm管理,由vm中的el决定符无哪个容器
- data必须咬写成函数,为什么? 避免组件被复用时,数据存在引用关系
- 备注:使用template可以配置组件结构
二、如何注册组件?
1、局部注册:靠new Vue的时候传入components选项
2、全局注册:靠Vue.component(‘组成名’,组成)
三、编写组件标签:
<div id="box">
<!-- 3、使用组件 -->
<xuexiao></xuexiao>
<hr>
<xuesheng></xuesheng>
</div>
<script>
// 1、创建组件
const school = Vue.extend({
template: `
<div>
<h2>{{name}}</h2>
<h2>{{address}}</h2>
</div>
`,
data() {
return {
name: '珠海科技学院',
address: '珠海三灶'
}
}
})
const student = Vue.extend({
template: `
<div>
<h2>{{name}}</h2>
<h2>{{age}}</h2>
</div>
`,
data() {
return {
name: 'Robber',
age: 18
}
}
})
//2、注册逐渐
new Vue({
el: '#box',
components: {
xuexiao: school,
xuesheng: student
}
})
</script>
Vue组件注意点
一、关于组件名:
- 一个单词组成
- 第一种写法(首字母小写):school
- 第二种写法(首字母大写):School
- 多个单词组成:
- 第一种写法:my-school
- 第二种写法:MySchool(需要Vue脚手架支持)
- 备注
- 组件尽可能回避HTML中已有的元素名称。例如:h2、H2都不行
- 可以使用name配置项指定组件在开发者工具中呈现的名字
二、关于组件标签:
- 第一种写法:
- 第二种写法:
- 备注:不用脚手架时,会导致后续组件不能渲染
三、一个简写的方式:
- const school = Vue.extend(options) 可简写为:const school = option
<div id="box">
<h1>vue组件的注意点</h1>
<hr>
<!-- 使用组件 -->
<school></school>
<hr>
<my-school></my-school>
</div>
<script>
const school = {
template: `
<div>
<h2>{{name}}</h2>
</div>
`,
data() {
return {
name: 'Robber',
age: 24
}
}
}
const MySchool = {
template: `
<div>
<h2>{{name}}</h2>
</div>
`,
data() {
return {
name: '珠海科技学院'
}
}
}
new Vue({
el: '#box',
components: {
school: school,
'my-school': MySchool
}
})
</script>
VueComponent构造函数
1、school组件本质是一个名为VueComponent构造函数,且不是程序员定义的,是Vue.entends生成的
2、我们只需要写或者是,Vue解析时会帮我们创建school组件的实例对象,即Vue帮我们执行的:new VueComponenet(options)
3、特别注意:每次调用Vue.extend,返回的都是一个全新的VueComponent!!!
4、关于this指向:
1)组件配置中:
- data函数、method中的函数、wathch中的函数、computed中的函数它们的this均是【VueComponent实例对象】
2)new Vue(options)配置中:
- data函数、methods中的函数、watch中的函数、computed中的函数 它们this均为【Vue实例对象】
5、VueComponent的实例对象,以后简称vc(也可成为:组件实例对象),则Vue实例对象,以后简称vm
重要的内置对象关系
1、一个重要的内置关系:VueComponent.prototype.proto === Vue.prototype
2、为什么要有这个关系:让组件实例对象(vc)可以访问到Vue原型上的属性、方法。
单文件组件的结构
1、组件:Student.vue
2、老大:APP.vue
3、主页:index.html
4、main.js
Student.vue
<template>
<div class="demo">
<h2>{{ name }}</h2>
</div>
</template>
<script>
export default {
data() {
return {
name: "Robber",
};
},
};
</script>
<style>
.demo {
background-color: orange;
}
</style>
APP.vue
<template>
<div>
<school></school>
<student></student>
</div>
</template>
<script>
import school from "./School";
import student from "./Student";
export default {
components: {
school,
student,
},
};
</script>
<style>
</style>
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>Document</title>
<script src="../js/vue.min.js"></script>
</head>
<body>
<div id="box">
<app></app>
</div>
<script src="./main.js"></script>
</body>
</html>
main.js
import app from "./App.vue"
new Vue({
el: '#box',
data: {
},
components: {
app
}
})
三、Vue 脚手架
入门案例
第一步:配置 npm 淘宝镜像
npm config set registry https://registry.npm.taobao.org
第二步:全部安装@vue/cli
npm install -g @vue/cli
第三步:切换到你要创建项目的目录
vue create xxxx
第四步:启动项目
npm run serve
render函数
关于不同版本Vue:
1、vue.js 与 vue.runtime.xxx.js的区别:
1)vue.js 是完整版的Vue 包含:核心功能+模板解析器
2)vue.runtime.xxx.js是运行版的Vue,只包含:核心功能:没有模板解析器
2、因为vue.runtime.xxx.js没有模板解析器。所以不能使用template配置项,需要使用render函数接收的createElement函数去指定具 体内容
模板项目的结构
├── 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:包版本控制文件
vue.config.js配置文件
- 使用vue inspect > output.js可以查看到Vue脚手架的默认配置
- 使用vue.config.js可以对脚手架进行个性化定制,详情见:https://cli.vuejs.org/zh/config/#lintonsave
ref属性
-
被用来给元素或子组件注册引用信息(id的替代者)
-
应用在html标签上获取的是真是DOM元素,应用在组件标签上是组件实例对象(vc)
-
使用方式:
- 打标识:
…
或者 - 获取:this.$refs.xxx
- 打标识:
pops属性
- 功能:让组件接收外部传过来的数据
- 传递数据:
- 接受数据:
- 第一种(只接收):pops:[‘name’]
- 第二种(限制类型):props:{ name: String }
- 第三种(限制类型、限制必要性、指定默认值):
props:{
name:{
type:String, //类型
required: true, //必要性
default: '老王' //默认值
}
}
备注
props是只读的,Vue底层会检测你对props的修改,如果进行了修改,就会发出警告,若业务需求确实需要修改,那么请复制 props的内容到data中一份,然后修改data中的数据
注意
若前端想传进来的是一个数字或者一个对象等非字符串的形式,则要在 属性上加前v-bind,或者是使用冒号代替
mixins混入
- 通过mixins的属性来达到公共方法的途径
特点:
1、vue会先使用自己写,如果没有的话再使用mixins属性引入的
2、mixins混入的mouted函数会在,自己定义的mouted函数后面执行
mixins.js
export const hunhe = {
methods: {
showName() {
alert("哈哈哈哈哈")
}
},
}
Student.vue
import {hunhe} from "../mixins.js"
new Vue({
data(){
return{
name: 'Robber'
}
},
mixins:[hunhe] // 此时就拥有了mixins中的方法
})
插件
- 功能:用于增强Vue
- 本质:包含install方法的一个对象,install的第一个参数是Vue,第二个以后是插件使用者传递的数据。
- 定义插件:
对象.install = function(Vue, options){
//1、添加全局过滤器
Vue.filter(....)
//2、添加全局指令
Vue.directive(....)
//3、配置全局混入
Vue.mixin(....)
//4、添加实例方法
Vue.prototype.$myMethod = function(){...}
Vue.prototype.$myProperty = xxxx
}
- 使用插件:Vue.use()
scopred 样式
- 作用:让样式再局部生成,防止组件中相互影响
- 写作:
<style scoped>
.demo {
background-color: pink
}
</style>
总结TodoList案例
1、组件化编码流程:
1)拆分静态组件:组件要按照功能点拆分,命名不哟啊html元素冲突
2)实现动态组件:考虑数据的存存放数据,数据是一个组件在用,还是一些组件再用:
a)一个组件在用:放在组件自身即可。
b)一些组件在用:放在她们公共的父组件上(状态提升)
2、propos适用于:
1)父组件 ===> 子组件 通信
2)子组件 ===> 父组件 通信(要求父先给子一个函数)
3、使用v-model时要切记:v-model并当的值不能时props传进来的值,因为props是不可以修改的!
4、props传过来的若时对象类型的值,修改对象中的属性Vue不会报错,但不推荐这样做
自定义事件
-
一种组件间通信的方式,适用于:子组件 === > 父组件
-
使用场景:A是父组件,B是子组件,B享给A传数据,那么就要在A中给B帮i的那个自定义事件(事件的回调在A中)
-
绑定自定义事件:
1)第一种方式:在父组件中:<Demo @robber=“test”/> 或
2)第二种方式:在父组件中:
<Demo ref="demo"/> ..... mounted(){ this.$refs.xxx.$on('robber', this.test) }
3)若想让自定义事件只能触发一次,可以使用once 修饰符,或$once方法
-
触发自定义事件:this.$emit(‘robber’, 数据)
-
解绑自定义事件:this.$off(‘robber’)
-
组件上也可以绑定原生DOM事件,需要使用native修饰符
-
注意通过this. r e f s . x x x . refs.xxx. refs.xxx.on(‘robber’, 回调)绑定自定义事件时,回调要么配置在method中,要么用箭头函数,否则this指向会出问题
全局事件总线
-
一种组件间通信的方式,适用于任何组件间通信,
-
安装全局事件总线:
new Vue({ ..... beforeCreate(){ Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vue } ..... })
-
使用事件总线:
1)接受数据:A组件想接受数据,则在A组件总给$bus绑定自定义事件,事件的回调留在A组件自身。
School.vue
mounted(){ this.$bus.$on('robber', (data)=>{ csl(data); }) }
2)提供数据:this. b u s . bus. bus.emit(‘xxxx’, 数据)
Student.vue
methods:{ sendStudentName(){ this.$bus.$emit('robber', this.name) } }
-
最好在beforeDestroy钩子中,用$off去解绑当前组件所用的事件
消息订阅
-
一种组件间通信的方式,适用于任何组件间通信\
-
使用步骤:
1)安装pubsub:
npm i pubsub-js
2)引入:
import pubsub from 'pubsub-js'
3)接受数据:A组件想接受数据,则在A组件中订阅消息,订阅的回调留在A组件自身
methods(){ demo(data){....} } ..... mounted(){ this.pid = pubsub.subscribe('robber', this.demo) //订阅消息 }
4)提供数据:
pubsub.publish('robber', 数据)
5)最好在beforeDestroy钩子中,用
PubSub.unsubscribe(pid)
去取消订阅
nextTick
- 语法:
this.$nextTick(回调函数)
- 作用:在下一次 DOM 更新结束后执行其指定的回调
- 什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行
过度与动画
- 作用:在插入、更新或移除DOM元素时,在核实的时候给元素添加样式类名
- 图示:
-
写法:
1)准备好样式:
-
元素进入的样式:
a)v-enter:进入的起点
b)v-enter-active:进入过程中
c)v-enter-to:进入的终点
-
元素离开的样式:
a)v-leave:离开的起点
b)v-leave-active:离开过程中
c)v-leave-to:离开的终点
2)使用
<transition>
包裹要过度的元素,并配置name属性:<transition name="demo"> <h1 v-show="isShow">你好呀</h1> </transition>
3)备注:若有多个元素需要过度,则需要使用
<transition-group>
,且每个元素都要指定key
值 -
-
第三方库:animate.style
<transition appear name="animate__animated animate__bounce" enter-active-class="animate__swing" leave-active-class="animate__backOutUp" > <h1 v-show="isShow">哈哈哈哈</h1> </transition>
四、Vue中的Ajax
解决开发环境Ajax跨域问题
- 引入axios包
npm i axios
- 编写请求资源代码
methods:{
getStudents(){
axios.get('http://localhost:8080/robber/students').then(
response => {
csl(response.data)
},
error => {
csl(error.message)
}
)
}
}
方式一
在vue.config.js中添加如下配置
devServer:{
proxy:"http://localhost:5000"
}
说明:
- 优点:配置简单,请求资源时直接发送给前端 8080 即可
- 缺点:不能配置多个代理,不能灵活的控制请求是否走代理
- 工作方式:若按照上诉配置代理,当请求了前端不存在的资源时,那么该请求会转发给服务器(优先匹配前端资源)
方式二
编写vue.config.js配置具体代理规则:
module.exports = {
devServer: {
proxy: {
'/robber': { //匹配所有以 '/api'开头的请求路径
target: 'http://localhost:5000', // 代理目标的基础路径
changeOrigin: true // 屏蔽端口号
pathRewrite: {'^/robber': ''}
},
}
}
}
slot 插槽
-
作用:让父组件可以向子组件指定位置插入html结构,也是一种组件间通信的方式,适用于 父组件 ===> 子组件
-
分类:默认插槽、具体插槽、作用域插槽
-
使用方式:
1)默认插槽
父组件: <Category> <div>html结构</div> </Category> 子组件: <div> <!--定义插槽--> <slot>插槽默认内容...</slot> </div>
2)居名插槽:
父组件: <Category> <template slot="center"> <div>html结构</div> </template> <template v-slot:footer> <div>html结构</div> </template> </Category> 子组件: <template> <div> <slot name="center">插槽默认内容</slot> <slot name="footer">插槽默认内容</slot> </div> </template>
3)作用域插槽:
a:理解:数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定 (games数据在Category组件中,但使用数据所遍历出来的结构由App组件决定)
b:具体编码
父组件: <Category> <template scope="scopeData"> <!--生成的ul列表--> <ul> <li v-for="g in scopeData.games" :key="g"> {{g}} </li> </ul> </template> </Category> <Category> <template slot-scope="scopeData"> <!--生成的h4列表--> <h4 v-for="g in scopeData.games" :key="g"> {{g}} </h4> </template> </Category> 子组件: <template> <div> <slot :games="games"></slot> </div> </template> <sciprt> export default{ name: 'Category', props: ['title'], data(){ return { games:['红色警戒','穿越火线'] } } } </sciprt>
五、Vuex
理解Vuex
-
概念:在Vue中实现集中式状态(数据)管理的一个Vue插件,对vue应用中多个组件的共享状态进行集中式的管理(读/写)也是一种组件间通信的方式,且适用于人一组件间通信。
-
何时使用?
多个组件需要共享数据时
-
搭建Vuex环境
1)下载
npm i vuex@3
2)创建文件:
src/store/index.js
//引入Vue核心库 import Vue from 'vue' //引入Vuex import Vuex from 'vuex' //应用Vuex组件 Vue.use(Vuex) //准备action对象-响应组件中用户的动作 const actions = {} //准备mutations独享-修改state中的数据 const mutations = {} //准备state对象-保存具体的数据 const state = {} //创建并暴露store export default new Vuex.Store({ actions, mutations, state })
3)在
main.js
中创建vm时传入store
配置项..... //引入store import store from './store' ...... //创建vm new Vue({ el: '#app', render: h => h(App), store })
-
基本使用
1)组件中读取vuex中的数据:
$store.state.sum
2)组件中修改vuex中的数据:
$store.dispatch('action中的方法名', 数据)
或$store.commit('mutations中的方法名', 数据)
备注:若没有网络请求或者其他业务逻辑,组件中也可以越过actions,不用写 dispatch ,直接编写commit
求和案例
index.js
//引入Vue核心库
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//应用Vuex组件
Vue.use(Vuex)
//准备action对象-响应组件中用户的动作
const actions = {
jia(context, value) {
console.log("actions-jia被调用");
context.commit('JIA', value)
},
jian(context, value) {
console.log('actions-jian被调用');
context.commit('JIAN', value)
},
odd(context, value) {
if (context.state.sum % 2) {
context.commit('JIA', value)
}
},
time(context, value) {
setTimeout(() => {
context.commit('JIA', value)
}, 1000);
}
}
//准备mutations独享-修改state中的数据
const mutations = {
JIA(state, value) {
console.log('mutations-jia被调用');
state.sum += value
},
JIAN(state, value) {
console.log('mutations-jian被调用');
state.sum -= value
}
}
//准备state对象-保存具体的数据
const state = {
sum: 0
}
//创建并暴露store
export default new Vuex.Store({
actions,
mutations,
state
})
ComputedDemo.vue
<template>
<div id="ComputedDemo">
<h1>求和案例:{{ this.$store.state.sum}}</h1>
<hr>
<select v-model.number="m">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button @click="incream">+</button>
<button @click="decream">-</button>
<button @click="increamOdd">奇数加一</button>
<button @click="increamWait">等会儿再加</button>
</div>
</template>
<script>
export default {
data() {
return {
m:1
}
},
compute:{
sum(){
return this.$store.state.sum
}
},
methods: {
incream(){
this.$store.dispatch('jia',this.m)
},
decream(){
this.$store.dispatch('jian',this.m)
},
increamOdd(){
this.$store.dispatch('odd',this.m)
},
increamWait(){
this.$store.dispatch('time',this.m)
}
},mounted() {
console.log(this);
},
}
</script>
<style>
button {
margin-left: 5px;
}
</style>
getters的使用
- 概念:当state中的数据需要经过加工后再使用时,可以用getters家加工。
- 在
store.js
中追加getters
配置
......
const getters = {
bigSum(state){
return state.sum * 10
}
}
//创建并暴露store
export default new Vuex.Store({
....
getters
})
- 组件中读取数据:
$store.getters.bigSum
四个map方法的使用
- mapState方法:用于帮助我们映射
state
中的数据为计算属性
computed:{
//sum(){
// return this.$store.state.sum
//}
//借助mapState生成计算属性:sum、school、subject(对象写法)
...mapState({sum: 'sum', school: 'school', subject: 'subject'})
//借助mapState生成计算属性:sum、school、subject(数组写法)
...mapState(['sum', 'school', 'subject'])
},
- mapGetter方法:用于帮助我们映射
getters
中的数据为计算属性
computed:{
//bigSum(){
// return this.$store.getters.bigSum
//}
//借助mapGetter生成计算属性:sum、school、subject(对象写法)
...mapGetter({bigSum: 'bigSum'})
//借助mapGetter生成计算属性:sum、school、subject(数组写法)
...mapGetter(['bigSum'])
}
- mapActions方法:用于帮助我们生成与
actions
对话的方法,即:包含$store.dipatch(xxx)
函数
methods: {
// incream(){
// this.$store.commit('JIA',this.m)
// },
// decream(){
// this.$store.commit('JIAN',this.m)
//
//第一种 对象的形式访问
...mapMutations({incream: 'JIA',decream: 'JIAN'}),
//第二种:数组的形式(需要方法和mutations的方法名相同)
...mapMutations(['JIA','JIAM']),
}
- mapMutations方法:用于帮助我们生成与
mutation
对话的方法,即:包含$store.commit(xxx)
的函数
methods: {
// increamOdd(){
// this.$store.dispatch('odd',this.m)
// },
// increamWait(){
// this.$store.dispatch('time',this.m)
// }
//第一种 对象的形式访问
...mapActions({increamOdd:'odd',increamWait: 'time'}),
//第二种:数组的形式(需要方法和mutations的方法名相同)
...mapActions(['odd','time'])
}
模块化+ 命名空间
- 目的:让代码更好维护,让多种数据分类冯家明确
- 修改
store.js
const countAbout = {
namespaced: true, //开启命名空间
state: {x:1},
mutations: { ... },
actions: { ... },
getters: {
bigSum(state){
return state.sum * 10
}
}
}
const personAbout = {
namespaced: true, //开启命名空间
state: { ... },
mutations: { ... },
actions: { ... }
}
const store = new Vuex.Store({
modules:{
countAbout,
personAbout
}
})
- 开启命名空间后,组件种读取state数据
//方式一:自己直接读取
this.$store.state.personAbout.list
//方式二:借助mapState读取
...mapState('personAbout',['sum', 'school', 'subject'])
- 开启命名空间后,组件种读取getters数据:
//方式一:自己直接读取
this.$store.getters['personAbout/firstPersonName']
//方式二:借助mapGetters读取
...mapGetters('countAbout',['bigSum'])
- 开启命名空间后,组件中调用dispatch
//方式一:自己直接dispatch
this.$store.dispatch('personAbout/addPersonWang',person)
//方式二:借助mapActions:
...mapActions('countAbout', {incrementOdd:'jiaOdd', incrementWait: 'jiaWait'})
- 开启命名后组件中调用commit
//方式一:自己直接commit
this.$store.commit('personAbout/ADD_PERSON',person)
//方式二:借助mapMutations
...mapMutations('countAbout', {increment: 'JIA', decrement: 'JIAN'})
六、路由
- 理解:一个路由(route)就是一组映射关系(key-value),多个路由需要路由器(router)管理
- 前端:key是路劲,value是组件
基本使用
- 安装vue-router,命令:
npm i vue-router@3
- 应用插件:
Vue.use(VueRouter)
- 创建文件
/router/index.js
//引入VueRouter
import VueRouter from "vue-router";
import AboutCop from "../components/AboutCop"
import HomeCop from '../components/HomeCop'
//创建路由器
export default new VueRouter({
routes: [
{
path: '/home',
component: HomeCop
},
{
path: '/about',
component: AboutCop
},
]
})
- 实现切换(actice-class可配置高亮样式)
<router-link active-class='active' to="/home">Home</router-link>
- 指定展示位置
<rout-view></rout-view>
几个注意点
- 路由组件通常存放在
pages
文件夹,一般组件通常存放在components
文件夹 - 通过切换,"隐藏"了的路由组件,默认是被销毁的,需要的时候再去挂载
- 每个组件都有自己
$route
属性,里面存储自己的路由信息 - 整个应用只有一个router,可以通过组件的
$router
属性获取到
多级路由
-
配置路由规则,使用children配置项:
routers:[ { path:'/about', component: About, }, { path:'/home', component:Home, children:[ { path: 'news', component:News }, { path:'message', component:Message } ] } ]
-
跳转(要写完完整路径)
<router-link to="/home/news">News</router-link>
路由传参
-
传递参数
<!-- 第一种传参 --> <router-link :to="`/home/message/detail?id=${m.id}&title=${m.title}`"></router-link> <!-- 第二种对象形式传参 --> <router-link :to="{ path:'/home/message/detail', query:{ id: m.id, title: m.title }">{{m.title}}</router-link>
-
接受参数
$route.query.id $route.query.title
命名路由
-
作用:可以简化路由的跳转
-
如何使用
1)给路由命名
children:[ { path: 'news', component:News }, { name:'robber' path:'message', component:Message } ]
2)简化跳转
<!-- 简化前,需要些完整的路径 --> <router-link :to="`/home/message/detail`">跳转</router-link> <!--简化后,直接通过名字跳转--> <router-link :to="{name: robber}">跳转</router-link> <!-- 第二种对象形式传参 --> <router-link :to="{ name:'robber', query:{ id: m.id, title: m.title }">{{m.title}}</router-link>
路由的params参数
-
配置路由,声明接受params参数
children:[ { path: 'news', component:News }, { name:'messsage' path:'message', component:Message, children:[ { name:'robber', path: 'detail/:id/:title', component: Detail } ] } ]
-
传递参数
<!-- 跳转并携带params参数,to的字符串写法 --> <router-link :to="`/home/message/detail/666/你好呀`">跳转</router-link> <!--跳转并携带params参数,to的对象写法--> <router-link :to="{ name: 'robber', params:{ id:666, title: '你好' } }">跳转</router-link>
特别注意:路由携带params参数时,若使用to的对象写法,则不能使用path配置项,必须使用name配置
-
接受参数:
$route.params.id $route.params.title
props配置项
-
作用:让路由组件更方便的接受参数
{ name: 'robber', path: 'detail', components: 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 } } }
<router-link>
的replace属性
- 作用:控制路由跳转时操作浏览器例是记录的模式
- 浏览器的历史记录有两种写法:分别是
push
和replace
,push
时追加例是记录,replace
时替换当前记录,路由跳转时时默认为:push
- 如何开启
replace
模式:<router-link replace .....>News</route-link>
编程式路由导航
-
作用:可以不借助
<router-link>
实现路由跳转,让路由跳转更加灵活 -
具体编码:
//$router的两个API this.$router.push({ name: 'robber', query: { id: m.id, title: m.titlem } }) this.$router.replace({ name: 'robber', query: { id: m.id, title: m.title } }) this.$router.forward() //前进 this.$router.back() //后退 this.$router.go(1) //前进一步
缓存路由组件
-
作用:让不展示的路由组件保持挂载,不被销毁
-
具体编码:
<keep-alive include="News"> <router-view></router-view> </keep-alive>
特别注意:News是组件名(vue开发工具看到的那个)
两个新的声明周期钩子
-
作用:路由组件所独有的两个钩子,用于捕获路由组件的激活状态
-
具体名字:
1)
activated
路由组件被激活时触发2)
deactivated
路由组件失活时触发
路由守卫
全局守卫
-
作用:对路由进行权限控制
-
分类:全局守卫、独享守卫、组件内守卫
-
全局守卫:
//全局前置守卫,初始化执行、每次路由切换前执行 router.beforeEach((to,from,next)=>{ if(to.meta.isAuth){ if(localStorage.getItem('name') === 'robber'){ next() }else{ alert('暂无权限查看') } }else{ next() } }) //全局后置守卫,初始化执行,每次路由切换后执行 router.afterEach((to,from)=>{ if(to.meta.title){ document.title = to.meta.title }else{ document.title = 'vue_test' } })
独享路由
routes:[
{
path: '/home',
component: HomeCop
children:[],
beforeEnter(to,from,next){
if(to.meta.isAuth){
if(localStorage.getItem('name') === 'robber'){
next()
}else{
alert('暂无权限查看')
}
}else{
next()
}
}
}
]
组件内守卫
//进入守卫,通过路由规则,进入该组件时被调用
beforeRouteEnter(to, from, next){
....
}
//离开守卫,通过路由规则,离开该组件被调用
beforeRouteLeave(to, from, next){
....
}
区别各个守卫
1、全局路由:相当于紫禁成大门的守卫不管是【皇上、太子、妃子】都要进行检查
2、独享路由:相当于【皇上】的护卫
3、组件守卫:相当于进了【皇上寝宫】室内守卫(太监)
路由器的两种工作模式
-
对呀一个url来说,什么屎hash值?-------#/及其后面的内容就是hash值
-
hash值不会包括含在 HTTP 请求中,即:hash值不会带给服务器
-
hash模式:
1)地址中永远带着#号,不美观
2)若以后将地址通过第三方收集app分享,若app校验严格,则地址会被标记为不合法
3)兼容性较好。
-
history模式:
1)地址干净,美观
2)兼容性和hash模式相对略差
3)应用部署上线时需要后端人员支持,解决刷新页面服务器404的问题
七、Vue UI 组件库
移动端常用 UI 组件库
- Vant:https://youzan.github.io/vant
- Cube UI:https://didi.github.io/cube-ui
- Mini UI:http://mint-ui.github.io
- Nut UI:https://nutui.jd.com/#/(京东)
PC端常用 UI 组件库
- Element UI:https://element.eleme.cn
- IView UI:https://www.iwiewui.com
Element UI 全局使用
-
安装
npm i element-ui
-
引入
import Vue from 'vue' import App from './App.vue' import ElementUI from 'element-ui'; import 'element-ui/lib/theme-chalk/index.css'; Vue.config.productionTip = false Vue.use(ElementUI); export default new Vue({ render: h => h(App), beforeCreate() { Vue.prototype.$bus = this } }).$mount('#app')
Element UI 安需引入
-
修改
main.js
文件import Vue from 'vue' import App from './App.vue' import { Button } from 'element-ui'; Vue.config.productionTip = false // Vue.component(Button.name, Button); export default new Vue({ render: h => h(App), beforeCreate() { Vue.prototype.$bus = this } }).$mount('#app')
-
修改
babel.config.js
文件module.exports = { presets: [ '@vue/cli-plugin-babel/preset', ["@babel/preset-env", { "modules": false }] ], plugins: [ [ "component", { "libraryName": "element-ui", "styleLibraryName": "theme-chalk" } ] ] }
-
分类:全局守卫、独享守卫、组件内守卫
-
全局守卫:
//全局前置守卫,初始化执行、每次路由切换前执行 router.beforeEach((to,from,next)=>{ if(to.meta.isAuth){ if(localStorage.getItem('name') === 'robber'){ next() }else{ alert('暂无权限查看') } }else{ next() } }) //全局后置守卫,初始化执行,每次路由切换后执行 router.afterEach((to,from)=>{ if(to.meta.title){ document.title = to.meta.title }else{ document.title = 'vue_test' } })
独享路由
routes:[
{
path: '/home',
component: HomeCop
children:[],
beforeEnter(to,from,next){
if(to.meta.isAuth){
if(localStorage.getItem('name') === 'robber'){
next()
}else{
alert('暂无权限查看')
}
}else{
next()
}
}
}
]
组件内守卫
//进入守卫,通过路由规则,进入该组件时被调用
beforeRouteEnter(to, from, next){
....
}
//离开守卫,通过路由规则,离开该组件被调用
beforeRouteLeave(to, from, next){
....
}
区别各个守卫
1、全局路由:相当于紫禁成大门的守卫不管是【皇上、太子、妃子】都要进行检查
2、独享路由:相当于【皇上】的护卫
3、组件守卫:相当于进了【皇上寝宫】室内守卫(太监)
路由器的两种工作模式
-
对呀一个url来说,什么屎hash值?-------#/及其后面的内容就是hash值
-
hash值不会包括含在 HTTP 请求中,即:hash值不会带给服务器
-
hash模式:
1)地址中永远带着#号,不美观
2)若以后将地址通过第三方收集app分享,若app校验严格,则地址会被标记为不合法
3)兼容性较好。
-
history模式:
1)地址干净,美观
2)兼容性和hash模式相对略差
3)应用部署上线时需要后端人员支持,解决刷新页面服务器404的问题
七、Vue UI 组件库
移动端常用 UI 组件库
- Vant:https://youzan.github.io/vant
- Cube UI:https://didi.github.io/cube-ui
- Mini UI:http://mint-ui.github.io
- Nut UI:https://nutui.jd.com/#/(京东)
PC端常用 UI 组件库
- Element UI:https://element.eleme.cn
- IView UI:https://www.iwiewui.com
Element UI 全局使用
-
安装
npm i element-ui
-
引入
import Vue from 'vue' import App from './App.vue' import ElementUI from 'element-ui'; import 'element-ui/lib/theme-chalk/index.css'; Vue.config.productionTip = false Vue.use(ElementUI); export default new Vue({ render: h => h(App), beforeCreate() { Vue.prototype.$bus = this } }).$mount('#app')
Element UI 安需引入
-
修改
main.js
文件import Vue from 'vue' import App from './App.vue' import { Button } from 'element-ui'; Vue.config.productionTip = false // Vue.component(Button.name, Button); export default new Vue({ render: h => h(App), beforeCreate() { Vue.prototype.$bus = this } }).$mount('#app')
-
修改
babel.config.js
文件module.exports = { presets: [ '@vue/cli-plugin-babel/preset', ["@babel/preset-env", { "modules": false }] ], plugins: [ [ "component", { "libraryName": "element-ui", "styleLibraryName": "theme-chalk" } ] ] }