Vue
1.Vue的两个特性
- 数据驱动视图
- 数据的变化会驱动视图自动更新
- 好处:程序员只管把数据维护好,那么页面的结构会被vue自动渲染出来
- 双向数据绑定
- 在网页中,form表单负责采集数据,Ajax负责提交数据
- js数据的变化,会被自动渲染到页面上
- 页面上表单采集的数据发生变化时,会被vue自动获取到,并更新到js数据中
注意:数据驱动视图和双向绑定数据的底层实现原理时MVVM(model数据源,view视图,viewmodel就是vue的实例)
2.Vue指令
- 内容渲染指令
- v-text指令缺点:会覆盖元素内部原有的内容
- {{ }}插值表达式:在实际开发中用的最多,只是内容上的占位,不会覆盖原有的内容
- v-html指令的作用:可以把带有标签的字符串,渲染成真正的HTML内容
<body>
//希望Vue能够控制下面的这个div,帮我们把数据填充到div内部
<div id="app">
<p v-text="username"></p>
<p v-text="gender">性别</p>//会覆盖元素内部原有内容
<hr>
//插值表达式的使用
<p>姓名:{{username}}</p>
<p>性别:{{gender}}</p>
<hr>
<div v-html="info"></div>
</div>
1.导入Vue的库文件,这样在window全局就有了Vue这个构造函数
<script src="./lib/vue.js"></script>
2.创建Vue实例对象
const vm = new Vue({
//el属性固定的写法,表示当前vm实例要控制页面上的哪个区域,接收的值是一个选择器
el:'#app',
//data对象就是要渲染到页面上的数据
data:{
username:'zhangsan',
gender:'女',
info:'<h4 style="color:red;font-weight:bold;">欢迎</h4>'
}
})
</script>
<body>
- 属性绑定指令
注意:插值表达式只能用在元素的内容节点中,不能用在元素的属性节点中
- 在vue中,可以使用v-bind:指令,为元素的属性动态绑定值,v-bind vue规定v-bind: 指令可以简写为:
<body>
//希望Vue能够控制下面的这个div,帮我们把数据填充到div内部
<div id="app">
<input type="text" :placeholder="tips">
<img v-bind:src="img" alt="图片不能打开的说明文字" style="width: 150px;">
</div>
1.导入Vue的库文件,这样在window全局就有了Vue这个构造函数
<script src="./lib/vue.js"></script>
2.创建Vue实例对象
const vm = new Vue({
//el属性固定的写法,表示当前vm实例要控制页面上的哪个区域,接收的值是一个选择器
el:'#app',
//data对象就是要渲染到页面上的数据
data:{
tips:'请输入用户名',
img:'http://images/logo.svg'
}
})
</script>
<body>
- 事件绑定指令
- v-on可以简写为@
- 绑定事件的时候可以通过 ()进行传参
<button v-on:click="add(1)">+1</button>
<button v-on:click="sub">-1</button>
methods:{
add(n){
this.count += n
},
sub(){
this.count -= 1
}
}
//不传参也会有事件e
<button v-on:click="add">+1</button>
add(e){
e.target = "button";
}
//此时e不是事件,而是1
<button v-on:click="add(1)">+1</button>
add(e){
e.target = "button";
}
//参数和事件一起接收
<button v-on:click="add(1,$event)">+1</button>
add(n,e){
e.target = "button";
}
- 事件修饰符
//.prevent阻止默认行为,例如阻止a链接的跳转、阻止表单的提交
<a href= "http://www.baidu.com" @click.prevent="show">跳转到百度首页</a>
methods:{
show() {
console.log("点击了按钮,但是没有跳转百度首页")
}
}
//阻止表单默认提交刷新,但是add方法还是正常执行的
<form @click.prevent="add">
</form>
//.stop阻止事件冒泡,阻止了外层的点击
<div @click="divclick">
<button @click.stop="btnclick"></button>
</div>
methods:{
divclick(){
console.log("点击了"divclick")
},
btnclick(){
console.log("点击了"btnclick")
}
}
- 按键修饰符
//esc清空内容,enter提交内容
<input type="text" @keyup.esc="clearInput" @keyup.enter="commit">
methods: {
clearInput(e){
e.target.value = ''
},
commit(){
console.log("点击了enter按键")
}
}
- v-model(双向绑定)
- input输入框
- type=“radio”
- type=“checkbox”
- type=“xxx”
- textarea
- select
- input输入框
为了方便对用户输入的内容进行处理,vue为v-model指令提供了三个修饰符
.number 自动将用户输入的值转换为数值类型 <input v-model.number="age">
.trim 自动过滤用户输入的首尾空白字符 <input v-model.trim="msg">
.lazy 在"change"时而非"input"时更新
- 条件渲染指令
- v-show原理:动态为元素添加和移除display:none样式,来实现元素的显示和隐藏
- 如果频繁的切换元素的显示状态,用v-show性能会更好
- v-if原理:每次动态创建和移除元素,实现元素的显示和隐藏
- 如果刚进入页面的时候,某些元素默认不被展示,而且后期很肯能也不需要被展示,此时的v-if性能更好
- v-show原理:动态为元素添加和移除display:none样式,来实现元素的显示和隐藏
实际开发中,使用v-if
-
列表渲染指令
- v-for用法 v-for=“(item,index) in list” :key=“item.id”
-
label的for属性:放大可操作区域
3.过滤器
//私有过滤器语法结构
<div id="app">
<p>message的值是{{ message | capi }}<p>
</div>
<script>
//过滤器函数与data同级,必须被定义到filters节点下
//过滤器本质是函数
data: {
el: "#app",
message: 'hello vue'
},
filters: {
//val是管道符|前面的值
capi(val){
//强调:过滤器中,一定要有一个返回值
return 'abc'
}
}
</script>
//全局过滤器语法结构
<script>
Vue.filter('capi',function(str){
const first = chartAt(0).toUpperCase
const other = str.slice(1)
return first + other
})
</script>
- 过滤器的注意点
- 要定义到filters节点下,本质是一个函数
- 在过滤器的函数中,一定要有return值
- 在过滤器的形参中,可以获取到管道符前面待处理那个值
- 如果全局过滤器和私有过滤器名字一样,此时按照就近原则,调用私有过滤器
- 过滤器可以串联的使用,第一个过滤的return值作为第二个过滤器的形参(开发很少使用过滤器串联)
- 可以给过滤器传参 ,参数从第二个位置接收
- Vue3.x不支持过滤器
<p>{{ message | filterA(arg1,arg2) }}</p>
<script>
Vue.filter('filterA',(msg,arg1,arg2){
return xxx
})
</script>
4.监听器
//与data同级
data:{
username:''
},
//新值在前,旧
watch:{
//对象格式侦听器
username(){
handler(newVal, oldVal){
conslole.log(newVal,oldVal)
},
immediate: true //默认为false,当设置为true时,控制侦听器自动触发一次
}
//方法格式侦听器,不能控制侦听器进入界面自动触发一次
username(newVal, oldVal){
conslole.log(newVal,oldVal)
}
}
- 如果监听对象,可以使用deep选项,监视对象里面的属性变化,也可以使用对象.属性的方式进行监听
watch: {
info: {
handler(newVal){
console.log(newVal)
},
//开启深度监听,只要对象中的任何一个属性变化了,都会触发对象的侦听器
deep: true
}
'info.username'(newVal) {
console.log(newVal)
}
}
5.使用计算属性computed
- 定义的时候,要被定义为方法
- 在使用计算属性的时候,当成普通的属性使用即可
- 实现了代码复用,只要计算属性中依赖的数据源变化了,则计算属性会自动重新求值
//计算属性在定义的时候,要定义成方法格式
computed: {
//rgb作为一个计算属性,被定义成了方法格式
//最终,在这个方法中,要返回一个生成好的rgb(x,x,x)的字符串
rgb() {
return `rgb(${this.r},${this.g},${this.b})`
}
}
6.axios
- axios是一个专注于网络请求的库
//axios基本用法
<script src="./lib/axios.js"></script>
<script>
//1.用axios方法得到的返回值是Promise对象
axios({
method: 'GET',
url: 'http://www.baidu/3306/api/getbooks'
}).then(function(result)){
console.log(result.data)
}
</script>
- axios发起get请求
<script src="./lib/axios.js"></script>
<script>
//1.用axios方法得到的返回值是Promise对象
axios({
method: 'GET',
url: 'http://www.baidu/3306/api/getbooks',
params: {
id: 1
}
}).then(function(result)){
console.log(result.data)
}
</script>
- axios发起post请求
<button id = "btnPost">发起POST请求</button>
<script src="./lib/axios.js"></script>
<script>
document.querySelector('#btnPost').addEventListener('click',function()) {
axios({
method: 'POST',
url: 'http://www.baidu/3306/api/post',
data: {
name: 'zs',
age: 20
}
}).then(function(result)){
console.log(result )
}
}
</script>
- async和await调用axiso
//如果调用某个方法的返回值是Promise实例,则前面可以添加await
//await只能用在被async修饰的方法中
<script>
document.querySelector('#btnPost').addEventListener('click',async function()) {
const result = await axios({
method: 'POST',
url: 'http://www.baidu/3306/api/post',
data: {
name: 'zs',
age: 20
}
})
console.log(result)
}
</script>
- 使用解构赋值
<script>
//解构赋值的时候,使用:进行重命名
//1.调用axiso之后,可以使用await和async进行简化
//2.使用解构赋值,从axiso封装的大对象中,把data属性解构出来
//3.把解构出来的data属性,使用冒号进行重命名,一般重命名为{ data: res}
document.querySelector('#btnGet').addEventListener('click',async function()) {
const {data: res} = await axios({
method: 'Get',
url: 'http://www.baidu/3306/api/getBooks'
})
console.log(res.data)
</script>
- 可以使用axios.get()和axiso.post()
7.vue.cli的使用
- 创建项目命令
vue create 项目名称(不要有中文)
- src目录构成
asserts文件夹:存放项目中用到的静态资源文件,例如css样式表、图片资源
components文件夹:程序员封装的、可复用的组件,都要放到components目录下
main.js是项目的入口文件。整个项目的运行,首先要执行main.js
APP.vue是项目的根组件
- vue项目的执行流程
在工程化的项目中,vue要做的事情很单纯:通过main.js把App.vue渲染到index.html的指定区域中
new Vue({
store,
render: h => h(App)
}).$mount('#app')
.$mount('#app')的作用等同于el: '#app'
8.组件的三个组成部分
<template>
<div class="test-box">
<h3>这是自定义的Test.vue---{{username}}</h3>
</div>
</template>
<script>
//默认导出,固定写法
export default{
//data数据源
//注意.vue中组件中的data必须是一个函数
data() {
return{
username: 'admin'
}
}
}
</script>
<style>
9.在组件中定义methods方法
<template>
<div class="test-box">
<h3>这是自定义的Test.vue---{{username}}</h3>
<button @click="changeUsername">修改用户名</button>
</div>
</template>
<script>
//默认导出,固定写法
export default{
//data数据源
//注意.vue中组件中的data必须是一个函数
data() {
return{
username: 'admin'
}
},
methods: {
changeUsername() {
this.username = '哇哈哈'
}
},
//当前组件的侦听器
watch: {
},
//当前组件的计算属性
computed: {
},
//当前组件的过滤器
filters: {
}
}
</script>
<style>
.test-box {
background-color: pink;
}
</style>
- 只能有一个根节点
<template>
<div>
</div>
</template>
- 启用less
//<style默认lang是css>
<style lang="less">
.test-box {
background-color: pink;
h3{
color: red;
}
}
</style>
10.组件的使用
- 步骤1:使用import语法导入需要的组件
- 步骤2:使用components节点注册组件(使用component注册的是私有子组件)
- 步骤3:以标签形式使用刚才注册的组件
11.全局组件的使用
- 在main.js中导入需要被全局使用的组件
import Count from '@/components/Count.vue'
- 使用Vue.component()函数进行全局注册
Vue.component('MyCount',Count)
12.组件的props
- props是组件的自定义属性,在封装通用组件的时候,合理的使用props可以提高组件的复用性
export default{
//组件的自定义属性
props: ['自定义属性A','自定义属性B','init'],
//组件的私有属性
data() {
return{
}
}
}
//在不同的父组件中可以给子组件自定义属性赋不同的值
<MyCount init="6"></Mycount>
<MyCount init="9"></Mycount>
//注意,此时的6和9是字符串,如过要赋数字,需要加v-bind绑定
<MyCount :init="6"></Mycount>
<MyCount :init="9"></Mycount>
- props是可读了,对props中自定义的属性进行修改,vue会报警告,如果要修改props中的值,可以把props的值转存到data中
//父组件给init一个初值
<MyCount :init="6"></Mycount>
export default{
//组件的自定义属性
props: ['init'],
//组件的私有属性
data() {
return{
count: this.init
}
}
}
- props-default、type、required属性
数组方式不能定义props属性的初始值,需要转换为对象形式
props: {
init: {
default: 0,
//父组件传的init类型必须是Number数字
type: Number,
//必填项
required: true
}
}
13.scoped使用及底层原理
//方式组件的样式冲突问题
<style scoped lang="less">
.footer {
padding:30rpx 20rpx;
box-sizing:border-box;
background:#fff;
width:100%;
}
</style>
- 底层原理
在UI的标签中会生成data-v-xxx的标识,目的是当前界面的样式只能标识当前界面的UI
14./deep/
- 使用/deep/在父组件中修改子组件中的样式
- 使用场景:当使用第三方组件库的时候,如果有修改第三方组件默认样式,需要用到/deep/
/deep/ h5{
...
}
15.生命周期
- 组件创建阶段
beforeCreated(){
//开发用不到
},
created(){
//生命周期函数,非常常用
//经常在它里面,调用methonds方法,请求服务器的数据
//并且,把请求到的数据,转存到data中,供template模板渲染的时候使用
this.initBooks()
},
beforeMounted(){
//开发用不到
},
mounted(){
//如果要操作当前组件的dom,最早只能在mounted阶段执行
const dom = document.querySelector('#myh3')
console.log(dom)
},
- 组件更新阶段
beforeUpdate(){
},
updated(){
//当数据发生变化时,为了能操作到最新的DOM结构,必须把代码写到updated生命周期函数中
}
16.组件传值
- 父组件像子组件共享数据
- 子组件像父组件共享数据
- 组件之间的共享
- 创建eventBus.js模块,并向外共享一个Vue的实例对象
- 在数据发送方,调用bus.$emit(‘事件名称’,要发送的数据)方法触发自定义事件
- 在数据接收方,调用bus.$on(‘事件名称’,事件处理函数)方法注册一个自定义事件