提莫找蘑菇

提莫找蘑菇 查看完整档案

成都编辑四川理工学院  |  软件工程 编辑  |  填写所在公司/组织 www.heyitong904.com 编辑
编辑

不要给我说什么:react/angular/Typescript/vue/es6/es7/babel/webpack....老夫就用Jquery!0.0

个人动态

提莫找蘑菇 发布了文章 · 1月30日

我去前面探路了,2020!

2020年,在这疫情冲击下的一年,就这样慢慢的无察觉的从我的身边走过了。而从校园毕业至今,我也快出来工作5年了,都说3年一小坎,5年一大坎,不知道今天的这个“大坎”对我来说,能顺利跨过不?回首这5年,我也来给自己总结,给2021年来个【牛转钱坤】!!!

工作历程

匍匐慢爬

2015年毕业的我,有机会在杭州工作,开始了自己的前端开发路。刚踏进社会,不是有着朝气蓬勃的青年人的满腔热血,而是满眼的迷茫,这个迷茫是对工作的未知迷茫,因为自己知道自己的水平,焦虑着是否能在公司做下来。

在刚开始的三个月中,自己是满足了劲的学习着,对!是学习,而不是干工作,因为刚开始,任务不是很多,都是一些简单的修改BUG,一些小需求,但是这样容易的事,对我来说都是天书般不知如何下手,所以每天都是自己加班忙着,学习着,想着能尽快的提高能力,因而每天基本都是11点过后下班,但是收效甚微。而对自己的转机,也是在这样不知前路如何情况悄然来临...

快步小走

15年的末尾,公司来了一位前端leader-【邱姐】,因为自己基础薄弱,邱姐就专门拿个10多分钟来给我讲解一些基础知识;后面在一次项目中,自己能很幸运的和邱姐一起开发,而就是在这一次合作中,让我慢慢的踏上了前端的开发正规,不在是一个满头撞墙的苍蝇。

也是在这次合作中,我收到了进入工作中的第一份肯定,邱姐说:你在做前端页面时,效率好快(虽然只是纯静态页面),而且你代码中的命名也很不错。就是这两点的肯定,让我当时非常的高兴,让我知道了在开始需求开发时,别一拿着需求就埋头敲代码,得先有个自己对这次需求的分析,开发前有个思路。代码开发完,可以思考自己写的功能需求,能否在精简一些,这个100行代码实现,那我能否50行就实现了,这样的话语也让我再后来开发中考虑代码的复用,封装,风格等等

自信随意踏步走

在杭州工作了2年半,后面就回老家四川了,目前在成都工作。在杭州的2年半中,工作中的伙伴都对我给与很多帮助,让我能在离开公司后,从容自信的重新开始,不在如刚开始那样的迷茫。生活依旧,工作继续,2021,我现在已经能昂首自信的向你走来了。

3年一小坎,5年一大坎

开头也说了,对于开发来说,3年一小坎,5年一大坎,不知道今年的“大坎”我能否顺利跨过,更进一步的提升自己了?相信有很多童鞋都有看见此类的信息,35岁的程序员的状况如何?前端工程师的这个职业能不能干的长呢,能干到什么程度呢,当前的前端到底发展状况如何呢,担心自己的职业路线,也会担心前端这个行业到底有没有前途。

因为在前端开发中,缺的不是前端,而是一些中高级前端,为什么前端工程师都说它天花板很低但是自己跟大牛还是有很大的差距?所以,目前我的考虑是,我怎样来提升自己,而不被淘汰了?

优秀的前端工程师,他们的共同特征:

首先对技术是有足够热情的,不管白天黑夜脑海里都是忍不住想着编程的事情,其次是很有好奇心,主要前端新出了个什么新技术,就想动手去玩一下,把它拆开看看。

其次就是责任性普遍很强,在工作中言出必行,以及对技术、视觉和相关信息很敏感,很希望做出完美的产品。

最后一点就是善于总结,在脑海中或者本地的笔记中,善于对自己过去的项目啊能力啊做复盘整理。

最最后一点就是这些优秀前端中相当多,都是懒人,懒到不想动手写代码,所以他们会再工作中想法设法的偷懒,这个偷懒不是指偷工减料,是说会用各种手段提高代码效率,提高交付质量,降低开发成本,能代码自动搞定的,尽量不人肉做。

因此,我对自己的总结,抛开大厂背景下的熏陶外,自己要做到自驱,如果没有自驱的话,无论什么样的因素,都足以让后浪把我们拍死在沙滩上。一定要善于利用,善于求助,善于积累,对于平时遇见的困难复盘总结。

image

本文参与了 SegmentFault 思否征文「2020 总结」,欢迎正在阅读的你也加入。
查看原文

赞 0 收藏 0 评论 0

提莫找蘑菇 发布了文章 · 1月29日

React严格模式-React.StrictMode

前言

因为才开始学习关于React的开发,所以很多时候会遇见一些未曾用过的API、方法等。因此将这些知识点记录下来,也可以供同我一样的新入门童鞋学习。

因为目前做的项目是直接上手公司现有的React项目,就用到的一些API可能就是洗项目中显有的,而今天看见一个标签 <React.StrictMode>,在目前项目中也没有出现,就在想这个是不是和Javascript中的严格模式“use strict”一样呢?那本着不懂就问的原则,开始了今天的知识学习。

严格模式(Strict Mode)

严格模式在官网中这样介绍:

StrictMode 是一个用以标记出应用中潜在问题的工具。就像 FragmentStrictMode 不会渲染任何真实的UI。它为其后代元素触发额外的检查和警告。

注意: 严格模式检查只在开发模式下运行,不会与生产模式冲突

你可以在代码的任何地方启用严格模式。例如:

// 文件入口
React.render(
    <React.StrictMode>
        <App />
    </React.StrictMode>,
    document.getElementById("root")
)

// 单个组件中
import React from "react";

function Home() {
    return (
        <div className="home">
            <React.StrictMode>
                <ComponentTable />
                <ComponentDialog />
            </React.StrictMode>
            <CommonInfo />
        </div>
    )
}

上述栗子🌰中,在文件入口中使用,会对它的所有后代元素都进行检查;在单个组件中使用,会对ComponentTableComponentDialog以及他们的所有后代元素进行检查,不会对CommonInfo组件运行严格模式。

使用StrictMode的优点:

  • 识别不安全的生命周期组件
  • 有关旧式字符串ref用法的警告
  • 关于使用废弃的 findDOMNode 方法的警告
  • 检测意外的副作用
  • 检测过时的 context API
查看原文

赞 0 收藏 0 评论 0

提莫找蘑菇 发布了文章 · 1月26日

前端缓存总结-HTTP缓存

前言

在前端面试中,可能或多或少都会被提及缓存问题,而这个问题大多数都是作为业务中不得不考虑的一个性能优化点,如果平时没有怎么关注或是特意去了解这块的童鞋们,可能就是不太了解其中的原由,那么今天我们就这个缓存问题来细细分析,帮助一些还不是太明白的或是刚入门的前端童鞋们梳理梳理,理解理解,那就话不多说,开始吧^-^。

概述

其实缓存有很多种,包括:HTTP缓存DNS缓存CDN缓存等等。今天主要介绍的就是HTTP缓存,在开始介绍之前,先简单说说HTTP报文。

HTTP报文就是浏览器和服务器之间通信时发送响应的数据块。
浏览器向服务器请求数据,发送请求(request)报文;服务器向浏览器返回数据,返回响应(response)报文。

报文主要包含以下两部分:

  1. 属性的头部(header);
  2. 数据的主体部分(body);

浏览器缓存策略

浏览器每次发起请求时,先在本地缓存中查找结果以及缓存标识,根据缓存标识来判断是否使用本地缓存。如果缓存有效,则使用本地缓存;否则,向服务器发起请求并携带缓存标识。根据是否需要向服务器发起HTTP请求,将缓存过程划分为两个部分:

  1. 强制缓存:服务器通知浏览器一个缓存时间,在缓存时间内,下次请求直接使用缓存,不在时间内,执行比较缓存策略;
  2. 协商缓存:让客户端与服务器之间能实现缓存文件是否更新的验证、提升缓存复用率,将缓存信息中的EtagLast-Modified通过请求发送给服务器,由服务器校验,返回304状态码时,浏览器直接使用缓存;

强缓存优先于协商缓存。

缓存运作的整体流程图如下:
流程图

强制缓存

当请求命中强制缓存时,浏览器不会将本次请求发往服务器,而是直接从缓存中读取内容,在Chrome中打开控制台,在network中显示的是memory cache或者是disk cache

强制缓存

强缓存可以通过设置两种HTTP Header实现:Expires(1.0)Cache-Control(1.1)

Expires

Expires是一个绝对时间,是缓存过期时间。用以表达在这个时间点之前发起请求可以直接从浏览器中读取数据,而无需重新发起请求。

Expires = max-age + 到期时间。由于受限于本地时间,如果修改了本地时间,可能会造成缓存失效。

该字段是服务器响应消息头字段,告诉浏览器在过期时间之前可以直接从浏览器缓存中存取数据。由于是绝对时间内,用户可能将本读时间进行修改,从而导致浏览器判断缓存失效,重新请求资源。在不考虑修改,时差或者误差等因素也可能照成客户端于服务端的时间不一致,致使缓存失效。

优点:

  1. HTTP 1.0产物,可以在HTTP 1.0和1.1中使用,简单、易用。
  2. 以时刻标识失效时间。

缺点:

  1. 时间是由服务器发送的,如果服务器时间和客户端时间不一致,可能会出现问题。
  2. 存在版本问题,到期之前的修改客户端是不可知的。

Cache-Control

Cache-Control的优先级比Expires的优先级高。该字段表示资源缓存最大有效时间,在该时间内,客户端不需要向服务器发送请求。前者的出现是为了解决Expires在浏览器中,时间被手动更改导致缓存判断错误的问题。如果同时存在则使用Cache-Control

常见的取值有(完整的列表可以查看MDN):

  • private(默认值):客户端可以缓存,代理服务器不能缓存
  • public:客户端和代理服务器都可缓存
  • no-cache:在发布缓存副本之前,强制要求缓存把请求提交给原始服务器进行验证(协商缓存验证)
  • max-age:设置缓存存储的最大周期,超过这个时间缓存被认为过期(单位秒)
  • no-store:缓存不应该存储有关客户端请求或服务器响应的任何内容,即使不使用任何缓存

举个栗子🌰:

强制缓存

图中Cache-Control指定了max-agepublic,缓存时间为31536000秒(365天)。
也就是说,在365天内再次请求这条数据,都会直接获取缓存数据库中的数据,直接使用。

优点:

  1. HTTP 1.1产物,以时间间隔标识失效时间,解决了Expires服务器和客户端相对时间的问题。
  2. 比Expires多了很多选项设置。

缺点:

  1. 存在版本问题,到期之前的修改客户端是不可知的。

协商缓存

协商缓存就是强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程。而整个过程是需要发出请求的。

协商缓存由2组字段(不是2个),控制协商缓存的字段有:

  • Last-Modified/If-Modified-since(http 1.0): 表示的是服务器的资源最后一次修改的时间;
  • Etag/If-None-match(http 1.1): 表示的是服务器资源的唯一标识,只要资源有变化,Etag就会重新生成;

Etag/If-None-match 的优先级高于Last-Modified/If-Modified-since。

Last-Modified/If-Modified-since

  1. 服务器通过 Last-Modified 字段告知客户端(返回资源的同时在header添加),表示资源最后一次被修改的时间,浏览器将这个值和内容一起记录在缓存数据库中
  2. 下一次请求相同的资源时,浏览器会从自己的缓存中找出“不确定是否过期的”缓存,因此在请求头中将上次的Last-Modified的值写入到请求头的If-Modified-since字段
  3. 服务器会将If-Modified-since的值与Last-Modified字段进行对比。如果相等,这表示未修改,响应304;反之则表示修改了,响应 200 状态码,并返回数据

优点:

  1. 不存在版本问题,每次请求都会去服务器进行校验。服务器对比最后修改时间如果相同则返回304,不同返回200及资源内容。
  2. 如果返回的是 304,返回的仅仅是一个状态码而已,并没有实际的文件内容,因此在响应体的体积上节省是很好的优点

缺点:

  1. 只要资源发生了修改,无论内容是否发生了实质性的改变,都会将该资源返回客户端。例如周期性重写,但这种情况下资源包含的数据实质是一样的。
  2. 以时刻作为标识,无法识别一秒内多次修改的情况。如果资源更新的速度是秒以下的单位,那么该缓存是不能被使用的,因为它的时间最低单位是秒。
  3. 某些服务器不能精确的得到文件最后修改时间。
  4. 如果文件是服务器动态生成的,那么该方法的更新时间永远是生成的时间,尽管文件可能并没有变化,所以也起不到缓存的作用。

Etag/If-None-match

为了解决上述问题,出现了一组新的字段Etag/In-None-Match

  1. Etag是上一次加载资源时,服务器返回的。它的作用是唯一用来标识资源是否有变化
  2. 浏览器在下一次发起请求时,会将上一次返回的Etag值赋值给If-None-Match并添加在 Request Header 中。服务端匹配传入的值与上次是否一致,如果一致返回304,浏览器则读取本地缓存,否则返回200和更新后的资源及新的Etag

优点:

  1. 可以更加精确的判断资源是否被修改,可以识别一秒内多次修改的情况
  2. 不存在版本问题,每次请求都会去服务器进行校验

缺点:

  1. 计算Etag值需要性能损耗
  2. 分布式服务器存储情况下下,计算Etag的算法如果不一致,会导致浏览器从一个服务器上获取得页面内容后到另一台服务器上进行验证时出现Etag不匹配的情况

总结

对于强制缓存,服务器通知浏览器一个缓存时间,在缓存时间内,下次请求,直接用缓存,不在时间内,执行比较缓存策略。

对于协商缓存,将缓存信息中的EtagLast-Modified通过请求发送给服务器,由服务器校验,返回304状态码时,浏览器直接使用缓存。

查看原文

赞 0 收藏 0 评论 0

提莫找蘑菇 回答了问题 · 1月22日

解决js/vue/react 如何把这种数据结构格式的data 转为同一个对象

function loop(data, arr) {
    for (var item of data) {
        if (item.children) {
            arr = loop(item.children, arr)
        } else {
            arr = arr.concat(data)
        }
    }

    return arr
}

var filterData = loop(data, []);
// 去重
var temp = {}
var result = filterData.reduce(function(sum, arrItem) {
    if (!temp[arrItem.id]) {
        temp[arrItem.id] = arrItem.id
        sum.push(arrItem)
    }
    return sum
}, [])
// console.log('result', result)

关注 3 回答 2

提莫找蘑菇 发布了文章 · 1月21日

浅析CDN原理

什么是CDN

CDN全称是Content Delivery Network,即内容分发网络,也称为内容传送网络CDN是构建在现有网络基础之上的智能虚拟网络,依靠部署在各地的边缘服务器,通过中心平台的负载均衡、内容分发、调度等功能模块,使用户就近获取所需内容,降低网络拥塞,提高用户访问响应速度和命中率。

简单理解了,CDN是利用缓存技术,解决如何将数据快速可靠从源站传递到用户的问题。用户获取数据时,不需要直接从源站获取,通过CDN分发,用户可以从一个较优的服务器获取数据,从而达到快速访问,并减少源站负载压力的目的。

CDN快速访问缘由

问:为什么我们不直接从源站直接获取数据,而要通过CDN这个中间转发了?

如果网络上的数据都是直接从源站返回给用户的话,那么极有可能就会出现网络拥塞,服务器过载的情况。那如果能有一种技术,将数据缓存在离用户最近的地方,使用户以最快的速度获取,那么就可以减少压力,减少网络传输的拥堵,起到很大的作用。CDN便是这样的一种技术方案,由此孕育出来。

CDN原理

CDN基本原理是采用各种缓存服务器,将这些缓存服务器分布到用户访问相对集中的地区或网络中,在用户访问网站时,利用全局负载技术将用户访问指向距离最近的工作正常的缓存服务器上,由缓存服务器直接响应用户请求。

用户通过浏览器访问未使用CDN加速的网站大致过程如下:
用户访问网站过程

  1. 用户在浏览器中输入要访问的域名;
  2. 浏览器向DNS服务器请求对该域名的解析;
  3. DNS服务器返回该域名的IP地址给浏览器;
  4. 浏览器使用该IP地址向服务器发送请求内容;
  5. 服务器将用户请求的内容返回给浏览器;

用户访问的网站使用了CDN,其过程会变成以下这样:
用户访问网站过程

  1. 用户向浏览器输入www.processon.com这个域名,浏览器第一次发现本地没有DNS缓存,则向网站的DNS服务器请求;
  2. 浏览器向DNS服务器请求对该域名的解析。由于CDN对域名进行了调整,DNS服务器最终会将域名解析权交给CNAME指向CDN专用的DNS服务器;
  3. CND的DNS负载均衡系统解析域名,把对用户响应速度最快的IP地址返回给用户;
  4. 用户向该IP地址(CND服务器)发出请求;
  5. CND负载均衡设备会为用户选择一台合适的缓存服务器提供服务;
  6. 用户向缓存服务器发出请求;
  7. 缓存服务器响应用户请求,将用户所需的内容返回给用户;

总结

网站和用户之间引入CDN之后,用户不会有任何与原来不同的感觉。CDN的基本思路就是尽可能避开互联网上有可能影响数据传输速度和稳定性的瓶颈和环节,使内容传输的更快,更稳定。CDN便是让用户以最短的路径,最快的速度对网站进行访问,减少源站中心的负载压力。

查看原文

赞 0 收藏 0 评论 0

提莫找蘑菇 发布了文章 · 2020-12-14

你真的会用setState吗?

setState函数是什么?

  1. 将需要处理的变化塞入组建的state对象中
  2. 告诉该组件及其子组件需要用更新的状态来重新渲染
  3. 响应事件处理喝服务端响应更新用户界面的主要方式

setState经典例子

constructor(props) {
    super(props);
    this.state = {
        count: 0
    };
    this.increase = this.increase.bind(this);
}

increase() {
    this.setState({ count: this.state.count + 1 });
    // 第一次输出
    console.log('第一次输出:', this.state.count);
    
    this.setState({ count: this.state.count + 1 });
    // 第二次输出
    console.log('第二次输出:', this.state.count);
    
    setTimeout(() => {
        this.setState({ count: this.state.count + 1 });
        // 第三次输出
        console.log('第三次输出:', this.state.count);
        
        this.setState({ count: this.state.count + 1 });
        // 第四次输出
        console.log('第四次输出:', this.state.count);
    }, 1000)
}

不知道你们心里是否有上面代码的答案了呢?不错,正确输出是:0 0 2 3。那可能刚开始学React的童鞋就会问了?为什么前面都是0,后面的正常加了了?那这个setState究竟是同步的还是异步的了?那么,接下来就为你解答心中的疑惑=。=

合并更新

前两次的setState,不会立即改变React组件中的state的值,这两次输出的都是0;合并更新,将多次的setState合并成一次,不引发重复渲染

setTimeout 同步更新,引发两次渲染。

setState是异步的吗?

其实,这只是React的障眼法。

setState是同步执行的!但是state并不一定会同步更新(异步更新合同步更新都存在)

setState究竟干了什么了?

setState()中有个特别重要的布尔属性isBatchingUpdates(默认为false),它决定了state同步更新还是异步更新

setState调用

setState只在合成事件何钩子函数中是“异步更新”的。

异步更新的背后,是同步代码处理(“合成事件何钩子函数”的调用在“更新”前)。

异步是为了实现批量更新的手段,也是React新能优化的一种方式。

为什么直接修改this.state无效

要知道setState本质是通过一个队列机制实现state更新的。执行setState时,会将需要更新的state合并后放入状态队列,而不会立刻更新state,队列机制可以批量更新state。如果不通过setState而直接修改this.state,那么这个state不会放入状态队列中,下次调用setState时,状态队列新进行合并时,会忽略之前直接被修改的state,这样我们就无法合并了,而且实际也没有把你想要的state更新上去。

React.setState中的同步更新

当然了,我们也是有办法同步获取state更新后的值:

  1. setTimeout等异步操作中调用setState函数
  2. DOM原生事件
  3. 利用setState回调函数
  4. 函数式setState用法

前两个都是比较好理解,因为没有前置的batchdUpdate调用,所以isBatchingUpdatesfalse。不会开启批量更新模式。

后面两个方法,是React本身提供的。要注意的是,setState回调函数要在render函数被重新执行后才执行。

// 回调函数
this.setState({ count: 1 }, () => {
    console.log(this.state.count)
})

// 函数式
this.setState(prevState => {
    return {
        count: prevState.count + 1
    }
})

对比VUE批量更新

Vue在监听到数据变化后,会开启一个队列,并缓冲在同一事件循环中发生的所有数据变更(如果同一个watcher被多次触发,只会被推入到队列中一次)。然后,在下一次事件循环Tick/微任务中,Vue刷新队列执行实际工作。

vue更新

vue批量更新体现为:

  1. Mutation Observer(变动观察器)是侦听DOM变动的接口。当DOM对象树发生任何变动时,Mutation Observer会得到通知。
  2. 概念上,它很接近事件。可以理解为,当DOM发生变动会触发Mutation Observer事件。但是,它与事件有一个本质区别:事件是同步触发,DOM发生变化会立刻出发响应的事件
  3. Mutation Observer是异步触发,DOM发生变动,并不会立刻触发,而是要等到当前所有DOM操作都结束后才会触发。

针对以上的知识点,我们来看看下面这段代码,看我们是否理解setState:

class Demo extends React.Component {
  state = {
    count: 0,
  };
  componentDidMount() {
    this.setState({
      count: this.state.count + 1,
    });
    console.log("console: " + this.state.count);

    this.setState({ count: this.state.count + 1 }, () => {
      console.log("console from callback: " + this.state.count);
    });

    this.setState(
      (prevState) => {
        console.log("console from func: " + prevState.count);
        return {
          count: prevState.count + 1,
        };
      },
      () => {
        console.log("last console: " + this.state.count);
      }
    );
  }

  render() {
    console.log("render: " + this.state.count);
    return <h4>test</h4>;
  }
}

总结

  1. state更新需要通过setState,而不能直接操作state
  2. 调用setState更新,state不会立刻生效。
  3. 多个顺序执行的setState不是同步一个个执行,会加入到一个队列中,然后最后一起执行,及批量更新。
查看原文

赞 0 收藏 0 评论 0

提莫找蘑菇 发布了文章 · 2020-12-07

正则表达式解析,让你一次明白正则表达式

正则表达式是每个程序员都绕不过的一道坎,也是最容易被忽视的一门艺术,相信大多数程序员跟我之前一样在工作中使用正则的时候,都是Google一下,然后复制过来改一改。正则给程序员的感觉就是一门很难掌握和利用好的一门工具。最近,在爬虫项目中大量用到了正则,为此,查询了好多文献资料,系统的学习了一下正则表达式,做了一些总结分享给大家。

正则表达式是利用单个字符来描述、匹配一系列符合某个句法规则字符串的技术。在很多文本编辑器里,正则表达式通常被用来检索、替换那些符合某个模式的文本。在编程项目中,正则也常被用于文本字符串的查找、替换、切分和提取

几乎所有的编程语言都支持正则表达式,但不同的编程语言对正则的规则和定义有所差别,本文对于正则的讨论是基于python语言的

一、五类元字符

元字符就是指那些在正则表达式中具有特殊意义的专用字符,元字符是构成正则表达式的基本元件。正则就是由一系列的元字符组成的,比如d可以表示0-9之间的任意数字,w可以表示字母、数字、下划线中任意字符。正则中的元字符非常多,可分为以下几类:

1、特殊单字符:英文的.表示换行以外的任意字符,d表示任意单个数字,w表示字母、数字、下划线的任意字符,s表示任意的空白字符,相应地D,W,S分别是对对应小写的取反。

还在被正则表达式摩擦吗?一文搞定正则表达式

2、空白符:除了特殊单字符外,你在处理文本的时候肯定还会遇到空格、换行等空白符。其实在写代码的时候也会经常用到,换行符 n,TAB 制表符 t

还在被正则表达式摩擦吗?一文搞定正则表达式

s可以表示以上所有空白字符,包括空格

3、量词:特殊单字符和空白符都只能匹配单个字符,但在实际匹配过程中会有出现几次、至少出现几次、最多出现几次等规则,这时候就需要用到量词,正则中的量词元字符主要有六种:*表示0次或多次,+表示1次或多次,?表示0次或1次,{m}m次,{m,}至少m次,{m,n}m到n次

还在被正则表达式摩擦吗?一文搞定正则表达式

4、范围:之前所有元字符都只能匹配单个字符或者单字符的重复,有时需要对完整字符串(比如匹配good或者well等表示好的单词)进行匹配或者多个不同单字符(比如匹配元音aeiou),这时候就用到了范围元字符,范围元字符主要四种:| 或运算符,可匹配前后两个字符串中的任意一个可用于字符串的匹配,比如ab|bc 可匹配文本中的ab和bc,[....]中括号 [] 代表多选一,可以表示里面的任意单个字符,所以任意元音字母可以用 [aeiou] 来表示,中括号中的中划线可表示范围[a-z]可表示a到z的任意字母,如果中括号第一个是脱字符(^),那么就表示非,表达的是不能是里面的任何单个元素。

还在被正则表达式摩擦吗?一文搞定正则表达式

5、断言:断言又称锚点,用来限定字符出现的位置,比如要替换tom字符串为jerry,如果不用断言那么tomorrow的前三个字符也会被替换,这显然是错误的。断言主要分为三类

替换前:tom asked me if I would go fishing with him tomorrow.
替换后:jerry asked me if I would go fishing with him jerryorrow.

5.1单词边界:b 来表示单词的边界,

还在被正则表达式摩擦吗?一文搞定正则表达式

利用断言来就可以完成以上替换案例案例了

str =" tom asked me if I would go fishing with him tomorrow."
re.sub(r"btomb","jerry",str)
替换后:jerry asked me if I would go fishing with him tomorrow.

5.2行的开始/结束:行的开头和结尾有两套元字符^和$,A和Z,两者的区别是当匹配为多行模型【后面会有介绍】时,前者匹配的是每一行的开头和结尾,而后者一直匹配整个字符串的开头和结尾。

还在被正则表达式摩擦吗?一文搞定正则表达式

还在被正则表达式摩擦吗?一文搞定正则表达式

5.3 环视又称零款断言:在一些场景下需要对要匹配的字符串左右做限定,这就用到了环视。比如我们需要对11位的电话号码做匹配时,12位数字的前11位也会被匹配上,22位数也会被匹配前11位,此时就需要用到环视,即左右都不能是数字。

还在被正则表达式摩擦吗?一文搞定正则表达式

还在被正则表达式摩擦吗?一文搞定正则表达式

对于环视可能上边的表有点复杂,其实本质就是左尖括号代表看左边,没有尖括号是看右边,感叹号是非的意思

还在被正则表达式摩擦吗?一文搞定正则表达式

二、两种量词匹配方式

上一个模块已经介绍了六种量词元字符,其实{m,n}这种形式完全可以替代,+,?,其中+和因为有无穷多的属性,需要引进贪婪匹配和非贪婪匹配,先来看一个例子

还在被正则表达式摩擦吗?一文搞定正则表达式

+号的匹配比较好理解,的匹配会匹配上四个空,是因为是匹配0次或者多次。针对无穷次匹配属性就引入了贪婪模型:尽可能长的匹配,正则默认就是贪婪模式和非贪婪模式,尽可能少的去匹配,非贪婪模式就是在+或者*后面加个?就可以了。

还在被正则表达式摩擦吗?一文搞定正则表达式

三、四种匹配模式

所谓匹配模式就是改变元字符本身匹配行为的方式,具体分为四类:

1、忽略大小写模式,(?i)比如 不区分 大小写匹配a,python中提供了re.IGNORECASE参数也可以实现忽略大小写功能

还在被正则表达式摩擦吗?一文搞定正则表达式

2、.点号通配模式,也称为单行模式(?s).号本身是匹配换行以外的任意字符的,利用此模式可以使.号匹配任意字符功能相当于[Ww]

还在被正则表达式摩擦吗?一文搞定正则表达式

3、多行匹配模式,通常情况下,^ 匹配整个字符串的开头,$ 匹配整个字符串的结尾。多行匹配模式改变的就是 ^ 和 $ 的匹配行为,这在之前断言中介绍过,(?m)实现多行模式,多行模式匹配每一行的开头结尾,这在日志分析中识别每条以时间开始的日志行非常有用

还在被正则表达式摩擦吗?一文搞定正则表达式

4、注释模式,即为正则表达式添加注释,便于后期维护与复盘(?#comment)

四、小括号的作用

()小括号可以说是正则中使用频率最高,功能最强大的一类符号,那正则中的效果好具体有哪些功能呢?

还在被正则表达式摩擦吗?一文搞定正则表达式

除了分组引用以外,所有内容前面均已讲过了,这里主要介绍分组引用功能。

1、分组与编号

括号在正则中可以用于分组,被括号括起来的部分 “子表达式” 会被保存成一个子组。那分组和编号的规则是怎样的呢?其实很简单,用一句话来说就是,第几个括号就是第几个分组。这么说可能不好理解,我们来举一个例子看一下。这里有个时间格式 2020-05-10 20:23:05。假设我们想要使用正则提取出里面的日期和时间。

还在被正则表达式摩擦吗?一文搞定正则表达式

2、不保存分组

在括号里面的会保存成子组,但有些情况下,你可能只想用括号将某些部分看成一个整体,后续不用再用它,类似这种情况,在实际使用时,是没必要保存子组的。这时我们可以在括号里面使用?: 不保存子组。如果正则中出现了括号,那么我们就认为,这个子表达式在后续可能会再次被引用,所以不保存子组可以提高正则的性能。除此之外呢,这么做还有一些好处,由于子组变少了,正则性能会更好,在子组计数时也更不容易出错。那到底啥是不保存子组呢?我们可以理解成,括号只用于归组,把某个部分当成 “单个元素”,不分配编号,后面不会再进行这部分的引用

还在被正则表达式摩擦吗?一文搞定正则表达式

3、括号嵌套

前面讲完了子组和编号,但有些情况会比较复杂,比如在括号嵌套的情况里,我们要看某个括号里面的内容是第几个分组怎么办?不要担心,其实方法很简单,我们只需要数左括号(开括号)是第几个,就可以确定是第几个子组。

还在被正则表达式摩擦吗?一文搞定正则表达式

4、命名分组

前面我们讲了分组编号,但由于编号得数在第几个位置,后续如果发现正则有问题,改动了括号的个数,还可能导致编号发生变化,因此一些编程语言提供了命名分组(named grouping),这样和数字相比更容易辨识,不容易出错。命名分组的格式为 (?P < 分组名> 正则)。

5、后向引用

在知道了分组引用的编号 (number)后,python中,我们就可以使用 “反斜扛 + 编号”,即 number 的方式来进行引用

还在被正则表达式摩擦吗?一文搞定正则表达式

**
五、四类正则转义场景
**

1、转义字符:反斜杠是python中的转义字符,后的字符就会改变字符本来的意思

还在被正则表达式摩擦吗?一文搞定正则表达式

2、字符串转义和正则转义

可能所有的编程教程中都讲过在正则中要表示反斜杠需要用四个反斜杠,估计有很多程序员可能都不知道底层的原理。从输入字符串到 最终的正则表达式经历了两次转义过程。其中可以使用r避免字符串转义,即用原生字符串进行匹配

还在被正则表达式摩擦吗?一文搞定正则表达式

3、正则中的元字符转义

如果现在我们要查找比如星号(*)、加号(+)、问号(?)本身,而不是元字符的功能,这时候就需要对其进行转义,直接在前面加上反斜杠就可以了

4、括号的转义

在正则中方括号 [] 和 花括号 {} 只需转义开括号,但圆括号 () 两个都要转义

>>> import re>>> re.findall('()[]{}', '()[]{}')['()[]{}']>>> re.findall('()[]{}', '()[]{}')  # 方括号和花括号都转义也可以['()[]{}']

5、字符组中的转义

以上描述了元字符的转义,字符组中的转义有三种情况

5.1脱字符在中括号中,且在第一个位置需要转义

>>> import re>>> re.findall(r'[^ab]', '^ab')  # 转义前代表"非"['^']>>> re.findall(r'[^ab]', '^ab')  # 转义后代表普通字符['^', 'a', 'b']

5.2中划线在中括号中,且不在首尾位置

>>> import re>>> re.findall(r'[a-c]', 'abc-')  # 中划线在中间,代表"范围"['a', 'b', 'c']>>> re.findall(r'[a-c]', 'abc-')  # 中划线在中间,转义后的['a', 'c', '-']>>> re.findall(r'[-ac]', 'abc-')  # 在开头,不需要转义['a', 'c', '-']>>> re.findall(r'[ac-]', 'abc-')  # 在结尾,不需要转义['a', 'c', '-']

5.3右括号在中括号中,且不在首位

​>>> import re>>> re.findall(r'[]ab]', ']ab')  # 右括号不转义,在首位[']', 'a', 'b']>>> re.findall(r'[a]b]', ']ab')  # 右括号不转义,不在首位[]  # 匹配不上,因为含义是 a后面跟上b]>>> re.findall(r'[a]b]', ']ab')  # 转义后代表普通字符[']', 'a', 'b']

6、字符组中其他的元字符

一般来说如果我们要想将元字符(.+?() 之类)表示成它字面上本来的意思,是需要对其进行转义的,但如果它们出现在字符组中括号里,可以不转义。这种情况,一般都是单个长度的元字符,比如点号(.)、星号()、加号(+)、问号(?)、左右圆括号等。它们都不再具有特殊含义,而是代表字符本身。但如果在中括号中出现 d 或 w 等符号时,他们还是元字符本身的含义。

>>> import re>>> re.findall(r'[.*+?()]', '[.*+?()]')  # 单个长度的元字符 ['.', '*', '+', '?', '(', ')']>>> re.findall(r'[d]', 'd12')  # w,d等在中括号中还是元字符的功能['1', '2']  # 匹配上了数字,而不是反斜杠和字母d

六、一个方法论

正则表达式的一个方法论:某个位置上可能有多个字符的话,就⽤字符组。某个位置上有多个字符串的话,就⽤多选结构。出现的次数不确定的话,就⽤量词。对出现的位置有要求的话,就⽤锚点锁定位置。

查看原文

赞 0 收藏 0 评论 0

提莫找蘑菇 提出了问题 · 2020-12-04

React Ant Design文件上传,获取文件真实路径

使用Antd的Upload组件,但是目前需要获取上传文件的真实路径,应该如何做?有遇见过类似问题吗

关注 1 回答 0

提莫找蘑菇 发布了文章 · 2020-12-02

webpack 基础配置解析

针对webpack,是大家(前端开发)在日常的开发中都会遇见的,通过书写的方式输出,学习到的关于前端工程化的小知识点的总结和学习,形成自己的知识体系

概念

webpack官网定义:

webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。

webpack 资源打包

在开始了解webpack配置前,首先需要理解四个核心概念

  1. 入口(entry):webpack 应该使用哪个模块,来作为构建其内部依赖图的开始。
  2. 输出(output):webpack 在哪里输出它所创建的 bundles,以及如何命名这些文件。
  3. loader:能够去处理那些非 JavaScript 文件(webpack 自身只理解 JavaScript)。
  4. 插件(plugins):用于执行范围更广的任务。

安装及构建

// npm 安装
npm install webpack webpack-cli -g

// yarn 安装
yarn global add webpack webpack-cli 

安装好后,可以在不适用配置文件的方法,直接对文件进行打包:
webpack <entry> [<entry>] -o <output>

新建一个项目,就一个入口文件,测试webpack打包:
项目结构

运行打包命令:
webpack index.js
webpack构建

在这里我们会看见一个WARNING的信息,这是因为没有设置mode,我们只需要加一个参数-p即可:

webpack -p index

webpack构建

这样默认会生成一个dist文件夹,里面有个main.js文件:
webpack构建

有了入口文件我们还需要通过命令行定义一下输入路径dist/bundle.js

 webpack -p index.js -o dist/bundle.js

webpack构建

webpack 配置文件

命令行的打包构建方式仅限于简单的项目,如果在生产中,项目复杂,多个入口,我们就不可能每次打包都输入一连串的入口文件地址,也难以记住;因此一般项目中都使用配置文件来进行打包;配置文件的命令方式如下:

webpack [--config webpack.config.js]

配置文件默认的名称就是webpack.config.js,一个项目中经常会有多套配置文件,我们可以针对不同环境配置不同的额文件,通过--config来进行更换:

// 开发环境
webpack --config webpack.config.dev.js

// 生产环境
webpack --config webpack.config.prod.js

多种配置类型

config配置文件通过module.exports导出一个配置对象:

// webpack.config.js
const path = require('path')

const resolve = function (dir) {
    return path.resolve(__dirname, dir)
}

module.exports = {
    entry: {
        app: resolve('../index.js')
    },
    output: {
        filename: '[name].[hash:8].js',
        path: resolve('../dist')
    },
}

除了导出为对象,还可以导出为一个函数,函数中会带入命令行中传入的环境变量等参数,这样可以更方便的对环境变量进行配置;比如我们可以通过ENV来区分不同环境:

const path = require('path')

const resolve = function (dir) {
    return path.resolve(__dirname, dir)
}

module.exports = function(ENV, argv) {
    return {
        // 其他配置
        entry: resolve('../index.js'),
        output: {}
    }
}

还可以导出为一个Promise,用于异步加载配置,比如可以动态加载入口文件:

entry: () => './demo'

或

entry: () => new Promise((resolve) => resolve(['./demo', './demo2']))

入口

正如在上面提到的,入口是整个依赖关系的起点入口;我们常用的单入口配置是一个页面的入口:

module.exports = {
    entry: resolve('../index.js')
}

但是我们项目中可能不止一个模块,因此需要将多个依赖文件一起注入,这时就需要用到数组了:

module.exports = {
    entry: [
        '@babel/polyfill',
        resolve('../index.js')
    ]
}

如果我们项目中有多个入口起点,则就需要用到对象形式了:

// webpack 就会构建两个不同的依赖关系
module.exports = {
    entry: {
        app: resolve('../index.js'),
        share: resolve('../share.js')
    }
}

输出

output选项用来控制webpack如何输入编译后的文件模块;虽然可以有多个entry,但是只能配置一个output

module.exports = {
    entry: resolve('../index.js'),
    output: {
        filename: 'index.js',
        path: resolve('../dist')
    },
}

这里我们配置了一个单入口,输出也就是index.js;但是如果存在多入口的模式就行不通了,webpack会提示Conflict: Multiple chunks emit assets to the same filename,即多个文件资源有相同的文件名称;webpack提供了占位符来确保每一个输出的文件都有唯一的名称:

module.exports = {
    entry: {
        app: resolve('../index.js'),
        share: resolve('../index.js'),
    },
    output: {
        filename: '[name].bundle.js',
        path: resolve('../dist')
    },
}

这样webpack打包出来的文件就会按照入口文件的名称来进行分别打包生成三个不同的bundle文件;还有以下不同的占位符字符串:

占位符描述
[hash]模块标识符(module identifier)的 hash
[chunkhash]chunk 内容的 hash
[name]模块名称
[id]模块标识符
[query]模块的 query,例如,文件名 ? 后面的字符串

在这里引入modulechunkbundle的概念,上面代码中也经常会看到有这两个名词的出现,那么他们三者到底有什么区别呢?首先我们发现module是经常出现在我们的代码中,比如module.exports;而chunk经常和entry一起出现,bundle总是和output一起出现。

  • module:我们写的源码,无论是commonjs还是amdjs,都可以理解为一个个的module
  • chunk:当我们写的module源文件传到webpack进行打包时,webpack会根据文件引用关系生成chunk文件, webpack 会对这些chunk文件进行一些操作
  • bundle:webpack处理好chunk文件后,最后会输出bundle文件,这个bundle文件包含了经过加载和编译的最终源文件,所以它可以直接在浏览器中运行。

我们通过下面这张图看可以加深对这三个概念的理解:
enter description here

hash、chunkhash、contenthash

理解了chunk的概念,相信上面表中chunkhash和hash的区别也很容易理解了;

  • hash:是跟整个项目的构建相关,只要项目里有文件更改,整个项目构建的hash值都会更改,并且全部文件都共用相同的hash值。
  • chunkhash:跟入口文件的构建有关,根据入口文件构建对应的chunk,生成每个chunk对应的hash;入口文件更改,对应chunk的hash值会更改。
  • contenthash:跟文件内容本身相关,根据文件内容创建出唯一hash,也就是说文件内容更改,hash就更改。

模式

在webpack2和webpack3中我们需要手动加入插件来进行代码的压缩、环境变量的定义,还需要注意环境的判断,十分的繁琐;在webpack4中直接提供了模式这一配置,开箱即可用;如果忽略配置,webpack还会发出警告。

module.exports = {
    mode: 'development/production'
}

开发模式是告诉webpack,我现在是开发状态,也就是打包出来的内容要对开发友好,便于代码调试以及实现浏览器实时更新。

生产模式不用对开发友好,只需要关注打包的性能和生成更小体积的bundle。看到这里用到了很多Plugin,不用慌,下面我们会一一解释他们的作用。

相信很多童鞋都曾有过疑问,为什么这边DefinePlugin定义环境变量的时候要用JSON.stringify("production"),直接用"production"不是更简单吗?

我们首先来看下JSON.stringify("production")生成了什么;运行结果是""production"",注意这里,并不是你眼睛花了或者屏幕上有小黑点,结果确实比"production"多嵌套了一层引号

我们可以简单的把DefinePlugin这个插件理解为将代码里的所有process.env.NODE_ENV替换为字符串中的内容。假如我们在代码中有如下判断环境的代码:

// webpack.config.js
module.exports = {
  plugins: [
    new webpack.DefinePlugin({ 
      "process.env.NODE_ENV": "production"
    }),
  ]
}
// index.js
if (process.env.NODE_ENV === 'production') {
    console.log('production');
}

这样生成出来的代码就会编译成这样:

//dist/bundle.js
//代码中并没有定义production变量
if (production === 'production') {
    console.log('production');
}

但是我们代码中可能并没有定义production变量,因此会导致代码直接报错,所以我们需要通过JSON.stringify来包裹一层:

//webpack.config.js
module.exports = {
  plugins: [
    new webpack.DefinePlugin({ 
      //"process.env.NODE_ENV": JSON.stringify("production")
      //相当于
      "process.env.NODE_ENV": '"production"'
    }),
  ]
}
//dist/bundle.js
if ("production" === 'production') {
    console.log('production');
}

生成HTML文件(html-webpack-plugin)

在上面的代码中我们发现都是手动来生成index.html,然后引入打包后的bundle文件,但是这样太过繁琐,而且如果生成的bundle文件引入了hash值,每次生成的文件名称不一样,因此我们需要一个自动生成html的插件;首先我们需要安装这个插件:
yarn add html-webpack-plugin -D 或者 npm install html-webpack-plugin -D

使用:

const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
    // 其他代码
    plugins: [
        new HtmlWebpackPlugin({
            // 模板文件
            template: resolve('../public/index.html'),
            // 生成的html名称
            filename: 'index.html',
            // icon
            favicon: resolve('../public/logo.ico')
        }),
    ]
}

webpack loader

loader 用于对模块的源代码进行转换。默认webpack只能识别commonjs代码,但是我们在代码中会引入比如vue、ts、less等文件,webpack就处理不过来了;loader拓展了webpack处理多种文件类型的能力,将这些文件转换成浏览器能够渲染的js、css。

module.rules允许我们配置多个loader,能够很清晰的看出当前文件类型应用了哪些loader。

module.exports = {
      module: {
            rules: [
                  { test: /\.css$/, use: 'css-loader' },
                  { test: /\.ts$/, use: 'ts-loader' }
            ]
      }
};

loader 特性

  • loader 支持链式传递。能够对资源使用流水线(pipeline)。一组链式的 loader 将按照相反的顺序执行。loader 链中的第一个 loader 返回值给下一个 loader。在最后一个 loader,返回 webpack 所预期的 JavaScript。
  • loader 可以是同步的,也可以是异步的。
  • loader 运行在 Node.js 中,并且能够执行任何可能的操作。
  • loader 接收查询参数。用于对 loader 传递配置。
  • loader 也能够使用 options 对象进行配置。
  • 除了使用 package.json 常见的 main 属性,还可以将普通的 npm 模块导出为 loader,做法是在 package.json 里定义一个 loader 字段。
  • 插件(plugin)可以为 loader 带来更多特性。
  • loader 能够产生额外的任意文件。

loader 通过(loader)预处理函数,为 JavaScript 生态系统提供了更多能力。 用户现在可以更加灵活地引入细粒度逻辑,例如压缩、打包、语言翻译和其他更多。

babel-loader

兼容低版本浏览器的痛相信很多童鞋都经历过,写完代码发现自己的js代码不能运行在IE10或者IE11上,然后尝试着引入各种polyfill;babel的出现给我们提供了便利,将高版本的ES6甚至ES7转为ES5;我们首先安装babel所需要的依赖:
yarn add -D babel-loader @babel/core @babel/preset-env @babel/plugin-transform-runtime

由于babel-loader的转译速度很慢,在后面我们加入了时间插件后可以看到每个loader的耗时,babel-loader是最耗时间;因此我们要尽可能少的使用babel来转译文件,正则上使用$来进行精确匹配,通过exclude将node_modules中的文件进行排除,include将只匹配src中的文件;可以看出来include的范围比exclude更缩小更精确,因此也是推荐使用include。

// 省略其他代码
module: {
      rules: [
            {
                  test: /\.js$/,
                  exclude: /node_modules/,
                  include: [resolve('src')]
                  use: {
                    loader: 'babel-loader',
                    options: {
                      presets: [
                        ['@babel/preset-env', { targets: "defaults" }]
                      ],
                      plugins: ['@babel/plugin-proposal-class-properties']
                    }
              }
            }
      ]
}

file-loader 和 url-loader

file-loaderurl-loader都是用来处理图片、字体图标等文件;url-loader工作时分两种情况:当文件大小小于limit参数,url-loader将文件转为base-64编码,用于减少http请求;当文件大小大于limit参数时,调用file-loader进行处理;因此我们优先使用url-loader

module: {
        rules: [
            {
                test: /\.(jpe?g|png|gif)$/i, //图片文件
                use: [
                    {
                        loader: 'url-loader',
                        options: {
                            // 10K
                            limit: 1024,
                            //资源路径
                            outputPath: resolve('../dist/images')
                        },
                    }
                ],
                exclude: /node_modules/
            },
        ]
    }

搭建webpack开发环境

在上面我们都是通过命令行打包生成 dist 文件,然后直接打开html或者通过static-server来查看页面的;但是开发中我们写完代码每次都来打包会严重影响开发的效率,我们期望的是写完代码后立即就能够看到页面的效果;webpack-dev-server就很好的提供了一个简单的web服务器,能够实时重新加载。

webpack-dev-server的用法和wepack一样,只不过他会额外启动一个express的服务器。我们在项目中webpack.config.dev.js配置文件对开发环境进行一个配置:

module.exports = {
    mode: 'development',
    plugins: [
        new Webpack.HotModuleReplacementPlugin()
    ],
    devtool: 'cheap-module-eval-source-map',
    devServer: {
        // 端口
        port: 3300,
         // 启用模块热替换
        hot: true,
        // 自动打开浏览器
        open: true,
        // 设置代理
         proxy:{
             "/api/**":{
                 "target":"http://127.0.0.1:8075/",
                 "changeOrigin": true
            }
        }
    }
}

通过命令行webpack-dev-server来启动服务器,启动后我们发现根目录并没有生成任何文件,因为webpack打包到了内存中,不生成文件的原因在于访问内存中的代码比访问文件中的代码更快。

我们在public/index.html的页面上有时候会引用一些本地的静态文件,直接打开页面的会发现这些静态文件的引用失效了,我们可以修改server的工作目录,同时指定多个静态资源的目录:

contentBase: [
  path.join(__dirname, "public"),
  path.join(__dirname, "assets")
]

热更新(Hot Module Replacemen简称HMR)是在对代码进行修改并保存之后,webpack对代码重新打包,并且将新的模块发送到浏览器端,浏览器通过新的模块替换老的模块,这样就能在不刷新浏览器的前提下实现页面的更新。

webpack plugins

上面介绍了DefinePlugin、HtmlWebpackPlugin等很多插件,我们发现这些插件都能够不同程度的影响着webpack的构建过程,下面还有一些常用的插件:

clean-webpack-plugin
clean-webpack-plugin用于在打包前清理上一次项目生成的bundle文件,它会根据output.path自动清理文件夹;这个插件在生产环境用的频率非常高,因为生产环境经常会通过hash生成很多bundle文件,如果不进行清理的话每次都会生成新的,导致文件夹非常庞大。

const { CleanWebpackPlugin } = require('clean-webpack-plugin')

module.exports = {
    plugins: [
        new CleanWebpackPlugin(),
    ],
}

mini-css-extract-plugin

我们在使用webpack构建工具的时候,通过style-loader,可以把解析出来的css通过js插入内部样式表的方式到页面中,mini-css-extract-plugin插件也是用来提取css到单独的文件的,该插件有个前提条件,只能用于webpack 4及以上的版本,所以如果使用的webpack版本低于4,,那还是用回extract-text-webpack-plugin插件。

const MiniCssExtractPlugin = require("mini-css-extract-plugin")
module.exports = {
    // 省略其他代码
    module: {
        rules: [
            {
                test: /\.less$/,
                use: [
                    {
                        loader: dev ? 'style-loader': MiniCssExtractPlugin.loader
                    },
                    {
                        loader: 'css-loader'
                    },
                    {
                        loader: 'less-loader'
                    }
                ]
            }
        ]
    },
    plugins: [
        new MiniCssExtractPlugin({
            filename: "[name].[hash:8].css",
        })
    ]
}

copy-webpack-plugin
我们在public/index.html中引入了静态资源,但是打包的时候webpack并不会帮我们拷贝到dist目录,因此copy-webpack-plugin就可以很好地帮我做拷贝的工作了。

const CopyWebpackPlugin = require('copy-webpack-plugin')

module.exports = {
    plugins: [
        new CleanWebpackPlugin(),
        new CopyWebpackPlugin([{
            from: path.resolve(__dirname, '../static'),
            to: path.resolve(__dirname, '../dist/static')
        }])
    ],
}

ProvidePlugin

ProvidePlugin可以很快的帮我们加载想要引入的模块,而不用require。一般我们加载jQuery需要先把它import

import $ from 'jquery'

$('#layout').html('test')

但是我们在config中配置ProvidePlugin插件后能够不用import,直接使用$

module.exports = {
    plugins: [
        new webpack.ProvidePlugin({
            $: 'jquery',
            jQuery: 'jquery'
        }),
    ]
}

在项目中引入了太多模块并且没有require会让人摸不着头脑,因此建议加载一些常见的比如jQuery、vue、lodash等。

loader和plugin的区别(面试中常遇见)

  • 对于loader,它是一个转换器,将A文件进行编译形成B文件,这里操作的是文件,比如将A.scss转换为A.css,单纯的文件转换过程
  • plugin是一个扩展器,它丰富了webpack本身,针对是loader结束后,webpack打包的整个过程,它并不直接操作文件,而是基于事件机制工作,会监听webpack打包过程中的某些节点,执行广泛的任务
查看原文

赞 0 收藏 0 评论 0

提莫找蘑菇 回答了问题 · 2020-11-26

文本,可以根据字数的长度,控制...显示吗

你可以根据判断长度截取;或者可以使用多行文本省略的样式

display:-webkit-box;
overflow:hidden;
text-overflow:ellipsis;
-webkit-line-clamp:2; // 控制显示行数
-webkit-box-orient:vertical;

适用范围:
因使用了WebKit的CSS扩展属性,该方法适用于WebKit浏览器及移动端;

关注 6 回答 5

认证与成就

  • 获得 92 次点赞
  • 获得 16 枚徽章 获得 1 枚金徽章, 获得 3 枚银徽章, 获得 12 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2017-04-05
个人主页被 2.3k 人浏览