watch 侦听器允许开发者监视数据的变化
,从而针对数据的变化做特定的操作。
语法格式如下:
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>Documenttitle>
head>
<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: 'ggg'
},
watch: {
username(newVal, oldVal) {
console.log('监听到了username值的变化', newVal, oldVal)
}
}
})
script>
body>
html>
监听 username 值的变化,并使用 axios 发起 Ajax 请求,检测当前输入的用户名是否可用:
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>Documenttitle>
head>
<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 节点下
watch: {
// 侦听器本质上是一个函数,要监视哪个数据的变化,就把数据名作为方法名即可
// 新值在前,旧值在后
username(newVal) {
if (newVal === '') return
// 1. 调用 jQuery 中的 Ajax 发起请求,判断 newVal 是否被占用!!!
$.get('https://www.escook.cn/api/finduser/' + newVal, function (result) {
console.log(result)
})
}
}
})
script>
body>
html>
默认情况下,组件在初次加载完毕后不会调用 watch 侦听器。如果想让 watch 侦听器立即被调用,则需要使用 immediate 选项
。示例代码如下:
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>Documenttitle>
head>
<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 节点下
watch: {
// 定义对象格式的侦听器
username: {
// 侦听器的处理函数
handler(newVal, oldVal) {
console.log(newVal, oldVal)
},
// immediate 选项的默认值是 false
// immediate 的作用是:控制侦听器是否自动触发一次!
immediate: true
}
}
})
script>
body>
html>
如果 watch 侦听的是一个对象,如果对象中的属性值发生了变化,则无法被监听到。此时需要使用 deep 选 项
,代码示例如下:
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>Documenttitle>
head>
<body>
<div id="app">
<input type="text" v-model="info.username">
<input type="text" v-model="info.address.city">
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',
address: {
city: '北京'
}
}
},
// 所有的侦听器,都应该被定义到 watch 节点下
watch: {
info: {
handler(newVal) {
console.log(newVal)
},
// 开启深度监听,只要对象中任何一个属性变化了,都会触发“对象的侦听器”
deep: true
}
}
})
script>
body>
html>
如果只想监听对象中单个属性的变化
,则可以按照如下的方式定义 watch 侦听器:
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>Documenttitle>
head>
<body>
<div id="app">
<input type="text" v-model="info.username">
<input type="text" v-model="info.address.city">
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',
address: {
city: '北京'
}
}
},
// 所有的侦听器,都应该被定义到 watch 节点下
watch: {
// 如果要侦听的是对象的子属性的变化,则必须包裹一层单引号
'info.username'(newVal) {
console.log(newVal)
}
}
})
script>
body>
html>
计算属性指的是通过一系列运算
之后,最终得到一个属性值。
这个动态计算出来的属性值可以被模板结构或
methods 方法`使用。示例代码如下:
未使用计算属性的代码:
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Documenttitle>
<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 class="box" :style="{ backgroundColor: `rgb(${r}, ${g}, ${b})` }">
{{ `rgb(${r}, ${g}, ${b})` }}
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})`)
}
},
});
script>
body>
html>
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Documenttitle>
<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 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(this.rgb)
}
},
// 所有的计算属性,都要定义到 computed 节点之下
// 计算属性在定义的时候,要定义成“方法格式”
computed: {
// rgb 作为一个计算属性,被定义成了方法格式,
// 最终,在这个方法中,要返回一个生成好的 rgb(x,x,x) 的字符串
rgb() {
return `rgb(${this.r}, ${this.g}, ${this.b})`
}
}
});
console.log(vm)
script>
body>
html>
① 虽然计算属性在声明的时候
被定义为方法
,但是计算属性的本质是一个属性
② 计算属性会缓存计算的结果
,只有计算属性依赖的数据变化时
,才会重新进行运算
axios (发音:艾克c奥斯)是一个专注于网络请求
的库!
中文官网地址:http://www.axios-js.com/
英文官网地址:https://www.npmjs.com/package/axios
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>Documenttitle>
head>
<body>
<script src="./lib/axios.js">script>
<script>
// http://www.liulongbin.top:3006/api/getbooks
// 1. 调用 axios 方法得到的返回值是 Promise 对象
const result = axios({
// 请求方式
method: 'GET',
// 请求的地址
url: 'http://www.liulongbin.top:3006/api/getbooks',
})
result.then(function (books) {
console.log(books.data)
})
script>
body>
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>Documenttitle>
head>
<body>
<button id="btnPost">发起POST请求button>
<script src="./lib/axios.js">script>
<script>
document.querySelector("#btnPost").addEventListener('click',async function () {
// 如果调用某个方法的返回值是 Promise 实例,则前面可以添加 await!返回值是实际的对象
// await 只能用在被 async “修饰”的方法中
const result = await axios({
method: 'POST',
url: 'http://www.liulongbin.top:3006/api/post',
data: {
name: 'zs',
age: 20
}
})
console.log(result)
})
script>
body>
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>Documenttitle>
head>
<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 () {
// 如果调用某个方法的返回值是 Promise 实例,则前面可以添加 await!
// await 只能用在被 async “修饰”的方法中
const { data } = await axios({
method: 'POST',
url: 'http://www.liulongbin.top:3006/api/post',
data: {
name: 'zs',
age: 20
}
})
console.log(data)
})
document.querySelector('#btnGet').addEventListener('click', async function () {
// 解构赋值的时候,使用 : 进行重命名
// 1. 调用 axios 之后,使用 async/await 进行简化
// 2. 使用解构赋值,从 axios 封装的大对象中,把 data 属性解构出来
// 3. 把解构出来的 data 属性,使用 冒号 进行重命名,一般都重命名为 { data: res }
const { data: res } = await axios({
method: 'GET',
url: 'http://www.liulongbin.top:3006/api/getbooks'
})
console.log(res.data)
})
// $.ajax() $.get() $.post()
// axios() axios.get() axios.post() axios.delete() axios.put()
script>
body>
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>Documenttitle>
head>
<body>
<button id="btnGET">GETbutton>
<button id="btnPOST">POSTbutton>
<script src="./lib/axios.js">script>
<script>
document.querySelector('#btnGET').addEventListener('click', async function () {
/* axios.get('url地址', {
// GET 参数
params: {}
}) */
const { data: res } = await axios.get('http://www.liulongbin.top:3006/api/getbooks', {
params: { id: 1 }
})
console.log(res)
})
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', gender: '女' })
console.log(res)
})
script>
body>
html>
单页面应用程序
(英文名:Single Page Application)简称 SPA
,顾名思义,指的是一个 Web 网站中只有唯一的一个 HTML 页面
,所有的功能与交互都在这唯一的一个页面内完成。
例如资料中的这个 Demo 项目:
vue-cli 是 Vue.js 开发的标准工具。它简化了程序员基于 webpack 创建工程化的 Vue 项目的过程。
引用自 vue-cli 官网上的一句话:
程序员可以专注在撰写应用上,而不必花好几天去纠结 webpack 配置的问题。
中文官网:https://cli.vuejs.org/zh/
vue-cli 是 npm 上的一个全局包
,使用 npm install
命令,即可方便的把它安装到自己的电脑上:npm install -g @vue/cli
出现的问题:npm安装时卡在
sill idealTree buildDeps
,npm安装速度慢,npm安装卡在一个地方不动
解决的链接:http://t.csdn.cn/wK740
基于 vue-cli
快速生成工程化
的 Vue 项目:vue create 项目的名称
项目的名称最好使用英文
建立vue项目之前:
① 先确定要建立项目的目录
(文件的存储位置),
② 打开当前文件夹,在目录里面直接输入cmd
③vue create 项目的名称
按enter键,选择项目的样式,初学者选择第三项
让程序员自己选择安装的功能
按enter
按 enter
assets 文件夹:存放项目中用到的静态资源文件,例如:css 样式表、图片资源
components 文件夹:程序员封装的、可复用的组件,都要放到 components 目录下
main.js 是`项目的入口文件`。整个项目的运行,要先执行 main.js
App.vue 是项目的根组件,定义ui结构
在工程化的项目中,vue 要做的事情很单纯:通过 main.js
把 App.vue
渲染到 index.html 的指定区域
中。
其中:
① App.vue
用来编写待渲染的模板结构
② index.html
中需要预留一个 el 区域
③ main.js 把 App.vue 渲染到了 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>
body>
html>
aaa
bbb
ccc
// 导入 vue 这个包,得到 Vue 构造函数
import Vue from 'vue'
// 导入 App.vue 根组件,将来要把 App.vue 中的模板结构,渲染到 HTML 页面中
// import App from './App.vue'
import Test from './Test.vue'
Vue.config.productionTip = false
// 创建 Vue 的实例对象
new Vue({
// 把 render 函数指定的组件,渲染到 HTML 页面中
render: h => h(Test)
}).$mount('#app')
// Vue 实例的 $mount() 方法,作用和 el 属性完全一样!
组件化开发指的是:根据封装
的思想,把页面上可重用的 UI 结构封装为组件
,从而方便项目的开发和维护。
vue 是一个支持组件化开发的前端框架
。
vue 中规定:组件的后缀名是 .vue
。之前接触到的 App.vue 文件本质上就是一个 vue 的组件。
每个 .vue 组件都由 3 部分构成,分别是:
必须包含 template 模板结构
,而 script 行为和 style 样式是可选的组成部分。<template>
<div>
<div class="test-box">
<h3>这是用户自定义的 Test.vue --- {{ username }}</h3>
</div>
<div>123</div>
</div>
</template>
<script>
// 默认导出。这是固定写法!
export default {
// data 数据源
// 注意:.vue 组件中的 data 不能像之前一样,不能指向对象。
// 注意:组件中的 data 必须是一个函数
data() {
// 这个 return 出去的 { } 对象中,可以定义数据
return {
username: 'admin'
}
},
}
</script>
<style >
background-color: pink;
</style>
注意:
容器标签
,只起到包裹性质的作用
,它不会被渲染为真正的 DOM 元素只能包含唯一的根节点
(只能有一个div)vue 规定:开发者可以在 节点中
封装组件的 JavaScript 业务逻辑。
节点的基本结构如下:
.vue 组件中的 data 必须是函数
vue 规定:.vue 组件中的 data 必须是一个函数
,不能
直接指向一个数据对象。
因此在组件中定义 data 数据节点时,下面的方式是错误的:
会导致多个组件实例
共用同一份数据
的问题,请参考官方给出的示例:
https://cn.vuejs.org/v2/guide/components.html#data-必须是一个函数
vue 规定:组件内的 节点是
可选的
,开发者可以在 节点中
编写样式美化当前组件的 UI 结构
。
节点的基本结构如下:
让style 中支持 less 语法
在 标签上添加
lang="less"
属性,即可使用 less 语法编写组件的样式:
<template>
<div>
<div class="test-box">
<h3>这是用户自定义的 Test.vue --- {{ username }}</h3>
<button @click="chagneName">修改用户名</button>
</div>
<div>123</div>
</div>
</template>
<script>
// 默认导出。这是固定写法!
export default {
// data 数据源
// 注意:.vue 组件中的 data 不能像之前一样,不能指向对象。
// 注意:组件中的 data 必须是一个函数
data() {
// 这个 return 出去的 { } 对象中,可以定义数据
return {
username: 'admin'
}
},
methods: {
chagneName() {
// 在组件中, this 就表示当前组件的实例对象
console.log(this)
this.username = '哇哈哈'
}
},
// 当前组件中的侦听器
watch: {},
// 当前组件中的计算属性
computed: {},
// 当前组件中的过滤器
filters: {}
}
</script>
<style lang="less">
.test-box {
background-color: pink;
h3 {
color: red;
}
}
</style>
组件在被封装好之后,彼此之间是相互独立的
,不存在父子关系
在使用组件的时候
,根据彼此的嵌套关系
,形成了父子关系
、兄弟关系
<template>
<div class="app-container">
<h1>App 根组件</h1>
<button @click="flag = !flag">Toggle Flag</button>
<Test info="你好" v-if="flag"></Test>
<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'
import Test from '@/components/Test.vue'
export default {
data() {
return {
flag: true
}
},
// 2. 注册组件
components: {
Left,
Right,
Test
}
}
</script>
<style lang="less">
.app-container {
padding: 1px 20px 20px;
background-color: #efefef;
}
.box {
display: flex;
}
</style>
例如:
在组件 A
的 components 节点下,注册了组件 F
。
则组件 F 只能用在组件 A 中;不能被用在组件 C
中。
全局组件
在 vue 项目的 main.js
入口文件中,通过 Vue.component()
方法,可以注册全局组件。示例代码如下:
// main.js文件
import Vue from 'vue'
import App from './App.vue'
// 导入需要被全局注册的那个组件
import Count from '@/components/Count.vue'
Vue.component('MyCount', Count)
Vue.config.productionTip = false
new Vue({
// render 函数中,渲染的是哪个 .vue 组件,那么这个组件就叫做 “根组件”
render: h => h(App)
}).$mount('#app')
props 是组件的自定义属性
,在封装通用组件
的时候,合理地使用 props 可以极大的提高组件的复用性
!
它的语法格式如下:
vue 规定:组件中封装的自定义属性是只读的
,程序员不能直接修改
props 的值。否则会直接报错:
要想修改 props 的值,可以把 props 的值转存到 data 中
,因为 data 中的数据都是可读可写的!
default
默认值在声明自定义属性时,可以通过 default
来定义属性的默认值
。示例代码如下:
type
值类型在声明自定义属性时,可以通过 type
来定义属性的值类型
。示例代码如下:
required
必填项在声明自定义属性时,可以通过 required
选项,将属性设置为必填项
,强制用户必须传递属性init的值。示例代码如下:
默认情况下,写在 .vue 组件中的样式会全局生效
,因此很容易造成多个组件之间的样式冲突问题。
导致组件之间样式冲突的根本原因是:
① 单页面应用程序中,所有组件的 DOM 结构,都是基于唯一的 index.html 页面进行呈现的
② 每个组件中的样式,都会影响整个 index.html 页面
中的 DOM 元素
为每个组件分配唯一的自定义属性
,在编写组件样式时,通过属性选择器
来控制样式的作用域
,示例代码如下:
为了提高开发效率和开发体验,vue 为 style 节点
提供了 scoped
属性,从而防止组件之间的样式冲突问题:
/deep/
样式穿透如果给当前组件的 style 节点添加了 scoped 属性,则当前组件的样式对其子组件是不生效的
。如果想让某些样式对子组件生效,可以使用 /deep/ 深度选择器
<template>
<div class="left-container">
<h3>Left 组件</h3>
<hr />
<MyCount :init="9"></MyCount>
</div>
</template>
<script>
export default {}
</script>
<style lang="less" scoped>
.left-container {
padding: 0 20px 20px;
background-color: orange;
min-height: 250px;
flex: 1;
}
h3 {
color: red;
}
// h5[data-v-3c83f0b7]
// [data-v-3c83f0b7] h5
// 当使用第三方组件库的时候,如果有修改第三方组件默认样式的需求,需要用到 /deep/
/deep/ h5 {
color: pink;
}
</style>
当使用第三方组件库的时候,如果有修改第三方组件默认样式的需求,需要用到
/deep/