GVKun编程网logo

Nacos深入浅出(二)(nacos底层实现原理)

12

本文将介绍Nacos深入浅出的详细情况,特别是关于二的相关信息。我们将通过案例分析、数据研究等多种方式,帮助您更全面地了解这个主题,同时也将涉及一些关于awk语法深入浅出(一)、Axios深入浅出、D

本文将介绍Nacos深入浅出的详细情况,特别是关于的相关信息。我们将通过案例分析、数据研究等多种方式,帮助您更全面地了解这个主题,同时也将涉及一些关于awk 语法深入浅出(一)、Axios深入浅出、DOM 事件深入浅出(一)、DOM 事件深入浅出(二)的知识。

本文目录一览:

Nacos深入浅出(二)(nacos底层实现原理)

Nacos深入浅出(二)(nacos底层实现原理)

如果你的服务已经能正常跑起来,个人建议可以先感受下nacos的魅力,也就是怎么使用吧

直接上代码

@Controller
@NacosPropertySource(dataId = "springboot2-nacos-config", autoRefreshed = true)
public class HealthController {


    @NacosValue(value = "${nacos.test.propertie:123}", autoRefreshed = true)
    private String testProperties;

    @ResponseBody
    @GetMapping("/nacos/test")
    public String test(){
        return testProperties;
    }

    @ResponseBody
    @RequestMapping(value = "/readiness", method = RequestMethod.GET)
    public Object readiness(HttpServletRequest request) {
        boolean isConfigReadiness = true;
        boolean isNamingReadiness = false;

        if (isConfigReadiness && isNamingReadiness) {
            return ResponseEntity.ok().body("OK");
        }

        if (!isConfigReadiness && !isNamingReadiness) {
            return ResponseEntity.status(500).body("Config and Naming are not in readiness");
        }

        if (!isConfigReadiness) {
            return ResponseEntity.status(500).body("Config is not in readiness");
        }

        return ResponseEntity.status(500).body("Naming is not in readiness");
    }
}

我们直接用postman请求下,直接返回结果了,

我们在nacos中的相关配置如下:

这样我就很方便的把我们需要的值取过来了,而且不是通过DB,cache方式获取,而是在远程nacos中配置,我们能够实时获取的到;

那么问题就来了,nacos是怎么做到的呢,当然nacos的功能远不止于此;

我们就根据这个作为一个最直观的入口,跟下代码:

在nacos中发布的操作

 

 找代码v1/cs/configs,当然再试的时候可以找V1/CS

Constants.java
public static final String BASE_PATH = "/v1/cs";

public static final String CONFIG_CONTROLLER_PATH = BASE_PATH + "/configs";

 

 

/**
     * 增加或更新非聚合数据。
     *
     * @throws NacosException
     */
    @RequestMapping(method = RequestMethod.POST)
    @ResponseBody
    public Boolean publishConfig(HttpServletRequest request, HttpServletResponse response,
                                 @RequestParam("dataId") String dataId, @RequestParam("group") String group,
                                 @RequestParam(value = "tenant", required = false, defaultValue = StringUtils.EMPTY)
                                     String tenant,
                                 @RequestParam("content") String content,
                                 @RequestParam(value = "tag", required = false) String tag,
                                 @RequestParam(value = "appName", required = false) String appName,
                                 @RequestParam(value = "src_user", required = false) String srcUser,
                                 @RequestParam(value = "config_tags", required = false) String configTags,
                                 @RequestParam(value = "desc", required = false) String desc,
                                 @RequestParam(value = "use", required = false) String use,
                                 @RequestParam(value = "effect", required = false) String effect,
                                 @RequestParam(value = "type", required = false) String type,
                                 @RequestParam(value = "schema", required = false) String schema)
        throws NacosException {
        final String srcIp = RequestUtil.getRemoteIp(request);
        String requestIpApp = RequestUtil.getAppName(request);
// 参数校验 ParamUtils.checkParam(dataId, group,
"datumId", content); ParamUtils.checkParam(tag); Map<String, Object> configAdvanceInfo = new HashMap<String, Object>(10); if (configTags != null) { configAdvanceInfo.put("config_tags", configTags); } if (desc != null) { configAdvanceInfo.put("desc", desc); } if (use != null) { configAdvanceInfo.put("use", use); } if (effect != null) { configAdvanceInfo.put("effect", effect); } if (type != null) { configAdvanceInfo.put("type", type); } if (schema != null) { configAdvanceInfo.put("schema", schema); } ParamUtils.checkParam(configAdvanceInfo); // 白名单 if (AggrWhitelist.isAggrDataId(dataId)) { log.warn("[aggr-conflict] {} attemp to publish single data, {}, {}", RequestUtil.getRemoteIp(request), dataId, group); throw new NacosException(NacosException.NO_RIGHT, "dataId:" + dataId + " is aggr"); } final Timestamp time = TimeUtils.getCurrentTime(); String betaIps = request.getHeader("betaIps"); ConfigInfo configInfo = new ConfigInfo(dataId, group, tenant, appName, content);
if (StringUtils.isBlank(betaIps)) { if (StringUtils.isBlank(tag)) {
// 插入或者更新 persistService.insertOrUpdate(srcIp, srcUser, configInfo, time, configAdvanceInfo,
false);
// 发布事件 EventDispatcher.fireEvent(
new ConfigDataChangeEvent(false, dataId, group, tenant, time.getTime())); } else { persistService.insertOrUpdateTag(configInfo, tag, srcIp, srcUser, time, false); EventDispatcher.fireEvent(new ConfigDataChangeEvent(false, dataId, group, tenant, tag, time.getTime())); } } else { // beta publish persistService.insertOrUpdateBeta(configInfo, betaIps, srcIp, srcUser, time, false); EventDispatcher.fireEvent(new ConfigDataChangeEvent(true, dataId, group, tenant, time.getTime())); } ConfigTraceService.logPersistenceEvent(dataId, group, tenant, requestIpApp, time.getTime(), LOCAL_IP, ConfigTraceService.PERSISTENCE_EVENT_PUB, content); return true; }

就从这个controller去做分析,其实核心一下就能看出来就是一个insertOrUpdate,一个fireEvent;

insertorUpdate这个nacos没有用mybatis、hibernate这些ORM框架(减少依赖耦合吧);

下方高能!!!!!!!!!!!!!!!!!!!!!!!!!!

直接用jdbcTemblate就行数据插入,大家在进行debug的时候注意下,jdbcTemplate中有个事务超时时间,

nacos中有两种模板,

private JdbcTemplate jt;
 private TransactionTemplate tjt;

这个他都设置了默认超时时间(还有另外一个地方一个3秒一个5秒,建议大家在DEBUG的时候把这个时间调大一点,300,,500^_^)

BasicDataSourceServiceImpl.java
 @PostConstruct
    public void init() {
        queryTimeout = NumberUtils.toInt(System.getProperty("QUERYTIMEOUT"), 300);
        jt = new JdbcTemplate();
        /**
         *  设置最大记录数,防止内存膨胀
         */
        jt.setMaxRows(50000);
        jt.setQueryTimeout(queryTimeout);

        testMasterJT = new JdbcTemplate();
        testMasterJT.setQueryTimeout(queryTimeout);

        testMasterWritableJT = new JdbcTemplate();
        /**
         * 防止login接口因为主库不可用而rt太长
         */
        testMasterWritableJT.setQueryTimeout(1);
        /**
         * 数据库健康检测
         */
        testJTList = new ArrayList<JdbcTemplate>();
        isHealthList = new ArrayList<Boolean>();

        tm = new DataSourceTransactionManager();
        tjt = new TransactionTemplate(tm);
        /**
         *  事务的超时时间需要与普通操作区分开
         */
        tjt.setTimeout(TRANSACTION_QUERY_TIMEOUT);
        if (!STANDALONE_MODE || PropertyUtil.isStandaloneUseMysql()) {
            try {
                reload();
            } catch (IOException e) {
                e.printStackTrace();
                throw new RuntimeException(DB_LOAD_ERROR_MSG);
            }

            TimerTaskService.scheduleWithFixedDelay(new SelectMasterTask(), 10, 10,
                TimeUnit.SECONDS);
            TimerTaskService.scheduleWithFixedDelay(new CheckDBHealthTask(), 10, 10,
                TimeUnit.SECONDS);
        }
    }

 

看下config_info的表结构,他这边判断更新的依据就是直接插入,

捕获唯一索引的异常,data_id+group+tenant重复的话就会抛出异常,捕获这个异常进行更新操作!

表结构

persistService.insertOrUpdate
/**
     * 写入主表,插入或更新
     */
    public void insertOrUpdate(String srcIp, String srcUser, ConfigInfo configInfo, Timestamp time,
                               Map<String, Object> configAdvanceInfo, boolean notify) {
        try {
            addConfigInfo(srcIp, srcUser, configInfo, time, configAdvanceInfo, notify);
        } catch (DataIntegrityViolationException ive) { // 唯一性约束冲突
            updateConfigInfo(configInfo, srcIp, srcUser, time, configAdvanceInfo, notify);
        }
    }

后面就是最核心的地方了

EventDispatcher.fireEvent(new ConfigDataChangeEvent(false, dataId, group, tenant, time.getTime()));

通过event的dispatcher,进行相应的操作,这个就是观察者模式的体现,Apollo中也有类似的操作;

篇幅过长,这里就不再追加了,看下一篇吧^_^

 

awk 语法深入浅出(一)

awk 语法深入浅出(一)

awk 语法深入浅出(一)

在详细阐述awk命令之前,我们首先了解一下awk是如何完成自己的任务的,我们将以一个名为names(仅有三行记录)的文件为例进行说明。

张三    Savage  100
李二    Lee     200
王五    Doe     300

使用下面的awk命令:awk打印字段时,将以下面方式使用print函数

$ awk ''{print $1,$3}'' names

1 .awk使用一行作为输入(通过文件或者管道),并将这一行赋给内部变量$0 ,默认时每一行也可以称为一个记录,以换行符结束。

张三    100
李二    200
王五    300

awk 指令由模式、操作、或模式与操作的组合组成。

模式是由某种类型的表达式组成的语句。如果某个表达式中没有出现关键字if,但实际计算时却暗含if这个词,那么,这个表达式就是模式。

操作由括在大括号内的一条或多条语句组成,语句之间用分号或换行符隔开,模式则不能被括在大括号中,模式由括在两个正斜杠之间的正则表达式、一个或多个awk 操作符组成的表达式组成。

1 从文件输入

awk的格式如下($是Linux命令的提示符):

$ awk ''pattern'' filename
$ awk ''{action}'' fi1ename
$ awk ''pattern {action}'' fi1ename

下面演示一个名为employees的文件。

$ cat employees
Tom    Jones   4424  5/12/66 543354
Mary   Adams   5346  11/4/63 28765
Sally  Chang   1654  7/22/54 650000
Billy  B1ack   1683  9/23/44 336500

awk 打印出所有包含模式Mary的行。

$ awk ''/Mary/'' employees
Mary Adams 5346 11/4/63 28765

awk打印出文件employees的第一个字段,字段从行的左端开始,以空白符分隔。

$ awk ''{print $1}'' employees
Tom
Mary
Sally
Billy

awk打印出文件employees的第一个字段,字段从行的左端开始,以空白符分隔。

$ awk ''/Sally/{print $1,$2}'' employees
Sally Chang

2 从命令输入

可以将一条或多条Linux命令的输出通过管道发给awk处理。格式如下:

$ command | awk ''pattern''
$ command | awk ''{action}''
$ command | awk ''pattern {action}''

范例:当文件employees中的某一行含有模式Sally时,awk打印该行的头第一个字段。

$ cat employees | awk ''/Sally/{print $1}''
Sally Chang
谢谢您的观看,欢迎关注我的公众号。

image.png

Axios深入浅出

Axios深入浅出

安装axios:
yarn add axios
为什么项目的接口是异步加:
避免加载缓慢。这样当你访问的时候才会加载。
get请求:
get请求一般都是需要参数的。

created(){
// get的两种不同的写法
 axios.get(''/data.json'',{
            params:{
                id: 12
            }
        }).then((res)=>{
            console.log(res)
        })
        axios({
            method:''get'',
            url:''/https://www.baidu.com'',
            params:{
                id: 12,
            }
        }).then(res =>{
            console.log(res)
        })
        
        //post 用于表单提交
        //application/json
        let data = {
            id: 12
        }
        axios.post(''/post'', data).then(
            res => {
                console.log(res)
            }
        )
        axios({
            method:''post'',
            url:''/post'',
            data:data
        }).then(res =>{
            console.log(res)
        })
        
        // form-data请求
        let formData = new formData(); //对象一般是key value键值对
        for (let key in data){
            // append可以把键值添加到formData里面去
            // 第一个参数是key,第二个参数是value,可以用data[key]表示。
            formData.append(key,data[key])
        }
        axios.post(''/post'',formData).then(res =>{
            console.log(res)
        })
        
        // put请求
        axios.put(''/put'',data).then(res => {
            console.log(res)
        })
        
        // patch请求
        axios.patch(''/patch'',data).then(res => {
            console.log(res)
        })
}

开发过程中,所有的put和patch都可以用post代替,只是不太符合规范。
put和patch都是更新数据。
put是推送全部的数据到后端。
patch是只推送修改的数据。
delete删除:

// delete删除
        // 删除需要将后端接口拼到url上面
        // delete方法只有两个参数 url和config
        // 在url中传输 request paystring
        sxios.delete(''/delete'',{
            params:{
                id: 12            }
        }).then(res =>{
            console.log(res)
        })
        //方法2
        // 不是在url中进行传输的  request payload
        sxios.delete(''/delete'',{
            data:{
                id: 12            }
        }).then(res =>{
            console.log(res)
        })

routers

{
   title:''axios-demo''
   path:''/pages/axiosDemo'',
   component: Loadable({
        loader: () => import(''@/pages/axiosDemo''),
        loading: Loading,
      }),
}

拦截器:

export default{
     created(){
     // 请求拦截器
     axios.interceptors.request.use()
     },
}

service.js

const CONTACT_API={
// 获取联系人列表
    getContactList:{
       method:''get'',
       url:''contactList'',
    }
// 新建联系人列表
    newContactList:{
       method:''post'',
       url:''/cont/new''
    }
 // 编辑联系人
 editContact:{
       method:''put'',
       url:''/contact/edit''
 }
 
    
    
}

DOM 事件深入浅出(一)

DOM 事件深入浅出(一)

在项目开发时,我们时常需要考虑用户在使用产品时产生的各种各样的交互事件,比如鼠标点击事件、敲击键盘事件等。这样的事件行为都是前端DOM事件的组成部分,不同的DOM事件会有不同的触发条件和触发效果。本文就将带大家深入浅出地了解DOM事件的那些属性和方法。

首先在介绍DOM事件之前我们先来认识下DOM的不同级别。针对不同级别的DOM,我们的DOM事件处理方式也是不一样的。

DOM级别与DOM事件

DOM级别一共可以分为4个级别:DOM0级,DOM1级,DOM2级和 DOM3级,而DOM事件分为3个级别:DOM0级事件处理,DOM2级事件处理和DOM3级事件处理。如下图所示:

DOM级别与DOM事件

有人可能会问,为什么没有DOM1级事件处理呢?因为1级DOM标准中并没有定义事件相关的内容,所以没有所谓的1级DOM事件模型。

关于DOM级别这里不做详细的介绍,下面主要介绍下不同级别DOM中的不同事件。

1.DOM0级事件

在了解DOM0级事件之前,我们有必要先了解下HTML事件处理程序,也是最早的这一种的事件处理方式,代码如下:

<button type="button" onclick="showFn()"></button>

<script>
    function showFn() {
        alert(''Hello World'');
    }
</script>

以上代码我们通过直接在HTML代码里定义了一个onclick的属性触发showFn方法,这样的事件处理程序最大的缺点就是HTML于JS强耦合,我们一旦需要修改函数名就得修改两个地方。当然其优点是不需要操作DOM来完成事件的绑定。

那么什么是DOM0级处理事件呢?DOM0级事件就是将一个函数赋值给一个事件处理属性,比如:

<button id="btn" type="button"></button>

<script>
    var btn = document.getElementById(''btn'');
    
    btn.onclick = function() {
        alert(''Hello World'');
    }
    
    // btn.onclick = null; 解绑事件 
</script>

以上代码我们给button定义了一个id,通过JS获取到了这个id的按钮,并将一个函数赋值给了一个事件处理属性onclick,这样的方法便是DOM0级处理事件的体现。我们可以通过给事件处理属性赋值null来解绑事件。

DOM0级事件处理程序的缺点在于一个处理程序无法同时绑定多个处理函数,比如我还想在按钮点击事件上加上另外一个函数。

2.DOM2级事件

DOM2级事件在DOM0级事件的基础上弥补了一个处理程序无法同时绑定多个处理函数的缺点,允许给一个处理程序添加多个处理函数。代码如下:

<button id="btn" type="button"></button>

<script>
    var btn = document.getElementById(''btn'');
    
    function showFn() {
        alert(''Hello World'');
    }
    
    btn.addEventListener(''click'', showFn, false);
    
    // btn.removeEventListener(''click'', showFn, false); 解绑事件 
</script>

DOM2级事件定义了addEventListener和removeEventListener两个方法,分别用来绑定和解绑事件,方法中包含3个参数,分别是绑定的事件处理属性名称(不包含on)、处理函数和是否在捕获时执行事件处理函数。如果我们还需要添加一个鼠标移入的方法,只需要:

btn.addEventListener(''mouseover'', showFn, false);

这样点击按钮和鼠标移入时都将触发showFn方法。

需要注意的是IE8级以下版本不支持addEventListener和removeEventListener,需要用attachEvent和detachEvent来实现:

btn.attachEvent(''onclick'', showFn); // 绑定事件 
btn.detachEvent(''onclick'', showFn); // 解绑事件 

这里我们不需要传入第三个参数,因为IE8级以下版本只支持冒泡型事件。

3.DOM3级事件

DOM3级事件在DOM2级事件的基础上添加了更多的事件类型,全部类型如下:

  1. UI事件,当用户与页面上的元素交互时触发,如:load、scroll

  2. 焦点事件,当元素获得或失去焦点时触发,如:blur、focus

  3. 鼠标事件,当用户通过鼠标在页面执行操作时触发如:dbclick、mouseup

  4. 滚轮事件,当使用鼠标滚轮或类似设备时触发,如:mousewheel

  5. 文本事件,当在文档中输入文本时触发,如:textInput

  6. 键盘事件,当用户通过键盘在页面上执行操作时触发,如:keydown、keypress

  7. 合成事件,当为IME(输入法编辑器)输入字符时触发,如:compositionstart

  8. 变动事件,当底层DOM结构发生变化时触发,如:DOMsubtreeModified

同时DOM3级事件也允许使用者自定义一些事件。

DOM事件流

上文中讲到了addEventListener的第三个参数为指定事件是否在捕获或冒泡阶段执行,设置为true表示事件在捕获阶段执行,设置为true表示事件在捕获阶段执行,而设置为false表示事件在冒泡阶段执行。那么什么是事件冒泡和事件捕获呢?可以用下图来解释:

事件冒泡与事件捕获

1.事件冒泡

所谓事件冒泡就是事件像泡泡一样从最开始生成的地方一层一层往上冒,比如上图中a标签为事件目标,点击a标签后同时也会触发p、li上的点击事件,一层一层向上直至最外层的html或document。下面是代码示例:

<div id="box">
    <a id="child">事件冒泡</a>
</div>

<script>
    var box = document.getElementById(''box''),
        child = document.getElementById(''child'');

    child.addEventListener(''click'', function() {
        alert(''我是目标事件'');
    }, false);

    box.addEventListener(''click'', function() {
        alert(''事件冒泡至DIV'');
    }, false);
</script>

上面的代码运行后我们点击a标签,首先会弹出''我是目标事件''提示,然后又会弹出''事件冒泡至DIV''的提示,这便说明了事件自内而外向上冒泡了。

那么我们如何阻止事件冒泡呢?这里就涉及事件的Event对象中的stopPropagation方法,如下:

child.addEventListener(''click'', function(e) {
    alert(''我是目标事件'');
    e.stopPropagation();
}, false);

加上stopPropagation方法后,我们再次点击a标签就不会触发div上的click事件了。

2.事件捕获

和事件冒泡相反,事件捕获是自上而下执行,我们只需要将addEventListener的第三个参数改为true就行。

<div id="box">
    <a id="child">事件冒泡</a>
</div>

<script>
    var box = document.getElementById(''box''),
        child = document.getElementById(''child'');

    child.addEventListener(''click'', function() {
        alert(''我是目标事件'');
    }, true);

    box.addEventListener(''click'', function() {
        alert(''事件冒泡至DIV'');
    }, true);
</script>

此时我们点击a标签,首先弹出的是''事件冒泡至DIV'',其次弹出的是''我是目标事件'',正好与事件冒泡相反。

总结

本文主要介绍了不同DOM级别下的事件处理程序,同时介绍了事件冒泡和捕获的触发原理和方法。熟练地使用不同级别的DOM事件并且解决相应的浏览器兼容性问题对我们的前端项目开发会很有帮助。在下篇文章中,我将给大家介绍DOM事件中Event对象的属性和方法。文章地址:DOM 事件深入浅出(二)。

本文为劳卜原创文章,首发于微信公众号:前端呼啦圈(Love-FED)
转载请注明来自——微信公众号:前端呼啦圈(Love-FED)

DOM 事件深入浅出(二)

DOM 事件深入浅出(二)

在DOM事件深入浅出(一)中,我主要给大家讲解了不同DOM级别下的事件处理程序,同时介绍了事件冒泡和捕获的触发原理和方法。本文将继续介绍DOM事件中的知识点,主要侧重于DOM事件中Event对象的属性和方法。

那么什么是DOM事件中Event对象呢?事件对象(event object)指的是与特定事件相关且包含该事件详细信息的对象。我们可以通过传递给事件处理程序的参数获取事件触发后所产生的一系列方法和属性。

Event对象

Event对象其实是一个事件处理程序的参数,当调用事件时,我们只需要将其传入事件函数就可以获取。代码如下:

function getEvent(event) {
    event = event || window.event;
}

上面的事件函数传入了一个名叫Event的参数作为事件对象,同时做了浏览器兼容处理。在IE8及以前本版之中,通过设置属性注册事件处理程序时,调用的时候并未传递事件对象,需要通过全局对象window.event来获取。所以上述代码中我们利用 || 来做判断,如果event对象存在则使用event,不存在则使用window.event。

Event对象包含了几个方法和多个属性,通过这些方法和属性我们可以获取事件的详细信息并进行相关处理。

Event对象方法

Event对象主要有以下两个方法,用于处理事件的传播(冒泡、捕获)和事件的取消。

1.stopPropagation

stopPropagation方法主要用于阻止事件的进一步传播,比如阻止事件继续向上层冒泡。

function getEvent(event) {
    event.stopPropagation();
}

child.addEventListener(''click'', getEvent, false);

如果你需要兼容IE8及以下版本浏览器,则需要利用cancelBubble来代替stopPropagation,因为低版本IE不支持stopPropagation方法。

function getEvent(event) {
    event = event || window.event;
        
    if (event.stopPropagation) {
        event.stopPropagation();
    } else {
        event.cancelBubble = true;
    }
}

cancelBubble是IE事件对象的一个属性,设置这个属性为true能阻止事件进一步传播。

2.preventDefault

preventDefault方法用于取消事件的默认操作,比如a链接的跳转行为和表单自动提交行为就可以用preventDefault方法来取消。代码如下:

<a id="go" href="https://www.baidu.com/">禁止跳转</a>
var go = document.getElementById(''go'');

function goFn(event) {
    event.preventDefault();

    console.log(''我没有跳转!'');
}

go.addEventListener(''click'', goFn, false);

通过preventDefault,我们成功阻止了a链接的跳转行为。不过,在IE9之前的浏览器中需要设置returnValue属性为false来实现。如下:

function goFn(event) {
    event = event || window.event;
    
    if (event.preventDefault) {
        event.preventDefault();
    } else {
        event.returnValue = false;
    }
    
    console.log(''我没有跳转!'');
}

除了以上Event对象的两个主要方法,当前DOM事件规范草案在Event对象上还定义了另一个方法,命名为stopImmediatePropagation。

3.stopImmediatePropagation

和stopPropagation相比,stopImmediatePropagation同样可以阻止事件的传播,不同点在于其还可以把这个元素绑定的同类型事件也阻止了。如:

var go = document.getElementById(''go'');

function goFn(event) {
    event.preventDefault();
    event.stopImmediatePropagation(); // 阻止事件冒泡并阻止同类型事件
    
    console.log(''我没有跳转!'');
}

function goFn2(event) {
    console.log(''我是同类型事件!'');
}

go.addEventListener(''click'', goFn, false);
go.addEventListener(''click'', goFn2, false);

我们在a链接上继续加了一个点击事件,如果我们在goFn方法中添加了stopImmediatePropagation方法,那么goFn2方法将不会被执行,同时也不会将点击事件冒泡至上层。

需要注意的是,stopImmediatePropagation目前一部分浏览器尚不支持,但是像jQuery这样的库封装了跨平台的stopImmediatePropagation方法。

Event对象属性

与Event对象的方法相比,因Event对象的属性相对较多,文本无法一一讲解,所以主要介绍实际项目中常用的Event对象属性。

1.type属性

通过type我们可以获取事件发生的类型,比如点击事件我们获取的是''click''字符串。

var go = document.getElementById(''go'');

function goFn(event) {
    console.log(event.type); // 输出''click''
}

go.addEventListener(''click'', goFn, false);

2.target属性

target属性主要用于获取事件的目标对象,比如我们点击a标签获取的是a标签的html对象。

var go = document.getElementById(''go'');

function goFn(event) {
    var target = event.target;
    
    console.log(target === go) // 返回true
}

go.addEventListener(''click'', goFn, false);

在IE8及之前版本,我们需要使用srcElement而非target。兼容方案如下:

function goFn(event) {
    var event = event || window.event,    
        target = event.target || event.srcElement;
    
    console.log(target === go) // 返回true
}

3. 鼠标事件属性

在用鼠标触发事件时,主要的事件属性包含鼠标的位置和按键的状态,比如:clientX和clientY指定了鼠标在窗口坐标中的位置,button和which指定了按下的鼠标键是哪个。

function moveFn(event) {
    console.log(event.screenX) // 获取鼠标基于屏幕的X轴坐标
    console.log(event.screenY) // 获取鼠标基于屏幕的Y轴坐标
    console.log(event.clientX) // 获取鼠标基于浏览器窗口的X轴坐标
    console.log(event.clientY) // 获取鼠标基于浏览器窗口的Y轴坐标
    console.log(event.pageX) // 获取鼠标基于文档的X轴坐标
    console.log(event.pageY) // 获取鼠标基于文档的Y轴坐标
}

function clickFn(event) {
    console.log(event.button) // 获取鼠标按下的键。非IE浏览器中0为鼠标左键,1为鼠标中键,2为鼠标右键
    console.log(event.which) // 获取指定事件上哪个键盘键或鼠标按钮被按下
}

document.addEventListener(''mouseover'', moveFn, false);
document.addEventListener(''click'', clickFn, false);

4.键盘事件属性

在用键盘触发事件时,主要的事件属性包含键盘的按键keyCode和是否按下特殊键,比如:keyCode指定了按下键的键码值,ctrlKey指定是否按下了ctrl键。

function keyFn(event) {
    console.log(event.keyCode); // 获取按下键的键码值
    console.log(event.ctrlKey); // 获取是否按下了ctrl键
    console.log(event.shiftKey); // 获取是否按下了shift键
    console.log(event.altKey); // 获取是否按下了alt键
    console.log(event.metaKey); // 获取是否按下了meta键
}

document.addEventListener(''keyup'', keyFn, false);

类似的事件属性还有表单事件属性和window事件属性等,这里不再做详细介绍。有兴趣的同学可以查阅相关资料。

总结

本文主要讲解了DOM事件中Event对象的常用属性和方法,同时也介绍了其在IE中的兼容性问题及解决方案。然而关于DOM事件的知识点远不止这些,希望仅此能够帮助初识DOM的开发者。

备注:文本参考自《Javascript权威指南》一书及慕课网教程《DOM事件揭秘》。

本文为劳卜原创文章,首发于微信公众号:前端呼啦圈(Love-FED)
转载请注明来自——微信公众号:前端呼啦圈(Love-FED)

我们今天的关于Nacos深入浅出的分享已经告一段落,感谢您的关注,如果您想了解更多关于awk 语法深入浅出(一)、Axios深入浅出、DOM 事件深入浅出(一)、DOM 事件深入浅出(二)的相关信息,请在本站查询。

本文标签: