Day03_Vue基础入门
今日学习内容:
- 侦听器
- 计算属性
- vue-cli
- vue组件
1、watch侦听器
1.1、什么是侦听器
watch侦听器允许开发者检测数据的变化,从而针对数据的变化做特定的操作。
声明格式如下:
const vm = new Vue({
el:"#app", //此处的#app可以看做是css中选择器的命名方式,如果为p则vue会渲染第一个p元素
data:{
message:"hello vue.js",
info:"title info"
},
watch:{
//监听info数据的变化
//newVal是变化后的新值,oldVal是变化前的新值
info(newVal,oldVal){
console.log(newVal,oldVal)
}
}
})
用处实例:可以使用watch检测用户名是否可用
监听username值的变化,并且使用axios发起Ajax请求,检测当前输入的用户名是否可用
watch: {
// 监听 username 值的变化
async username(newVal) {
if (newVal === '') return
// 使用 axios 发起请求,判断用户名是否可用
const { data: res } = await axios.get('https://www.escook.cn/api/finduser/' + newVal)
console.log(res)
return res
}
}
1.2、immediate选项
默认情况下,组件会在初次加载完毕后不会调用watch侦听器,如果想让watch侦听器立刻被调用,则需要使用immediate选项。
watch:{
username:{
//handler是固定写法,表示当前username的值变化是,自动调用handler处理函数
handler:async function(newVal){
if(newVal === "") return
const{data:res}= await axios.get('https://www.escook.cn/api/finduser/' + newVal)
console.log(res)
},
//表示页面初次渲染好之后,就立即触发当前watch侦听器
immediate:true
}
}
1.3、deep选项???
如果watch侦听的是一个对象,如果对象中的属性值发生了变化,则无法被监听到。此时需要使用deep选项。
watch:{
username:{
//handler是固定写法,表示当前username的值变化是,自动调用handler处理函数
handler:async function(newVal){
if(newVal === "") return
const{data:res}= await axios.get('https://www.escook.cn/api/finduser/' + newVal)
console.log(res)
},
//表示页面初次渲染好之后,就立即触发当前watch侦听器
deep:true
}
}
1.4、监听对象单个属性的变化
适用于监听对象中单个属性的变化。
const vm = new Vue({
el:"#app", //此处的#app可以看做是css中选择器的命名方式,如果为p则vue会渲染第一个p元素
data:{
message:"hello vue.js",
info:"title info"
},
watch:{
//监听info数据的变化
//newVal是变化后的新值,oldVal是变化前的新值
'info.username':{
handler(newVal,oldVal){
console.log(newVal);
}
}
}
})
2、计算属性
2.1、什么是计算属性
计算属性是指通过一系列运算之后,最终得到一个属性值。
这个动态计算出来的属性可以被模板结构或methods方法使用。
var vm = new Vue({
el:"#app",
data:{
r:0
},
computed:{
rs(){
return this.r++;
}
},
methods:{
rsmethod(){
this.r++;
}
}
})
2.2、计算属性的特点
- 虽然计算属性在声明的时候会被定义成方法,但是计算属性的本质是一个属性。
- 计算属性会缓存计算结果,只有计算属性依赖的数据变化时,才会重新进行运算
- 缓存计算结果是指,当再次调用计算属性的结果时,可以直接调用结果,而不用进行再次计算。
计算属性、方法、监听的区别
- 计算属性(computed):一定要返回一个值,属性的结果会被缓存,除非依赖的数据属性变化才会重新计算,主要当做属性来使用,适合计算的逻辑
- 方法(methods):表示一个具体的操作,主要书写业务逻辑
- 监听(watch):一个对象,键是需要观察的数据属性名,值是对应回调函数。主要用来监听某些特定数据的变化,从而进行某些具体的业务逻辑操作;可以看做 computed 和 methods 的结合体
总结:watch用的相对来说比较少,重点放到计算属性和方法上
3、vue-cli的使用
3.1、什么是单页面应用程序?
单页面应用程序简称SPA,指的是一个Web网站中只有唯一的一个HTML页面,所有的功能与交互都是在这文艺的一个页面内完成。
3.2、什么是vue-cli?
vue-cli是一个Vue.js开发的标准工具,它简化了程序员基于webpack创建工程化的vue项目过程。
使用此工具程序员可以专注在专业应用上,而不必花好几天去纠结webpack配置的问题。
3.3、vue项目的创建与使用
在运行vue项目之前需要先创建一个vue项目,在命令行通过vue create XXX
可以创建一个XXX项目。
在工程化的项目中,vue想要做的很单纯:通过main.js吧App.vue渲染到index.html指定的区域中。
其中:
- App.vue用来编写待渲染的模板结构。
- index.html中需要预留一个el区域。
- main.js把App.vue渲染到index.html所预留的区域中。
vue项目的目录结构
- node_modules
- public
- favicon.ico
- index.html(build后产生一个.js文件,此文件可调用)
- src
- assest:存放项目中静态的资源文件
- components:程序员封装的可复用自建都放在这里
- main.js :项目的入口文件,所有项目优先执行的项目
- App.vue:项目的根组件
- babel.config.js
- package.json
- package-lock.json
- README.md
main.js
4、vue组件
4.1、什么是组件化开发?
组件化开发指的是:根据 封装的思想,把页面上可重用的UI结构封装为组件,从而方便项目的开发与维护。方便代码的重用。
4.2、vue中的组件化开发
vue是一个支持组件化开发的框架。
vue中规定:组件的后缀名是==.vue==。之前接触到的App.vue文件实质上就是一个vue组件。
每个.vue组件分为三个组成部分:
- template:组件的 模板结构、
- script:组件的 JavaScript行为
- style:组件的 样式
其中,template是每个组件都必须要包含的内容,js行为和css样式是可选的部分。
template:
vue规定:每个组件对应的模板结构,都要定义到template中。
<template>
当前组件的DOM结构,需要定义到template标签内部
</template>
注意:
- template是vue提供的容器标签,只起到包裹性质的作用,不会被渲染成真正的DOM元素。
- template中只能包含唯一的root节点。
script:
vue规定:开发者可以在
<script>
//组件相关的data数据、methods方法等都要定义到export default中,只有向外暴露了此js逻辑,代码才能够被其他代码调用。
export default {}
</script>
注意:
-
vue规定:.vue组件中的data必须是函数,不能直接指向一个数据对象。
应为如果data作为一个函数,返回一个对象,那么每个template实例可以维护一份返回的对象的独立拷贝,如果返回的是一个数据对象,则会影响所有的data数据。
//错误案例,会导致多个组件实例共用一份数据 data:{ count:0 } //正确案例 data(){ return { count:0, } }
style
vue 规定:组件内的<style>节点是可选的开发者可以在其中编写样式美化当前组件的UI结构。将style的lang属性设置为"less",就可以使用less语法编写组件。
<style>
h1{
font-weight:normal
}
</style>
4.3、组件间的父子关系
当组件被封装好之后,彼此之间是相互独立的,不存在父子关系。如下:
在使用组件的时候,根据彼此嵌套关系,形成了父子关系、兄弟关系。如下:
使用组件的三个步骤
- 使用import语法导入需要的组件
- 使用components节点注册自建
- 以标签形式使用组件
<script>
//导入组件
import Left from "./components/Left.vue"
//使用components节点注册组件
export default {
components:{
//完成的注册形式为:Left:Left,
Left //由于组件名和导入的组件名重名所以可以简写成这样
}
}
</script>
通过components注册的是私有子组件
例如:
在组件A中的components节点下,注册了组件F。则F组件只能用在组件A中,不能用于组件C。
因为若是 组件A布局注册了组件F那么组件F只能作为组件A的一部分存在,也就是template F 只能作为 template A中的一个<F>。
注册全局组件
//导入需要注册的全局组件
import Count from '@/components/Count.vue'
//arg1表示将要注册的组件名
//arg2表示需要全局祖册的那个组件
Vue.component("MyCount",Count)
4.4、组件的 props
props是组件的 自定义属性,在封装通用组件的时候,合理地使用props可以大大提高组件的复用性。当一个值被传递给prop 属性是,他就变成了那个组件实例的一个属性。
当我们申请注册一个全局component时,可以为其做声明:
//组件的使用
<blog-post title="My journey with Vue"></blog-post>
<blog-post title="Blogging with Vue"></blog-post>
<blog-post title="Why Vue is so fun"></blog-post>
//声明组件
Vue.compoent('blog-post',{
props:['title'],
template:'<h3>{{title}}</h3>'
})
在更常见的应用中,我们往往见到的是一个props数组对象
//使用
<blog-post
v-for="post in posts"
v-bind:key="post.id"
v-bind:title="post.title"
></blog-post>
//组件声明
var vm =new Vue({
el: '#blog-post-demo',
data: {
posts: [
{ id: 1, title: 'My journey with Vue' },
{ id: 2, title: 'Blogging with Vue' },
{ id: 3, title: 'Why Vue is so fun' }
]
}
})
vue规定:组件中封装的自定义属性都是只读的,程序员不能直接修改props的值,否则会直接报错。
如果想要修改props的值,需要把props的值传到data中,因为data中的数据都是可读可写的。
export default {
name:"",
props:['init']
data(){
return {
count:this.init;
}
}
}
Prop的大小写(camelCase vs kebab-case)
HTML 中的attribute名大小写是不敏感的,所以浏览器会把所有大写字符解释为小写字符,这意味着所有的驼峰命名法都可以通过短横线分隔命名:
<!-- 在 HTML 中是 kebab-case 的 -->
<blog-post post-title="hello!"></blog-post>
//在js中的camelCase(驼峰命名法)
Vue.component('blog-post', {
// 在 JavaScript 中是 camelCase 的
props: ['postTitle'],
template: '<h3>{{ postTitle }}</h3>'
})
props常用的方式
以字符串列出prop
props: ['title', 'likes', 'isPublished', 'commentIds', 'author']
指定传递的prop类型
props: {
title: String,
likes: Number,
isPublished: Boolean,
commentIds: Array,
author: Object,
callback: Function,
contactsPromise: Promise // or any other constructor
}
prop动态赋值
<!-- 动态赋予一个变量的值 -->
<blog-post v-bind:title="post.title"></blog-post>
<!-- 动态赋予一个复杂表达式的值 -->
<blog-post
v-bind:title="post.title + ' by ' + post.author.name"
></blog-post>
Prop验证***
Vue.component('my-component', {
props: {
// 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
propA: Number,
propB: [String, Number], // 多个可能的类型
propC: {
type: String,
required: true // 必填的字符串
},
// 带有默认值的数字
propD: {
type: Number,
default: 100
},
// 带有默认值的对象
propE: {
type: Object,
// 对象或数组默认值必须从一个工厂函数获取
default: function () {
return { message: 'hello' }
}
},
// 自定义验证函数
propF: {
validator: function (value) {
// 这个值必须匹配下列字符串中的一个
return ['success', 'warning', 'danger'].indexOf(value) !== -1
}
}
}
})
4.5、组件之间的样式冲突问题
默认情况下,写在.vue组件中的样式会全局生效,因此很容易造成多个组件之间的样式冲突。
导致组件之间样式冲突的根本问题是:
- 单页面应用程序中,所有组件的DOM结构,都是基于唯一index.html文件进行呈现的。
- 每个组件中的样式,都会影响整个index.html页面中的dom元素。
两种解决方式
- 为每个组件分配唯一的自定义属性,在编写组件样式时,通过属性选择器来控制样式的作用域,
- 为了提高开发效率和开发体验,vue 为 style 节点提供了 scoped 属性,从而防止组件之间的样式冲突问题。
第二种代码演示
<template>
<div class="container">
<h3>
轮播图组件
</h3>
</div>
</template>
<style scoped>
.container{
border:1px solid red;
}
</style>
如果给当前组件的 style 节点添加了 scoped 属性,则当前组件的样式对其子组件是不生效的。如果想让某些样
式对子组件生效,可以使用 /deep/ 深度选择器。
<style scoped>
.container{
border:1px solid red;
}
/deep/ .container{
border:1px solid red;
}
</style>
5、axios
axios是前端圈最火的、专注于数据请求的库。
5.1、基础使用
//axios请求返回的是Promise对象
axios({
method:"请求的方式",
url:"请求的地址",
data:{} //URL中的请求体参数,POST时用到的请求
params:{}//URL的查询参数,GET 用到的数据
})
请求过程:
注意:如果调用摸个方法的返回值是Promise实例,前面可以添加await! awati只能用在async修饰的方法中
axios方法的调用一般步骤
- 调用axios后使用async/await进行简化
- 使用解构后,从axios封装的大对象中把data属性解构出来
- 把解构的数据进行重命名
常见的方法调用方式
async getInfo(){
const {data:res} =await axios({ //此处用到了数据的解构
method:"GET",
url:'http://www.liulongbin.to:3006/api/post'
data:{
name:'zs',
age:20
}
})
}
axios的简化使用
axios.get("url地址",{
params:{
数据体
}
})
axios.post("url地址",{
数据体
})
求的地址",
data:{} //URL中的请求体参数,POST时用到的请求
params:{}//URL的查询参数,GET 用到的数据
})
请求过程:
[外链图片转存中...(img-Qv555z8A-1645812500951)]
**注意:如果调用摸个方法的返回值是Promise实例,前面可以添加await! awati只能用在async修饰的方法中**
axios方法的调用一般步骤
1. 调用axios后使用async/await进行简化
2. 使用解构后,从axios封装的大对象中把data属性解构出来
3. 把解构的数据进行重命名
常见的方法调用方式
```vue
async getInfo(){
const {data:res} =await axios({ //此处用到了数据的解构
method:"GET",
url:'http://www.liulongbin.to:3006/api/post'
data:{
name:'zs',
age:20
}
})
}
axios的简化使用
axios.get("url地址",{
params:{
数据体
}
})
axios.post("url地址",{
数据体
})