前端知识1-3:模块化+浏览器详解

script标签两个变量参数 - async & defer
javascript 复制代码
    <script src="main.js" async></script>
  • 普通 - 解析到标签,立刻pending,并且下载执行
  • defer - 解析到标签,开始异步下载,解析完成之后开始执行
  • async - 解析到标签,开始异步下载,下载完成后立刻执行并阻塞原解析,执行结束后,继续解析
问题出现:
  • 污染作用域=>不利于大型项目的开发以及多人团队的共建

commonjs规范

nodejs制定

  • 优点:
    CJS率先在服务实现了从框架层面解决依赖、模块化的问题
  • 缺憾
    针对的是服务端,对于异步依赖没有很友好地处理解决

特征:

  • 通过module+export对外暴露接口
  • 通过require进行其他模块的调用

main.js

javascript 复制代码
    const depModule1 = require('./dependecncyModule1')
    const depModule2 = require('./dependecncyModule2')

    let count = 0;
    const obj = {
        increase: () => ++count;
        reset: () => {
            count = 0;
            // fn(depModule1);
            // depModule1, depModule2
        }
    }

    exports.increase = increase;
    exports.reset = reset;

    module.export = {
        increase,
        reset
    }
javascript 复制代码
    const {
        increase,
        reset
    } = require('main.js');

    increase();
    reset();

实际执行处理

javascript 复制代码
    (function(thisValue, exports, require, module) {
        const depModule1 = require('./dependecncyModule1')
        const depModule2 = require('./dependecncyModule2')

        // 业务逻辑......
    }).call(thisValue, exports, require, module);

    // 部分开源源码,分别传入全局、指针、框架作为参数
    (function(window, _, undefined) {
        // 业务逻辑......
    })(window, lodash);
    // window
    // 1. 避免全局变化 | 全局作用域转化为局部的时候,提升效率
    // 2. 编译时候优化压缩 (function(c){c}(window))
    // lodash
    // 1. 独立定制复写,保障稳定
    // 2. 防止全局工具的全局污染
    // undefined
    // 防止被重写

AMD规范

通过异步加载 + 允许定制回调函数

经典框架:require.js

javascript 复制代码
    define(id, [depModule], callback);
    require([module], callback);

    // 例子
    define('amdModule', [depModule1, depModule2], (depModule1, depModule2) => {
        let count = 0;
        const obj = {
            increase: () => ++count;
            reset: () => {
                count = 0;
                // fn(depModule1);
                // depModule1, depModule2
            }
        }
        // ....
    })

    // 使用
    require(['amdModule'], amdModule => {
        amdModule.increase();
    })

CMD规范 - sea.js

  • 优点:按需加载,依赖就近
  • 缺憾:依赖打包,加载逻辑存在于每个模块中,扩大了模块的体积
javascript 复制代码
    define('module', (require, exports, module) => {
        let $ = require('jquery');
        let depModule1 = require('./dependencyModule1');
        // ......
    })

ESM

引入------import

导出------export

javascript 复制代码
    import depModule1 from './dependecncyModule1';
    import depModule2 from './dependecncyModule2';

    let count = 0;
    const obj = {
        increase: () => ++count;
        reset: () => {
            count = 0;
            // fn(depModule1);
            // depModule1, depModule2
        }
    }


    export default {
        increase,
        reset
    }

    // 异步加载
    import('xxx').then(a => {
        // ../
    })

浏览器相关

一、认识浏览器运行态下的JS

包含:BOM DOM ECMAScript
js 复制代码
    (function(global, context, undefined) {
        const _class = ['js', 2, {
            name: 'vue_base'
        }, {
            name: 'vue_promote',
            index: {}
        }];

        global.classArr = _class.map(item => item);

        const _url = location.href; // 路径地址相关

        document.title = 'browser';

        document.getElementById('app');
    })(window, this)

    // 简述
    // ECMAScript - 基础逻辑、数据处理
    // DOM - 对于浏览器视窗内HTML文本的相关操作
    // BOM - 对浏览器本身功能区域做的处理

二、BOM

1. location

location.href => 'https:// www.course.com/search?class=browser#comments'

.origin => https://www.course.com

.protocol => https

.host => www.course.com

.port => ''

.pathname => /search/

.search => ?class=browser

.hash => #comments

location.assign('url') 跳转到指定path,并替换pathname => path

.replace('url') 效果同上,同时替换浏览历史

.reload()

.toString()

  • 面试方向
  1. location本身api操作
  2. 路由相关:跳转、参数、操作 => 场景:history hash
  3. url处理 ------ 正则 or 手写处理
2. history

history.state => 存储获取当前页面状态

.replaceState => 替换当前状态

  • 浏览器系统信息大集合
js 复制代码
    navigator.userAgent

面试方向:

  1. UA读取系统信息 => 浏览器兼容性
  2. 剪切板、键盘
  3. 系统信息采集 => 数据上报 => 数据采集汇总
4. screen

表示显示区域 ------ 屏幕

  • 面试方向 - 判断区域大小

    window视窗判断:

    全局入口处

    window.innerHeight

    window.innerWidth

    文本处获取

    document.documentElement.clientHeight

    document.documentElement.clientWidth

    网页内容size => offsetHeight = clientHeight + 滚动条 + 边框

    document.documentElement.offsetHeight

    document.documentElement.offsetWidth

    定位:

    scrollLeft / scrollTop - 距离常规左/上滚动的距离

    offsetLeft / offsetTop - 距离常规左/上距离

    el.getBoundingClientRect()

    el.getBoundingClientRect().top

    el.getBoundingClientRect().left

    el.getBoundingClientRect().bottom

    el.getBoundingClientRect().right

    • 兼容性 - IE会多出2像素

三、事件模型

js 复制代码
    <div id="app">
        <p id="dom">Click</p>
    </div>

    // 冒泡: p => div => body => html => document
    // 捕获:document => html => body => div => p
    el.addEventListener(event, function, useCaption) // useCaption 默认 - false

    // 追问:
    // 1. 如何去阻止事件传播?
    event.stopPropagation(); // => 无法阻止默认事件的发生,比如:a标签跳转

    // 2. 如何阻止默认事件
    event.preventDefault();

    // 3. 相同节点绑定了多个同类事件,如何阻止?
    event.stopImmediatePropagation();
    // 面试核心:区分不同阻止

    // 4. 手写,实现一个多浏览器兼容的事件绑定
    // attachEvent vs addEventListener
    // 区别:
    // a. 传参 attachEvent对于事件名需要加上'on'
    // b. 执行顺序 attachEvent - 后绑定先执行;addEventListener - 先绑定先执行
    // c. 解绑 dettachEvent vs removeEventListener
    // d. 阻断 e.cancelBubble = true vs e.stopPropagation()
    // e. 阻断默认事件 e.returnValue vs e.preventDefault
    class bindEvent {
        constructor(element) {
            this.element = element;
        }
        addEventListener = (type, handler) => {
            if (this.element.addEventListener) {
                this.element.addEventListener(type, handler, false);
            } else if (this.element.attachEvent) {
                const element = this.element;
                this.element.attachEvent('on' + type, () => {
                    handler.call(element);
                });
            } else {
                this.element['on' + type] = handler;
            }
        }

        removeEventListener = (type, handler) => {
            if (this.element.removeEventListener) {
                this.element.removeEventListener(type, handler, false);
            } else if (this.element.detachEvent) {
                const element = this.element;
                this.element.detachEvent('on' + type, () => {
                    handler.call(element);
                });
            } else {
                this.element['on' + type] = null;
            }
        }

        static stopPropagation(e) {
            if (e.stopPropagation) {
                e.stopPropagation();
            } else {
                e.cancelBubble = true;
            }
        }

        static preventDefault(e) {
            if (e.preventDefault) {
                e.preventDefault();
            } else {
                e.returnValue = false;
            }
        }
    }

    // 5. 性能优化 - 事件代理
    <ul class="list">
        <li>1</li>
        <li>2</li>
        <li>3</li>
        <li>4</li>
        <li>5</li>
        <li>6</li>
    </ul>
    <div class="content"></div>

    var list = document.querySelector(".list");
    var li = list.getElementsByTagName("li");
    var content = document.querySelector(".content");

    // 硬碰硬
    for(var n = 0; n < li.length; n++) {
        li[n].addEventListenr('click', function() {
            // 点击逻辑
        })
    }

    // 代理后 - 利用冒泡
    function onClick(e) {
        var e = e || window.event;
        if (e.target.nodeName,toLowerCase() === 'li') {
            const liList = this.querySelectorAll("li")
            index = Arrary.prototype.indexOf.call(liList, target);
        }
    }

    list.addEventListener('click', onClick, false)

四、网络

js 复制代码
    // 实例化
    const xhr = new XMLHttpRequest()

    // 发送
    // 初始化连接 - open初始化连接不会发起真正的请求
    xhr.open(method, url, async)

    // 发送请求
    // 当请求方法为post时 - body请求体
    // 当请求方法为get时 - 可以为空
    // 都需要encodeURIComponent进行转码
    xhr.send(data)

    // 接受
    xhr.readyStatus
    // 0 - 尚未调用open
    // 1 - 已调用open
    // 2 - 已调用send
    // 3 - 已接受请求返回数据
    // 4 - 已完成请求

    xhr.onreadystatechange = () => {
        if (xhr.readyStatus === 4) {
            if (xhr.status === 200) {
                // 逻辑
            }
        }
    }

    // 超时
    xhr.timeout = 1000;

    // 超时触发方法
    xhr.ontimeout = () => {
        // trace
    }

    // 手写请求封装
    ajax({
        url: 'reqUrl',
        method: 'get',
        async: true,
        timeout: 30000,
        data: {
            payload: 'text'
        }
    }).then(
        res => console.log('成功'),
        err => console.log('失败')
    )

    function ajax(options) {
        // 参数读取
        const {
            url,
            method,
            async,
            data,
            timeout
        } = options;

        // 实例化
        const xhr = new XMLHttpRequest()

        return new Promise((resolve, reject) => {
            // 成功
            xhr.onreadystatechange = () => {
                if (xhr.readyStatus === 4) {
                    if (xhr.status === 200) {
                        // 逻辑
                        resolve && resolve(xhr.responseText)
                    } else {
                        reject && reject()
                    }
                }
            }

            // 失败
            xhr.ontimeout = () => reject && reject('超时')
            xhr.onerror = () => reject && reject(err)

            // 传参处理
            let _params = []
            let encodeData

            if (data instanceof Object) {
                for (let key in data) {
                    _params.push(encodeURIComponent(key) + '=' + encodeURIComponent(data[key]))
                }
                encodeData = _params.join('&')
            }

            // methods处理
            // get类型拼接到url
            if (method === 'get') {
                const index = url.indexOf('?')
                if (index === -1) {
                    url += '?'
                } else if (index !== url.length -1) {
                    url += '&'
                }

                url += encodeData
            }

            // 建立连接
            xhr.open(method, url, async)

            if (method === 'get') {
                xhr.send(null)
            } else {
                xhr.setRequestHeader(
                    'Content-type', 'application/x-www-form-urlencoded;chartset=UTF-8'
                )
                xhr.send(encodeData)
            }
        })
    }
相关推荐
GIS程序媛—椰子1 小时前
【Vue 全家桶】7、Vue UI组件库(更新中)
前端·vue.js
DogEgg_0011 小时前
前端八股文(一)HTML 持续更新中。。。
前端·html
ZL不懂前端1 小时前
Content Security Policy (CSP)
前端·javascript·面试
木舟10091 小时前
ffmpeg重复回听音频流,时长叠加问题
前端
王大锤43911 小时前
golang通用后台管理系统07(后台与若依前端对接)
开发语言·前端·golang
我血条子呢2 小时前
[Vue]防止路由重复跳转
前端·javascript·vue.js
黎金安2 小时前
前端第二次作业
前端·css·css3
啦啦右一2 小时前
前端 | MYTED单篇TED词汇学习功能优化
前端·学习
半开半落2 小时前
nuxt3安装pinia报错500[vite-node] [ERR_LOAD_URL]问题解决
前端·javascript·vue.js·nuxt