目录
1. 侦听器
1)watch侦听器
watch侦听器允许开发者监视数据的变化,从而针对数据的变化做特定的操作。
<body>
<div id="app">
<input type="text" v-model="username">
</div>
<script src="lib/vue-2.6.12.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
username: ''
},
watch: {
//侦听器本质上是一个函数,要监视哪个数据的变化,就把哪个数据名作为方法名即可
//监听username的变化
//newVal:变化后的新值,oldVal:变化之前的旧值
username(newVal, oldVal) {
console.log('监听到username发生了变化' + '\t'+ oldVal + '\t' + newVal);
}
}
})
</script>
</body>
结果为:
2)使用watch侦听器检测用户名是否可用
监听username值的变化,并使用axios发起Ajax请求,检测当前输入的用户名是否可用:
<body>
<div id="app">
<input type="text" v-model="username">
</div>
<script src="lib/vue-2.6.12.js"></script>
<script src="lib/jquery-v3.6.0.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
username: ''
},
watch: {
username(newVal) {
if(newVal === '') return
//使用axios发起请求,判断用户名是否可用
$.get('https://www.escook.cn/api/finduser/' + newVal, function(result) {
console.log(result);
})
}
}
})
</script>
</body>
结果为:
3)对象格式的侦听器:immediate选项
方法格式的侦听器:无法在刚进入页面的时候自动触发
对象格式的侦听器:可以通过immediate选项让侦听器自动触发
<body>
<div id="app">
<input type="text" v-model="username">
</div>
<script src="lib/vue-2.6.12.js"></script>
<script src="lib/jquery-v3.6.0.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
username: 'admin'
},
watch: {
//对象格式的侦听器
username: {
handler(newVal, oldVal) {
console.log(newVal + '\t' + oldVal);
},
// 通过immediate选项让侦听器自动触发
immediate: true
}
}
})
</script>
</body>
结果为:
5)对象格式的侦听器:deep选项
方法格式的侦听器:如果侦听的是一个对象,对象中的属性发生了变化时,不会触发侦听器
对象格式的侦听器:可以通过deep选项,让侦听器深度监听对象中每个属性的变化
<body>
<div id="app">
<input type="text" v-model="info.username">
</div>
<script src="lib/vue-2.6.12.js"></script>
<script src="lib/jquery-v3.6.0.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
info: {
username: 'admin'
}
},
watch: {
//对象格式的侦听器
info: {
handler(newVal) {
console.log(newVal);
},
// 通过deep选项深度监听对象的每个属性
deep: true
}
//如果要侦听的是对象的子属性,可以用单引号包裹
// 'info.username'(newVal) {
// console.log(newVal);
// }
}
})
</script>
</body>
结果为:
2. 计算属性
计算属性指的是通过一系列运算之后,最终得到一个属性值,这个动态计算出来的属性值可以被模版结构或methods方法使用。
- 被定义成方法的格式
- 在使用计算属性的时候,但普通的属性使用即可
- 实现了代码的复用
- 只要计算属性中依赖的数据源变化了则计算属性会自动重新求值
<script src="./lib/vue-2.6.12.js"></script>
<style>
.box {
width: 200px;
height: 200px;
border: 1px solid #ccc;
}
</style>
</head>
<body>
<div id="app">
<div>
<span>R:</span>
<input type="text" v-model.number="r">
</div>
<div>
<span>G:</span>
<input type="text" v-model.number="g">
</div>
<div>
<span>B:</span>
<input type="text" v-model.number="b">
</div>
<hr>
<!-- 专门用户呈现颜色的 div 盒子 -->
<!-- 动态绑定一个样式对象,键值对的形式 -->
<!-- <div class="box" :style="{ backgroundColor: `rgb(${r}, ${g}, ${b})` }">
{{ `rgb(${r}, ${g}, ${b})` }}
</div> -->
<div class="box" :style="{ backgroundColor:rgb }">
{{ rgb }}
</div>
<button @click="show">按钮</button>
</div>
<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
// 红色
r: 0,
// 绿色
g: 0,
// 蓝色
b: 0
},
methods: {
// 点击按钮,在终端显示最新的颜色
show() {
// console.log(`rgb(${this.r}, ${this.g}, ${this.b})`)
console.log(this.rgb);
}
},
// 计算属性都要放在computed节点下
computed: {
rgb() {
return `rgb(${this.r}, ${this.g}, ${this.b})`
}
}
});
</script>
</body>
结果为:
2. Ajax的基础用法
1)axios的基础语法
axios是一个专注于网络请求的库。
<body>
<script src="lib/axios.js"></script>
<script>
const result = axios({
method: 'GET',
url: 'http://www.liulongbin.top:3006/api/getbooks'
})
// 调用axios()方法得到的是Promise对象
console.log(result);
//then():请求成功后的回调函数
// 形参books:请求之后的结果
result.then(function(books) {
console.log(books);
console.log(books.data);
})
</script>
</body>
结果为:
1⃣️发起GET请求
<body>
<script src="lib/axios.js"></script>
<script>
axios({
method: 'GET',
url: 'http://www.liulongbin.top:3006/api/getbooks',
// 1.发起GET请求,URL中的查询参数
params: {
id: 1
}
}).then(function(books) {
console.log(books.data);
})
</script>
</body>
2⃣️发起POST请求
<body>
<script src="lib/axios.js"></script>
<script>
axios({
method: 'POST',
url: 'http://www.liulongbin.top:3006/api/getbooks',
// 2.发起POST请求,请求体参数
data: {
name: 'zs',
age: 20
}
}).then(function(books) {
console.log(books.data);
})
</script>
</body>
2)组合async和await调用axios
<body>
<button id="btnPost">发起post请求</button>
<script src="lib/axios.js"></script>
<script>
document.querySelector("#btnPost").addEventListener("click", async function() {
// axios()方法返回的事Promise对象
//如果调用某个方法的返回值是await,则前面就可以添加await
//await只能用在被 async 修饰的方法中
const res = await axios({
method: 'POST',
url: 'http://www.liulongbin.top:3006/api/post',
data: {
name: 'zs',
age: 20
}
})
console.log(res);
})
</script>
</body>
结果为:
3)使用解构赋值
<body>
<button id="btnPost">发起post请求</button>
<button id="btnGet">发起get请求</button>
<script src="lib/axios.js"></script>
<script>
document.querySelector("#btnPost").addEventListener("click", async function() {
// axios()方法返回的事Promise对象
//如果调用某个方法的返回值是await,则前面就可以添加await
//await只能用在被 async 修饰的方法中
const res = await axios({
method: 'POST',
url: 'http://www.liulongbin.top:3006/api/post',
data: {
name: 'zs',
age: 20
}
})
console.log(res);
})
document.querySelector("#btnGet").addEventListener("click", async function() {
// 解构赋值,进行重命名
//1.从axios封装的大对象中,把data属性解构出来
//2.把解构出来的data属性,使用 : 进行重命名,一般都是{ data : res }
const { data : res } = await axios({
method: 'GET',
url: 'http://www.liulongbin.top:3006/api/getbooks',
})
console.log(res);
console.log(res.data);
})
</script>
</body>
结果为:
4)基于axios.get和axios.post发起请求
<body>
<button id="btnGet">GET</button>
<button id="btnPost">POST</button>
<script src="lib/axios.js"></script>
<script>
document.querySelector("#btnGet").addEventListener("click", async function() {
// axios.get('url', { params:{GET请求体参数} })
const { data : res} = await axios.get('http://www.liulongbin.top:3006/api/getbooks', {
params: {
id: 1
}
})
console.log(res);
console.log(res.data);
})
document.querySelector("#btnPost").addEventListener("click", async function() {
// axios.post('url', { POST请求体参数 })
const { data : res } = await axios.post('http://www.liulongbin.top:3006/api/post', {
name: 'zs',
age: 20
})
console.log(res);
})
</script>
结果为:
4. vue-cli
1)单页面应用程序
单页面应用程序(Single Page Application)简称SPA,指的是一个Web网站中只有唯一的一个HTML页面,所有的功能与交互都在这唯一的一个页面内完成。
2)vue-cli
vue-cli是Vue.js开发的标准工具。它简化了程序员基于webpack创建工程化的Vue项目的过程。
官网:Vue CLI
全局安装:
npm install -g @vue/cli
初始化一个项目:
vue create demo-first
是否使用更快的淘宝镜像,选择 No.
C:\Users\Administrator\Desktop>vue create my_project
? Your connection to the default npm registry seems to be slow.
Use https://registry.npm.taobao.org for faster installation? No
使用 Vue create 创建项目是使用默认配置(包含babel, eslint)还是手动选择。
手动配置:上下键控制上下,空格进行确定/取消,a 全选。i 反选。
3)vue项目下src目录的构成
- assets文件夹:存放项目中用到的静态资源文件,例如css样式表、图片资源
- components文件夹:程序员封装的、可复用的组件
- main.js:是项目的入口文件,整个项目的运行要先执行main.js
- App.vue:是项目的根组件
4)vue项目的运行流程
在工程化的项目中,vue要做的事很单纯:通过main.js把App.vue渲染到index.html的指定区域中。其中:
- App.vue用来编写待渲染的模版结构
- index.html中需要预留一个el区域
- main.js把App.vue渲染到了index.html所预留的区域中
1⃣️main.js
// 导入vue包,得到Vue构造函数
import Vue from 'vue'
// 导入App.vue根组件,能够把App.vue中的模版结构渲染到index.html页面中
import App from './App.vue'
// import Test from './Test.vue'
Vue.config.productionTip = false
// 创建Vue的实例对象
new Vue({
// 把render函数指定的组件,渲染到HTML页面上
render: h => h(App),
// render: h => h(Test)
}).$mount('#app')
// $mount():和el属性一样
2⃣️App.vue
<template>
<!-- 会占领index.html中<div id="app"></div>的位置 -->
<div>
<h1>App.vue组件</h1>
<p>aaa</p>
<p>bbb</p>
</div>
</template>
3⃣️index.html
<!DOCTYPE html>
<html lang="">
<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">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
结果为:
4. vue组件
1)组件化开发
根据封装的思想,把页面上可重用的UI结构封装为组件,从而方便项目的开发和维护。
vue是一个支持组件化开发的前端框架。
vue中规定:组件的后缀名是.vue。eg.App.vue文件本质上就是一个vue组件。
2).vue组件的三个组成部分
- template -> 组件的模版结构,只能有一个根节点
- script -> 组件的JavaScript行为
- style -> 组件的样式,嵌套则<style lang="less"></style>
<template>
<!-- 会占领index.html中<div id="app"></div>的位置 -->
<div>
<h1>App.vue组件-- {{ username }}</h1>
<p>aaa</p>
<p>bbb</p>
</div>
</template>
<script>
export default {
// .vue中的data不能像之前一样指向对象
// setup() {
// data: {
// username: 'zs'
// }
// },
//组件中的data必须是一个函数
data() {
// return{}:可以定义数据
return {
username: 'zs'
}
}
}
</script>
<style>
div {
background-color: pink;
}
</style>
结果为:
3)在组件中定义methods方法
<template>
<!-- 会占领index.html中<div id="app"></div>的位置 -->
<div>
<h1>App.vue组件-- {{ username }}</h1>
<p>aaa</p>
<p>bbb</p>
<button @click="changeName">修改用户名</button>
</div>
</template>
<script>
export default {
// .vue中的data不能像之前一样指向对象
// setup() {
// data: {
// username: 'zs'
// }
// },
//组件中的data必须是一个函数
data() {
// return{}:可以定义数据
return {
username: 'zs'
}
},
methods: {
changeName() {
console.log(this);
this.username = 'ls'
}
}
}
</script>
<style>
div {
background-color: pink;
}
</style>
结果为:
4)组件之间的父子关系
5)使用组件的三个步骤
- 使用import语法导入需要的组件
- 使用components节点注册组件
- 以标签形式使用刚才注册的组件
<template>
<div class="app-container">
<h1>App 根组件</h1>
<hr />
<div class="box">
<!-- 渲染 Left 组件和 Right 组件 -->
<!-- 3. 以标签形式使用刚才注册的组件 -->
<Left></Left>
<Right></Right>
</div>
</div>
</template>
<script>
// 1.导入需要使用的.vue组件
import Left from '@/components/Left.vue'
import Right from '@/components/Right.vue'
export default {
// 2.使用components节点注册组件
components: {
// Left: 'Left'
Left,
Right
}
}
</script>
<style lang="less">
.app-container {
padding: 1px 20px 20px;
background-color: #efefef;
}
.box {
display: flex;
}
</style>
结果为:
1⃣️通过components注册的是私有子组件
例如:在组件A的components节点下,注册了组件F,则组件F只能用在组件A中,不能被用在组件C中。
如果要在组件C中使用组件F,则应该在components节点下注册组件F。
2⃣️注册全局组件
在vue项目的main.js入口文件中,通过Vue.component()方法,可以注册全局组件。
//导入需要全局注册的组件
import Count from '@/components/Count.vue'
//参数1:字符串格式,表示组件的注册名称 参数2:需要被全局注册的那个组件
Vue.component('MyCount', Count)
6)组件的props
props是组件的自定义属性,在封装通用组件的时候,合理地使用props可以极大地提高组件的复用性。
1⃣️结合v-bind使用自定义属性
<!-- init="6":字符串 -> :init="6":js形式,数字 -->
<MyCount :init="6"></MyCount>
2⃣️props是只读的
vue规定:组件中封装的自定义属性是只读的,程序员不能直接修改props的值,否则会直接报错。
想要修改props的值,可以把props的值转存到data中,因为data的值都是可读可写的。
// props是只读的,因此不能直接修改props
props: ['init'],
data() {
return{
count: this.init
}
}
3⃣️props的default默认值
在声明自定义属性时,可以通过default来定义属性的默认值。
export default {
//props:自定义属性,允许使用者通过自定义属性为当前组件指定初始值
// props是只读的,因此不能直接修改props
// props: ['init'],
props: {
init: {
// default来定义属性的默认值
// 如果外界使用Count组件的时候,没有传递init属性,则默认值生效
default: 0
}
}
}
4⃣️props的type值类型
在声明自定义属性时,可以通过type来定义属性的值类型。
export default {
//props:自定义属性,允许使用者通过自定义属性为当前组件指定初始值
// props是只读的,因此不能直接修改props
// props: ['init'],
props: {
init: {
// default来定义属性的默认值
// 如果外界使用Count组件的时候,没有传递init属性,则默认值生效
default: 0,
// type来定义属性的值类型
// 如果传递过来的值不符合此类型,则会在终端报错
type: Number
}
}
}
5⃣️props的required必填项
在声明自定义属性时,可以通过required选项,将属性设置为必填项,强制用户必须传递属性的值。
export default {
//props:自定义属性,允许使用者通过自定义属性为当前组件指定初始值
// props是只读的,因此不能直接修改props
// props: ['init'],
props: {
init: {
// default来定义属性的默认值
// 如果外界使用Count组件的时候,没有传递init属性,则默认值生效
default: 0,
// type来定义属性的值类型
// 如果传递过来的值不符合此类型,则会在终端报错
type: Number,
// 必选项校验
required: true
}
}
}
7) 组件之间的样式冲突问题
默认情况下,写在.vue组件中的样式会全局生效,因此很容易造成多个组件之间的样式冲突问题。
导致组件之间样式冲突的根本原因是:
- 单页面应用程序中,所有组件的DOM结构,都是基于唯一的index.html页面进行呈现的
- 每个组件的样式,都会影响整个index.html页面中的DOM元素
1⃣️解决样式冲突 -> scoped属性
- 给当前组件内的元素都加上data-v-0*属性,不同组件的元素不一样,这样就可以解决样式冲突。
- 给style加上scoped属性:用来自动为每个组件分配唯一的自定义属性,并自动为当前组件的DOM标签和style样式应用这个自定义属性,防止组件的样式冲突问题
<style lang="less" scoped>
2⃣️/deep/样式穿透
如果当前组件的style节点添加了scoped属性,则当前组件的样式对其子组件是不生效的。如果想让某些样式对子组件生效,可以使用/deep/深度选择器。
<style lang="less" scoped>
h3 {
color: red;
}
// 不使用:h5[data-v-3c83f0b7]
// 使用之后变成:[data-v-3c83f0b7] h5
// 当使用第三方组件库的时候,如果要修改第三方组件库的默认样式,则需要用到/deep/
// h5是Count.vue组件库的
/deep/ h5 {
color: pink;
}
</style>