精华内容
下载资源
问答
  • 程序员是什么的?怎么成为程序员?
    万次阅读
    2021-12-06 19:36:06

    我是一个程序员,我知道程序员是做什么的,但是我身边有很多亲朋好友并不知道。
    他们经常认为程序员就是对电脑很懂得人,包括电脑的软硬件问题,包括电脑装系统。
    实际上,可能不只是我的这些亲朋好友,绝大多数不做程序员的人可能都不知道程序员究竟是做什么的,也不太清楚何谓程序、何谓软件。程序员到底是做什么的,这可能是很多想要进入软件行业的人都很想知道的一个问题,那么我就根据我目前的理解说说我的看法,如果有理解有误之处,欢迎指正。

    软件和硬件的区分及关系

    首先,现在电脑和手机都应用非常普遍,大家都知道手机和电脑上都有很多软件,例如微信、QQ、支付宝,他们都是软件,甚至于手机的基础功能打电话、发短信,手机和电脑的基本操作系统等,他们也全都属于软件。
    那么与这些软件对应的,我们可以直接摸得到的设备如电脑、手机、平板等等,就是硬件,软件最终依托于硬件之上。

    软件的部分分类

    上边举例了很多的软件,但是就如硬件各种各样,软件也是各不相同的,软件也有很多的分类。
    例如微信、QQ、支付宝、打电话、发短信等,就属于应用软件,而像android系统、windows系统这些就属于操作系统软件。
    应用软件一般都具有特定的直接服务于用户的功能,操作系统则是可以直接管理硬件资源,应用软件一般依托于操作系统之上。
    虽然这些都是软件,但是可能区别就相当于飞机和火车一样,会开飞机的未必会开火车,会造火车的未必会造飞机。

    软件和程序的区别

    那么,大概了解了软件和硬件的区别,对软件有个大概概念之后,就可以进一步理解软件和程序的区别了。
    一般而言,软件是由程序构成的,软件一般是具有具体功能、可以使用的程序包,而程序可能只是一小段代码。
    假如说一辆小轿车就是一个软件,那么上边的轮子、方向盘、离合器就都是不同的程序,各种各样的零件最终组装成一辆小轿车,就如同各种各样的程序片段最终形成一个软件。

    程序和代码

    上边提到了程序可能是一小段代码,生活中可能也会听到有人说程序员就是码农,就是敲代码的,那么代码是什么呢?
    某种意义上,代码和程序可能可以理解为一个意思,通俗点讲,代码就是用编程语言写出来的一行行的字符串。
    字符串是什么,简单理解就是若干个字符的组合,字符又是什么,常见的英文字母以及数字都可以称为字符。
    如果不考虑中文编程语言的情况下,可以理解为代码就是根据特定规则,用英文字母和数字以及各种符号组成的字符串。
    这种字符串由于遵循了某些特定的格式,因此最终可以被电子设备如电脑、手机等识别并做一些事情。

    什么是编程语言

    那么上边说了,代码就是根据特定规则编写的字符串,因为这些特定的规则,所以最终这些字符串能转换成电信号,而电信号才是直接和电子设备的硬件打交道。
    电信号从某种意义上来说只有有电和没电的区别,那么最初要直接转换成代码的字符串,就是由0和1组成,一个代表有,一个代表没有,专业的说法就是真或假,true或者false,这种格式组成的代码,称之为机器语言。
    单纯的0和1组成的字符串,对于人类来说肯定是不友好的,完全看不懂,所以需要特定的对比规则进行对比翻译。
    为了对人类更加友好,于是0和1组成的机器语言的基础上又发展出增加了一些英语单词规则的新的编程语言,称之为汇编语言,而 那些英语单词称之为助记符。
    汇编语言相对于机器语言,由于增加了助记符,所以肯定是更容易被人看懂和记住的,但是由于助记符是有限的,实际上依旧不是那么容易,因此在此基础上就又发展出了对人类更加友好的编程语言,称之为高级编程语言,这个阶段语言的思想是面向过程的,代表行的就是C语言。
    什么是面向过程呢,我理解的就是关注点在于这一步做什么、下一步做什么。
    那么随着软件技术的发展,又进一步发展出了其他的高级编程语言,例如C++、JAVA、C#、IOS等等,像JAVA等语言的思想就是面向对象,关注点就是这个东西能做什么、有什么,而不是关注过程。
    面向对象的这种思想相对于面向过程,就对重用、复用更友好,但是做一件事情必然有先后顺序,因此最终还是有面向过程的东西在里边,并不是说面向对象就不管过程了。

    程序员是什么

    那么有了上边的知识基础,就能再来说程序员是什么了。
    通过上边的叙述,应该可以知道,软件是由根据编程语言写出来的程序组成的,是用来操作电子设备的,那么写这种程序的人,肯定就是程序员。
    但是,如果说程序员就是敲代码的,那就不完全正确,因为程序员的分类至少就有开发、测试、运维、DBA这些,开发是主要写代码的,测试和运维以及DBA不一定写代码,但他们其实都算程序员。
    程序员有哪些分类
    其实上边也提到,开发、测试、运维、DBA都算程序员,这是一个比较大的分类,但不是唯一,如果是用来简单初步了解程序员,我想应该够了。
    开发主要是实现软件的功能,主要敲代码的。
    测试主要是进行专业的测试和验证,但是并不局限于功能,还需要性能测试,很多时候可能也需要写脚本类的代码支持自动化测试。
    运维主要负责软件运行环境的搭建和部署以及部分软件使用问题的支持,同时也可能需要写脚本或者其他代码支持自动化部署。
    DBA主要是处理数据库相关的事情,经常需要优化操作数据库的sql,sql本身也是一种代码。
    除了上边说的,单纯的开发来说,实际还可以有更多细分,例如主要负责看得到的页面的,称作UI,但是很多时候实际没有专门的UI,而是页面由前端一起负责,前端程序员实际就是直接操作看得到的页面功能的程序员。
    与前端对应的就是后端开发程序员,实现一些看不到的功能逻辑,有的直接有前端的功能页面对应,有的就是纯粹的后端程序。
    上边的简单分类实际是站在软件分层的角度说的,那么实际还可以站在编程语言的角度分类,例如java程序员、c++程序员、c#程序员等等,这些分类实际就是以命名的这个编程语言为主,但是实际未必就只会这一种。
    就拿java后端程序员来说,一般可能都还会一些前端javascript语言以及linux的shell语言。

    怎么成为程序员

    可能有一些不是程序员的朋友想要成为一个程序员,因为目前公认的,程序员普遍收入是比较高的。
    那么怎么成为一个程序员呢?
    首先,我觉得要先确定好自己的方向,是想做软件开发,还是软件测试,还是软件运维,还是DBA?或者所有都做,成为一个全栈程序员?
    众所周知,人的精力是有限的,术业有专攻,虽然也有很多人是全栈程序员,是什么都做,但是个人认为如果零基础想要直接成为一个全栈,实际是有很大难度的,所以个人觉得还是先找一个熟了再说。
    话再说回来,怎么成为一个程序员呢?
    首先,还在读书的,可以选择计算机专业,很多计算机专业,学校都会有程序员相关的专业和课程。
    其次,如果已经选择了非计算机专业或者已经出了社会,也可以自学或者参加培训机构的培训,或者有人脉的也可以直接找熟人教。
    计算机专业就不说了,应该也不需要看这篇文章。
    先说自学,这个对于多数人都是很有难度的,很容易信心满满的开始,垂头丧气的放弃,虽然我觉得很多编程语言入门都很简单,但是自学依然还是会有很大难度。
    再说培训,这个是很多野生程序员的选择,正所谓术业有专攻,人家就是吃这碗饭的,自然有能吃这饭的道理。相对于自学,参加培训肯定更容易入门学会,只是不同的培训机构水平不同,不同的培训机构理念不能,能否找到好的培训机构就需要注意。
    另外,培训机构一般讲的都很快,最好是先试听一段时间再说。
    最后再说熟人教这事,这应该选择的人比较少,虽然可能看起来省钱,但是也可能因此没有那么大的压迫感,也因为并不是专业教学的,所以最终很难学会。

    更多相关内容
  • 给小师妹展开说说,Spring Bean IOC、AOP 循环依赖

    千次阅读 多人点赞 2021-05-06 07:33:23
    沉淀、分享、成长,让自己和他人都能有所收获!???? 一、前言 延迟满足能给你带来什么? 大学有四年时间,但几乎所有人都是临近毕业才发现找一份好工作费劲,尤其是我能非常熟悉的软件开发行业,即使是毕业了还需要...


    作者:小傅哥
    博客:https://bugstack.cn

    沉淀、分享、成长,让自己和他人都能有所收获!😄

    一、前言

    延迟满足能给你带来什么?

    大学有四年时间,但几乎所有人都是临近毕业才发现找一份好工作费劲,尤其是我能非常熟悉的软件开发行业,即使是毕业了还需要额外花钱到培训机构,在学一遍编程技术才能出去找工作。好像在校这几年压根就没学到什么!

    就我个人而言可能是因为上学期间喜欢编程,也从师哥、师姐那里听到一些关于毕业后找工作的不容易,也了解了一些社会上对程序员开发技能的要求级别。也就是得到了这些消息,又加上自己乐于折腾,我给自己定了一个每天都能完成的小目标:

    红尘世界几个王,我自不服迎头上。
    日敲代码两百行,冲进世界五百强。
    

    哈哈哈,就这么每天两百行代码,一个月就是6千行,一年就是6万行,三年后开始实习就有18万行,一个应届实习生有将近20万行代码的敲击量,几乎已经可以非常熟练的完成各类简单的工作,在加上实习中对整个项目流程真正的断链后,找一个正经的开发工作,还是很容易的。

    而这时候找工作的容易,就来自于你一直以来的学习和沉淀,但如果你没经过这些努力,可能等毕业后就会变得非常慌乱,最后没办法只能去一些机构再学习一遍。

    二、面试题

    谢飞机,小记!,以前感觉Spring没啥,看过一篇getBean,我的天!

    谢飞机:面试官,最近我看了 Spring 的 getBean 发现这里好多东西,还有一个是要解决循环依赖的,这玩意面试有啥要问的吗?

    面试官:有哇,Spring 是如何解决循环依赖的?

    谢飞机:嗯,通过三级缓存提前暴露对象解决的。

    面试官:可以哈,那这三个缓存里都存放了什么样的对象信息呢?

    谢飞机:一级缓存存放的是完整对象,也叫成品对象。二级缓存存放的是半成品对象,就是那些属性还没赋值的对象。三级缓存存放的是 ObjectFactory<?> 类型的 lambda 表达式,就是这用于处理 AOP 循环依赖的。

    面试官:可以呀,谢飞机有所准备嘛!那如果没有三级缓存,只有二级或者一级,能解决循环依赖吗?

    谢飞机:其实我看过资料了,可以解决,只不过 Spring 要保证几个事情,只有一级缓存处理流程没法拆分,复杂度也会增加,同时半成品对象可能会有空指针异常。而将半成品与成品对象分开,处理起来也更加优雅、简单、易扩展。另外 Spring 的两大特性中不仅有 IOC 还有 AOP,也就是基于字节码增强后的方法,该存放到哪,而三级缓存最主要,要解决的循环依赖就是对 AOP 的处理,但如果把 AOP 代理对象的创建提前,那么二级缓存也一样可以解决。但是,这就违背了 Spring 创建对象的原则,Spring 更喜欢把所有的普通 Bean 都初始化完成,在处理代理对象的初始化。

    面试官:飞机,不错嘛,这次了解了不少。那问个简单的,你撸过循环依赖的解决方案?

    谢飞机:哦哦,这没有,没实践过!!!确实应该搞一下,试试。

    三、什么是循环依赖?

    1. 问题描述

    了解问题的本质再分析问题,往往更利于对问题有更深入的了解和研究。所以我们在分析 Spring 关于循环依赖的源码之前,先要了解下什么是循环依赖。

    • 循环依赖分为三种,自身依赖于自身、互相循环依赖、多组循环依赖。
    • 但无论循环依赖的数量有多少,循环依赖的本质是一样的。就是你的完整创建依赖于我,而我的完整创建也依赖于你,但我们互相没法解耦,最终导致依赖创建失败。
    • 所以 Spring 提供了除了构造函数注入和原型注入外的,setter循环依赖注入解决方案。那么我们也可以先来尝试下这样的依赖,如果是我们自己处理的话该怎么解决。

    2. 问题体现

    public class ABTest {
    
        public static void main(String[] args) {
            new ClazzA();
        }
    
    }
    
    class ClazzA {
    
        private ClazzB b = new ClazzB();
    
    }
    
    class ClazzB {
    
        private ClazzA a = new ClazzA();
    
    }
    
    • 这段代码就是循环依赖最初的模样,你中有我,我中有你,运行就报错 java.lang.StackOverflowError
    • 这样的循环依赖代码是没法解决的,当你看到 Spring 中提供了 get/set 或者注解,这样之所以能解决,首先是进行了一定的解耦。让类的创建和属性的填充分离,先创建出半成品Bean,再处理属性的填充,完成成品Bean的提供。

    3. 问题处理

    在这部分的代码中就一个核心目的,我们来自己解决一下循环依赖,方案如下:

    public class CircleTest {
    
        private final static Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
    
        public static void main(String[] args) throws Exception {
            System.out.println(getBean(B.class).getA());
            System.out.println(getBean(A.class).getB());
        }
    
        private static <T> T getBean(Class<T> beanClass) throws Exception {
            String beanName = beanClass.getSimpleName().toLowerCase();
            if (singletonObjects.containsKey(beanName)) {
                return (T) singletonObjects.get(beanName);
            }
            // 实例化对象入缓存
            Object obj = beanClass.newInstance();
            singletonObjects.put(beanName, obj);
            // 属性填充补全对象
            Field[] fields = obj.getClass().getDeclaredFields();
            for (Field field : fields) {
                field.setAccessible(true);
                Class<?> fieldClass = field.getType();
                String fieldBeanName = fieldClass.getSimpleName().toLowerCase();
                field.set(obj, singletonObjects.containsKey(fieldBeanName) ? singletonObjects.get(fieldBeanName) : getBean(fieldClass));
                field.setAccessible(false);
            }
            return (T) obj;
        }
    
    }
    
    class A {
    
        private B b;
    
        // ...get/set
    }
    
    class B {
        private A a;
    
    		// ...get/set
    }
    
    • 这段代码提供了 A、B 两个类,互相有依赖。但在两个类中的依赖关系使用的是 setter 的方式进行填充。也就是只有这样才能避免两个类在创建之初不非得强依赖于另外一个对象。

    • getBean,是整个解决循环依赖的核心内容,A 创建后填充属性时依赖 B,那么就去创建 B,在创建 B 开始填充时发现依赖于 A,但此时 A 这个半成品对象已经存放在缓存到singletonObjects 中了,所以 B 可以正常创建,在通过递归把 A 也创建完整了。

    四、源码分析

    1. 说说细节

    通过上面的例子我们大概了解到,A和B互相依赖时,A创建完后填充属性B,继续创建B,再填充属性A时就可以从缓存中获取了,如下:

    那这个解决事循环依赖的事放到 Spring 中是什么样呢?展开细节!

    虽然,解决循环依赖的核心原理一样,但要放到支撑起整个 Spring 中 IOC、AOP 特性时,就会变得复杂一些,整个处理 Spring 循环依赖的过程如下;

    • 以上就是关于 Spring 中对于一个有循环依赖的对象获取过程,也就是你想要的说说细节
    • 乍一看是挺多流程,但是这些也基本是你在调试代码时候必须经过的代码片段,拿到这份执行流程,再调试就非常方便了。

    2. 处理过程

    关于本章节涉及到的案例源码分析,已更新到 github:https://github.com/fuzhengwei/interview - interview-31

    以下是单元测试中对AB依赖的获取Bean操作,重点在于进入 getBean 的源码跟进;

    @Test
    public void test_alias() {
        BeanFactory beanFactory = new ClassPathXmlApplicationContext("spring-config.xml");
        Bean_A bean_a = beanFactory.getBean("bean_a", Bean_A.class);
        logger.info("获取 Bean 通过别名:{}", bean_a.getBean_b());
    }
    

    org.springframework.beans.factory.support.AbstractBeanFactory.java

    @Override
    public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
    	return doGetBean(name, requiredType, null, false);
    }
    
    • 从 getBean 进入后,获取 bean 的操作会进入到 doGetBean。
    • 之所以这样包装一层,是因为 doGetBean 有很多不同入参的重载方法,方便外部操作。

    doGetBean 方法

    protected <T> T doGetBean(
    		final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
    		throws BeansException {
    	
      // 从缓存中获取 bean 实例
    	Object sharedInstance = getSingleton(beanName);
    	
    			// mbd.isSingleton() 用于判断 bean 是否是单例模式
    			if (mbd.isSingleton()) {
    			  // 获取 bean 实例
    				sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
    					@Override
    					public Object getObject() throws BeansException {
    						try {
    						  // 创建 bean 实例,createBean 返回的 bean 实例化好的
    							return createBean(beanName, mbd, args);
    						}
    						catch (BeansException ex) {
    							destroySingleton(beanName);
    							throw ex;
    						}
    					}
    				});
    				// 后续的处理操作
    				bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
    			}
    			
    	// ...
    
      // 返回 bean 实例
    	return (T) bean;
    }
    
    • 按照在源码分析的流程图中可以看到,这一部分是从 getSingleton 先判断是否有实例对象,对于第一次进入是肯定没有对象的,要继续往下走。
    • 在判断 mbd.isSingleton() 单例以后,开始使用基于 ObjectFactory 包装的方式创建 createBean,进入后核心逻辑是开始执行 doCreateBean 操作。

    doCreateBean 方法

    protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
    		throws BeanCreationException {
    	
    	  // 创建 bean 实例,并将 bean 实例包装到 BeanWrapper 对象中返回
    		instanceWrapper = createBeanInstance(beanName, mbd, args);
    	
    		// 添加 bean 工厂对象到 singletonFactories 缓存中
    		addSingletonFactory(beanName, new ObjectFactory<Object>() {
    			@Override
    			public Object getObject() throws BeansException {
    			  // 获取原始对象的早期引用,在 getEarlyBeanReference 方法中,会执行 AOP 相关逻辑。若 bean 未被 AOP 拦截,getEarlyBeanReference 原样返回 bean。
    				return getEarlyBeanReference(beanName, mbd, bean);
    			}
    		});
    		
    	try {
    	  // 填充属性,解析依赖关系
    		populateBean(beanName, mbd, instanceWrapper);
    		if (exposedObject != null) {
    			exposedObject = initializeBean(beanName, exposedObject, mbd);
    		}
    	}
    	
    	// 返回 bean 实例
    	return exposedObject;
    }
    
    • 在 doCreateBean 方法中包括的内容较多,但核心主要是创建实例、加入缓存以及最终进行属性填充,属性填充就是把一个 bean 的各个属性字段涉及到的类填充进去。
    • createBeanInstance,创建 bean 实例,并将 bean 实例包装到 BeanWrapper 对象中返回
    • addSingletonFactory,添加 bean 工厂对象到 singletonFactories 缓存中
    • getEarlyBeanReference,获取原始对象的早期引用,在 getEarlyBeanReference 方法中,会执行 AOP 相关逻辑。若 bean 未被 AOP 拦截,getEarlyBeanReference 原样返回 bean。
    • populateBean,填充属性,解析依赖关系。也就是从这开始去找寻 A 实例中属性 B,紧接着去创建 B 实例,最后在返回回来。

    getSingleton 三级缓存

    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
      // 从 singletonObjects 获取实例,singletonObjects 是成品 bean
    	Object singletonObject = this.singletonObjects.get(beanName);
    	// 判断 beanName ,isSingletonCurrentlyInCreation 对应的 bean 是否正在创建中
    	if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
    		synchronized (this.singletonObjects) {
    		  // 从 earlySingletonObjects 中获取提前曝光未成品的 bean
    			singletonObject = this.earlySingletonObjects.get(beanName);
    			if (singletonObject == null && allowEarlyReference) {
    			  // 获取相应的 bean 工厂
    				ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
    				if (singletonFactory != null) {
    				  // 提前曝光 bean 实例,主要用于解决AOP循环依赖
    					singletonObject = singletonFactory.getObject();
    					
    					// 将 singletonObject 放入缓存中,并将 singletonFactory 从缓存中移除
    					this.earlySingletonObjects.put(beanName, singletonObject);
    					this.singletonFactories.remove(beanName);
    				}
    			}
    		}
    	}
    	return (singletonObject != NULL_OBJECT ? singletonObject : null);
    }
    
    • singletonObjects.get(beanName),从 singletonObjects 获取实例,singletonObjects 是成品 bean
    • isSingletonCurrentlyInCreation,判断 beanName ,isSingletonCurrentlyInCreation 对应的 bean 是否正在创建中
    • allowEarlyReference,从 earlySingletonObjects 中获取提前曝光未成品的 bean
    • singletonFactory.getObject(),提前曝光 bean 实例,主要用于解决AOP循环依赖

    综上,是一个处理循环依赖的代码流程,这部分提取出来的内容主要为核心内容,并没与长篇大论的全部拆取出来,大家在调试的时候会涉及的比较多,尽可能要自己根据流程图操作调试几遍。

    3. 依赖解析

    综上从我们自己去尝试解决循环依赖,学习了循环依赖的核心解决原理。又分析了 Spring 解决的循环依赖的处理过程以及核心源码的分析。那么接下来我们在总结下三级缓存分别不同的处理过程,算是一个总结,也方便大家理解。

    1. 一级缓存能解决吗?

    • 其实只有一级缓存并不是不能解决循环依赖,就像我们自己做的例子一样。
    • 但是在 Spring 中如果像我们例子里那么处理,就会变得非常麻烦,而且也可能会出现 NPE 问题。
    • 所以如图按照 Spring 中代码处理的流程,我们去分析一级缓存这样存放成品 Bean 的流程中,是不能解决循环依赖的问题的。因为 A 的成品创建依赖于 B,B的成品创建又依赖于 A,当需要补全B的属性时 A 还是没有创建完,所以会出现死循环。

    2. 二级缓存能解决吗?

    • 有了二级缓存其实这个事处理起来就容易了,一个缓存用于存放成品对象,另外一个缓存用于存放半成品对象。
    • A 在创建半成品对象后存放到缓存中,接下来补充 A 对象中依赖 B 的属性。
    • B 继续创建,创建的半成品同样放到缓存中,在补充对象的 A 属性时,可以从半成品缓存中获取,现在 B 就是一个完整对象了,而接下来像是递归操作一样 A 也是一个完整对象了。

    3. 三级缓存解决什么?

    • 有了二级缓存都能解决 Spring 依赖了,怎么要有三级缓存呢。其实我们在前面分析源码时也提到过,三级缓存主要是解决 Spring AOP 的特性。AOP 本身就是对方法的增强,是 ObjectFactory<?> 类型的 lambda 表达式,而 Spring 的原则又不希望将此类类型的 Bean 前置创建,所以要存放到三级缓存中处理。
    • 其实整体处理过程类似,唯独是 B 在填充属性 A 时,先查询成品缓存、再查半成品缓存,最后在看看有没有单例工程类在三级缓存中。最终获取到以后调用 getObject 方法返回代理引用或者原始引用。
    • 至此也就解决了 Spring AOP 所带来的三级缓存问题。本章节涉及到的 AOP 依赖有源码例子,可以进行调试

    五、总结

    • 回顾本文基本以实际操作的例子开始,引导大家对循环依赖有一个整体的认识,也对它的解决方案可以上手的例子,这样对后续的关于 Spring 对循环依赖的解决也就不会那么陌生了。
    • 通篇全文下来大家也可以看到,三级缓存并不是非必须不可,只不过在满足 Spring 自身创建的原则下,是必须的。如果你可以下载 Spring 源码对这部分代码进行改动下,提前创建 AOP 对象保存到缓存中,那么二级缓存一样可以解决循环依赖问题。
    • 关于循环依赖可能并不是一个好的编码方式,如果在自己的程序中还是要尽可能使用更合理的设计模式规避循环依赖,可能这些方式会增加代码量,但在维护上会更加方便。当然这不是强制,可以根据你的需要而来。

    六、系列推荐

    展开全文
  • 一、背景Webpack 最初的目标是实现前端项目的模块化,旨在更高效地管理和维护项目中的每一个资源模块化最早的时候,我们会通过文件划分的形式实现模块化,也就是将每个功能及其相关状态数据各自...

    一、背景

    Webpack 最初的目标是实现前端项目的模块化,旨在更高效地管理和维护项目中的每一个资源

    模块化

    最早的时候,我们会通过文件划分的形式实现模块化,也就是将每个功能及其相关状态数据各自单独放到不同的JS 文件中

    约定每个文件是一个独立的模块,然后再将这些js文件引入到页面,一个script标签对应一个模块,然后调用模块化的成员

    <script src="module-a.js"></script>
    <script src="module-b.js"></script>
    

    但这种模块弊端十分的明显,模块都是在全局中工作,大量模块成员污染了环境,模块与模块之间并没有依赖关系、维护困难、没有私有空间等问题

    项目一旦变大,上述问题会尤其明显

    随后,就出现了命名空间方式,规定每个模块只暴露一个全局对象,然后模块的内容都挂载到这个对象中

    window.moduleA = {
      method1: function () {
        console.log('moduleA#method1')
      }
    }
    

    这种方式也并没有解决第一种方式的依赖等问题

    再后来,我们使用立即执行函数为模块提供私有空间,通过参数的形式作为依赖声明,如下

    // module-a.js
    (function ($) {
      var name = 'module-a'
    
      function method1 () {
        console.log(name + '#method1')
        $('body').animate({ margin: '200px' })
      }
        
      window.moduleA = {
        method1: method1
      }
    })(jQuery)
    

    上述的方式都是早期解决模块的方式,但是仍然存在一些没有解决的问题。例如,我们是用过script标签在页面引入这些模块的,这些模块的加载并不受代码的控制,时间一久维护起来也十分的麻烦

    理想的解决方式是,在页面中引入一个JS入口文件,其余用到的模块可以通过代码控制,按需加载进来

    除了模块加载的问题以外,还需要规定模块化的规范,如今流行的则是CommonJSES Modules

    二、问题

    从后端渲染的JSPPHP,到前端原生JavaScript,再到jQuery开发,再到目前的三大框架VueReactAngular

    开发方式,也从javascript到后面的es5es6、7、8、9、10,再到typescript,包括编写CSS的预处理器lessscss

    现代前端开发已经变得十分的复杂,所以我们开发过程中会遇到如下的问题:

    • 需要通过模块化的方式来开发

    • 使用一些高级的特性来加快我们的开发效率或者安全性,比如通过ES6+、TypeScript开发脚本逻辑,通过sass、less等方式来编写css样式代码

    • 监听文件的变化来并且反映到浏览器上,提高开发的效率

    • JavaScript 代码需要模块化,HTML 和 CSS 这些资源文件也会面临需要被模块化的问题

    • 开发完成后我们还需要将代码进行压缩、合并以及其他相关的优化

    webpack恰巧可以解决以上问题

    三、是什么

    webpack 是一个用于现代JavaScript应用程序的静态模块打包工具

    • 静态模块

    这里的静态模块指的是开发阶段,可以被 webpack 直接引用的资源(可以直接被获取打包进bundle.js的资源)

    webpack处理应用程序时,它会在内部构建一个依赖图,此依赖图对应映射到项目所需的每个模块(不再局限js文件),并生成一个或多个 bundle

    webpack的能力:

    「编译代码能力」,提高效率,解决浏览器兼容问题「模块整合能力」,提高性能,可维护性,解决浏览器频繁请求文件的问题「万物皆可模块能力」,项目维护性增强,支持不同种类的前端模块类型,统一的模块化方案,所有资源文件的加载都可以通过代码控制

    参考文献

    • https://webpack.docschina.org/concepts/

    • https://zhuanlan.zhihu.com/p/267875652

    --The End--

    系列正在更新:1/10

    点击下方卡片解锁更多

    创作不易,星标、点赞、在看 三连支持

    展开全文
  • 闲谈IPv6-说说IPv6地址分配和BGP

    千次阅读 2019-03-16 08:01:40
    曾经IANA总管全球的IPv4地址分配,但后来的事实表明,它的并不是很好。互联网这么开放的组织,统一的规则存在是合理的,但大一统的机构监管,确实别扭。 后来地址分配和管理这件事就逐渐被一些非营利性公司或者...

    曾经IANA总管全球的IPv4地址分配策略,但后来的事实表明,按照最初的IPv4分配策略,它做的并不是很好。互联网这么开放的组织,统一的规则存在是合理的,但大一统的机构监管,确实别扭。

    后来地址分配和管理这件事就逐渐被一些非营利性公司或者组织接管,比如最大的ICANN,开始行使IANA的职能。

    不仅仅是为了防止IPv6地址的浪费,更多的是为了提供聚合性以减轻路由器的负担。目前IPv6的地址空间的管理是严格按规定的层次结构在全局全球范围内分配的。

    下面以IANA保留地址段为例,看它如何按照层级分配,它可以在这段地址行使它的分配管理权限,这段地址为:
    2001::/16
    IANA不能再像最初管理IPv4地址时那样管理这段IPv6地址了,它必须同样遵守规则,按照层次聚合分配地址,层次如下:

    • IANA(固定的0x2001开头)
    • 区域注册机构RIR
    • 国家注册机构NIR-ISP/本地注册机构LIR
    • 最终用户或ISP的层次结构

    这非常类似我们的身份证,也比较类似我们的银行卡。

    在这种分配策略下,当有下层机构需要IPv6地址块时,上层注册机构将地址按块状划分给下层注册机构进行分配与管理,就像一个卖切糕的组织一样。同级别注册机构不能随意交换和分配地址,如果注册机构A希望获取一段新的地址空间,它不能从同级别的B获取,它必须把需求告诉它的上级注册机构,由上级机构统一统筹分配。

    更重要的是,一个注册机构不能指定需要哪段地址空间,它只能提出它需要多少地址,不然,这不就又落回了IPv4那种在地址空间随意挖洞的局面了吗。


    进入IPv6时代,事情变得更加简单而不是更加复杂了。

    使用IPv6,我甚至感觉IBGP不再需要了。要理解这个,我们先看看为什么需要IBGP。

    我理解的BGP是 路由的路由 , 更严格的说是 路由集合的路由 。这没有任何问题,在BGP看来, n n n条路由其实就是一跳可达,它实际上实在说 到达网段1,网段2,网段3,…网段n全部可以交给EBGP对端路由器R1 ,在BGP眼里, n n n个网段其实就是 一条路由!

    但是,如果把这 n n n条路由注入到IGP,比如被OSPF学到会怎样?

    完蛋,这一下子OSPF就要学习 n n n条路由项啊! n n n越大,OSPF路由器的学习成本越高,随着 n n n的增加,直到路由器死机!死机的原因是,运行OSPF协议需要大量的收发控制报文,全网洪泛报文,以及需要大量的CPU资源处理这些报文。

    那么怎么办?

    IBGP来解决!

    IBGP实际上是将BGP路由进行了预处理,只在同一个AS内运行BGP协议的路由器之间相互交换路由信息,然后算出最优路径后,以 默认路由 或者 汇聚路由 的形式注入IGP。我们知道,宇宙中最猛的汇聚路由就是默认路由了!它只有一条!这将大大减轻AS内路由器控制平面的压力,减少路由抖动,收敛更快,数据平面更加稳定。

    所以一般的运营商AS不接受前缀长度大于19的路由通告,对于我国的特殊国情,这个数字目前上升到了24!毕竟嘛,地址空间挖洞越厉害,这个数字就越大,如果可以随意分配独立的IP地址,那么这个数字就是32,这意味着理论上一台路由器上将会有43亿条的路由表项…

    没错,就是43亿条!

    经过测试的权威表明,目前的路由器处理50万条路由就开始吃力…路由查找算法将会消耗大量的CPU,产生大量的延迟!最终的效果就是,网速 慢,慢,慢!

    这一切,都是地址空间挖洞造成的!但是我国国情嘛,网速慢一点没有关系,大家习惯了的。


    IPv4地址我没有亲测过,我也不知道,但是手机号码我是测试过的。我想运营商在管理IPv4地址时和管理手机号码时是一个套路吧。

    我的手机号码133168XXXXX,这是我在深圳注册登记的,以往如果你打我的手机或者我打你的手机,显示的我的电话号码都是广东深圳的号码,后来我搬到了杭州,为了减少麻烦,我不准备换手机号码,我想大不了就是每个月多交一笔钱呗,像什么漫游费啥的。然而运营商可以提供 异地迁移服务 ,现在,你再打我电话或是接我的电话,显示的我的号码所在地就是浙江杭州了。是不是有点意思呢。


    IPv6严格按照层次化分配地址,如果AS和地址注册机构是对应的,这就意味着最终每一个AS理论上只需要通告一条路由即可,它可以毫无压力地注入到IGP!IBGP还需要吗?

    路由器表项更少,查找路由的时间更短,造成的延迟更低,所以说,IPv6减少了延迟,它虽然无法提高光速,但是它可以减少处理。


    浙江温州皮鞋湿,下雨进水不会胖。

    展开全文
  • 说说TCP和UDP源端口的确定

    万次阅读 2018-09-22 13:42:53
    到达杭州已经两周了,基本已经...本周来说一个老话题,即 一个TCP连接如何确定自己的源端口。这个问题在几年前就分析过,正好前些天一个朋友又问了,我就又进一步进行了思考,觉得正好可以作为本周的话题来讨论一...
  • 2016 年,曾经有人问:“儿童学习编程是不是为了将来'程序猿'?”。 我当时给的回答是: 编程说白了就是用一种简单的符号语言描述一种解决方案来解决实际问题。编出程序的效果取决于两个方面:1、对于实际业务...
  • 在大公司凤尾,还是在小公司鸡头?

    万次阅读 多人点赞 2019-08-18 21:40:01
    别看我现在在大厂,但是我也待过小到不能小的小公司,也许你会感兴趣,那么不妨听我说说,我在学习编程路上待过的公司吧。   不到20人的小公司 我在大学的时候学的不是计算机,上的课程基本上和计算机...
  • Android 说说Bitmap那些事

    千次阅读 2022-03-07 17:20:28
    过了一个年,发现自己懈怠,没怎么去写博客了,项目中遇到的问题也很想把它写出来,但是都没有付诸行动,最近重构完项目的一些烂代码,闲下来时也是时候把项目中遇到的问题分享给大家。好了,唠叨说完,今天主要说下...
  • SpringBoot应用越来越广泛,在这里说说我对SpringBoot和SpringMVC之间的区别
  • 以前我在某外企银行实习的时候,需要处理将近七年的财务报表,如果按照传统的方式,我估计七天七夜都处理...直白点来说,VBA的执行效率确实不咋地,稍微复杂点的功能总是卡死(假死)。语法也挺奇怪的,但是由于和ex...
  • 最初的原因是组里的小朋友们看了文档后, 表情都是这样的: (摘自webpack的评论区) 和这样的: 是的, 即使是外国佬也在吐槽这文档不是人能看的. 回想起当年自己啃webpack文档的血与泪的往事, 觉得有必要整一个教程, ...
  • 第一次发博客,随便说说

    千次阅读 2021-12-10 10:12:35
    在早之前还买过一个最初级的,几十块钱,有一个球,随机滚来滚去,带动着一个轻薄的外壳,壳子下面粘着无纺布,现在看来是很脑残的设计,但我觉得是挺不错的发明,是现在智能扫地机器人的鼻祖。 关注科技动态,了解...
  • slackbot:Slack Hubot

    2021-07-11 12:14:50
    一定要更新完善,说说自己的实例,如何使用和部署,他有什么功能等等! 在本地运行 slackbot 您可以通过运行以下命令来测试您的hubot。 您可以通过运行以下命令在本地启动 slackbot: % bin/hubot 你会看到...
  • 一定要更新完善,说说自己的实例,如何使用和部署,他有什么功能等等! 在本地运行 eric-bot 您可以通过运行以下命令来测试您的 hubot,但是除非设置了它们所依赖的,否则某些插件不会按预期运行。 您可以通过...
  • 一定要更新完善,说说自己的实例,如何使用和部署,他有什么功能等等! 在本地运行 hubot 您可以通过运行以下命令来测试您的hubot。 您可以通过运行以下命令在本地启动 hubot: % bin/hubot 你会看到一些关于...
  • 像每一滴酒回不了最初的葡萄,我回不到年少。爱情亦是如此,这就是写一篇小程序的初衷,用来记录我和她最美的恋爱。什么是最美恋爱?就是繁忙之余的一封书信,一起奋斗的目标,精彩的瞬间,旅游的足迹,和那无数的...
  • 说说为什么要写这篇文章,最初的原因是组里的小朋友们看了 文档后,表情都是这样的:摘自 webpack 一篇文档的评论区) 和这样的: 是的,即使是外国佬也在吐槽这文档不是人能看的。回想起当年自己啃 webpack 文档...
  • 很无语的心情说说带图

    千次阅读 2020-12-19 13:58:31
    很无语的心情说说带图【篇一:很无语的心情说说带图】在生活我们有时候会遇到很无奈很无语的时候,那么和心情和无语的相关说说又有什么呢?下面学习啦小编分享一下心情很无语的说说心情很无语的说说最新每个人是每个...
  • 说说Zepto

    千次阅读 2018-02-02 23:06:13
    在第一阶段复习的时候我就学习并写了一篇关于zepto的文章(具体地址: ...)下面简单的再来总结一下,用3w1...zepto的用法可以看看我上面给的链接,要不就自己看看api也行。这里就不重复写了 。。。。。
  • 如果谁想利用一台计算机点事情,那么就必须排队,是的,就像景区游乐设施排队的那种,有时候自己的程序明明只需要执行5分钟,排队可能要排一天,因为有太多的人需要用这台计算机运行5分钟左右的程序了,当然,也有...
  • 最初自己的Presto内核,现在使用Blink内核 问题2:主要分为两个部分:渲染引擎和js引擎。 (1)渲染引擎:负责获取网页的内容(HTML、XML、图像等)、整理讯息(例如加入css等),以及计算网页的显示方式,然后会...
  • 百度手机输入法iPhone版从最初的满足基本输入需求,到现在全方位情感化的输入体验,经历了10多个版本的蜕变。随着功能和体验不断提升也得到了越来越多的用户所喜爱,已经成为iPhone上最好用的输入法。对于它的体验...
  • 说说#条目化需求#

    千次阅读 2015-03-28 17:02:02
    之4:条目化需求的状态管理能够将需求的最初识别,采纳,确认,实现,验证,上线融合在一起,尤其是变更也能融合在一起,得到目前为止最高效的需求管理。而且支持产品的全生命周期,超越项目级别的生命周期。 之5: ...
  • 《认识我们人类自己》江湖一剑客

    万次阅读 2018-11-05 01:29:00
    认识我们人类自己 文 / 江湖一剑客 人类和高等动物大脑之间的差别,显然在于程度上而不是本质上的差异。 ——达尔文 人类骨架图: 世界终究还是物质的。(江湖一剑客) 人类的演化史: 1859...
  • 具体点说说,作业。以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!分类ip地址中,保留地址有哪些?具体点说说,作业。A类地址中的私有地址和保留地址...
  • 最初自学 Web 开发的时候,那时没有所谓的 DIV/CSS 布局,一概 Table 布局的天下。当时有个问题就来了,如何补全宫格空白的单元格呢?——这是我在弄公司产品页头痛的问题。因为我不是学程序出身的,碰到这稍带...
  • 最初的第一枚BCH币怎么来的?BCH币挖矿怎么挖?币圈精英聚集地,免费交流讨论最新的币圈趋势,加我微信拉你进群很多人都想知道现在的币圈到底是见顶还是底部,某个币种是否还会继续上涨,还能不能继续持有?接下来我...
  • 从逻辑思维角度提升自己的表达技巧 逻辑性 从事软件开发行业的同学们或多或少都具有相当不错的逻辑性,毕竟编程开发本身就是逻辑性较强的任务。但是大家是否考虑过这种逻辑性应该怎么应用到社交技巧上?下面就跟着...
  • 在国内,大部分的Spark用户都是由Hadoop过渡而来,因此YARN也成了大多Spark应用的底层资源调度保障。而随着Spark应用的逐渐加深,各种问题也随之暴露出来,比如资源调度的粒度问题。...TalkingData最初引入

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 25,221
精华内容 10,088
关键字:

做最初的自己说说