习题详解

用table表格标签渲染以上数据,表格第一列是学生总分

'''
...
    ...
        <tr>
            ...  # 表头字段
        </tr>
        
        <tr v-for="(score, i) in scores">  
            <td>{{i + 1}}</td>  # 1 
            <td v-for="(v, k, i) in score">{{v}}</td>  # Bob	97	89	67	253
        </tr>
    ...
...


...
    // 模拟页面加载成功, 从后台获取数据
    `let scores = null;
    $.ajax({
        url: '',
        success(response) {
            scores = response.data
        }
    });`;

    let scores = [
        ...
    ];

    // // for in遍历的是取值索引或者key|for of 遍历的是值(字典除外)
    // for (v in scores) {
    //     console.log(v)  // 0 1 2 3 4
    // }
    for (score of scores) {
        score.total = score.math + score.chinese + score.english
    }
    for (let i = 0; i < scores.length - 1; i++) {  
        for (let j = 0; j < scores.length - 1 - i; j++) {
            if (scores[j].total < scores[j + 1].total) {  // 总分排序
            	
            	// 调整整个数据对象顺序
                let temp = scores[j];  
                scores[j] = scores[j + 1];
                scores[j + 1] = temp
            }
        }
    }
    
    new Vue({
        ...
        data: {
            // scores:scores
            scores  // 属性名与变量名相同可省略只写一个
        },
        ...
    })
...
'''

只渲染所有科目都及格了的学生

'''
        <tr v-for="(score, i) in scores" v-if="score.math >= 60 && score.chinese >= 60 && score.english >= 60">
            ...
        </tr>
'''

i)有三个按钮:语文、数学、外语,点击谁谁高亮,且当前筛选规则采用哪门学科
ii)两个输入框,[]~[],前面填最小分数,后面填最大分数,全部设置完毕后,表格的数据会被更新: 只渲染满足所有条件的结果

'''
    <style>
        .click {
            background-color: pink;
        }
    </style>
    
    
<div id="app">
    <button type="button" @click="subject = 'chinese'" :class="{click: subject === 'chinese'}">语文</button>
    ...
    <p>
        请输入分数段:
        <input type="number" min="0" max="100" v-model="min">
        ~
        <input type="number" min="0" max="100" v-model="max">
    </p>
    <table border="1" style="margin: auto">  # margin: auto, 居中
        <tr>
            <th>排名</th>  # 添加排名字段
            
            # 单独的v-if, 条件满足则渲染该标签, 不满足则不渲染该标签
            <th v-for="(v, k, i) in scores[0]" v-if="k === 'name' || k === subject || !subject">{{k}}</th>  
        </tr>
        <tr v-for="(score, i) in scores" v-if="(score[subject] >= +min && score[subject] <= +max) || (!max || !min)">
            <td>{{i + 1}}</td>
            <td v-for="(v, k, i) in score" v-if="k=== 'name' || k === subject || !subject">{{v}}</td>  # 循环嵌套及v-if嵌套
        </tr>
    </table>
</div>


...
        data: {
            ...
            subject: '',
            min: '',
            max: '',
        },
...
'''

组件介绍

组件: HTML + CSS + JS 封装的集合体, 组件具有复用性

组件分类

  • 根组件: new Vue() 生成的组件
  • 局部组件: 组件名 = {}, {} 内部采用的是vue语法
  • 全局组件: Vue.component('组件名', {}), {}内部采用的是vue语法

组件都有管理其HTML页面结果的template实例成员

'''
<div id="app">
    {{info}}
</div>


    new Vue({
        el: '#app',  // 挂载点本质是被组件中的template成员指定的虚拟DOM替换的占位符
        data: {
            info: '根组件信息',
        },
        ...
        template: '<div>{{info}}</div>'
    })
    // 根组件可以不明确template, 此时template默认采用挂载点的HTML页面结构
    // 如果设置了template, 挂载点的HTML页面结构失效
    // 挂载点不能为body标签和html标签
'''

子组件

  • 根组件都是作为最顶层的父组件, 局部与全局组件作为根组件的子组件, 组件间的父子关系是相对的
  • 子组件间的数据是隔离的, 每一个子组件拥有自己独立的数据空间
  • 局部组件必须注册后才能使用, 而全局组件不需要注册(提前加载到了内存中, 占用内存), 推荐使用局部组件(使用时才占用内存)
  • 变量在哪个组件中出现就由该组件提供变量值并管理该变量
'''
	# 样式复习
    <style>
        body {
            margin: 0; /*取出浏览器页边留白*/
        }

        .box {
            width: 250px; /*调整图片宽度, 长度自动等比缩放*/
            border-radius: 20px; /*图片四周设置20px圆角弧度*/
            overflow: hidden; /*图片超出部分隐藏*/
            background-color: aqua; /*设置图片及文字背景颜色*/
            float: left;
            margin: 10px;
        }

        .box img {
            width: 100%; /*设置图片宽度占比为父标签的100%*/
        }

        .box h2 {
            margin: 0; /*取出标题标签与父标签留白*/
            text-align: center; /*标题居中*/
        }

        .wrap {
            width: 1100px;
            margin: 0 auto; /*标签居中*/
            border: 3px solid red;  /*标签边框样式*/
        }

        .wrap:after { /*清除浮动影响*/
            content: '';
            display: block;
            clear: both;
        }
    </style>
'''
'''
<div id="app">
    <div class="wrap">
        <local-tag></local-tag>  <!--能渲染-->
        <local-Tag></local-Tag>  <!--能渲染-->
        <global-tag></global-tag>
    </div>
</div>


    let localTag = {  // 定义局部组件, 需要在使用它的父组件中注册才能正常渲染
        template: `
        <div class="box" @click="fn">
            <img src="img/444.jpg" alt="">
            <h2>美女</h2>
        </div>
        `,
        methods: {
            fn() {
                alert(123)
            },
        }
    };

    Vue.component('globalTag', {  // 定义全局组件, 不需要注册就可以正常渲染
        template: `
        <div class="box">
            <img src="img/555.jpg" alt="">
            <h2>旺仔牛奶</h2>
        </div>
        `
    });

    new Vue({
        ...
        components: {
            // localTag: localTag,  // css语法不识别大小写, js中的驼峰体对应css中的-
            localTag  // // 注册局部组件, 属性名与变量名相同时可省略只写一个
        }

    })
    // 局部, 全局, 和根组件都是一个vue实例, 一个实例对应一套html, css, js结构
'''

组件数据局部化

类比

类比:
class A:
    name = 'Owen'
    def __init__(self, name):  # 实例化一次自动执行一次__init__函数产生一个局部作用域
        self.name = name


a1 = A('jason')
a2 = A('tank')
a1.name
a2.name
'''
    let localTag = {  // 定义局部组件, 需要在使用它的父组件中注册才能正常渲染
        template: `
        <div class="box" @click="fn">
            <img src="img/444.jpg" alt="">
            <h2>锤了美女{{count}}下</h2>
        </div>
        `,
        methods: {
            fn() {
                this.count++
            },
        },
        // data: function () {
        //     return {count: 0,}  // 返回对象类型数据
        // },

        data() {  // 简写形式, 局部或全局组件可能会被复用多次, 每次被渲染的组件都有自己独立的变量名称空间
            return {count: 0};  // 执行方法会产生局部作用域, 所以方法的返回值作为局部化数据
        },
    };
'''

组件传参

父传子

  • 子组件以字符串形式在props中自定义组件属性, 该字符串可以通过反射机制在子组件中以变量形式使用
  • 子组件在父组件中渲染时, 会将父组件的对应变量绑定给子组件的自定义属性, 实现父组件向子组件传参
'''
<div id="app">
    <div class="wrap">
        <local-tag v-for="girl in girls" :xxx="girl"></local-tag>
    </div>
</div>


<script>
    let girls = [
        {
            name: '美女四号',
            img_path: 'img/444.jpg',
        },
        ...
    ];
    let localTag = {
        props: ['xxx',],
        template: `
        <div class="box">
            <img :src="xxx.img_path" alt="">
            <h2>{{xxx.name}}</h2>
        </div>
        `,
    };
'''

子传父

  • 子组件中的template中最多只能写一个根标签, 因为挂载点只能挂载一个标签
  • 子组件中定义的事件, 事件是属于子组件的, 但事件绑定的方法由父组件实现
  • 在子组件中通过 this.emit('自定义事件名', 参数) 控制自定义事件在子组件中触发的位置
'''
<div id="app">
    <h1>{{h1}}</h1>
    <h2>{{h2}}</h2>
    <tag @action1="action1Fn" @action2="action2Fn"></tag>  <!--方法action1Fn属于根组件, 由根组件实现, 事件action1属于子组件-->
</div>


    let tag = {
        template: `
        <div>
        主标题内容: <input type="text" v-model="input_h1" @input="h1Change">
        子标题内容: <input type="text" v-model="input_h2">
        </div>
        `,
        data() {
            return {
                input_h1: '',
                input_h2: '',
            };
        },
        methods: {
            h1Change() {
                this.$emit('action1', this.input_h1);
            },
        },
        watch: {
            input_h2() {
                this.$emit('action2', this.input_h2);
            },
        },
    };
    
    new Vue({
        el: '#app',
        methods: {
            action1Fn(a) {
                if (!a) {
                    this.h1 = '主标题';
                }
                else {
                    this.h1 = a;
                }
            },
            ...,
        },
        components: {
            tag
        },
        data: {
            girls,
            h1: '主标题',
            h2: '子标题',
        }
    });
'''