特效开发总结

1,基于rem单位的屏幕等比例缩放

1,什么是rem

rem是浏览器描述长度的单位,含义为:相对于 html 的字体大小的单位。1rem = html 根节点上1个字符的宽度

2,rem的作用

使用 rem 单位设置的元素尺寸,会在不同的设备屏幕宽度下(例如:手机屏幕和平板屏幕)等比例缩放

总结

  1. 确立参考系,定义标准设备的屏幕宽度和字体大小
  2. 比例公式(等式左右两边比例尺相同,从而达到等比例缩放的目的):标准屏幕宽度 / 标准字体大小 = 新的屏幕宽度 / 新的屏幕字体大小
  3. 将页面样式中的 px 单位换算并替换为 rem,方法是 ?rem = 元素的尺寸 / 标准字体大小
  4. 绑定窗口的 resizeload 事件,触发事件时计算出新的屏幕宽度时的字体大小,设置 html 的字体大小

2,css预编译工具

预编译工具:sass
sass 工具用于对 css 进行预编译,预编译的css内容,是一个 sass/scss 文件,文件中的语法,大部分和 css 相同,有一部分是预编译的语法。
作用:在 css 的基础上扩展一些实用的功能。
语法

Nesting 嵌套

nav {
    // 嵌套 的内容 最终被解释翻译成了 子代选择器
    // 这样的话 内部的样式 只有在 nav 的子代才会生效
  ul {
    margin: 0;
    padding: 0;
    list-style: none;
  }

  li { display: inline-block; }

  a {
    display: block;
    padding: 6px 12px;
    text-decoration: none;
  }
}

使用use引入其他的sass文件

假设有个 base.scss 文件

// base.scss
$color: red;

.content {
    font-size: 12px;
}

假设有个 main.scss 文件 在文件中引入 base.scss 且 两个文件在同一目录下

// main.scss
// 使用@use引入外部 scss 文件
@use 'base'; // 该路径是相对路径,相对参考的是当前文件(main.scss)的路径

.box {
    background-color: base.$color; // 调用变量是要加上 base 命名空间(namespace)
}

mixin 混合

// 定义函数
// 类似定义一个函数
// 圆括号中是参数列表
@mixin fn($primary-color: #00f, $secondary-color: #ccc) {
    color: $primary-color;
    background-color: $secondary-color;
    width: 200px;
    height: 100px;
}

.child1 {
    // 不带参数调用函数fn
    // 这样的话 child1 将具备 fn 的所有样式
    @include fn;
}

.child2 {
    // 带参数调用函数fn
    // 可以给参数显示的声明参数名
    // 多个参数间用逗号隔开
    @include fn($secondary-color: #000, $primary-color: #fff);
}

.child3 {
    // 带参数调用函数fn
    // 类似js调用函数,参数按顺序传入
    @include fn(red, green);
}

extend 继承

// 声明父类
%parent {
    border: 1px solid black;
    font-size: 16px;
    font-weight: 200;
    font-family: "微软雅黑";
}

// 声明子类
.child1 {
    // 通过 @extend 关键字继承父类
    @extend %parent;
    // 子类可以有自己的属性
    // 且 子类属性若和父类属性重复的话 子类属性会覆盖父类的属性
    color: green;
    border-color: yellow;
    font-size: 64px;
}

四则运算

.content {
    // sass 中数值可以做四则运算,但是四则运算的两个数字必须单位相同
    // 或者 乘除运算可以直接和不带单位的数字进行运算(相当于放大或缩小一定的倍数)


    // width: 10ex / 3ex * 10 * 5px;
    // height: 15px * 5;
    width: 10px + 2 * 5px;
    height: ((1px + 1px) / ((50rem/25rem) * 1px)) * 5rem;
    transition: (1ms / 12ms * 5s) linear forward;
    transform: rotateX(30deg * 3);
}

3,bootstrap

bootstrap当中包括了很多的布局

container布局

<div class="container">
        <div class="content">container</div>
    </div>

display显示方式

<!-- 当屏幕宽度小于 sm 时显示 -->
    <div class="container d-block d-sm-none">小于 sm</div>
    <!-- 当屏幕宽度为 md 时 显示 -->
    <div class="container d-none d-sm-block d-md-none">md</div>
    <!-- 当屏幕宽度大于 lg 时 显示 -->
    <div class="container d-none d-md-block">大于 lg</div>

float_position_flex

<div class="clearfix">
        <div class="box bg-warning float-start"></div>
        <div class="box bg-success float-start"></div>
        <div class="box bg-danger float-end"></div>
    </div>

gird网格

<div class="container">
        <!-- 行 -->
        <div class="row">
            <!-- 列
                col 语法: col-{breakpoints}-{value}
                例如: col-sm-3 col-lg-12
                value: 范围在 1~12
                bootstrap 中 一行 row 被等分为 12 分 那么col的value值代表的是占多少份
                例如: col-3 此单元格占 12分之3份
            -->
            <div class="col">
                <div class="bg-primary">1</div>
            </div>
            <div class="col">
                <div class="bg-primary">2</div>
            </div>
            <div class="col">
                <div class="bg-primary">3</div>
            </div>
        </div>
        <div class="row">
            <div class="col-8">
                <div class="bg-primary">1</div>
            </div>
            <div class="col">
                <div class="bg-primary">2</div>
            </div>
            <div class="col">
                <div class="bg-primary">3</div>
            </div>
        </div>
    </div>

    <h1>纵向排列方式</h1>
    <div class="container">
        <!-- 在 row 上可以使用flex 的 align-items 来进行竖直方向的排列 -->
        <div class="row border border-3 align-items-center" style="height: 300px;">
            <div class="col">
                <div class="bg-success">1</div>
            </div>
            <div class="col">
                <div class="bg-success">2</div>
            </div>
            <div class="col">
                <div class="bg-success">3</div>
            </div>
        </div>
    </div>

    <h1>横向排列方式</h1>
    <div class="container">
        <!-- 在 row 上可以使用 flex 的 justify-content 来进行水平方向的排列 -->
        <div class="row border border-3 justify-content-between">
            <div class="col-2">
                <div class="bg-warning">1</div>
            </div>
            <div class="col-2">
                <div class="bg-warning">2</div>
            </div>
            <div class="col-2">
                <div class="bg-warning">3</div>
            </div>
        </div>
    </div>

    <h1>单元格偏移</h1>
    <div class="container">
        <div class="row border border-3">
            <!-- offset 设置单元格左侧的偏移量 数字代表的含义和 col 相同 -->
            <div class="col-3 offset-3">
                <div class="bg-danger">1</div>
            </div>
            <div class="col-3 offset-3">
                <div class="bg-danger">2</div>
            </div>
        </div>
    </div>

    <h1>单元格间距</h1>
    <div class="container">
        <!-- 添加单元格间距使用 gutter 首字母为 g
            可以使用 g-{value} 或 gx-{value} gy-{value}
            g-{value} 水平和竖直间距
            gx-{value} 水平间距
            gy-{value} 竖直间距
        -->
        <div class="row text-light g-5 border border-3">
            <div class="col-4">
                <div class="bg-info">1-1</div>
            </div>
            <div class="col-4">
                <div class="bg-info">1-2</div>
            </div>
            <div class="col-4">
                <div class="bg-info">1-3</div>
            </div>
            <div class="col-4">
                <div class="bg-info">2-1</div>
            </div>
            <div class="col-4">
                <div class="bg-info">2-2</div>
            </div>
            <div class="col-4">
                <div class="bg-info">2-3</div>
            </div>
        </div>
    </div>

    <h1>可以指定每一行显示多少列</h1>
    <div class="container">
        <!-- row-cols-{breakpoints}-{value} 让一行显示多少列 -->
        <div class="text-light row row-cols-1 row-cols-md-2 row-cols-lg-3">
            <div class="col">
                <div class="bg-dark">1</div>
            </div>
            <div class="col">
                <div class="bg-dark">2</div>
            </div>
            <div class="col">
                <div class="bg-dark">3</div>
            </div>
            <div class="col">
                <div class="bg-dark">4</div>
            </div>
            <div class="col">
                <div class="bg-dark">5</div>
            </div>
            <div class="col">
                <div class="bg-dark">6</div>
            </div>
            <div class="col">
                <div class="bg-dark">7</div>
            </div>
            <div class="col">
                <div class="bg-dark">8</div>
            </div>
            <div class="col">
                <div class="bg-dark">9</div>
            </div>
            <div class="col">
                <div class="bg-dark">10</div>
            </div>
            <div class="col">
                <div class="bg-dark">11</div>
            </div>
            <div class="col">
                <div class="bg-dark">12</div>
            </div>
        </div>
    </div>

color

<h1>背景色</h1>
    <!-- bg-{value} -->
    <div class="bg-primary">1</div>
    <div class="bg-secondary">1</div>
    <div class="bg-success">1</div>
    <div class="bg-danger">1</div>
    <div class="bg-warning">1</div>
    <div class="bg-info">1</div>
    <div class="bg-light">1</div>
    <div class="bg-dark">1</div>

    <h1>文本色</h1>
    <!-- text-{value} -->
    <div class="text-primary">hello world</div>
    <div class="text-secondary">hello world</div>
    <div class="text-success">hello world</div>
    <div class="text-danger">hello world</div>
    <div class="text-warning">hello world</div>
    <div class="text-info">hello world</div>
    <div class="text-light">hello world</div>
    <div class="text-dark">hello world</div>

    <h1>文本+背景色</h1>
    <!-- text-bg-{value} -->
    <div class="text-bg-primary">hello world</div>
    <div class="text-bg-secondary">hello world</div>
    <div class="text-bg-success">hello world</div>
    <div class="text-bg-danger">hello world</div>
    <div class="text-bg-warning">hello world</div>
    <div class="text-bg-info">hello world</div>
    <div class="text-bg-light">hello world</div>
    <div class="text-bg-dark">hello world</div>

size_space_stack

<h1>元素大小</h1>
    <div class="text-bg-primary w-25">25%</div>
    <div class="text-bg-secondary w-50">50%</div>
    <div class="text-bg-success w-75">75%</div>
    <div class="text-bg-warning w-100">100%</div>
    <div class="text-bg-danger w-auto">auto</div>
    <div class="container d-flex" style="height:400px;">
        <div class="text-bg-primary h-25">25%</div>
        <div class="text-bg-secondary h-50">50%</div>
        <div class="text-bg-success h-75">75%</div>
        <div class="text-bg-warning h-100">100%</div>
        <div class="text-bg-danger h-auto">auto</div>
    </div>

text

<!-- 对齐方式 -->
    <div class="border border-5 text-end">hello world</div>

    <!-- 换行和溢出 -->
    <div style="width: 300px" class="border border-5 text-nowrap overflow-scroll">
        hello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello
        worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello world
    </div>

    <!-- 字体大小 -->
    <div class="fs-1">hello world</div>

    <!-- 粗细和加斜 fst-italic fst-normal -->
    <div class="fw-bolder">hello world</div>
    <div class="fst-italic">hello world</div>

    <!-- 行高 
        lh-{value}
        value: 取值范围 1 sm base lg
    -->
    <div style="width: 300px" class="border border-5 lh-1">
        hello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello
        world
    </div>

    <!-- 文本装饰线 -->
    <div class="d-inline text-decoration-underline">
        hello world
    </div>
    <div class="d-inline text-decoration-line-through">
        hello world
    </div>
    <a class="text-decoration-none" href="#">hello world</a>

form-control

<div class="card p-3" style="width: 300px;">
        <div class="vstack gap-2">
            <div>
                <label class="form-label">姓名</label>
                <input class="form-control" type="text" />
            </div>
            
            <div>
                <label class="form-label">年龄</label>
                <!-- 设置大小 -->
                <input class="form-control form-control-sm" type="number" />
                <input class="form-control form-control-lg" type="number" />
                <!-- 禁用 disabled -->
                <input class="form-control" type="number" disabled />
                <!-- 只读 readonly -->
                <input class="readonly form-control" type="number" readonly />
            </div>

            <div>
                <label class="form-label">邮箱</label>
                <!-- 朴素输入框 form-control-plaintext -->
                <input class="form-control form-control-plaintext" type="text" readonly value="xxx@xxx.com" />
            </div>

            <div>
                <label class="form-label">简介</label>
                <!-- form-control 相关样式都可以用在文本域上 -->
                <textarea disabled rows="4" class="form-control" type="text"></textarea>
            </div>
        </div>
    </div>

    <!-- label 与 表单元素 左右排列 -->
    <div class="card p-3" style="width: 500px">
        <div class="row">
            <label class="col-3 col-form-label">姓名</label>
            <div class="col">
                <input class="form-control" type="text">
            </div>
        </div>
    </div>

原生表单验证

<body class="d-flex justify-content-center">
    <!-- novalidate 屏蔽默认的表单提交时的验证报告
        屏蔽掉自动的验证报告的目的 是为了我们自己好去控制验证报告
    -->
    <form class="card p-3" style="width: 500px;" onsubmit="return false" novalidate>
        <div class="vstack gap-3">
            <div class="row">
                <label class="col-3 col-form-label">姓名</label>
                <div class="col">
                    <!-- required 必填 -->
                    <!-- <input name="name" required type="text" class="form-control"> -->
                    <!-- pattern 正则表达式 -->
                    <!-- <input name="name" pattern="[\s\S]*张三[\s\S]*" type="text" class="form-control"> -->
                    <!-- minlength 最小长度 maxlength 最大长度 -->
                    <!-- <input name="name" minlength="2" maxlength="10" type="text" class="form-control"> -->
                    <!-- min 最小值 max 最大值 -->
                    <input name="name" min="0" max="200" type="number" class="form-control">
                    <!-- type 类型验证 -->
                    <!-- <input type="email" class="form-control"> -->
                </div>
            </div>
            <div class="row">
                <div class="col-3"></div>
                <div class="col">
                    <button class="btn btn-primary w-100">提交</button>
                </div>
            </div>
        </div>
    </form>

    <!-- 1. 添加 novalidate -->
    <form class="form2 card p-3" style="width: 500px;" onsubmit="return false" novalidate>
        <div class="vstack gap-3">
            <div class="row">
                <label class="col-3 col-form-label">姓名</label>
                <div class="col">
                    <input required minlength="2" maxlength="10" name="name" type="text" class="form-control">
                </div>
            </div>
            <div class="row">
                <label class="col-3 col-form-label">年龄</label>
                <div class="col">
                    <input required min="0" max="150" name="age" type="number" class="form-control">
                </div>
            </div>
            <div class="row">
                <div class="col-3"></div>
                <div class="col">
                    <button class="btn2 btn btn-primary w-100">提交</button>
                </div>
            </div>
        </div>
    </form>
</body>

<script>
    let btn = document.querySelector('button')
    let form = document.querySelector('form')
    let nameInp = document.querySelector('input[name=name]')

    let form2 = document.querySelector('.form2')
    let btn2 = document.querySelector('.btn2')
    let nameInp2 = document.querySelector('.form2 input[name=name]')
    let ageInp = document.querySelector('.form2 input[name=age]')

    btn.addEventListener('click', () => {
        // 可以在此处通过代码验证用户的输入

        // 可以通过输入框的 validity 属性 来判断用户是否输入正确

        // validity.valueMissing -> required 用户没填数据时为 true
        // validity.patternMismatch -> pattern 用户输入不满足正则 true
        // validity.rangeOverflow -> max 用户输入超过最大值 true
        // validity.rangeUnderflow -> min 用户输入小于最小值 true
        // validity.tooLong -> maxlength 用户输入超出长度 不会触发 true
        // validity.tooShort -> minlength 用户输入小于了指定长度 true
        // validity.valid -> 输入没有问题 验证通过 true

        // console.log(nameInp.validity);

        // if(nameInp.validity.tooShort) {
        //     console.log('姓名长度不够');
        // }

        if (nameInp.validity.rangeOverflow) {
            // 手动设置错误提示
            nameInp.setCustomValidity('年龄不能大于200')
        } else if (nameInp.validity.rangeUnderflow) {
            nameInp.setCustomValidity('年龄不能小于0')
        } else {
            console.log(nameInp.validity);
            // 若验证没有问题时应当设置为 空字符串
            nameInp.setCustomValidity('')
        }

        // checkValidity 检测是否表单输入有效
        // 若返回true 代表输入有效 否则为false

        if (!form.checkValidity()) {
            // 显示验证报告
            form.reportValidity()
        } else {
            // 若表单没有问题 输入都有效 则可以执行后续的代码
            // 后续代码写在此处 例如 发送网络请求
            console.log('验证通过');
        }
    })

    // 总结表单验证
    btn2.addEventListener('click', () => {
        // 2. 验证所有表单项
        if (nameInp2.validity.valueMissing) {
            // 3. 设置错误信息
            nameInp2.setCustomValidity('请输入姓名')
        } else if (nameInp2.validity.tooShort) {
            nameInp2.setCustomValidity('请输入至少2个字的姓名')
        } else {
            nameInp2.setCustomValidity('')
        }

        if (ageInp.validity.valueMissing) {
            ageInp.setCustomValidity('请输入年龄')
        } else if (ageInp.validity.rangeOverflow) {
            ageInp.setCustomValidity('请输入不超过150的年龄')
        } else if (ageInp.validity.rangeUnderflow) {
            ageInp.setCustomValidity('请输入不低于0的年龄')
        } else {
            ageInp.setCustomValidity('')
        }

        if (!form2.checkValidity()) {
            // 4. 发起验证报告
            form2.reportValidity()
        } else {
            // 5. 验证通过 执行后续逻辑
            console.log('验证通过');
        }
    })
</script>

bootstrap自动表单验证

<body class="d-flex justify-content-center">
    <!-- 给form添加类名 was-validated 就可以开启验证效果 -->
    <form class="card p-3" style="width: 500px" onsubmit="return false" novalidate>
        <div class="vstack gap-3">
            <div>
                <label class="form-label">姓名</label>
                <input required type="text" class="form-control">
                <!-- 验证的提示需要写在被验证的输入框下面 -->
                <!-- valid-feedback 验证通过的提示文本 -->
                <div class="valid-feedback">
                    ok
                </div>
                <div class="invalid-feedback">
                    请输入姓名
                </div>
            </div>
            <div>
                <label class="form-label">年龄</label>
                <input min="0" max="150" type="number" class="form-control">
                <div class="valid-feedback">
                    ok
                </div>
                <div class="invalid-feedback">
                    请输入0~150的年龄
                </div>
            </div>
            <div>
                <button class="btn btn-primary w-100">提交</button>
            </div>
        </div>
    </form>
</body>

<script>
    let btn = document.querySelector('button')
    let form = document.querySelector('form')
    btn.addEventListener('click', () => {
        form.classList.add('was-validated')
    })
</script>

bootstrap手动表单验证

<body class="d-flex justify-content-center">
    <form class="card p-3" style="width: 500px" onsubmit="return false" novalidate>
        <div class="vstack gap-3">
            <div class="row">
                <label class="col-3 col-form-label">姓名</label>
                <div class="col">
                    <input name="name" type="text" class="form-control">
                    <div class="valid-feedback">ok</div>
                    <div class="invalid-feedback">error</div>
                </div>
            </div>
            <div class="row">
                <label class="col-3 col-form-label">年龄</label>
                <div class="col">
                    <input name="age" type="number" class="form-control">
                    <div class="valid-feedback">ok</div>
                    <div class="invalid-feedback">error</div>
                </div>
            </div>
            <div class="row">
                <div class="col-3"></div>
                <div class="col"><button class="btn btn-primary w-100">提交</button></div>
            </div>
        </div>
    </form>
</body>

<script>
    let nameInp = document.querySelector('input[name=name]')
    let ageInp = document.querySelector('input[name=age]')
    // 查询错误提示的元素
    let nameErrTip = document.querySelector('input[name=name]~.invalid-feedback')
    let ageErrTip = document.querySelector('input[name=age]~.invalid-feedback')

    let btn = document.querySelector('button')

    btn.addEventListener('click', () => {
        // 手动通过代码进行验证

        // 清空所有的 is-valid 和 is-invalid
        nameInp.classList.remove('is-valid', 'is-invalid')
        ageInp.classList.remove('is-valid', 'is-invalid')

        // 定义一个代表验证通过的变量
        let nameValid = true
        if (nameInp.value.trim() === '') {
            nameErrTip.textContent = '请输入姓名'
            nameValid = false
        } else if (nameInp.value.trim().length < 2 || nameInp.value.trim().length > 10) {
            nameErrTip.textContent = '请输入2~10个字的姓名'
            nameValid = false
        }

        let ageValid = true
        if (Number(ageInp.value) < 0 || Number(ageInp.value) > 150) {
            ageErrTip.textContent = '请输入0~150之间的年龄'
            ageValid = false
        }

        nameInp.classList.add(nameValid ? 'is-valid' : 'is-invalid')
        ageInp.classList.add(ageValid ? 'is-valid' : 'is-invalid')

        if (nameValid && ageValid) {
            // 整个表单输入正确的情况
            // 可以执行后续的网络请求
            console.log('验证通过');
        }
    })
</script>

4,react

jsx语法

<body>
<div id="app"></div>
</body>
<script type="text/babel">

    const root = ReactDOM.createRoot(document.querySelector('#app'));

    // 使用jsx创建一个react-dom对象
    // react-dom 是 react 封装的类似 dom 对象的对象
    // 作用: 用于描述页面元素
    const h1 = <h1>hello world</h1>

    // jsx 使用 圆括号 声明多行的 react-dom 对象
    const box = (
        <div className="box">
            <span>hello box</span>
        </div>
    )

    const element = (
        <div>
            root1
        </div>
        // react-dom 中只能有一个根节点
        // <div>
        //     root2
        // </div>
    )


    let now = new Date()

    // 插值
    const chazhi = (
        <div>
            { /*使用花括号进行插值 插值的内容可以是js表达式*/}
            {'当前时间: ' + now.toLocaleString()}
        </div>
    )

    let id = 'thisIsMyId'

    // 定义样式对象
    let myStyle = {
        width: '100px',
        height: '100px',
        backgroundColor: '#f00'
    }

    // 插值 html 属性
    const htmlAttr = (
        <div>
            {/* 插值id */}
            <div id={id}>
                hello attr
            </div>
            {/* 插值 style */}
            {/* style 属性无法直接赋值一个字符串如下: */}
            {/*<div style="width: 100px; height: 100px; background-color: #f00"></div>*/}
            <div style={myStyle}></div>

            {/* 插值 class */}
            <div className="box active"></div>
        </div>
    )

    // 使用函数创建react-dom对象
    const fnDom = React.createElement(
        // 标签名
        'div',
        // 标签上的属性
        {id: 'myFunctionDom', className: 'my-dom', style: {width: '500px', height: '50px', backgroundColor: '#00f'}},
        // 子元素数组 或 标签体字符串
        [
            // 此处的第三个参数就是一个普通的字符串充当 span 标签的标签体
            React.createElement('span', {key: '1'}, 'hello world'),
            // 若此处 createElement 第三个参数是一个数组的话,需要给元素添加 key 属性
            // key 属性是一个唯一值 不重复即可
            React.createElement('span', {key: '2'}, 'hello world')
        ]
    )

    // root.render 函数可以渲染一个 react-dom 对象
    root.render((
        <div className="container">
            {/* 插值的内容 若是 react-dom 对象 那么就会被页面显示出来 */}
            {h1}
            {box}
            {element}
            {chazhi}
            {htmlAttr}
            {fnDom}
        </div>
    ))

</script>

元素渲染

<script type="text/babel">
    let root = ReactDOM.createRoot(document.querySelector('#root'))

    // 初始化渲染
    let now = new Date()
    root.render((
        <div>
            <div className="now">当前时间:</div>
            <div className="time">
                {now.toLocaleString()}
            </div>
        </div>
    ))

    // 渲染循环
    let timer = setInterval(() => {
        // 修改渲染逻辑
        now = new Date()

        // 渲染内容
        root.render((
            <div>
                <div className="now">当前时间:</div>
                {/* react 在每次更新的时候,都会去对比每一个 react-dom 节点
                    只有发现该被对比的节点有变化时(标签体变化 子节点数量变化 节点元素变化等) 才会更新节点
                 */}
                <div className="time">
                    {now.toLocaleString()}
                </div>
            </div>
        ))
    }, 1000)
</script>

条件渲染

<body>
<div id="root"></div>
</body>
<script type="text/babel">
    let sex = 'male'

    ReactDOM.createRoot(document.querySelector('#root')).render((
        <div>
            {/* 使用 && 进行短路运算 前一个表达式为true时 就显示后面表达式的内容 */}
            {sex === 'male' && <div style={{color: '#00f'}}>男</div>}
            {sex === 'female' && <div style={{color: 'pink'}}>女</div>}
            {sex === 'other' && <div style={{color: '#ff0'}}>其他</div>}

            {/* 使用三元运算符 按条件显示不同的内容 */}
            {sex === 'male' ? <div style={{color: '#00f'}}>男</div> :
                sex === 'female' ? <div style={{color: 'pink'}}>女</div> :
                    <div style={{color: '#ff0'}}>其他</div>
            }
        </div>
    ))
</script>

循环渲染

<body>
<div id="root"></div>
</body>
<script type="text/babel">
    let students = [
        {
            name: '张三',
            sex: 'male',
            age: 17
        },
        {
            name: '李四',
            sex: 'female',
            age: 24
        },
        {
            name: '隔壁老王',
            sex: 'other',
            age: 30
        },
    ]

    ReactDOM.createRoot(document.querySelector('#root')).render((
        <div>
            <ul>
                {/* 循环渲染,使用一个数组的map函数 返回一个由 react-dom 充当成员形成的一个新数组
                    循环渲染一定要添加 key
                */}
                {students.map((item,index) => <li key={index}>姓名: {item.name}; 性别: {item.sex === 'male' ? '男' :
                    item.sex === 'female' ? '女' : '不详'
                }; 年龄: {item.age}</li>)}
            </ul>
        </div>
    ))
</script>

受控组件

<div id="root"></div>
</body>
<script type="text/babel">
    // 知识点
    // 什么是受控组件?
    //      被 react 的 state 控制显示和输入的表单元素称为受控组件
    //      受控组件的数据来自 state 而不是表单元素自身
    // react 中 所有的事件都不能通过 return false 来屏蔽默认事件
    // 声明 input[type=text] 、select 和 textarea 的受控组件
    // 声明 input[type=radio] input[type=checkbox] 的受控组件
    function App() {
        const [name, setName] = React.useState('')
        const [clazz, setClazz] = React.useState('')
        const [desc, setDesc] = React.useState('')
        const [sex, setSex] = React.useState('other')
        const [hobbies, setHobbies] = React.useState(['dlq', 'ymq'])

        function submit(ev) {
            ev.preventDefault()
            console.log(name)
            console.log(clazz)
            console.log(desc)
            console.log(sex)
            console.log(hobbies)
        }

        function onNameInput(ev) {
            console.log(ev)
            console.log(ev.target.value)
            // 由于此处的输入框是受控组件
            // 所以若不改变状态, name 值不发生改变,页面就不会更新
            setName(ev.target.value)
        }

        function onClazzChange(ev) {
            console.log(ev.target.value)
            setClazz(ev.target.value)
        }

        function onDescInput(ev) {
            console.log(ev.target.value)
            setDesc(ev.target.value)
        }

        function onSexChange(ev) {
            console.log(ev.target.value)
            setSex(ev.target.value)
        }

        function onHobbiesChange(ev) {
            console.log(ev.target.value)
            if (hobbies.includes(ev.target.value)) {
                // 若已经包含就了删除
                setHobbies(_hobbies => {
                    let i = _hobbies.findIndex(item => item === ev.target.value)
                    _hobbies.splice(i, 1)
                    return [..._hobbies]
                })
            } else {
                // 不存在时 就添加进数组
                setHobbies(_hobbies => {
                    _hobbies.push(ev.target.value)
                    return [..._hobbies]
                })
            }
        }

        return (
            <div>
                <form>
                    <div>
                        <label>
                            姓名:
                            <input type="text" value={name} onInput={onNameInput}/>
                        </label>
                    </div>
                    <div>
                        <label>
                            班级:
                            <select value={clazz} onChange={onClazzChange}>
                                <option value="" disabled>请选择</option>
                                <option value="1">一班</option>
                                <option value="2">二班</option>
                                <option value="3">三班</option>
                            </select>
                        </label>
                    </div>
                    <div>
                        <label>
                            简介:
                            <textarea rows="4" value={desc} onInput={onDescInput}></textarea>
                        </label>
                    </div>
                    <div>
                        <label>
                            性别:
                            <label><input onChange={onSexChange} checked={sex === 'male'} type="radio" value="male"/>男</label>
                            <label><input onChange={onSexChange} checked={sex === 'female'} type="radio"
                                          value="female"/>女</label>
                            <label><input onChange={onSexChange} checked={sex === 'other'} type="radio" value="other"/>其他</label>
                        </label>
                    </div>
                    <div>
                        <label>
                            爱好:
                            <label><input onChange={onHobbiesChange} checked={hobbies.includes('dlq')} type="checkbox"
                                          value="dlq"/>打篮球</label>
                            <label><input onChange={onHobbiesChange} checked={hobbies.includes('tzq')} type="checkbox"
                                          value="tzq"/>踢足球</label>
                            <label><input onChange={onHobbiesChange} checked={hobbies.includes('ymq')} type="checkbox"
                                          value="ymq"/>羽毛球</label>
                        </label>
                    </div>
                    <div>
                        <button onClick={submit}>提交</button>
                    </div>
                </form>
            </div>
        )
    }

    ReactDOM.createRoot(document.querySelector('#root')).render(<App/>)
</script>

状态提升

<div id="root"></div>
</body>
<script type="text/babel">
    // 文档:https://zh-hans.reactjs.org/docs/lifting-state-up.html

    // 知识点
    // 什么是状态提升?
    //      组件向上级组件汇报自己状态的过程叫做状态提升
    // 应用场景
    //      1. 在父组件中需要读取子组件状态时,可以让子组件状态提升给父组件
    //      2. 子组件需要发出某个事件,且事件将携带参数时
    // 如何实现?
    //      理论原理:
    //          组件可以利用自身的 props 属性将自身状态提升到上级组件
    //          表现形式类似于触发事件
    //          上级组件只要绑定事件(本质是提供一个回调函数)接收参数即可
    //      操作方法:
    //          1. 父组件中,给子组件添加 props 属性,并分配一个函数值,该函数接收子组件的状态值为参数
    //          2. 子组件中,在适当时机调用父组件提供的 props,并将自身状态作为参数传入父组件提供的函数

    function Child(props) {
        const [count, setCount] = React.useState(0);

        function increase() {
            setCount(_c => _c + 1)
        }

        // 监视 count 的变化
        React.useEffect(() => {
            // count 发生变化
            // 通知父组件
            // 将状态值作为参数传入
            props.countChange(count)
        }, [count])

        return (
            <div>
                <div>count: {count}</div>
                <div>
                    <button onClick={increase}>+</button>
                </div>
            </div>
        )
    }

    function App() {

        // 此函数的参数用于接收子组件状态
        function onCountChange(args) {
            console.log(args)
        }

        return (
            <div>
                {/* 给子组件分配一个函数 用于接收组件状态 */}
                <Child countChange={onCountChange}></Child>
            </div>
        )
    }

    ReactDOM.createRoot(document.querySelector('#root')).render(<App/>)
</script>

组件通信

<body>
<div id="root"></div>
</body>
<script type="text/babel">
    // 知识点
    // 什么是组件通信
    //      父组件和子组件相互传递参数的过程,就是组件间的通信
    // 应用场景
    //      子组件的状态依赖于父组件传入的 props
    //      由于 props 是只读的
    //      所以要修改 props 子组件只能通知父组件更新数据
    //      这个过程就会用到组件间的通信方法
    // 通信方法:
    //      1. 父组件将自身 state 作为参数传入子组件 props
    //      2. 子组件依赖 props 显示内容
    //      3. 子组件内希望修改 props 中的值,则发出一个事件
    //      4. 父组件绑定子组件发出的事件并接收参数
    //      5. 父组件接收事件后更新自身 state,此时 react 会自动更新子组件 props

    class MyTr extends React.Component {
        onNameInput(ev) {
            console.log(ev.target.value)
            // 若想修改 this.props.name
            // 那么只能通过通知父组件,让父组件修改
            // this.props.onNameInput(this.props.listId, ev.target.value)
            console.log(this)
        }

        render() {
            return (
                <tr>
                    <td>{this.props.listId}</td>
                    <td><input type="text" value={this.props.name} onInput={this.onNameInput.bind(this)}/></td>
                    <td>{this.props.sex}</td>
                    <td>{this.props.age}</td>
                    <td>
                        <button>删除</button>
                    </td>
                </tr>
            )
        }
    }

    function App() {
        const [list, setList] = React.useState([
            {
                id: 0,
                name: '张三',
                sex: 'male',
                age: 17
            },
            {
                id: 1,
                name: '李四',
                sex: 'female',
                age: 24
            },
            {
                id: 2,
                name: '老王',
                sex: 'other',
                age: 30
            },
        ]);

        // 父组件接收子组件的状态提升
        function onNameInput(listId, newValue) {
            console.log(listId, newValue)
            // 修改父组件的状态
            // 从而更新子组件
            setList(_list => {
                // 查找id相同的对象索引
                let i = _list.findIndex(item => item.id === listId)
                // 赋值新的值
                _list[i].name = newValue
                return [..._list]
            })
        }

        return (
            <div>
                <table border="true">
                    <thead>
                    <tr>
                        <th>id</th>
                        <th>姓名</th>
                        <th>性别</th>
                        <th>年龄</th>
                        <th>操作</th>
                    </tr>
                    </thead>
                    <tbody>
                    {list.map(item => <MyTr onNameInput={onNameInput} key={item.id} listId={item.id} name={item.name}
                                            sex={item.sex}
                                            age={item.age}></MyTr>)}
                    </tbody>
                </table>
            </div>
        )
    }

    ReactDOM.createRoot(document.querySelector('#root')).render(<App/>)
</script>

错误边界(异常处理)

<body>
<div id="root"></div>
</body>
<script type="text/babel">
    // 知识点
    // 什么是错误边界
    //      react 的错误边界类似 js 的 catch
    //      错误边界是一个 react 组件,其内部的子组件一旦发生异常就会触发错误边界
    // 应用场景
    //      需要对页面异常做出更友好的提示的时候,可以使用错误边界
    // 如何使用?
    //      1. 错误边界需要是一个 class 组件(函数组件不能充当错误边界)
    //      2. 错误边界需要声明一个 hasError 状态
    //      3. 错误边界需要实现生命周期钩子函数: static getDerivedStateFromError(error) 该函数返回要修改的状态,通常是{hasError: true} ; componentDidCatch(error, errorInfo) [该函数非必要]
    //      4. 错误边界的渲染函数的内容,根据 hasError 状态进行调整
    //      5. 使用错误边界的时候,在错误边界的标签体中嵌入其他子组件
    //      6. 子组件生命周期任何阶段发生异常,都会被错误边界捕获到,从而显示错误的提示

    // 使用 类组件声明错误边界
    class ErrorBoundary extends React.Component {
        state = {
            // hasError 状态值用于指示 错误边界 内的子组件是否有错误
            hasError: false
        }

        // 当组件发生异常时,在调用 render 函数前先会调用此函数
        // error 当前正被触发的异常对象
        static getDerivedStateFromError(error) {
            console.log('getDerivedStateFromError')
            console.log(error)
            // 此处在渲染之前修改 hasError 状态为 true 代表产生了异常
            return {hasError: true}
        }

        // 此为非必填函数
        // 该生命周期将在捕获异常完成并渲染完成后触发
        componentDidCatch(error, errorInfo) {
            console.log('componentDidCatch')
            console.log(error)
            console.log(errorInfo)
        }

        render() {
            // render 函数通过 hasError 判断应该显示什么内容
            // 没有异常就显示 ErrorBoundary 的标签体里的内容 否则显示一个友好的错误提示
            return this.state.hasError ? <div>错误时候看到的内容</div> :
                <div>{this.props.children}</div>
        }
    }

    class Child extends React.Component {
        render() {
            // 子组件中任意位置出现异常 则会被错误边界捕获到
            abc.ok()

            return (
                <div>
                    this is child
                </div>
            )
        }
    }

    function App() {
        return (
            <div>
                {/* 使用错误边界包裹正常显示的内容,此处的显示内容为 Child组件
                    那么当 Child 组件抛出异常时 则 ErrorBoundary 就显示友好的提示
                 */}
                <ErrorBoundary>
                    <Child></Child>
                </ErrorBoundary>
            </div>
        )
    }

    ReactDOM.createRoot(document.querySelector('#root')).render(<App/>)
</script>

元素分组Fragments

<body>
<div id="root"></div>
</body>
<script type="text/babel">
    function App() {
        const [list, setList] = React.useState([
            {
                name: '张三',
                sex: 'male'
            },
            {
                name: '李四',
                sex: 'female'
            },
        ]);

        return (
            <div>
                {/* React.Fragment 内置组件起到一个给元素分组的作用,最终不会显示到页面上
                    作用类似小程序中的 <block> 标签
                 */}
                {list.map((item, i) => (
                    <React.Fragment key={i}>
                        <div>{item.name}</div>
                        <div>{item.sex}</div>
                    </React.Fragment>
                ))}
            </div>
        )
    }

    ReactDOM.createRoot(document.querySelector('#root')).render(<App/>)
</script>

Context上下文

<body>
<div id="root"></div>
</body>
<script type="text/babel">
    // 文档:https://zh-hans.reactjs.org/docs/context.html

    // 知识点

    // 什么是 context?
    //      context 是一对 组件,用于向后代组件提供参数
    //      注意: context 的数据是只读的
    // 应用场景
    //      在一个典型的 React 应用中,数据是通过 props 属性自上而下(由父及子)进行传递的
    //      但此种用法对于某些类型的属性而言是极其繁琐的
    //      (例如:地区偏好,UI 主题,因为他们是全局属性,每个和ui相关组件都应该读取其值)
    //      所以:很多不同层级的组件需要访问一些同样的数据,可以考虑使用 context
    // 概念
    //      context 提供了一对组件 如下:
    //      Provider(供应商) 提供数据的组件
    //      Consumer(消费者) 消费数据的组件
    // 使用方法
    //      1. 创建 context
    //          语法:const MyContext = React.createContext(defaultValue);
    //          defaultValue 若组件找不到对应 context 时会获取默认值
    //          context 的值可以是一个对象
    //      2. 页面中将 MyContext.Provider 作为父节点使用,并传入 value 参数作为 Provider 提供的数据
    //      3. 子组件接收 Provider 提供的数据
    //          3.1 类组件接收方法:
    //              1. 给类名的 contextType 赋值 context 对象,例如:
    //                  MyClassComponent.contextType = MyContext
    //              2. 在组件内使用 this.context 访问 Provider 提供的数据
    //          3.2 函数组件接收方法:
    //              1. 在页面中,给要使用数据的函数组件套上一个 Consumer 父组件
    //              2. 在 Consumer 内 通过工厂函数返回要接收 Provider 数据的函数组件,工厂函数接收一个 value 参数,该参数即为 Provider 提供的数据
    //              3. 函数组件通过 props 接收 value 参数
    //                  例如:
    //                  <MyContext.Consumer>
    //                      {value => <FunctionComponent theme={value}></FunctionComponent>}
    //                  </MyContext.Consumer>
    // Provider 提供一个动态可变的值
    // 绑定多个 Context
    //      官网:https://zh-hans.reactjs.org/docs/context.html#consuming-multiple-contexts
    //      重点在于利用多重嵌套的 Context.Consumer,来实现绑定多个 Context


    // 创建 context 上下文
    // 默认值什么时候会被使用?
    // 读取上下文的组件不在上下文中时 会用到默认值
    const ThemeContext = React.createContext('dark');

    class ClazzComponent extends React.Component {
        render() {
            return (
                // 类组件中可以使用 this.context 来访问 Provider 提供的 value
                <div style={{
                    height: '100px',
                    backgroundColor: this.context === 'light' ? '#ff0' : '#000',
                    color: this.context === 'light' ? '#000' : '#fff'
                }}>
                    <h1>类组件</h1>
                </div>
            )
        }
    }

    function FnComponent(props) {
        return (
            // 函数组件通过 props 读取 context 赋值的内容
            <div style={{
                height: '100px',
                backgroundColor: props.theme === 'light' ? '#ff0' : '#000',
                color: props.theme === 'light' ? '#000' : '#fff'
            }}>
                <h1>函数组件</h1>
            </div>
        )
    }

    // 给组件指定对应的上下文对象
    ClazzComponent.contextType = ThemeContext


    function App() {
        // 保存主题的状态
        const [theme, setTheme] = React.useState('light');

        function changeTheme() {
            setTheme(_t => {
                return _t === 'light' ? 'dark' : 'light'
            })
        }

        return (
            <div>
                {/* 若组件在 Provider 的外部 则context将使用默认值 */}
                <ClazzComponent></ClazzComponent>
                <button onClick={changeTheme}>{theme}</button>
                {/* 声明供应商 value 属性就是供应商要提供给消费者的数据 */}
                <ThemeContext.Provider value={theme}>
                    <ClazzComponent></ClazzComponent>
                    <ThemeContext.Consumer>
                        {/* 函数组件使用 Consumer 进行包裹
                            要使用一个工厂函数返回一个函数组件
                            工厂函数的参数value就代表着 Provider 提供的value
                            通过函数组件的props将value设置进去
                        */}
                        {value => <FnComponent theme={value}></FnComponent>}
                    </ThemeContext.Consumer>
                </ThemeContext.Provider>
            </div>
        )
    }

    ReactDOM.createRoot(document.querySelector('#root')).render(<App/>)
</script>

refs转发

<body>
<div id="root"></div>
</body>
<script type="text/babel">

    // 文档:https://zh-hans.reactjs.org/docs/forwarding-refs.html
    // https://zh-hans.reactjs.org/docs/refs-and-the-dom.html

    // 知识点
    // 什么是转发?
    //      转发(ref) 的作用是获取组件中的 dom 或 一个组件
    // 应用场景
    //      1. 在 react 项目中 若希望通过 document.querySelector 获取某个元素时,我们应该使用 ref 替代
    //      2. 当需要获取某个子组件的状态或调用组件的方法的时候
    // 注意:函数组件上 不能添加 ref
    // 使用方法:
    //      类组件使用 React.createRef()
    //      函数组件使用 React.useRef()
    //      通用方法 使用箭头函数 el => { temp = el }
    // React.forwardRef

    class ClazzComponent extends React.Component {

        // 创建一个 ref 对象 并保存到组件属性中
        inpRef = React.createRef()

        inpEl

        // 声明属性保存子组件
        child
        child2

        onClick() {
            // 可以通过 this.inpRef.current 来获取 input 元素
            // console.log(this.inpRef.current.value)

            console.log(this.inpEl.value)
        }

        onClick2() {
            // 获取子组件
            console.log(this.child)
            // 调用子组件的属性
            console.log(this.child.state)
            // 调用子组件的方法
            this.child.clear()

            console.log(this.child2.value)
        }

        render() {
            return (
                <div>
                    <h1>类组件</h1>
                    {/* 给元素的 ref 属性 赋值一个 ref 对象 */}
                    {/*<input ref={this.inpRef} type="text"/>*/}
                    {/* 此处 el 代表的就是当前元素 input  */}
                    <input ref={el => {
                        this.inpEl = el
                    }} type="text"/>
                    <button onClick={this.onClick.bind(this)}>读取输入框的值</button>
                    {/* 注意: ref 属性可以写在 类组件上 但不可以写在函数组件上 */}
                    <Child ref={child => {
                        this.child = child
                    }}></Child>
                    <Child2 ref={child2 => {
                        this.child2 = child2
                    }}></Child2>
                    <button onClick={this.onClick2.bind(this)}>清空child的输入框</button>
                </div>
            )
        }
    }

    function FnComponent() {

        // 函数组件内使用 useRef 来创建ref 对象
        const inpRef = React.useRef()

        const [inpEl, setInpEl] = React.useState(null)

        function onClick() {
            // 此处的 inpRef 和类组件相同
            // 可以使用 inpRef.current 读取元素
            // console.log(inpRef.current.value)

            console.log(inpEl.value)
        }

        return (
            <div>
                <h1>函数组件</h1>
                {/*<input ref={inpRef} type="text"/>*/}
                <input ref={el => {
                    setInpEl(el)
                }} type="text"/>
                <button onClick={onClick}>读取输入框的值</button>
            </div>
        )
    }

    class Child extends React.Component {
        state = {
            value: ''
        }

        // 清空输入框
        clear() {
            this.setState({value: ''})
        }

        render() {
            return (
                <div>
                    <h3>child</h3>
                    <input value={this.state.value} onInput={ev => {
                        this.setState({value: ev.target.value})
                    }} type="text"/>
                </div>
            )
        }
    }

    // 函数组件上无法使用 ref
    // function Child2() {
    //     return (
    //         <div>child2</div>
    //     )
    // }

    // 我们可以使用 React.forwardRef 来创建一个函数组件
    // React.forwardRef 创建的函数组件就可以使用ref
    // props: 函数组件的 props
    // ref: ref对象
    // 返回值: 函数组件
    const Child2 = React.forwardRef((props, ref) => {
        // 此处的内容就是函数组件的内容

        const [value, setValue] = React.useState('');

        return (
            <div>
                <h3>child2</h3>
                <input ref={ref} value={value} onInput={ev => {
                    setValue(ev.target.value)
                }} type="text"/>
            </div>
        )
    })

    function App() {
        return (
            <div>
                <ClazzComponent></ClazzComponent>
                <FnComponent></FnComponent>
            </div>
        )
    }

    ReactDOM.createRoot(document.querySelector('#root')).render(<App/>)
</script>

Portals传送门

<body>
<div id="other"></div>
<div id="root"></div>
</body>
<script type="text/babel">
    // 知识点
    // 什么是传送门
    //      传送门是 react 通过 ReactDOM.createPortal 方法创建的一个特殊的 react-dom
    //      传送门的内容可以显示到 html 文档的任何位置 甚至是 react 根节点外面
    // 应用场景
    //      制作模态等脱离自身组件结构的内容
    // 注意:传送门的使用必须配合无状态的函数组件,class组件是无法使用的
    // 使用方法:
    //      1. 创建一个函数组件充当传送门组件(该函数组件不能有状态)
    //      2. 函数组件内使用 ReactDOM.createProtal 方法创建传送门
    //          ReactDOM.createPortal 函数将返回一个可以被渲染到页面的一组html元素
    //          第一个参数:要传送的 react-dom
    //          第二个参数:传送门的目标节点,最后元素将渲染到该节点

    // 传送门组件
    // 传送门组件必须是函数组件 且不能包含状态
    function Portal(props) {
        // 使用 props.show 属性来指示传送门是否显示
        return props.show ? ReactDOM.createPortal((
            <div>
                <h3>这是传送们的内容</h3>
                <div>我是传送门</div>
            </div>
        ), document.querySelector('#other')) : null
    }

    function App() {

        // 声明一个传送门当前显示状态
        const [show, setShow] = React.useState(false);

        // 开关函数
        function switchPortal() {
            setShow(_s => !_s)
        }

        return (
            <div>
                <h1>App</h1>
                <button onClick={switchPortal}>开关传送门</button>
                {/* 插入一个传送门组件 */}
                <Portal show={show}></Portal>
            </div>
        )
    }

    ReactDOM.createRoot(document.querySelector('#root')).render(<App/>)
</script>

Pofiler检测器

<body>
<div id="app"></div>
</body>
<script type="text/babel">
    // Profiler 探测器
    // 探测器是一个用于优化页面检测页面运行效率的工具,在生产环境下禁用,因为他会带来额外的开销

    function MyDate() {
        const [date, setDate] = React.useState(new Date())

        React.useEffect(() => {
            setInterval(() => {
                setDate(new Date())
            }, 1000)
            return () => {
            }
        }, [])

        return (
            <React.Fragment>
                {date.toLocaleString()}
            </React.Fragment>
        )
    }

    function App() {
        return (
            <div>
                <MyDate/>
            </div>
        )
    }

    ReactDOM.createRoot(document.querySelector('#app')).render((
        <React.Profiler id="App" onRender={appCallback}>
            <App/>
        </React.Profiler>
    ))

    function appCallback(
        id, // the "id" prop of the Profiler tree that has just committed
        phase, // either "mount" (if the tree just mounted) or "update" (if it re-rendered)
        actualDuration, // time spent rendering the committed update
        baseDuration, // estimated time to render the entire subtree without memoization
        startTime, // when React began rendering this update
        commitTime, // when React committed this update
        interactions // the Set of interactions belonging to this update
    ) {
        console.log(id)
        console.log(phase)
        console.log(actualDuration)
        console.log(baseDuration)
        console.log(startTime)
        console.log(commitTime)
        console.log(interactions)
    }
</script>

5,懒加载

什么是懒加载?

懒加载就是页面中看不到时,就不去加载它,当页面中出现该内容时再去加载

懒加载多用于图片和媒体文件

好处:

	好处在于用户看不见的东西就不用使用浏览器去下载了

	还可以让页面加载更快

需要使用的东西

  1. 给需要懒加载的元素添加 data-src
  2. 给滚动元素添加 scroll 事件监听器
  3. 计算显示图片的临界值 scrollTop = 内容高 - 图片高 - 窗口高
  4. 判断容器元素的 scrollTop 大于临界值 加载 data-src

举例:

<body>
    <div class="container">
        <div class="content">
            <img class="img" height="200" src="./img/avatar-max-img.png" data-src="./img/1.png" alt="">
        </div>
    </div>
</body>
<script>
    const container = document.querySelector('.container')
    const content = document.querySelector('.content')
    const img = document.querySelector('.img')

    // 绑定滚动事件
    container.addEventListener('scroll', ev => {
        // console.log(ev);
        console.log('scrollTop1', ev.target.scrollTop);

        // 计算显示图片的临界值
        // scrollTop = 内容高 - 图片高 - 窗口高
        let scrollTop = content.clientHeight - 200 - innerHeight
        console.log('scrollTop2', scrollTop);

        if (ev.target.scrollTop >= scrollTop) {

            console.log('done');
            console.log(img.src);
            console.log(!img.src);

            // 通过自定义属性 done 来控制懒加载的触发
            if (!img.getAttribute('done')) {
                // 加载图片
                // 读取资源url
                let url = img.getAttribute('data-src')
                // 赋值图片 url 赋值后 图片会被自动加载
                img.src = url

                img.setAttribute('done', 'true')
            }
        }
    })
</script>

6,总结

bootstrap和react都很好用,其中,bootstrap主要是框架,使用bootstrap可以帮助我们节约许多的代码。可以让我们的代码量更加的短
而react分为声明式和组件式
其中声明式也就是js中的数据决定页面最终渲染的结果
组件式组件化的思想可以将复杂页面,化繁为简的进行设计,并且组件可提高代码复用性
这两个在平时写前端的时候应该都能用的比较平凡

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值