Vue04--计算属性、生命周期钩子、axios以及组件

1.购物车案例

1.1 基本购物车

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="./js/vue.js"></script>
    <style>
        table, td {
            border: 1px solid black;
            text-align: center;
        }
    </style>
</head>
<body>
<div id="app">
    <h1>购物车</h1>
    <table>
        <tr>
            <td>商品名称</td>
            <td>价格</td>
            <td>数量</td>
            <td>选择</td>
        </tr>
        <tr v-for="item in dataList">
            <td>{{item.name}}</td>
            <td>{{item.price}}</td>
            <td>{{item.number}}</td>
            <td><input type="checkbox" v-model="choice" :value="item"></td>
        </tr>
    </table>

    选中的商品是:{{choice}}
    <br>
    商品价格:{{getPrice()}}
</div>
</body>
<script>
    let vm = new Vue({
        el: '#app',
        data: {
            dataList: [
                {name: '今瓶没', price: 99, number: 2},
                {name: '西柚记', price: 59, number: 1},
                {name: '水壶转', price: 89, number: 5},
            ],
            choice: []
        },
        methods: {
            getPrice() {
               let total = 0
                
                // js的循环方式:
                // 1.For循环
                // for (let i = 0; i < this.choice.length; i++) {
                //     total = total + this.choice[i].price * this.choice[i].number
                // }

                
                // 2.For In 循环 (i:对象就是键,数组就是索引)
                // for (let i in this.choice) {    // 这里的 i 是索引
                //     total += this.choice[i]['number'] * this.choice[i]['price']
                // }

                // 3.For Of 循环 (v:对象值本身)    --ES6语法
                // for (let v of this.choice) {    // 这里的 v 是数组中每一个对象
                //      total += v.price*v.number
                //  }

                // 4.数组的forEach() 为每个数组元素调用一次函数(回调函数)
                this.choice.forEach((value, index, array) => {    # 值,索引,数组本身
                    total += value.price * value.number
                })
                
                return total
            }
        },
    })
</script>
</html>

1.2 全选全不选

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="./js/vue.js"></script>
    <style>
        table, td {
            border: 1px solid black;
            text-align: center;
        }
    </style>

</head>
<body>
<div id="app">
    <h1>购物车</h1>
    <table>
        <tr>
            <td>商品名称</td>
            <td>价格</td>
            <td>数量</td>
            <td>选择 <input type="checkbox" v-model="checkAll" @change="handleAll"></td>
        </tr>
        <tr v-for="item in dataList">
            <td>{{item.name}}</td>
            <td>{{item.price}}</td>
            <td>{{item.number}}</td>
            <td><input type="checkbox" v-model="choice" :value="item" @change="checkOne"></td>
        </tr>
    </table>
    选中的商品是:{{choice}}
    <br>
    商品价格:{{getPrice()}}
</div>
</body>
<script>
    let vm = new Vue({
        el: '#app',
        data: {
            dataList: [
                {name: '今瓶没', price: 99, number: 2},
                {name: '西柚记', price: 59, number: 1},
                {name: '水壶转', price: 89, number: 5},
            ],
            choice: [],
            checkAll: false
        },
        methods: {
            getPrice() {
                let total = 0
                for (v of this.choice) {    // 这里的 v 是数组中每一个对象
                    total += v.price * v.number
                }
                return total
            },
            handleAll() {
                if (this.checkAll) {
                    this.choice = this.dataList
                } else {
                    this.choice = []
                }
            },
            checkOne() {
                if (this.choice.length == this.dataList.length) {
                    this.checkAll = true
                } else {
                    this.checkAll = false
                }
            }
        },
    })


</script>
</html>

1.3 数量加入加减

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="./js/vue.js"></script>
    <style>
        table, td {
            border: 1px solid black;
            text-align: center;
        }
    </style>
</head>
<body>
<div id="app">
    <h1>购物车</h1>
    <table>
        <tr>
            <td>商品名称</td>
            <td>价格</td>
            <td>数量</td>
            <td>选择 <input type="checkbox" v-model="checkAll" @change="handleAll"></td>
        </tr>
        <tr v-for="item in dataList">
            <td>{{item.name}}</td>
            <td>{{item.price}}</td>
            <td><button @click="handleJ(item)">-</button>{{item.number}}<button @click="item.number++">+</button></td>
            <td><input type="checkbox" v-model="choice" :value="item" @change="checkOne"></td>
        </tr>
    </table>
    选中的商品是:{{choice}}
    <br>
    商品价格:{{getPrice()}}
</div>

</body>
<script>
    let vm = new Vue({
        el: '#app',
        data: {
            dataList: [
                {name: '今瓶没', price: 99, number: 2},
                {name: '西柚记', price: 59, number: 1},
                {name: '水壶转', price: 89, number: 5},
            ],
            choice: [],
            checkAll: false
        },
        methods: {
            getPrice() {
                let total = 0
                for (v of this.choice) {    // 这里的 v 是数组中每一个对象
                    total += v.price * v.number
                }
                return total
            },
            handleAll() {
                if (this.checkAll) {
                    this.choice = this.dataList
                } else {
                    this.choice = []
                }
            },
            checkOne() {
                if (this.choice.length == this.dataList.length) {
                    this.checkAll = true
                } else {
                    this.checkAll = false
                }
            },
            handleJ(item){
                if(item.number<=1){
                    alert('受不了了,不能再少了')
                }else {
                    item.number--
                }
            }
        },
    })


</script>
</html>

2.v-model进阶

# v-model的修饰符
    lazy:等待input框的数据绑定后,发生改变后再变化
    number:数字开头,只保留数字,后面的字母不保留;字母开头,都保留
    trim:去除首尾的空格
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="./js/vue.js"></script>
</head>
<body>
<div id="app">
<!--    lazy的使用:<input type="text" v-model.lazy="msg"> {{msg}}-->
<!--    number的使用:<input type="text" v-model.number="msg"> {{msg}}-->
    trim的使用:<input type="text" v-model.number="msg"> {{msg}}
</div>

</body>
<script>
    let vm = new Vue({
        el: '#app',
        data: {
            msg:''
        },
        methods: {
        },
    })
</script>
</html>

3.生命周期钩子

beforeCreate	创建Vue实例之前调用
created  	创建Vue实例成功后调用(可以在此处发送异步请求后端数据)  ******
beforeMount	渲染DOM之前调用
mounted	        渲染DOM之后调用
beforeUpdate	重新渲染之前调用(数据更新等操作时,控制DOM重新渲染)
updated	        重新渲染完成之后调用
beforeDestroy	销毁之前调用
destroyed	销毁之后调用


# 重点:
1 created:在这个里面向后端发送请求,获取数据
2 mounted:
    2.1 定时任务 每隔3s执行一次
       this.t = setInterval(function () {
                    console.log('daada')
                }, 3000)

    # 定时操作的应用场景:可以设置成 间隔不断的 朝后端发送 http请求,以达到实时更新的情况。******
    
    2.2 延迟任务(3s后执行该任务)
    setTimeout( ()=> {
                console.log('3s后执行')
                    # 3s后执行这个代码
                }, 3000)
       
3 destroyed:
    3.1关掉定时器
    	clearInterval(this.t)
        t= null # 保险起见,并把定时器设置空

4.发送ajax请求

# axios: 第三方模块,专门发送Ajax请求
	是一个基于 promise 的网络请求库,可以用于浏览器和 node.js
    
# 语法
 axios
    .get('http://127.0.0.1:5000/')
    .then( response =>{this.info = response} )  // 请求成功处理  (响应回来的是对象,数据在.data中)
    .catch( function (error) { // 请求失败处理
        console.log(error);
      }) 
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>跟后端交互</title>
    <script src="./js/vue.js"></script>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
<div id="box">
<button @click="handleClick">点我看美女</button>
    <br>
    我的名字是:{{name}}
    <br>
    我的年龄是:{{age}}
</div>
</body>
<script>
    let vm = new Vue({
        el: '#box',
        data: {
            name:'',
            age:0
        },
        methods:{
            handleClick(){
                // 会出跨域问题----使用 CORS 允许跨源访问:原生django响应,添加响应头***
                axios.get('http://127.0.0.1:5000/').then(res=>{
                    console.log(res.data)
                    this.name=res.data.name
                    this.age=res.data.age
                })
                //
            }
        }

    })

</script>
</html>


# 允许跨源访问--添加响应头
response["Access-Control-Allow-Origin"] = "*"

4.1 加载电影

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>跟后端交互</title>
    <script src="./js/vue.js"></script>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
<div id="box">
    <ul>
        <li v-for="item in dataList">
            <p>电影标题:{{item.name}}</p>
            <p>电影主演:{{item.director}}</p>
            <img :src="item.poster" alt="">            
        </li>
    </ul>
</div>
</body>
<script>
    let vm = new Vue({
        el: '#box',
        data: {
            dataList:[]
        },
        created(){
            axios.get('http://127.0.0.1:5000/').then(res=>{
                console.log(res.data)
                this.dataList=res.data.data.films  // 特别注意:后端返回的是json格式字符串,注意数据类型问题
                // this.dataList = JSON.parse(res.data).data.films
            })
        }
    })

</script>
</html>

5.计算属性

# 格式:
computed:{
	方法函数
	}

# 计算属性 Vs 方法 :
    不同的是计算属性是基于它们的响应式依赖进行缓存的。
    只在相关响应式依赖发生改变时它们才会重新求值。
    这就意味着只要变量还没有发生改变,多次访问 该计算属性会立即返回之前的计算结果,而不必再次执行函数。

5.1 基本使用

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>跟后端交互</title>
    <script src="./js/vue.js"></script>

</head>
<body>
<div id="box">

    <input type="text" v-model="msg">
    <br>
    1.直接在插值中写代码:
    {{msg.substring(0,1).toUpperCase()+msg.substring(1)}}
    <br> 
    
    2.写成一个函数:
    {{getMsg()}}

    {{getMsg()}}
    <br>
    
    3.使用计算属性
    {{getCMsg}}
    {{getCMsg}}
    {{getCMsg}}
    {{getCMsg}}
    {{getCMsg}}
    {{getCMsg}}
    {{getCMsg}}
    {{getCMsg}}
    {{getCMsg}}
</div>
</body>
<script>
    let vm = new Vue({
        el: '#box',
        data: {
            msg:''
        },
        methods:{
            getMsg(){
                // console.log('我执行了')
                return this.msg.substring(0,1).toUpperCase()+this.msg.substring(1)
            }
        },
        
        computed:{
            getCMsg(){
                console.log('我执行了')
                return this.msg.substring(0,1).toUpperCase()+this.msg.substring(1)
            }
        }

    })

</script>
</html>

5.2 重写过滤案例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>跟后端交互</title>
    <script src="./js/vue.js"></script>
</head>
<body>
<div id="box">
    <input type="text" v-model="msg">
    <li v-for="item in new_data_list">{{item}}</li>
</div>
</body>
<script>
    let vm = new Vue({
        el: '#box',
        data: {
            msg: '',
            datalist: ['aaa', 'abc', 'abcde', 'abcdef', 'bbb', 'bac'],
        },

        computed: {
            new_data_list() {
                return this.datalist.filter(item =>item.indexOf(this.msg) > -1)
            }
        }
    })

</script>
</html>

6.组件化开发

# 1.自定义组件需要有一个根标签 (root element),一般包裹在一个div中

# 2.父子组件的data是无法直接共享 (需要通过其他方法)

# 3.组件可以有data,methods,computed....,但是data 必须是一个函数, 且必须是 return

# 4.组件名两种:驼峰体和短横线 都可以,但短横线(推荐,最好)
    eg: Vue.component('my-component-name', { /* ... */ })

6.1 全局组件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>组件化开发</title>
    <script src="./js/vue.js"></script>
</head>
<body>
<div id="box">
    
    <child></child>
    <hr>
    <child></child>

</div>
</body>
<script>

    // 定义一个名为child的全局组件
    Vue.component('child', {
        template: `
            <div>
                <h1>我是子组件</h1>
                <br>
                <button @click="handleClick">点我看美女--{{name}}</button>
            </div>
        `,
        data() {
            return {
                name: 'lqz'
            }
        },
        methods: {
            handleClick() {
                alert('美女,致命诱惑')
            }
        },

    })

    let vm = new Vue({
        el: '#box',
        data: {},
    })

</script>
</html>

6.2 局部组件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>组件化开发</title>
    <script src="./js/vue.js"></script>

</head>
<body>
<div id="box">
    <child></child>
    <hr>
</div>
</body>
<script>
    // 定义一个全局组件,在全局组件中定义一个局部组件
    Vue.component('child', {
        template: `
            <div>
                <h1>我是子组件</h1>
                <br>
                <button @click="handleClick">点我看美女--{{name}}</button>
                <hr>
                <Header1></Header1>
            </div>
        `,
        data() {
            return {
                name: 'lqz'
            }
        },
        methods: {
            handleClick() {
                alert('美女,致命诱惑')
            }
        },
        components:{
            'Header1':{
                template:`<h2>我是局部组件---{{msg}}</h2>`,
                data(){
                    return {
                        msg:'SB'
                    }
                },
                methods:{},
            }
        }
    })

    let vm = new Vue({
        el: '#box',
        data: {},
    })

</script>
</html>

6.3 动态组件

在一个多标签的界面中使用 is 属性 来切换不同的组件

# 通过指定 component标签的 is属性值,实现显示某个组件
    <component :is="who"></component> 
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>组件化开发</title>
    <script src="./js/vue.js"></script>
</head>
<body>
<div id="box">
    <span @click="who='index'">首页</span>&nbsp&nbsp
    <span @click="who='girl'">美女</span>&nbsp&nbsp
    <span @click="who='boy'">帅哥</span>
    <hr>

    <component :is="who"></component>
</div>
</body>
<script>
    Vue.component('index', {
        template: `
        <div>
          首页
        </div>`,
    })
    Vue.component('girl', {
        template: `<div>
                   美女
                    </div>`,
    })
    Vue.component('boy', {
        template: `<div>
                   帅哥
                    </div>`,
    })

    let vm = new Vue({
        el: '#box',
        data: {
            who: 'index'
        },
        methods: {}
    })
</script>
</html>

6.4 keep-alive的使用

当在多个组件之间切换的时候,你有时会想保持这些组件的状态,
以避免反复重新渲染导致的性能问题,失活的组件所有内容将会被缓存

# 用一个<keep-alive> 元素将其动态组件包裹起来。
<keep-alive>
    <component :is="currentTabComponent"></component>
</keep-alive>


# 注意这个<keep-alive> 要求被切换到的组件都有自己的名字

7.组件通信

7.1 组件通信之父传子(通过自定义属性)

# 1.在子组件使用中:子组件上自定义属性,通过 属性指令 绑定好父组件的数据变量
    eg: :mymsg="msg"
        
# 2.在子组件定义中:在props方法中注册该属性
    eg: props:['mymsg','age'] 
	
    # Prop验证: props 也可以是对象,key为属性名,value为属性的类型
        eg: props:{mymsg:String}
            
# 3.在子组件定义中:template 就可以使用 该属性变量
	eg: {{mymsg}}
        
        
# 注意:
    HTML中的属性名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符。
    这意味着当你使用DOM中的模板时,驼峰体命名的prop名,属性必须使用其等价的 短横线分隔命名:
    
    <blog-post post-title="hello!"></blog-post>
    
    Vue.component('blog-post', {
      props: ['postTitle'],
      template: '<h3>{{ postTitle }}</h3>'
    })
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>组件化开发</title>
    <script src="./js/vue.js"></script>

</head>
<body>
<div id="box">
    # 1.在组件上自定义属性,属性指令绑定好变量
    <br>
    <child :mymsg="msg" :age="age"></child>
    <hr>
</div>
</body>
<script>

    Vue.component('child', {
        template: `
            <div>
                # 3.template 就可以使用 该属性变量
                <h1>我是子组件---从父组件传值过来--{{mymsg}}---{{age}}</h1>
                <br>
                <button @click="handleClick">点我看美女--{{name}}</button>
                <hr>
            </div>
        `,
        data() {
            return {
                name: 'lqz'
            }
        },
        methods: {
            handleClick() {
                alert('美女,致命诱惑')
            }
        },
        # 2.在props方法中注册该属性
        props:['mymsg','age']
    })

    let vm = new Vue({
        el: '#box',
        data: {
            msg:'我是父组件的数据',
            age:999
        },
    })

</script>
</html>

7.2 父子通信之子传父(通过自定义事件)

父组件是使用 props 传递数据给子组件,但如果子组件要把数据传递回去,就需要使用自定义事件!
    思路:通过子组件触发父组件事件的执行,从而执行父组件事件对应的方法函数 (同时,也可以把子组件中的数据变量传入到父组件事件对应的方法上)

# 监听和触发事件的方法
    使用 $on(eventName)  简写: @事件名   监听事件
    使用 $emit(eventName)               触发事件


# 自定义事件 命名:
    始终使用 kebab-case
    
# 步骤:
    # 1.父组件通过自定义事件,绑定方法
        <child v-if="isShow" @myevent="handleShow"></child>
    
    # 2.子组件触发父组件事件的执行,把子组件中的变量b,传入到父组件事件对应的方法上
        this.$emit('myevent',this.b)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>组件化开发</title>
    <script src="./js/vue.js"></script>

</head>
<body>
<div id="box">
    <br>
	# 1.父组件通过自定义事件,绑定方法
    <child v-if="isShow" @myevent="handleShow"></child>
    <hr>
</div>
</body>
<script>

    Vue.component('child', {
        template: `
            <div>
                <h1>我是子组件</h1>
                <br>
                <button @click="handleClick">点我消失</button>
                <hr>
            </div>
        `,
        data() {
            return {
                b: false
            }
        },
        methods: {
            handleClick() {
                # 2.子组件触发父组件事件的执行,把子组件中的变量b,传入到父组件事件对应的方法上
                this.$emit('myevent',this.b)
            }
        },
    })

    let vm = new Vue({
        el: '#box',
        data: {
            isShow: true,
        },
        methods:{
            handleShow(b){
                this.isShow=b
            }
        }
    })

</script>
</html>

7.3 ref属性

# Vue支持:给所有的标签,提供ref属性
    ref= '属性值'

# Vue中 就可以通过 this.$refs.属性值 拿到

    -若ref在普通标签上,拿到的就是普通标签本身
    -若ref在子组件上,拿到的就是子组件对象
    	-通过组件对象,可直接获取,修改属性
        -直接执行方法,传值,返回值
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>组件化开发</title>
    <script src="./js/vue.js"></script>

</head>
<body>
<div id="box">
    <input type="text" ref="myinput" name="lqz">
    <br>
    <child ref="mychild"></child>
    <hr>

    <button @click="handleClick">点我试一下</button>
</div>
</body>
<script>

    // 定义一个全局组件,在全局组件中定义一个局部组件
    Vue.component('child', {
        template: `
            <div>
                <h1 v-if="b">我是子组件</h1>
                <br>
                <button>点我消失和显示</button>
                <hr>
            </div>
        `,
        data() {
            return {
                b: true
            }
        },
        methods: {
            handle1(msg){
                // alert(msg)
                return 'lqz'
            }
        },
    })

    let vm = new Vue({
        el: '#box',
        data: {},
        methods: {
            handleClick() {
                // 若ref在普通标签上,拿到的就是普通标签本身
                console.log(this.$refs.myinput)
                // 若ref在子组件上,拿到的就是子组件对象
                console.log(this.$refs.mychild)
                // 修改子组件的数据
                this.$refs.mychild.b = !this.$refs.mychild.b
                // 调用子组件的方法
                let res=this.$refs.mychild.handle1('致命诱惑')
                console.log(res)
            }
        }
    })

</script>
</html>

7.4 事件总线(不同层级的组件通信)

问题:若是不同层级的组件通信,再采用父传子、子传父的形式就比较麻烦。

思路:(观察者模式)
新建一个vue对象,是一条总的处理线;
在该总线上,不同层级的组件去监听(订阅)和触发总线的事件,从而达到通信效果。
# 很少用了,最简单的方式直接使用 ref形式就可以

# 例:两个全局组件通信
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>组件化开发</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
</head>
<body>
<div id="box">
    <myhead></myhead>
    <hr>
    <myhead1></myhead1>
</div>
</body>

<script>
    // 定义一个事件总线
    let bus = new Vue()

    Vue.component('myhead', {
        template: `
          <div>
            <h4>我是组件一</h4>
            <h4>另一个组件的数据:{{ msg }}</h4>
          </div>
        `,
        data() {
            return {
                msg: ''
            }
        },
        methods: {},
        mounted() {
            console.log('当前组件挂载后执行')
            // 组件一:监听(订阅) suibian事件
            bus.$on('suibian', (item) => {
                console.log('收到了另一个组件的数据:', item)
                this.msg = item
            })
        }

    })

    Vue.component('myhead1', {
        template: `
          <div>
            <h4>我是组件二</h4>
            <input type="text" v-model='name'>
            <button @click="handleClick">点我传数据</button>
          </div>
        `,
        data() {
            return {
                name: ''
            }
        },
        methods: {
            handleClick() {
                // 组件二:触发suibian事件
                bus.$emit('suibian', this.name)
            }
        },
    })

    let vm = new Vue({
        el: '#box',
        data: {},
        methods: {}
    })

</script>
</html>
posted @ 2021-12-29 16:55  Edmond辉仔  阅读(30)  评论(0编辑  收藏  举报