夏天的风🎃
JerryYang954
签名是一种态度,我想我可以更酷...
关注数: 7
粉丝数: 27
发帖数: 313
关注贴吧数: 31
毕业了想回去学校,需要登记什么资料吗?
毕业了想回去学校,需要登记什么资料吗?
毕业了想回去学校,需要登记什么资料吗?
毕业了想回去学校,需要登记什么资料吗?
毕业了想回去学校,需要登记什么资料吗?
毕业了想回去学校,需要登记什么资料吗?
请问大四的今年已经出来实习了吗?
【打听消息】有计协会的小伙伴吗?谈个合作
# 兼职赚零钱,梦想早实现 招聘:电话销售兼职(周末双休,周结)绝对真实靠谱!!! 岗位职责: 1、公司会提供资源,负责电话或微信联系学生,邀约上门; 2、根据上门情况来调整合适的邀约话术,督促学生上门成交; 3、每日以表格形式总结、简历数量和跟进情况并存档,反馈给咨询主管以及seo专员同事。 兼职薪资待遇: 120元/天,无责任底薪+高提成,上不封顶,休闲压力小。 工作时间:早十晚七,中午休息两个小时,兼职时间可灵活调动,可按天/按周/按月进行打卡,公司全天免费提供咖啡和茶叶,甲级写字楼办公。 其他福利:公司不定时的抽奖、下午茶、户外游玩、聚餐等活动。 工作地址:广州CBD珠江新城华夏路26号雅居乐中心 微信添加: BlueJ2021 进行了解咨询
广州蓝景—遇到网页慢的时候,我们该怎么办? 前言 移动互联网时代,用户对于网页的打开速度要求越来越高。首屏作为直面用户的第一屏,其重要性不言而喻。优化用户体验更是我们前端开发非常需要 focus 的东西之一。 从用户的角度而言,当打开一个网页,往往关心的是从输入完网页地址后到最后展现完整页面这个过程需要的时间,这个时间越短,用户体验越好。所以作为网页的开发者,就从输入url到页面渲染呈现这个过程中去提升网页的性能。 所以输入URL后发生了什么呢?在浏览器中输入url会经历域名解析、建立TCP连接、发送http请求、资源解析等步骤。 http缓存优化是网页性能优化的重要一环。本文主要从网页渲染过程、性能指标的解读以及CDN应用优化三个角度,并对性能定位和性能优化做一个小结。 关键词: 1. 通过工具(如何使用工具,怎么去看)定位问题 2. 发现问题,对应的措施(提升页面) 3. 监控(优化的东西 是持续性,不像bug一次性解决) 本质:两大范畴:加载 和 渲染 网页渲染过程 首先谈谈拿到服务端资源后浏览器渲染的流程: 1. 解析 HTML 文件,构建 DOM 树,同时浏览器主进程负责下载 CSS 文件 2. CSS 文件下载完成,解析 CSS 文件成树形的数据结构,然后结合 DOM 树合并成 RenderObject 树 3. 布局 RenderObject 树 (Layout/reflow),负责 RenderObject 树中的元素的尺寸,位置等计算 4. 绘制 RenderObject 树 (paint),绘制页面的像素信息 5. 浏览器主进程将默认的图层和复合图层交给 GPU 进程,GPU 进程再将各个图层合成(composite),最后显示出页面经典面试题:输入url后,浏览器做了什么? Url 首先进行DNS解析,找到目的服务器IP,建立TCP链接,发送http请求,服务器响应网页,浏览器接收对应的文件并解析,html 和css 通过对应的解析器生成并融合的成渲染树,渲染页面。 总结出问题出现的地方: 1.加载时间过长,加载很久 2.解析的过程太久,导致渲染很慢 前端性能指标的建立和解读 在我们的Google浏览器中已经内置了一个perfomance的功能, Web Vitals 是 Google 的一项举措,旨在为web质量提供统一的指导,这些指标对于在网络上提供出色的用户体验至关重要。Web Vitals为了简化场景,帮助网站专注于最重要的指标,提出了Core Web Vitals。Core Web Vitals 是 Web Vitals 的子集,包含 LCP(Largest Contentful Paint),FID(First Input Delay) 和 CLS(Cumulative Layout Shift)。 ● LCP(Largest Contentful Paint):最大内容绘制,测量加载性能。为了提供良好的用户体验,LCP 应在页面首次开始加载后的2.5秒内发生。 ● FID(First Input Delay):首次输入延迟,测量交互性。为了提供良好的用户体验,页面的 FID 应为100毫秒或更短。 ● CLS(Cumulative Layout Shift):累积布局偏移,测量视觉稳定性。为了提供良好的用户体验,页面的 CLS 应保持在0.1或更少。 从这三个指标的含义中我们可以发现这三个指标分别从页面的加载速度,页面的交互性和页面的视觉稳定性这三个角度来衡量页面的性能。如果我们想自己采集页面的各项原始指标数据,该怎么做呢?浏览器为我们提供了原生的 Timing APICDN介绍及工作原理1、为什么要用CDN加速服务 如果一个网站(作为盈利渠道或是品牌窗口)需要吸引大流量, 以下几点因素是制胜的关键: ● 内容有吸引力 ● 访问速度快 ● 支持频繁的用户互动 ● 可以在各处浏览无障碍 满足这些条件的前提就是:网站访问快 那么CDN就是一个加速网站访问的优秀解决方案 除此之外,CDN还有一些作用: 1. 为了实现跨运营商、跨地域的全网覆盖 互联不互通、区域ISP地域局限、出口带宽受限制等种种因素都造成了网站的区域性无法访问。CDN加速可以覆盖全球的线路,通过和运营商合作,部署IDC资源,在全国骨干节点商,合理部署CDN边缘分发存储节点,充分利用带宽资源,平衡源站流量。阿里云在国内有500+节点,海外300+节点,覆盖主流国家和地区不是问题,可以确保CDN服务的稳定和快速。 2. 为了保障你的网站安全 CDN的负载均衡和分布式存储技术,可以加强网站的可靠性,相当无无形中给你的网站添加了一把保护伞,应对绝大部分的互联网攻击事件。防攻击系统也能避免网站遭到恶意攻击。 3. 为了异地备援 当某个服务器发生意外故障时,系统将会调用其他临近的健康服务器节点进行服务,进而提供接近100%的可靠性,这就让你的网站可以做到永不宕机。 4. 为了节约成本投入 使用CDN加速可以实现网站的全国铺设,你根据不用考虑购买服务器与后续的托管运维,服务器之间镜像同步,也不用为了管理维护技术人员而烦恼,节省了人力、精力和财力。 5. 为了让你更专注业务本身 CDN加速厂商一般都会提供一站式服务,业务不仅限于CDN,还有配套的云存储、大数据服务、视频云服务等,而且一般会提供7x24运维监控支持,保证网络随时畅通,你可以放心使用。并且将更多的精力投入到发展自身的核心业务之上。 优点: ● JS体积变小,使用CDN的第三方资源的JS代码,将不再打包到本地服务的JS包中。减小本地JS包体积,提高加载速度。 ● 给网页加载提速 缺点: ● 请求变多 ● 万一CDN资源路径有变动需要更改,建议自行搭建CDN库。 ● 花钱 推荐BootCDN(有很多经过CDN加速的工具链接) http://tieba.baidu.com/mo/q/checkurl?url=http%3A%2F%2Fwww.bootcdn.cn%2F&urlrefer=d5566c61abb3fd7d6b4dbc01f53b2964 2、什么是CDN(深度) 更多的是存放分发静态内容3、工作原理借用阿里云官网的例子,来简单介绍CDN的工作原理。 假设通过CDN加速的域名为http://tieba.baidu.com/mo/q/checkurl?url=http%3A%2F%2Fwww.a.com&urlrefer=7f04dde9f16bf1f81ed7855f0e29a0d4,接入CDN网络,开始使用加速服务后,当终端用户(北京)发起HTTP请求时,处理流程如下: 当终端用户(北京)向http://tieba.baidu.com/mo/q/checkurl?url=http%3A%2F%2Fwww.a.com&urlrefer=7f04dde9f16bf1f81ed7855f0e29a0d4下的指定资源发起请求时,首先向LDNS(本地DNS)发起域名解析请求。 LDNS检查缓存中是否有http://tieba.baidu.com/mo/q/checkurl?url=http%3A%2F%2Fwww.a.com&urlrefer=7f04dde9f16bf1f81ed7855f0e29a0d4的IP地址记录。如果有,则直接返回给终端用户;如果没有,则向授权DNS查询。 当授权DNS解析http://tieba.baidu.com/mo/q/checkurl?url=http%3A%2F%2Fwww.a.com&urlrefer=7f04dde9f16bf1f81ed7855f0e29a0d4时,返回域名CNAMEhttp://tieba.baidu.com/mo/q/checkurl?url=http%3A%2F%2Fwww.a.tbcdn.com&urlrefer=186d2f443a59135601f4bee4c0795f53对应IP地址。 域名解析请求发送至阿里云DNS调度系统,并为请求分配最佳节点IP地址。 LDNS获取DNS返回的解析IP地址。 用户获取解析IP地址。 用户向获取的IP地址发起对该资源的访问请求。 如果该IP地址对应的节点已缓存该资源,则会将数据直接返回给用户,例如,图中步骤7和8,请求结束。 如果该IP地址对应的节点未缓存该资源,则节点向源站发起对该资源的请求。获取资源后,结合用户自定义配置的缓存策略,将资源缓存至节点,例如,图中的北京节点,并返回给用户,请求结束。 从这个例子可以了解到: (1)CDN的加速资源是跟域名绑定的。 (2)通过域名访问资源,首先是通过DNS分查找离用户最近的CDN节点(边缘服务器)的IP (3)通过IP访问实际资源时,如果CDN上并没有缓存资源,则会到源站请求资源,并缓存到CDN节点上,这样,用户下一次访问时,该CDN节点就会有对应资源的缓存了。 想要了解更多关于IT前端技术问题,可以关注广州蓝景。或者留言给我们。
广州蓝景—6 个ES13 中非常实用的新 JavaScript 特性 首先作为前端最重要的编程语言JavaScript,每年都在不断发展,让该语言都会通过新功能变得更强大。今天由小蓝跟大家分享6 个ES13 中非常实用的新 JavaScript 特性。 接下来让我们开始: 1.at 当我们想要获取数组的第 N 个元素时,我们通常使用 [] 来获取。 const array = [ 'fatfish', 'medium', 'blog', 'fat', 'fish' ] console.log(array[ 1 ], array[ 0 ]) // medium fatfish 哦,这似乎不是什么稀罕事。但是请朋友们帮我回忆一下,如果我们想得到数组的最后第N个元素,我们会怎么做呢? const array = [ 'fatfish', 'medium', 'blog', 'fat', 'fish' ] const len = array.length console.log(array[ len - 1 ]) // fish console.log(array[ len - 2 ]) // fat console.log(array[ len - 3 ]) // blog 这看起来很难看,我们应该寻求一种更优雅的方式来做这件事。是的,以后请使用数组的at方法! 它使您看起来像高级开发人员。 const array = [ 'fatfish', 'medium', 'blog', 'fat', 'fish' ] console.log(array.at(-1)) // fish console.log(array.at(-2)) // fat console.log(array.at(-3)) // blog 2.Object.hasOwn 是的,通常有两种方式,它们有什么区别呢? 对象中的“名称” obj.hasOwnProperty('名称') “in”运算符 如果指定属性在指定对象或其原型链中,则 in 运算符返回 true。 const Person = function (age) { this.age = age } Person.prototype.name = 'fatfish' const p1 = new Person(24) console.log('age' in p1) // true console.log('name' in p1) // true pay attention here obj.hasOwnProperty hasOwnProperty 方法返回一个布尔值,指示对象是否具有指定的属性作为其自身的属性(而不是继承它)。 使用上面相同的例子 const Person = function (age) { this.age = age } Person.prototype.name = 'fatfish' const p1 = new Person(24) console.log(p1.hasOwnProperty('age')) // true console.log(p1.hasOwnProperty('name')) // fasle pay attention here 也许“obj.hasOwnProperty”已经可以过滤掉原型链上的属性,但在某些情况下并不安全,会导致程序失败。 Object.create(null).hasOwnProperty('name') // Uncaught TypeError: Object.create(...).hasOwnProperty is not a function Object.hasOwn 不用担心,我们可以使用“Object.hasOwn”来规避这两个问题,比“obj.hasOwnProperty”方法更方便也更安全。 let object = { age: 24 } Object.hasOwn(object, 'age') // true let object2 = Object.create({ age: 24 }) Object.hasOwn(object2, 'age') // false The 'age' attribute exists on the prototype let object3 = Object.create(null) Object.hasOwn(object3, 'age') // false an object that does not inherit from "Object.prototype" 3.在模块的顶层使用“await” 来自 mdn 的 await 操作符用于等待一个 Promise 并获取它的 fulfillment 值。 const getUserInfo = () => { return new Promise((rs) => { setTimeout(() => { rs({ name: 'fatfish' }) }, 2000) }) } // If you want to use await, you must use the async function. const fetch = async () => { const userInfo = await getUserInfo() console.log('userInfo', userInfo) } fetch() // SyntaxError: await is only valid in async functions const userInfo = await getUserInfo() console.log('userInfo', userInfo)事实上,在 ES13 之后,我们可以在模块的顶层使用 await,这对于开发者来说是一个非常令人高兴的新特性。那太棒了。 const getUserInfo = () => { return new Promise((rs) => { setTimeout(() => { rs({ name: 'fatfish' }) }, 2000) }) } const userInfo = await getUserInfo() console.log('userInfo', userInfo)4.使用“#”声明私有属性 以前我们用“_”来表示私有属性,但是不安全,仍然有可能被外部修改。 class Person { constructor (name) { this._money = 1 this.name = name } get money () { return this._money } set money (money) { this._money = money } showMoney () { console.log(this._money) } } const p1 = new Person('fatfish') console.log(p1.money) // 1 console.log(p1._money) // 1 p1._money = 2 // Modify private property _money from outside console.log(p1.money) // 2 console.log(p1._money) // 2 我们可以使用“#”来实现真正安全的私有属性 class Person { #money=1 constructor (name) { this.name = name } get money () { return this.#money } set money (money) { this.#money = money } showMoney () { console.log(this.#money) } } const p1 = new Person('fatfish') console.log(p1.money) // 1 // p1.#money = 2 // We cannot modify #money in this way p1.money = 2 console.log(p1.money) // 2 console.log(p1.#money) // Uncaught SyntaxError: Private field '#money' must be declared in an enclosing class 5. 更容易为类设置成员变量 除了通过“#”为类设置私有属性外,我们还可以通过一种新的方式设置类的成员变量。 class Person { constructor () { this.age = 1000 this.name = 'fatfish' } showInfo (key) { console.log(this[ key ]) } } const p1 = new Person() p1.showInfo('name') // fatfish p1.showInfo('age') // 1000 现在你可以使用下面的方式,使用起来确实更加方便。 class Person { age = 1000 name = 'fatfish' showInfo (key) { console.log(this[ key ]) } } const p1 = new Person() p1.showInfo('name') // fatfish p1.showInfo('age') // 1000 6.从数组末尾查找元素 当我们想从数组中找到满足一定条件的元素时,find 和 findIndex 都是不错的选择。 const array = Array(10000000).fill(1) array.push(2) const d1 = Date.now() const el = array.find((el) => el >= 2) const d2 = Date.now() console.log({ el, time: d2 - d1 })得到2,查找时间用了84毫秒,这是一个很恐怖的数字,而且耗时太长了。 幸运的是,从 ES13 开始,如果你之前指定目标元素更靠近尾部,使用 findLast 将大大减少其查找时间。 const array = Array(10000000).fill(1) array.push(2) const d1 = Date.now() const el = array.findLast((el) => el >= 2) const d2 = Date.now() console.log({ el, time: d2 - d1 })总结 通过以上文章对JavaScript新特性的介绍,如果想要了解更多前端开发技术,可以关注我们,也可以一起讨论。
分享给前端初学者编写更好代码的3个原则 作为初学者,您可能不会过多考虑代码风格。采用以下三个原则,可以立即提高您的代码质量。 1、使用“提前返回”代替嵌套条件语句 在Web开发中,您会遇到很多需要检查特定条件是否满足的情况。 举个例子,假设您有一个API路由,用于验证请求并返回一个用户对象: export const handler = async (req, res) => { if (req.method === "POST" || req.method === "OPTIONS") { const email = validateEmail(req.body.email); if (email) { const user = getUserByEmail(email); if (user) { return res.status(200).json({ user }); } else { return res.status(404).json({ message: 'No user found' }); } } else { return res.status(422).json({ message: 'Missing email' }); } } else { return res.status(405).json({ message: 'Unsupported message' }); } } 虽然这个函数中没有太多的逻辑封装,但它看起来已经有些杂乱无章了。具体来说,这段代码存在以下两个问题: 很难跟踪代码流程。我们需要从左到右而不是从上到下阅读代码(箭头反模式)。 很难找到每个 if 对应的 else 语句。它们被 if 语句的大体量隔开了。 改进这段代码的一个简单技巧是使用“提前返回”模式(Return-Early-Pattern)。“提前返回”模式会在不满足条件时终止函数的执行,以便函数的期望结果始终出现在最后。如果我们重新编写上面的 API 路由,它将如下所示: export const handler = async (req, res) => { if (req.method !== "POST" && !req.method !== "OPTIONS") { return res.status(405).json({ message: 'Unsupported message' }); } const email = validateEmail(req.body.email); if (!email) { return res.status(422).json({ message: 'Missing email' }); } const user = getUserByEmail(email); if (!user) { return res.status(404).json({ message: 'No user found' }); } return res.status(200).json({ user }); } 使用“提前返回”模式后,我们可以轻松地从上到下跟踪代码执行。由于我们假设一切顺利,只检查缺失的值,因此避免了嵌套太多条件。 最后,我们可以一眼看到函数的期望结果,它位于最底部。 2、为人类编写代码 归纳前一个提示的内容,我们得到了第二个原则:编写易于他人阅读而非机器的代码。 这听起来很平凡,但起初却让我彻底改变了思维方式。当我开始编程时,我总是把它看作是与计算机交流的一种方式。我们告诉计算机要做什么。但我们编写的代码是由同事阅读和理解的,而不是机器。 我们的同事是需要阅读和理解代码的人。最终,计算机将一切都转换为 0 和 1,并不关心可读性。让我们以 groupBy 函数为例: const groupBy = (arr, groupFn) => arr.reduce( (grouped, obj) => ({ ...grouped, [groupFn(obj)]: [...(grouped[groupFn(obj)] || []), obj], }), {} ); 我们清楚地展示了如何编写复杂的单行函数来执行简单的操作:对数组进行分组。 尽管这可能让您感觉更加专业,但对于任何需要审查代码的人来说,这确实会使事情更加难以理解。相比之下,考虑以下实现方式: const groupBy = (arr, groupFn) => { const grouped = {}; for (const obj of arr) { const groupName = groupFn(obj); if (!grouped[groupName]) { grouped[groupName] = []; } grouped[groupName].push(obj); } return grouped; }; 我们可以从上到下阅读这段代码,并立即了解每行代码的作用。 尽管这可能看起来没有之前的实现方式那么酷炫,但是以后所有需要重新审查这段代码的人都会因为这种易于阅读的实现方式而感谢您。 3、将信息隐藏在函数背后 作为初级开发人员,改进代码风格的最后一个想法是将不相关的信息隐藏在函数背后。这也有助于提高代码的可读性。 如果您熟悉 React,Hooks 是这一原则的一个很好的例子: import React, { useState, useEffect } from 'react'; function FriendListItem(props) { const [isOnline, setIsOnline] = useState(null); useEffect(() => { function handleStatusChange(status) { setIsOnline(status.isOnline); } ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange); return () => { ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange); }; }); return ( <li style={{ color: isOnline ? 'green' : 'black' }}> {props.friend.name} </li> ); } 在这里,我们有一个组件,它输出一个带有动态状态颜色的列表项。虽然这段代码可以正常运行,但它封装了与 FriendListItem 组件的目的不直接相关的逻辑。 如果我们提取该逻辑并创建一个名为 useFriendStatus 的自定义 Hook,我们可以简化该组件,如下所示: import React, { useState, useEffect } from 'react'; function FriendListItem(props) { const isOnline = useFriendStatus(props.friend.id); return ( <li style={{ color: isOnline ? 'green' : 'black' }}> {props.friend.name} </li> ); } 这样做有两个好处: 我们可以重用 useFriendStatus 的逻辑。 我们将组件简化为其功能的实质内容。 更一般地说,隐藏信息的原则是将不相关的信息封装在抽象函数背后。 因此,我们不需要关心抽象函数内部发生了什么(实现细节)——我们可以更专注于它的目的,即函数的名称(问题域的级别)。
作为前端开发人员应该知道的实用CLI命令 作为前端开发工程师,我们需要知道哪些命令?如果您熟悉这些命令,它们将大大提高您的工作效率。今天小蓝跟大家分享CLI命令的知识点。1.tree 朋友们,你们知道如何像下面这样列出一个目录的文件结构吗 它很好地显示了文件之间的目录关系,这真的很酷。 commands ├── a.js ├── b.js ├── c.js ├── copy-apps │ └── fe-apps │ └── a.js ├── fe-app │ └── a.js ├── test.log └── xxx └── yyy 在此之前,您需要安装命令tree。 brew install tre 然后只需在文件目录中执行tree命令。 2.wc wc是word count的缩写,常用于文件统计。它可以统计字数、行数、字符数、字节数等。 我经常用它来统计文件中的代码行数。 3.du 打印出一个目录的文件大小信息。我们用的比较少,但是是一个非常值得学习的命令。 du -h:打印出适合人类阅读的信息。 du -a:列出目录中文件大小的信息; du -s:只显示总大小,不显示具体信息。4. alias alias 命令用于设置命令的别名。如果您只键入 alias,将列出所有当前的别名设置。 让我们尝试为 git status 设置一个别名 alias gs="git status" 值得注意的是:如果你希望 gs 命令是永久的,你应该在 .profile 或 .zshrc 中设置它。 5. grep 我们经常需要查找服务器上日志文件的内容,grep 将是我们得心应手的帮手。 有一个日志文件test.log。它包含以下内容: const a = 1 const b = 2 const c = 3 console.log(a + b + c) 如何突出显示包含 a 字符的位置?这很容易,不是吗? grep a test.log 6.cat cat 的主要目的是查看文件的内容并将其打印在屏幕上。 但它至少还有一些其他用途。 1.清空a.js的内容 ➜ commands git:(master) ✗ cat a.js // There are two lines of code in a.js const a = 'fatfish' console.log(a)% ➜ commands git:(master) ✗ cat /dev/null > a.js // clear the contents of a.js ➜ commands git:(master) ✗ cat a.js // The content in a.js is cleared. ➜ commands git:(master) ✗ 2.将a.js的内容复制到b.js ➜ commands git:(master) ✗ cat a.js const name = 'fatfish' console.log(name) ➜ commands git:(master) ✗ cat b.js // No content in b.js ➜ commands git:(master) ✗ cat a.js > b.js // Copy the contents of a.js to b.js ➜ commands git:(master) ✗ cat b.js // The content in b.js is the same as in a.js const name = 'fatfish' console.log(name) ➜ commands git:(master) ✗ cat a.js const name = 'fatfish' console.log(name) 3.将a.js的内容添加到c.js的最后一个字符。 ➜ commands git:(master) ✗ cat a.js const name = 'fatfish' console.log(name)% ➜ commands git:(master) ✗ cat c.js const age = 100 console.log(age) ➜ commands git:(master) ✗ cat a.js >> c.js ➜ commands git:(master) ✗ cat c.js const age = 100 console.log(age)const name = 'fatfish' console.log(name) 7. clear 有时,我们需要在终端中进行一些操作,以至于屏幕上的内容足以让我们感到厌烦。 如何清除它们?我们需要逐行删除它们吗?8.cp cp 命令用于复制文件或目录。 cp -f:当要复制的文件覆盖已有的目标文件时,不会有提示信息。 cp -r:如果复制的文件是目录文件,则复制该目录下的所有子目录和文件。9. cd 这篇文章一定是没有技术含量的,因为cd真的没什么好写的,作为开发者,谁不熟悉呢? 也许你是对的,但我只是想说 cd - 可以回到你上次访问的目录。我认为这是一个好技巧。 10. ls 这是一个使用频率很高的命令,用来显示文件目录的内容列表。 它可以至少以 3 种方式使用。 ls -a:显示所有文件和目录(包括以.开头的目录) ls -A:显示所有文件和目录(不包括以.目录开头的目录) ls -R:显示所有文件和目录,如果目录中有文件,则按顺序列出 11. rm 它用于删除文件或目录。 rm -i: 将目录下的文件一个一个删除,删除前会询问是否删除文件。 rm -r:将指定目录及其子目录下的所有文件一起处理(注意:不删除文件。) rm -f:用于强制删除文件或目录。 12.tail 我想你一定也有在服务器上查看日志内容的经历,tail绝对是个好帮手。 tail -f filename 会在屏幕上显示filename尾部的内容,当它的内容发生变化时,你会在屏幕上看到最新的内容。13.MV 有时我们想更改文件或目录的名称,或者将其移动到另一个地方,这时我们可以使用 mv 命令。 1.修改文件名 ➜ commands git:(master) ✗ ls a.js ➜ commands git:(master) ✗ mv a.js xxx.js ➜ commands git:(master) ✗ ls xxx.js ➜ commands git:(master) ✗ 2.将文件移动到其他目录 ➜ commands git:(master) ✗ ls -R a.js fe-apps ./fe-apps: xxx.js ➜ commands git:(master) ✗ mv a.js fe-apps ➜ commands git:(master) ✗ ls -R fe-apps ./fe-apps: a.js xxx.js 14.touch 我经常使用 touch 命令创建一个新文件,尽管它用于修改文件或目录的时间属性。 15.which 如果要查看命令的具体路径,可以使用 which。 ➜ commands git:(master) ✗ which node /Users/dz0400229/.nvm/versions/node/v16.0.0/bin/node ➜ commands git:(master) ✗ which npm /Users/dz0400229/.nvm/versions/node/v16.0.0/bin/npm ➜ commands git:(master) ✗ which npx /Users/dz0400229/.nvm/versions/node/v16.0.0/bin/npx ➜ commands git:(master) ✗ 16. mkdir 是的,你以前肯定用过这个命令,没什么好说的! 但是mkdir -p dirname 真的是我们很少用到的东西,它有什么用呢?17.whoami 显示用户名。 ➜ commands git:(master) ✗ whoami dz0400229 总结 到这里,小蓝今天与大家分享的关于CLI的实用命令就结束了,如果您觉得我这篇文章对您有帮助的话,可以转发或者一起讨论。
有学校计算机专业的群吗?拉我一下谢谢
有学校计算机专业的群吗?拉我一下谢谢
一文盘点主流JavaScript框架的优缺点 当今互联网时代,前端开发框架的重要性越来越受到关注。随着JavaScript的不断发展,前端框架也不断涌现。但是,在这么多的框架中,该如何选择适合自己项目的框架呢?本文将会介绍主流的JavaScript前端框架:Vue.js、React、Angular等,并对它们的优劣势进行评估,帮助您做出最佳的选择。 1、ReactReact是一个用于构建用户界面的JavaScript库。它由Facebook和一群个人开发者以及公司共同维护。React可用作单页或移动应用程序开发的基础。然而,React仅关注将数据呈现到DOM中,因此创建React应用程序通常需要使用附加库来进行状态管理、路由和与API的交互。React还用于构建可重用的UI组件。在这方面,它的工作方式类似于Angular或Vue等JavaScript框架。然而,React组件通常采用声明式语法而不是使用命令式代码编写,使其更易于阅读和调试。因此,许多开发人员即使不将其作为整个前端框架使用,也更喜欢使用React构建UI组件。 优点: React使用虚拟DOM而不是操作真实DOM,因此速度快且高效。React采用声明式语法和清晰的文档,易于学习。React组件可重用,使代码维护更容易。 缺点: React是一个复杂的JavaScript库,具有较大的学习曲线。React并非完整的框架,因此需要使用附加库来完成许多任务。 2、Next.jsNext.js是一个JavaScript库,它使React应用程序实现了服务器端渲染。这意味着Next.js可以在将React应用程序发送到客户端之前,在服务器上呈现它们。这有几个好处。首先,它允许您预渲染组件,以便在用户请求它们时它们已经在客户端上可用。其次,它可以通过使爬虫更容易地索引您的内容,提高React应用程序的SEO效果。最后,它可以通过减少客户端呈现页面所需的工作量来提高性能。 以下是开发人员喜欢Next.js的原因: Next.js使得在不进行任何配置的情况下很容易开始使用服务器端渲染。 Next.js自动对应用程序进行代码拆分,因此每个页面仅在请求时加载,这可以提高性能。 缺点: 如果不小心处理,Next.js可能会使您的应用程序代码库变得更加复杂,难以维护。一些开发人员认为Next.js的内置功能很武断,缺乏灵活性。 3、Vue.jsVue.js是一个用于构建用户界面和单页应用程序的开源JavaScript框架。与React和Angular等其他框架不同,Vue.js的设计目的是轻量级和易于使用。Vue.js库可以与其他库和框架一起使用,也可以作为一个独立的工具用于创建前端Web应用程序。Vue.js的一个关键特性是其双向数据绑定,当模型改变时,视图会自动更新,反之亦然。这使它成为构建动态用户界面的理想选择。此外,Vue.js还带有一些内置功能,如模板系统、反应性系统和事件总线。这些功能使得可以创建复杂的应用程序,而无需依赖第三方库。因此,Vue.js近年来已成为最受欢迎的JavaScript框架之一。 优点: Vue.js由于其小巧的体积和清晰的文档而易于学习。Vue.js组件是可重用的,这使得代码维护更容易。Vue.js应用程序由于虚拟DOM和异步组件加载而非常快速。 缺点: 虽然Vue.js易于学习,但如果要掌握其所有功能,则具有较大的学习曲线。Vue.js没有像其他一些框架那样提供许多库和工具。 4、AngularAngular是一个用于构建JavaScript、HTML和TypeScript的Web应用程序和应用的JavaScript框架,由Google创建和维护。Angular提供双向数据绑定,使得对模型的更改自动传播到视图。它还提供了声明性语法,使得构建动态UI变得容易。最后,Angular提供了许多有用的内置服务,例如HTTP请求处理,支持路由和模板等。 优点: Angular有一个庞大的社区和许多可用的库和工具。由于其良好组织的文档和清晰的语法,Angular易于学习。 缺点: 虽然Angular易于学习,但如果要掌握其所有功能,则需要较长的学习曲线。与其他一些框架相比,Angular并不是很轻量级。 5、Svelte简而言之,Svelte 是类似于 React、Vue 或 Angular 的 JavaScript 框架。然而,那些框架使用虚拟 DOM (文档对象模型)差异计算来确定视图中的更改,而 Svelte 使用一种称为 DOM 差异计算的技术。这意味着它只更新已更改的 DOM 部分,从而实现更高效的渲染过程。此外,Svelte 还包含了其他框架不具备的一些内置优化,如自动批处理 DOM 更新和代码拆分。这些功能使得 Svelte 成为高性能应用程序的不错选择。 Svelte的主要卖点是编译器,它将您的应用程序代码转换为高度优化的原生JavaScript。这种方法消除了虚拟DOM的需求,从而导致更快速和更高效的渲染性能。Svelte还支持响应式编程,这意味着应用程序状态的更改会实时触发UI的更新。 延伸阅读:什么是DOM 差异计算技术 DOM 差异计算技术则是一种直接在实际 DOM 树上进行比较和操作的技术,它的思想是尽量避免不必要的 DOM 操作。这种技术需要进行大量的计算,因为它需要遍历整个 DOM 树来查找更改的元素。与虚拟 DOM 不同,它没有中间层,因此它的执行速度更快,但开发人员需要手动编写实际 DOM 操作的代码。 优点: Svelte 具有其他框架不具备的内置优化,如代码拆分。Svelte 由于其清晰的语法和组织良好的文档而易于学习。 缺点: 尽管 Svelte 易于学习,但要掌握其所有功能需要很大的学习曲线。Svelte 没有像其他框架那样多的库和工具可用。 6、GatsbyGatsby是一个基于React的免费、开源框架,可帮助开发人员构建快速的网站和应用程序。它使用尖端技术使构建网站和应用程序的过程更加高效。其关键功能之一是能够预取资源,以便在需要时立即可用。这使得Gatsby网站非常快速和响应。使用Gatsby的另一个好处是,它允许开发人员使用GraphQL从任何来源查询数据,使构建复杂的数据驱动型应用程序变得容易。此外,Gatsby附带了许多插件,包括用于SEO、分析和图像优化的插件,使其更易于使用。所有这些因素使得Gatsby成为构建现代网站和应用程序的极受欢迎的选择。 优点: 由于使用了预取功能,Gatsby网站非常快速和响应。由于支持GraphQL,Gatsby使构建复杂的数据驱动型应用程序变得容易。Gatsby附带了许多插件,使其更易于使用。 缺点: 虽然Gatsby易于使用,但如果您想掌握其所有功能,需要较长的学习曲线。Gatsby的库和工具可用性不如其他一些框架。 7、Nuxt.jsNuxt.js是一个用于构建JavaScript应用程序的渐进式框架。它基于Vue.js,并带有一组工具和库,使得创建可在服务器端和客户端上呈现的通用应用程序变得容易。Nuxt.js还提供了处理异步数据和路由的方式,这使得它非常适合构建高度交互式的应用程序。此外,Nuxt.js带有一个CLI工具,使得轻松搭建新项目并进行构建、运行和测试变得容易。使用Nuxt.js,你可以创建快速、可靠且可扩展的JavaScript应用程序。 优点: Nuxt.js易于使用和扩展。由于服务器端渲染,Nuxt.js应用程序快速且响应迅速。 缺点: 尽管Nuxt.js易于使用,但如果要掌握其所有功能,则需要较长的学习曲线。Nuxt.js的可用库和工具没有其他框架那么丰富。 8、Ember.jsEmber.js以约定优于配置的方法而闻名,这使得开发人员更容易开始使用该框架。它还提供了内置库,用于常见任务,如数据持久性和路由,这使得开发速度更快。虽然Ember.js有一个陡峭的学习曲线,但它为开发人员提供了很多灵活性和强大的功能,可以创建丰富的Web应用程序。如果您正在寻找用于构建单页应用程序(SPAs)的前端JavaScript框架,则Ember.js绝对值得考虑。 优点: Ember.js使用约定优于配置,这使得更容易开始使用该框架。Ember.js具有内置库,可用于常见任务,如数据持久性和路由。Ember.js为开发人员提供了很多灵活性和强大的功能,可以创建丰富的Web应用程序。 缺点: Ember.js具有陡峭的学习曲线。Ember.js可用的库和工具不如其他一些框架多。 9、Backbone.jsBackbone.js是一个轻量级的JavaScript库,可以让开发人员创建单页应用程序。它基于Model-View-Controller(MVC)架构,这意味着它将数据和逻辑与用户界面分离。这使得代码更易于维护和扩展,同时也更容易创建复杂的应用程序。Backbone.js还包括一些功能,使其成为开发移动应用程序的理想选择,例如其将数据绑定到HTML元素的能力以及对触摸事件的支持。因此,Backbone.js是开发人员想要创建快速响应的应用程序的流行选择。 优点: Backbone.js是轻量级的库,而非完整的框架。Backbone.js易于学习和使用。Backbone.js非常可扩展,有许多第三方库可用。 缺点: Backbone.js的内置功能不如其他一些框架多。Backbone.js的社区比其他一些框架小。 结束 在选择JavaScript框架时,没有一个完美的答案。每个框架都有其优点和缺点,取决于你的具体项目需求和团队技能。通过对每个框架的了解和比较,你可以更好地理解它们的差异,以便更好地选择适合你的项目的框架。在使用框架的过程中,不断学习和掌握新的技术和工具是非常重要的。通过本文的介绍,希望能帮到你,也可以一起讨论。
前端7个JavaScript 简写技巧,助你编写更好代码 在学习前端开发过程中会用到的JavaScript功能,今天小蓝整理了几个JavaScript速记优化技巧,希望这些技巧可以帮助大家编写更好的代码。 1. 多个字符串检查 通常,如果我们需要检查字符串是否等于多个值中的一个,往往很快会觉得疲惫不堪。幸运的是,JavaScript有一个内置的方法来帮助你解决这个问题。 // 普通写法 const isVowel = (letter) => { if ( letter === "a" || letter === "e" || letter === "i" || letter === "o" || letter === "u" ) { return true; } return false; }; // 简写方法 const isVowel = (letter) => ["a", "e", "i", "o", "u"].includes(letter); 2. For-of和For-in循环 For-of和For-in循环是迭代array或object的好方法,因为无需手动跟踪object键的索引。 For-of const arr = [1, 2, 3, 4, 5]; // 普通写法 for (let i = 0; i < arr.length; i++) { const element = arr[i]; // ... } // 简写方法 for (const element of arr) { // ... } For-in const obj = { a: 1, b: 2, c: 3, }; // 普通写法 const keys = Object.keys(obj); for (let i = 0; i < keys.length; i++) { const key = keys[i]; const value = obj[key]; // ... } // 简写方法 for (const key in obj) { const value = obj[key]; // ... } 3. Falsey(假值)检查 如果要检查变量是null、undefined、0、false、NaN还是空string,可以使用逻辑非 (!)运算符一次检查所有变量,而无需编写多个条件。这使得检查变量是否包含有效数据变得相对容易多了。 // 普通写法 const isFalsey = (value) => { if ( value === null || value === undefined || value === 0 || value === false || value === NaN || value === "" ) { return true; } return false; }; // 简写方法 const isFalsey = (value) => !value; 4. 三元运算符 作为JavaScript开发人员,你一定遇到过三元运算符。这是编写简洁if-else语句的好方法。但是,也可用来编写简洁的代码,甚至将它们链接起来来检查多个条件。 // 普通写法 let info; if (value < minValue) { info = "Value is too small"; } else if (value > maxValue) { info = "Value is too large"; } else { info = "Value is in range"; } // 简写方法 const info = value < minValue ? "Value is too small" : value > maxValue ? "Value is too large" : "Value is in range"; 5. 函数调用 在三元运算符的帮助下,你还可以根据条件确定要调用哪个函数。 注:函数的call signature必须相同,否则可能会遇到错误。 function f1() { // ... } function f2() { // ... } // 普通写法 if (condition) { f1(); } else { f2(); } // 简写方法 (condition ? f1 : f2)(); 6. Switch简写 通常我们可以使用以键作为switch条件并将值作为返回值的对象来优化长switch语句。 const dayNumber = new Date().getDay(); // 普通写法 let day; switch (dayNumber) { case 0: day = "Sunday"; break; case 1: day = "Monday"; break; case 2: day = "Tuesday"; break; case 3: day = "Wednesday"; break; case 4: day = "Thursday"; break; case 5: day = "Friday"; break; case 6: day = "Saturday"; } // 简写方法 const days = { 0: "Sunday", 1: "Monday", 2: "Tuesday", 3: "Wednesday", 4: "Thursday", 5: "Friday", 6: "Saturday", }; const day = days[dayNumber]; 7. 回退值 ||运算符可以为变量设置回退值。 // 普通写法 let name; if (user?.name) { name = user.name; } else { name = "Anonymous"; } // 简写方法 const name = user?.name || "Anonymous"; 以上就是今天所发布的内容,有问题的,可以一起讨论。
面对互联网大环境的就业难,我们该何去何从? 首先不可否认,口罩的原因和经济大环境等原因,让互联网这几年真的挺难受的。所以,在这样恶劣的环境下,把一些问题推上了风口浪尖,问题暴露无遗。那核心的矛盾是什么呢,为什么会出现前端就业难这样的一种“表现”。大家可以先看下面这一张图,其实不仅是前端从业者的人力结构是一个金字塔模型,各行各业更是如此。在假设行业发展稳定并且没有出现以下“消极情况”,如大批量公司倒闭、需求锐减或规模缩减等情况,从业人员的“保有量”是稳定缓慢增长,是可以做到“新陈代谢”的作用。但当一个行业或一个经济主体受到了严重事件影响,例如口罩、战争、人口等等一系列复杂的经济因素影响下,出现了上述的“消极情况”,这将导致了行业内大量的需求被延后,甚至是砍掉,无形之间打破了这样的一个“新陈代谢”的内循环机制,导致整体人力供大于求,所以,也就出现了我们开头提到的 就业难。 但一切的内卷,都是从底层发起的,所以金字塔的底层,也就是这一批新人和成长中的人,就成了风口浪尖中的那个小尖尖,尤为痛苦,主要表现在“已读不回”、“实习被嫌弃”、“工资压得低”等等。种种的迹象,都表明用人市场在分化,高端人才依然求贤若渴,而基层则是“卷”到飞起。 其实我们之所以会产生这些经济问题和所遇到的社会现状,归根到底,是对 生成效率的一种改变和适应过程。这句话该怎么去理解呢,我们可以从人类过去的几次生产效率大提升的历史节点来分析。目前是有四次工业革命: 第一次革命(18世纪60年代-19世纪中期)的标志是蒸汽机的出现,我们有了火车、汽船,他们取代马车 第二次工业革命(19世纪七八十年代-19世纪末20世纪初)的标志是内燃机、电气的运用,我们有了飞机、汽车 第三次革命(二战后-20世纪70年代)则是以计算机、航空航天、原子能为代表,我们进入信息时代 第四次工业革命(21世纪初至今)以人工智能、无人驾驶、清洁能源为代表的,全新的绿色工业革命。 其中第四次工业革命,以人工智能为代表,将改变创新链和产业链的升级模式,提高产品质量和服务水平。随着人工智能最新的GPT4模型发布,真正可以应用在大范围生产环境中的AI越来越近了。它确实可以帮助我们提高效率,减少很多基础性和重复性劳动力,并满足一些个性化功能需求。那对人类工作内容有什么新的要求呢,就拿无人驾驶来说,其实造就非常多的硬核工程师,这些工程师不仅要会java或者python等编程语言,还需要掌握各种深度学习、机器学习、神经网络、免疫算法等技术,甚至类unix系统也是必须掌握的。可见对于人才的的技能要求是全面而高深的。 这是我们个人无法抗逆的过程,我们能做的,应该是尽快去适应和捉紧时间修炼内功,提高自己的竞争力,不管是适应当下的竞争也好,还是为下一轮的经济周期性复苏做准备也好,我们都可以做下面的一些事情: 1. 不断学习和更新技术:前端技术和工具在不断更新,你需要不断学习新的技术和框架,以适应市场的变化。 2. 建立个人项目和作品集:建立自己的个人项目和作品集,展示自己的技术能力和创造力,以吸引潜在雇主的注意力。 3. 参加社区活动和开源项目:参加前端社区的活动,贡献到开源项目中,与其他前端开发人员互动和交流,建立自己的网络和社交圈。 4. 注重用户体验和设计:除了技术能力,注重用户体验和设计,能够提高自己的就业竞争力。 5. 发掘市场潜力:关注市场需求和趋势,例如移动应用程序、大数据、人工智能等,掌握相关的技术和知识,以发掘市场潜力。 6. 增强沟通和团队合作能力:前端开发需要与其他团队成员紧密合作,包括设计师、后端开发人员、产品经理等,增强沟通和团队合作能力,能够提高自己的竞争力。 除了第五点,蓝景在接下来的“中高阶课程”会全方位包含以上的提升内容,如果你对自己的自律能力、自学能力没信心,又对时间要求比较高,非常建议你来了解我们的这一个“课程”。我们会从源头抓起,让每一位学员的“教育背景”、“年龄”、“高校专业”、“个人学习能力”等,有一个全方位的综合评定。保证每一位同学都是在“高认知”的情况下,接受我们的高阶课程,整体课程围绕实际项目技术方案设计、执行、回顾,囊括了大前端的所有实际工作内容,包括整体架构设计、项目执行计划制定、服务器集群部署、数据采集方案、多维度优化手段掌握和掌握如何持续深入学习的方案。包含了大厂对于用人要求中的各个维度要求,目标是让每一位同学能脱离金字塔的第一层,进入相对竞争还不算太激烈的第二层以上。 历史的尘埃掉落在每个人身上,都是一座山,但也只有勇于积极寻求解决之道的人才能适应并挺过去。近段时间,我刚好看了一本名为《人类群星闪耀时》的书。人类群星闪耀时,是一句出自《Les Misérables》(《悲惨世界》)的经典台词。这句话是法国作家雨果在小说中描述巴黎暴动的场景时所写下的。在小说中,巴黎人民举起了起义的旗帜,挺身反抗反动政府的统治,他们站在巴黎的屋顶上,点燃了城市的灯火,高喊着口号,映照着天空,仿佛成了一颗颗璀璨的星星。这句话意味着,当人们团结起来,共同为正义和自由而奋斗时,他们的力量是巨大的,他们会闪耀着光芒,让整个世界都为之震动。这句话也被引申为,每个人都有自己的闪耀时刻,只要发挥自己的才能,做到最好,就能像群星一样闪耀在人类历史的舞台上。
广州蓝景分享—8种常见的 JavaScript ES6 使用技巧 1.添加对象属性值给对象添加属性,名称动态变化怎么办 let obj = {};let index = 1; let key = `topic${index}`; obj[key] = 'topic'; 为什么要创建一个额外的变量? 你不知道 ES6 中的对象属性名可以使用表达式吗? 改进后 let obj = {}; let index = 1; obj[`topic${index}`] = 'topic'; 2.列表搜索 在正式的项目开发中,前端一般负责实现一些没有分页的列表的搜索功能。 搜索一般分为精确搜索和模糊搜索,搜索也叫过滤。 一种是模糊搜索,一般用过滤器来实现 const a = [1, 2, 3, 4, 5] const result = a.filter((item) => { return item === 3}) console.log('result', result) 但是,如果是精确搜索,则需要使用ES6中的fin const a = [1,2,3,4,5]; const result = a.find( item =>{ return item === 3 }) 3.获取对象属性 const name = obj && obj.name 您可以在 ES6 中使用可选的链接运算符 const name = obj?.name 4. 展平数组 在开发ERP系统或者人事管理系统的过程中,经常会遇到一个应用场景。 一个部门的JSON数据中,属性名是部门id,属性值是部门成员id的数组集合。现在的需求是将部门的所有成员id提取到一个数组集合中。 const deps = { 'data01':[1,2,3], 'data02':[5,8,12], 'data03':[5,14,79], 'data04':[3,64,105], } let member = []; for (let item in deps){ const value = deps[item]; if(Array.isArray(value)){ member = [...member,...value] } } member = [...new Set(member)] 这时候,我好像听到前端组长开始骂了: 还需要遍历得到所有对象的属性值吗?性能优化好不好,Object.values忘记了?以前没用过 ES6?还有涉及到数组的扁平化过程,为什么不使用ES6提供的扁平化方法呢? const deps = { 'data01':[1,2,3], 'data02':[5,8,12], 'data03':[5,14,79], 'data04':[3,64,105], } let member = Object.values(deps).flat(Infinity); Infinity 用作平面参数,因此您不需要知道平面数组的维度。 5.if中的判断语: if( type == 1 || type == 2 || type == 3 || type == 4 || ){ //... } 改进后可简写为: const condition = [1, 2, 3, 4] const type = 11 if (condition.includes(type)) { console.log('ok') } 6.判断输入框不为空 在日常开发中,无论PC端还是移动端,在处理与输入框相关的业务时,往往会判断输入框没有输入值。 if(value !== null && value !== undefined && value !== ''){ //... } 可以改进为: if((value??'') !== ''){ //... } 是不是省了很多代码,惊喜还是意外? 7.获取对象属性值 const name = obj && obj.name 改进后: const name = obj?.name 8.异步函数 异步函数很常见,直接上案例 fconst fn1 = () =>{ return new Promise((resolve, reject) => { setTimeout(() => { resolve(1); }, 300); }); } const fn2 = () =>{ return new Promise((resolve, reject) => { setTimeout(() => { resolve(2); }, 600); }); } const fn = () =>{ fn1().then(res1 =>{ console.log(res1);// 1 fn2().then(res2 =>{ console.log(res2) }) }) }; 看着这样的代码,我仿佛看到了前端组长轻蔑的眼神。 这么写,跟回调地狱有什么区别? 改进后。 const fn = async () =>{ const res1 = await fn1(); const res2 = await fn2(); console.log(res1);// 1 console.log(res2);// 2 } 代码一下子简洁了许多,总算松了一口气。 如果是并发请求,可以使用Promise.all() const fn = () =>{ Promise.all([fn1(),fn2()]).then(res =>{ console.log(res);// [1,2] }) } 想要了解更多前端开发,可以欢迎留言。
广州蓝景分享—TypeScript开发中 6个泛型方法 TypeScript 原生提供了几种有用的实用类型来帮助我们执行一些常见的类型转换。 TypeScript 中的类型系统非常强大。它为我们提供了类型安全。虽然类型系统深受喜爱,但如果我们不规划和设计类型接口,它也会使我们的代码混乱和难以阅读。 TypeScript 原生提供了几种有用的实用类型来帮助我们执行一些常见的类型转换。这些实用类型都是全局可用的,而且都使用了泛型,在开始之前,先简单介绍一下泛型。 通用的 避免代码重复和创建可重用类型是编写干净代码的重要部分。泛型是 TypeScript 的一个特性,它允许我们编写可重用的类型。看下面的例子。 type Add<T> = (a: T, b: T) => T const addNumbers: Add<number> = (a, b) => { return a + b } const addStrings: Add<string> = (a, b) => { return a + b } 将正确的类型放入Add的泛型中,可以用来描述两个数字的相加或者两个字符串的连接。 我们不需要为每个函数都写一个类型,我们只需要用一个泛型类型写一次。这不仅节省了我们的精力,而且使我们的代码更干净,更不容易出错。 下面将跟大家一起来分享6种实用类型,也是我们经常用到的。 1. Partial<Type> Partial 构造一个类型,其所有类型属性都设置为可选。这在我们为对象编写更新逻辑时非常有用。 type User = { name: string age: number address: string occupation: string } type PartialUser = Partial<User> // type PartialUser = { // name?: string; // age?: number; // address?: string; // occupation?: string; // } 2. Required<Type> Required 与 Partial 相反。它构造一个类型,其中需要该类型的所有属性。它可用于确保没有可选属性出现在类型中。 type PartialUser = { name: string age: number address?: string occupation?: string } type User = Required<PartialUser> // type User = { // name: string; // age: number; // address: string; // occupation: string; // } 3. Pick<Type, Keys> Pick 将从 Type 中选取属性集 Keys 以创建新类型。键可以是字符串文字或字符串文字的并集。Keys 的值必须是 Type 的键,否则 TypeScript 编译器会报错。当您想要通过从具有许多属性的对象中选取某些属性来创建更轻的对象时,此实用程序类型特别有用。 type User = { name: string age: number address: string occupation: string } type BasicUser = Pick<User, "name" | "age"> // type BasicUser = { // name: string; // age: number; // } 4. Omit<Type, Keys> 省略与选择相反。keys 不是关于保留哪些属性,而是要省略的属性键集。当我们只想从对象中删除某些属性并保留其他属性时,这会更有用。 type User = { name: string age: number address: string occupation: string } type BasicUser = Omit<User, "address" | "occupation"> // type BasicUser = { // name: string; // age: number; // } 5. Readonly<Type> Readonly 构造一个类型,该类型的所有属性都设置为只读。给 TS 重新赋新值会报错。 type User = { name: string age: number address: string occupation: string } type ReadOnlyUser = Readonly<User> const user: ReadOnlyUser = { name: "Mark", age: 34, address: "Chicago", occupation: "IT Engineer" } user.name = "Maxwell" // Cannot assign to 'name' because it is a read-only property. 6. ReturnType<Type> ReturnType 从函数类型的返回类型构造一个类型。当我们处理来自外部库的函数类型并希望基于它们构建自定义类型时,它很有用。 import axios from 'axios' type Response = ReturnType<typeof axios> function callAPI(): Response{ return axios("url") } 除了上面提到的那些,还有其他实用程序类型可以帮助我们编写更干净的代码。可以在此处找到有关实用程序类型的 TypeScript 文档的链接。 实用程序类型是 TypeScript 提供的一项非常有用的功能,前端开发人员应该使用它们来避免硬编码类型。
广州蓝景实训部,在新的一年,祝学员们旗开得胜!继续勇往直前,早日实现自己的目标。
广州蓝景分享—CSS mask遮罩层详解 mask简介 CSS的mask属性允许使用者通过部分或者完全隐藏一个元素的可见区域。这种效果可以通过遮罩或者裁切特定区域的图片。mask和background用法是相仿的,mask的值有这些: mask-clip mask-composite mask-image mask-mode mask-origin mask-position mask-repeat mask-size mask-type 下面我们具体介绍每一个值的意义。 1. mask-clipmask-clip的默认值是border-box,而且支持多属性值,例如: mask-clip: content-box, border-box; 虽然支持的属性值挺多,但是对于普通元素而言,生效的其实就前面4个,Firefox浏览器还支持no-clip。fill-box,stroke-box,view-box要与SVG元素关联才有效果,目前还没有任何浏览器对其进行支持。 2. mask-composite以上属性值,目前仅Firefox浏览器支持,Chrome默认mask-composite计算值是source-over,和标准默认值add有些差异,作用是一样的,表示多个图片遮罩效果是累加。 3. mask-image mask-image指遮罩使用的图片资源,默认值是none,也就是无遮罩图片。所谓遮罩,就是原始图片只显示遮罩图片非透明的部分。 mask-image也支持多属性值,例如: mask-image: url(...), url(...); 4. mask-mode mask-mode属性的默认值是match-source,意思是根据资源的类型自动采用合适的遮罩模式。 例如,如果我们的遮罩使用的是SVG中的<defs>中的<mask>元素,则此时的mask-mode属性的计算值是luminance,表示基于亮度遮罩。如果是其他场景,则计算值是alpha,表示基于透明度遮罩。mask-mode也支持多属性值,例如: mask-mode: alpha, match-source; 目前,mask-mode仅Firefox浏览器支持,因此,Chrome浏览器是看到的依然是基于alpha遮罩的效果,颜色不像上图那样淡。 5. mask-repeatmask-repeat也支持多属性值,例如: mask-repeat: space round, no-repeat; 6. mask-position 支持单个关键字,如top,bottom,left,right,center(缺省关键字的解析为center) 支持各类数值(百分数或数值),例如:mask-position: 30% 50%; mask-position也支持多属性值,例如:mask-position: 0 0, center; Chrome和Firefox浏览器都支持mask-position属性,Chrome还需要-webkit-私有前缀,Firefox浏览器现在已经不需要了。 7. mask-originmask-origin的默认值是border-box,而且支持多属性值,例如: mask-origin: content-box, border-box; 虽然支持的属性值挺多,但是对于普通元素而言,生效的其实就前面4个。fill-box,stroke-box,view-box要与SVG元素关联才有效果,目前还没有任何浏览器对其进行支持。 8. mask-size mask-size作用是控制遮罩图片尺寸,默认值是auto。 支持contain和cover这两个关键字 支持各类数值(缺省高度会自动计算为auto),例如:mask-size: auto 6px; 同样支持多属性值,例如:mask-size: 50%, 25%, 25%; 9. mask-type mask-type属性功能上和mask-mode类似,都是设置不同的遮罩模式。 但还是有个很大的区别,那就是mask-type只能作用在SVG元素上,本质上是由SVG属性演变而来,因此,Chrome等浏览器都是支持的。 但是mask-mode是一个针对所有元素的CSS3属性,Chrome等浏览器并不支持,目前仅Firefox浏览器支持。 由于只能作用在SVG元素上,因此默认值表现为SVG元素默认遮罩模式,也就是默认值是luminance,亮度遮罩模式。如果需要支持透明度遮罩模式,可以这么设置:mask-type: alpha; 以上内容就是CSS mask属性介绍,希望对你有帮助,想了解更多前端知识,可以点个赞,关注我们。
8个HTML使用技巧 1. 使用capture属性打开设备摄像头 正如input标签具有email、text和password属性一样,我们也可以通过一些属性打开移动设备的摄像头以捕获图像。 那就是capture属性,属性值有两个: user用于前置摄像头 environment用于后置摄像头 <input type="file" capture="user" accept="image/*"> 2. 网站自动刷新 你可以在head标签中将网站设置为定时刷新! <head> <meta http-equiv="refresh" content="10"> </head> 此代码段可以实现每10秒刷新一次网站。 3. 激活拼写检查 你可以使用HTML的spellcheck属性并将其设置为true以激活拼写检查。使用lang属性指定待检查的语言。 <input type="text" spellcheck="true" lang="en"> 这是一个标准属性,得到了大多数浏览器的支持。4. 指定要上传的文件类型 你可以使用accept属性在input标签中指定允许用户上传的文件类型。 <input type="file" accept=".jpeg,.png"> 5. 阻止浏览器翻译 将translate属性设置为no会阻止浏览器翻译该内容。如果你不想翻译某个短语或单词,例如logo、公司或品牌名称,那就可以应用这个属性。 <p translate="no">Brand name</p> 6. 在input标签中输入多个项目 这可以通过multiple属性来完成。 <input type="file" multiple> 适用于文件和电子邮件。如果是电子邮件,则可以用逗号分隔。 7. 为视频创建海报(缩略图) 使用poster属性,我们可以在视频加载时,或者在用户点击播放按钮之前,显示指定的缩略图。 如果不指定图片,则默认使用视频的第一帧作为缩略图。 <video poster="picture.png"></video> 8. 点击链接自动下载 如果你希望在单击目标资源的链接时下载特定资源,那就添加download属性。 <a href="image.png" download> 今天就分享到这里,想要了解更多前端技术知识,可以关注我们。
广州蓝景分享—12个实用的 JavaScript 函数 今天收集了一些日常前端开发常用的 JavaScript函数,有的比较复杂,有的比较简单,希望对大家有所帮助。 话不多说,现在就开始。 1.生成随机颜色 你的网站是否需要生成随机颜色?下面一行代码就可以实现。 const generateRandomHexColor = () => `#${Math.floor(Math.random() * 0xffffff).toString(16)} console.log(generateRandomHexColor()) 2.数组重排序 对数组的元素进行重新排序是一项非常重要的技巧,但是原生 Array 中并没有这项功能。 const shuffle = (arr) => arr.sort(() => Math.random() - 0.5 const arr = [1, 2, 3, 4, 5] console.log(shuffle(arr)) 3.复制到剪切板 复制到剪切板是一项非常实用且能够提高用户便利性的功能。 const copyToClipboard = (text) => navigator.clipboard && navigator.clipboard.writeText && navigator.clipboard.writeText(text copyToClipboard("Hello World!") 4.检测暗色主题 暗色主题日益普及,很多用的都会在设备中启用案模式,我们将应用程序切换到暗色主题可以提高用户体验度。 const isDarkMode = () => window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches console.log(isDarkMode()) 5.滚动到顶部 将元素滚动到顶部最简单的方法是使用scrollIntoView。设置block为start可以滚动到顶部;设置behavior为smooth可以开启平滑滚动。 const scrollToTop = (element) => element.scrollIntoView({ behavior: "smooth", block: "start" }); 6.滚动到底部 与滚动到顶部一样,滚动到底部只需要设置block为end即可。 const scrollToBottom = (element) => element.scrollIntoView({ behavior: "smooth", block: "end" }); 7.检测元素是否在屏幕中 检查元素是否在窗口中最好的方法是使用IntersectionObserver。 const callback = (entries) => { entries.forEach((entry) => { if (entry.isIntersecting) { // `entry.target` is the dom element console.log(`${entry.target.id} is visible`); } }); } const options = { threshold: 1.0, }; const observer = new IntersectionObserver(callback, options); const btn = document.getElementById("btn"); const bottomBtn = document.getElementById("bottom-btn"); observer.observe(btn); observer.observe(bottomBtn); 8.检测设备 使用navigator.userAgent来检测网站运行在哪种平台设备上。 const detectDeviceType = () => /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test( navigator.userAgent ) ? "Mobile" : "Desktop" console.log(detectDeviceType()); 9.隐藏元素 我们可以将元素的style.visibility设置为hidden,隐藏元素的可见性,但元素的空间仍然会被占用。如果设置元素的style.display为none,会将元素从渲染流中删除。 const hideElement = (el, removeFromFlow = false) => { removeFromFlow ? (el.style.display = 'none') : (el.style.visibility = 'hidden') } 10.从 URL 中获取参数 JavaScript 中有一个 URL 对象,通过它可以非常方便的获取 URL 中的参数。 const getParamByUrl = (key) => { const url = new URL(location.href) return url.searchParams.get(key) } 11.深拷贝对象 深拷贝对象非常简单,先将对象转换为字符串,再转换成对象即可。 const deepCopy = obj => JSON.parse(JSON.stringify(obj)) 除了利用 JSON 的 API,还有更新的深拷贝对象的 structuredClone API,但并不是在所有的浏览器中都支持。 structuredClone(obj) 12.等待函数 JavaScript 提供了setTimeout函数,但是它并不返回 Promise 对象,所以我们没办法使用 async 作用在这个函数上,但是我们可以封装等待函数。 const wait = (ms) => new Promise((resolve)=> setTimeout(resolve, ms) const asyncFn = async () => { await wait(1000) console.log('等待异步函数执行结束') } asyncFn() 觉得有用的话,可以回复发表。
前端学习—Typescript开发实用技巧 Hello~~各位小伙伴,今天我们广州蓝景实训部,继续和大家分享前端技术干货,工作与学习中都会用到Typescript 开发实用技巧。 首先Typescript 在类型检查方面非常强大,但有时某些类型是其他类型的子集并且需要为它们定义类型检查时,它会变得乏味。举个例子,有两种响应类型: 用户配置文件响应 interface UserProfileResponse { id: number; name: string; email: string; phone: string; avatar: string; } 登录响应 interface LoginResponse { id: number; name: string; } 我们可以为 UserProfileResponse 定义类型并为 LoginResponse 选择一些属性,而不是定义相同上下文 LoginResponse 和 UserProfileResponse 的类型。 type LoginResponse = Pick<UserProfileResponse, "id" | "name">; 让我们了解一些可以帮助您编写更好代码的实用函数。 01、Uppercase 构造一个 Type 的所有属性都设置为大写的类型。 type Role = "admin" | "user" | "guest"; // Bad practice type UppercaseRole = "ADMIN" | "USER" | "GUEST"; // Good practice type UppercaseRole = Uppercase<Role>; // "ADMIN" | "USER" | "GUEST" 02、Lowercase 构造一个 Type 的所有属性都设置为小写的类型,与大写相反。 type Role = "ADMIN" | "USER" | "GUEST"; // Bad practice type LowercaseRole = "admin" | "user" | "guest"; // Good practice type LowercaseRole = Lowercase<Role>; // "admin" | "user" | "guest" 03、Capitalize 构造一个将 Type 的所有属性设置为大写的类型。 type Role = "admin" | "user" | "guest"; // Bad practice type CapitalizeRole = "Admin" | "User" | "Guest"; // Good practice type CapitalizeRole = Capitalize<Role>; // "Admin" | "User" | "Guest" 04、Uncapitalize 构造一个将 Type 的所有属性设置为 uncapitalize 的类型,与首字母大写相反。 type Role = "Admin" | "User" | "Guest"; // Bad practice type UncapitalizeRole = "admin" | "user" | "guest"; // Good practice type UncapitalizeRole = Uncapitalize<Role>; // "admin" | "user" | "guest" 05、Partial 构造一个类型,其中 Type 的所有属性都设置为可选。 interface User { name: string; age: number; password: string; } // Bad practice interface PartialUser { name?: string; age?: number; password?: string; } // Good practice type PartialUser = Partial<User>; Required 构造一个类型,该类型由设置为 required 的 Type 的所有属性组成,Opposite的对面。 interface User { name?: string; age?: number; password?: string; } // Bad practice interface RequiredUser { name: string; age: number; password: string; } // Good practice type RequiredUser = Required<User>; 06、Readonly 构造一个类型,该类型由设置为只读的 Type 的所有属性组成。 interface User { role: string; } // Bad practice const user: User = { role: "ADMIN" }; user.role = "USER"; // Good practice type ReadonlyUser = Readonly<User>; const user: ReadonlyUser = { role: "ADMIN" }; user.role = "USER"; // Error: Cannot assign to 'role' because it is a read-only property. 07、Record 构造一个具有一组类型 T 的属性 K 的类型,每个属性 K 都映射到类型 T。 interface Address { street: string; pin: number; } interface Addresses { home: Address; office: Address; } // Alternative type AddressesRecord = Record<"home" | "office", Address>; 08、Pick 只选择键在联合类型键中的 Type 的属性。 interface User { name: string; age: number; password: string; } // Bad practice interface UserPartial { name: string; age: number; } // Good practice type UserPartial = Pick<User, "name" | "age">; 09、Omit Omit其键在联合类型键中的 Type 属性。 interface User { name: string; age: number; password: string; } // Bad practice interface UserPartial { name: string; age: number; } // Good practice type UserPartial = Omit<User, "password">; 10、Exclude 构造一个具有 Type 的所有属性的类型,除了键在联合类型 Excluded 中的那些。 type Role = "ADMIN" | "USER" | "GUEST"; // Bad practice type NonAdminRole = "USER" | "GUEST"; // Good practice type NonAdmin = Exclude<Role, "ADMIN">; // "USER" | "GUEST" 11、Extract 构造一个具有 Type 的所有属性的类型,其键在联合类型 Extract 中。 type Role = "ADMIN" | "USER" | "GUEST"; // Bad practice type AdminRole = "ADMIN"; // Good practice type Admin = Extract<Role, "ADMIN">; // "ADMIN" 12、NonNullable 构造一个类型,其中 Type 的所有属性都设置为不可为空。 type Role = "ADMIN" | "USER" | null; // Bad practice type NonNullableRole = "ADMIN" | "USER"; // Good practice type NonNullableRole = NonNullable<Role>; // "ADMIN" | "USER" 总结 以上文章就是今天广州蓝景跟大家分享的一些前端技术知识,希望能帮到你学到新知识。可以点下赞或者关注我们。
广州蓝景分享—16个非常有用的React组件库,前端开发必备 Hello~~各位小伙伴,当你作为一个有实力的React开发人员,如果仅仅了解React是不够的。今天广州蓝景小编收集了一些最有用的React组件库,希望可以帮助大家提高开发效率。 这些组件包括使用表单,图表,日历,表格,用户引导、弹出窗口,颜色,动画,音乐,图像等等非常多的内容。 1.react-hook-form这是一个React钩子组件,其作用于表单状态管理和验证。 官方网站:http://tieba.baidu.com/mo/q/checkurl?url=https%3A%2F%2Freact-hook-form.com%2F&urlrefer=56c2b6201e63b0a06b9799ab171a6df0 2. recharts该组件重新定义了使用React和D3构建的图表库。 官方网站:http://tieba.baidu.com/mo/q/checkurl?url=https%3A%2F%2Frecharts.org%2Fen-US%2F&urlrefer=576be1a6821ae85c3d0aa85097e5d640 3. react-big-calendar这个为React和现代浏览器而构建的事件日历。 官方网站:http://tieba.baidu.com/mo/q/checkurl?url=https%3A%2F%2Fgithub.com%2Fintljusticemission%2Freact-big-calendar&urlrefer=3712943aabf785b248c146c12c7940df 4. react-beautiful-dnd这是通过React对列表进行美观且易于访问的拖放操作。 官方网站:http://tieba.baidu.com/mo/q/checkurl?url=https%3A%2F%2Fgithub.com%2Fatlassian%2Freact-beautiful-dnd&urlrefer=b3cc58c4ec5ed7fd36e0f3b3efd8d532 5. react-table这个库用于构建功能强大的表格和数据网格。 官方网站:http://tieba.baidu.com/mo/q/checkurl?url=https%3A%2F%2Fgithub.com%2Ftannerlinsley%2Freact-table%2F&urlrefer=5487e1341eb9b1b4619acc362edd2d00 6. react-joyride这个是为app创建引导式用户导览。 官方网站:http://tieba.baidu.com/mo/q/checkurl?url=https%3A%2F%2Fgithub.com%2Fgilbarbara%2Freact-joyride&urlrefer=eee96ba604e089e7b63e6dfa884bbdec 7. react-advanced-cropper该组件提供图像的自定义裁剪功能。 官方网站:http://tieba.baidu.com/mo/q/checkurl?url=https%3A%2F%2Fgithub.com%2FNorserium%2Freact-advanced-cropper&urlrefer=39fba295cd5301827297cbc7f2db7447 8. react-colorful这是一个轻量的、免依赖的、快速且易于访问的颜色选取器组件。 官方网站:http://tieba.baidu.com/mo/q/checkurl?url=https%3A%2F%2Fgithub.com%2Fomgovich%2Freact-colorful&urlrefer=3367cdf33ebea59283fb50a998d69d21 9. react-spring这是基于弹簧物理特性的React应用程序动画库。 官方网站:http://tieba.baidu.com/mo/q/checkurl?url=https%3A%2F%2Fgithub.com%2Fpmndrs%2Freact-spring&urlrefer=6d9616b5dc3ce7bb2f73b13ab3c9a2eb 10. react-tsparticles该组件可以帮助你轻松创建高度可自定义的粒子动画。 官方网站:http://tieba.baidu.com/mo/q/checkurl?url=https%3A%2F%2Fgithub.com%2Fmatteobruni%2Ftsparticles&urlrefer=8feb5eaf9a1d1c385330773cc35968e9 11. react-popper该组件以优雅、高性能的方式创建网页提示框和弹出框。 官方网站:http://tieba.baidu.com/mo/q/checkurl?url=https%3A%2F%2Fgithub.com%2Fpopperjs%2Freact-popper&urlrefer=8fd81e2fab6a99f75a9c78f2c7754d2d 12. react-pdf-viewer这是基于React而制作的PDF查看组件。 官方网站:http://tieba.baidu.com/mo/q/checkurl?url=https%3A%2F%2Fgithub.com%2Fphuoc-ng%2Freact-pdf-viewer&urlrefer=5438d786d55c027f89f7a142797216f7 13. react-i18next这个组件可以帮助你处理好React应用的国际化问题。 官方网站:http://tieba.baidu.com/mo/q/checkurl?url=https%3A%2F%2Fgithub.com%2Fi18next%2Freact-i18next&urlrefer=e32c728e171512e0559538154c15726f 14. react-icons这是一个比较流行的React SVG图标包。 官方网站:http://tieba.baidu.com/mo/q/checkurl?url=https%3A%2F%2Fgithub.com%2Fgorangajic%2Freact-icons&urlrefer=49d16ea401122eb2253c9622ba372e57 15. audio-player这是一个具有自定义控件,播放列表,过滤器和搜索功能的React音乐播放器组件。 官方网站:http://tieba.baidu.com/mo/q/checkurl?url=https%3A%2F%2Fgithub.com%2Fmadzadev%2Faudio-player&urlrefer=702c79a0542150ad169b56e78e3f314b 16. image-slider这是一个很不错的React图像滑块组件。 官方网站:http://tieba.baidu.com/mo/q/checkurl?url=https%3A%2F%2Fgithub.com%2Fmadzadev%2Fimage-slider&urlrefer=7b1f2128a2aa48534a39a2ed8d5e24ea 以上就是今天整理的一些内容,希望这些React组件对你工作有所帮助,想要了解更多前端开发知识,可以关注广州蓝景。
广州蓝景分享—Vue3 相关技巧,也许你还不知道 Hello~~各位小伙伴,Vue3从2022年9月正式发布以来,它在源码、性能、语法API方面都做了很大的优化,如果你想要学习Vue的话,今天广州蓝景小编将跟大家分享vue这篇文章内容,希望在学习中可以帮助到你。接下来就让我们开始吧。 01.VNode 钩子 在每个组件或html标签上,我们可以使用一些特殊的(文档没写的)钩子作为事件监听器。这些钩子有: onVnodeBeforeMount onVnodeMounted onVnodeBeforeUpdate onVnodeUpdated onVnodeBeforeUnmount onVnodeUnmounted 这里主要是在组件上使用onVnodeMounted,当需要在组件挂载时执行一些代码,或者在更新时使用onVnodeUpdated进行调试,可以确定的是所有这些钩子都能在某些情况下派上用场。 例子如下:应该注意的是,这些挂钩将一些参数传递给回调函数。它们只传递一个参数,即当前 VNode,除了onVnodeBeforeUpdate传递onVnodeUpdated两个参数,当前 VNode 和前一个 VNode。 02.调试挂钩 我们都知道 Vue 为我们提供的生命周期钩子。但是您知道 Vue 3 为我们提供了两个可用于调试目的的钩子吗?他们是: onRenderTracked onRenderTriggered onRenderTracked为已跟踪的每个反应性依赖项调用。onRenderTriggered当我们触发反应性更新时被调用,或者如文档所说:“当反应性依赖触发组件的渲染效果重新运行时”。03.从子组件公开插槽 如果您使用第三方组件,您可能会将其实现包装在您自己的“包装器”组件中。这是一个很好的实践和可扩展的解决方案,但那样的话,第三方组件的插槽就会丢失,我们应该找到一种方法将它们暴露给父组件: WrapperComponent.vue现在每个使用的组件都WrapperComponent可以使用ThirdPartyComponent的插槽。 04.作用域样式和多根节点不能很好地协同工作 在 Vue 3 中,我们终于可以拥有不止“一个根节点”的组件。这很好,但我个人在这样做时遇到了设计限制。假设我们有一个子组件: <template> <p class="my-p">First p</p> <p class="my-p">Second p</p> </template> 和一个父组件:无法从多根父组件的作用域样式设置子组件的 p 标签的样式。 所以简而言之,一个多根组件,不能使用作用域样式来定位多根子组件的样式。 解决这个问题的最好方法是包装父组件或子组件(或两者),这样我们就只有一个根元素。 但是如果你绝对需要两者都有多根节点,你可以: 使用非作用域样式 <style> .my-p { color: red; } </style> 使用 css 模块既然我们在这里指定了一个类,那么多根子组件就得显式指定属性 fallthrough 行为。 如果你想要我的意见,除非你绝对需要一个多根节点组件,否则请使用单个根节点并且根本不要处理这个设计限制。 05.使用 CSS 选择器时要小心 #main-nav > li {}将比 . 慢很多倍.my-li { color: red }。从文档: 由于浏览器呈现各种 CSS 选择器的方式,p { color: red } 在范围内(即与属性选择器结合使用时)会慢很多倍。如果您改用类或 ID,例如在 .example { color: red } 中,那么您几乎可以消除性能损失。 如果您想更深入地研究这个主题,我强烈建议您阅读Efficiently Rendering CSS 。 06.布尔转换 在 Vue 2 或 Vue 3 的早期版本中,对于具有布尔类型的道具,我们根据顺序有不同的行为: 第一种情况: props: { hoverColor: [String, Boolean] // <- defaults to '' } 第二种情况: props: { hoverColor: [Boolean, String] // <- defaults to false } 不仅如此,如果你像这样传递 prop: <my-component hover-color></my-component> 在第一种情况下,它将是一个空字符串''。在第二种情况下,它将是true. 如您所见,这有点混乱和不一致。幸运的是,在 Vue 3 中,我们有一个一致且可预测的新行为: Boolean无论类型出现顺序如何,行为都将适用。 所以: hoverColor: [String, Boolean] // <- defaults to false hoverColor: [Boolean, String] // <- defaults to false hoverColor: [Boolean, Number] // <- defaults to false 07.带有 v-for 的模板引用 - 不能保证顺序 记住这个,这样你就不会浪费数小时的调试时间来弄清楚发生了什么 在下面的代码中:我们在列表数组上循环,并创建 itemRefs 数组。itemRefs不保证与列表数组有相同的顺序。如果你想了解更多这方面的信息,你可以阅读这个issue。 总结:以上内容就是今天跟大家分享的vue知识,希望这些技巧对你有所帮助,想要了解更多前端知识,可以关注我们。
广州蓝景分享—程序员必备3个JavaScript插件,让你的视频更实用 今天,广州蓝景小编跟大家分享一些干货,程序员必备的3个JavaScript插件,让你的视频更实用。作为一个喜欢编程的程序员,他总会把自己的知识经验通过文字呈现在读者面前,并且让读者通过文字,就能将知识进行学以致用。 下面就从这篇技术文章开始,我倾向于利用以图形交换格式 (GIF) 格式编码的屏幕截图,以尽可能减少额外的文本描述块。例如,在更倾向于 Web 开发内容的文章中,例如: 在这种情况下,我总是发现嵌入动画 GIF 来演示我创建的开源工具的任何用法是有益的:鉴于 Microsoft Powerpoint 的内置屏幕录制功能很容易访问,虽然,可以轻松完成自己的屏幕录制以输出屏幕截图,但我觉得更有益的探索领域将是 - 易于访问转换视频的工具 将屏幕截图(.mp4、.avi 等)转换为 .GIF 图像文件。 — 注意:按照惯例,如果要渲染动画短片(≤ 30 秒),则 GIF 文件更合适。 使用 GIFEncoder.js 将视频构建到 GIF Maker 为了构建浏览器实用程序,如下所示:这 3 个 JavaScript 插件是必需的: GIFEncoder.js LZWEncoder.js NeuQuant.js 仅供参考:这些插件最初是由 GitHub 用户 Kevin Kwok(创建者)从 GitHub repo jsgif 中检索到的。视频到 GIF 转换概述 先决条件:包含在上述3个文件中+ b64.js如下: <script type="text/javascript" src="LZWEncoder.js"></script> <script type="text/javascript" src="NeuQuant.js"></script> <script type="text/javascript" src="GIFEncoder.js"></script> <script type="text/javascript" src="b64.js"></script> 技术实施——总共 4 个步骤 步骤(1):上传视频片段(≤30秒) 因此,在 HTML 代码中,包含一个简单的用户输入界面: <input id='inputVideoClipFile' type='file' multiple='false' accept='.mp4,.webm,.avi,.mpeg,.flv,.mov,.3gp' /> 将事件处理程序(onchange)标记到上面并继续在 JavaScript 中初始化 FileReader 实例: inputVideoClipFile.onchange = function(uploadFle) { let file = inputVideoClipFile.files[0]; let fileName=file.name; let fileSize=(file.size/1024).toFixed(2); let fileType=file.type; let fileredr = new FileReader(); fileredr. = function (fle) { var b64Str=fle.target.result; }; // end file-reader fileredr.readAsDataURL(file); }; 请注意,新的 FileReader() 实例调用 readAsDataURL,因此分配给 b64Str 的视频文件内容被读取为 字符串。 从文件对象中检索视频文件的信息以供稍后显示。 步骤(2):处理视频二进制数据并提取帧,有两个主要部分需要考虑: Part I. 通过创建 <video></video> DOM 元素并在 JavaScript 中分配相应的属性来预览视频内容以供显示. // rendered as <video></video> in HTML code var videoObj = document.('video'); var displayedHeight=500; if(videoObj.canPlayType(fileType)) { videoObj.setAttribute('id','inputVideo'); videoObj.setAttribute('src', b64Str); videoObj.setAttribute('height', displayedHeight); } 注意b64Str是前面步骤中FileReader()读取的视频文件数据。 第二部分,帧提取——每个视频帧都指的是剪辑在唯一时间戳的图像快照。 由于 GIF 文件是通过合并一组连续的图像创建的,因此,对于视频的每一次时间图形更新,都需要提取一个嵌入了图像数据的帧,用于后续的 GIF 创建过程。 虽然不能直接从 DOM 元素 <video></video> 中提取每个视频帧所需的图像数据,但可以将 <video></video> 中的预览内容渲染到 <canvas></canvas> 元素上 帧图像数据提取。 接下来,在 JavaScript 中创建一个 <canvas></canvas> 元素并为其分配相应的属性(类似于 <video></video>): var vidHeight=videoObj.videoHeight; var vidWidth=videoObj.videoWidth; var bitmap = document.('canvas'); bitmap.setAttribute('id', 'bitmap'); bitmap.setAttribute('width', vidWidth); bitmap.setAttribute('height', vidHeight); 请注意,vidWidth 和 vidHeight 是从 <video></video> 检索的。(这些是剪辑的原始尺寸。) 接下来的几行代码基于标记到每个元素的 'id' 属性来引用 <video></video> 和 <canvas></canvas>: const inputVideo=document.getElementById('inputVideo'); const bitmapCanvas=document.getElementById('bitmap'); const bitmapCtx = bitmapCanvas.getContext('2d'); inputVideo.muted = true; inputVideo.loop = false; inputVideo.autoplay=true; const background = () => { bitmapCtx.fillStyle = '#FFFFFF'; bitmapCtx.fillRect(0, 0, vidWidth, vidHeight); }; 由于视频设置为自动播放,当播放事件被发出时,这会触发 background() 的执行,它不仅将 GIF 背景填充为白色,而且还设置了绘制到 canvas 元素上的每一帧的尺寸: const step = async() => { let bgStatus=await background(); await new Promise(resolve => { bitmapCtx.drawImage(inputVideo, 0, 0, vidWidth, vidHeight); frameB64Str = bitmapCanvas.toDataURL(); resolve(); }); window.requestAnimationFrame(step); }; inputVideo.addEventListener('play', () => { step(); window.requestAnimationFrame(step); }); window.requestAnimationFrame(step) 接受一个回调函数(即step())来处理每一帧的图像数据。 bitmapCtx.drawImage() 继续将每个图像快照渲染到画布上,以便 bitmapCanvas.toDataURL() 返回每个快照的 编码图像。 步骤(3):依次合并所有图像快照 最后实现了JS插件GIFEncoder.js。使用与上面相同的代码片段,以下粗体代码行指的是 GIFEncoder 编码器捕获嵌入在 <canvas></canvas> 中的每个数据帧的实例。 const encoder = new GIFEncoder(vidWidth, vidHeight); encoder.setRepeat(0); encoder.setDelay(500); const step = async() => { let bgStatus=await background(); await new Promise(resolve => { bitmapCtx.drawImage(inputVideo, 0, 0, vidWidth, vidHeight); frameB64Str = bitmapCanvas.toDataURL(); encoder.addFrame(bitmapCtx); resolve(); }); window.requestAnimationFrame(step); }; inputVideo.addEventListener('play', () => { encoder.start(); step(); window.requestAnimationFrame(step); }); 当上传的视频最终播放完整时长时,应在 GIFEncoder 调用方法 finish() 的地方发出结束事件: inputVideo.addEventListener('ended', () => { encoder.finish(); }); 步骤 (4):通过 GIFEncoder 创建 GIF 要从编码器中提取所有帧的合并版本(即 GIF 输出),需要实现以下 JavaScript 代码片段: var fileType='image/gif'; var readableStream=encoder.stream(); var binary_gif=readableStream.getData(); var b64Str='data:'+fileType+';,'+encode64(binary_gif); encode64() 是 b64.js 中的一种方法,用于将 GIFEncoder 捕获的流数据转换为 格式。 b64Str 通过合并 GIFEncoder 中存在的所有帧来引用为 GIF 文件编码的数据。因此,在 HTML 代码中,继续包含:<img id='outputGif' src='${b64Str}' alt='${fileName}' /> 以预览输出 GIF 文件。 最后创建一个GIF文件的下载链接,如下: let dwnlnk = document.('a'); dwnlnk.download = fileName; dwnlnk.innerHTML = ` <small>Save</small>`; dwnlnk.className = 'btn btn-outline-dark'; dwnlnk.href = b64Str; 总结 虽然,有进一步探索 GIFEncoder.js 中输出 GIF 文件的可定制调整的空间,但本文介绍的是一种基本的轻量级方法,该方法可以完全在浏览器中和纯客户端将视频剪辑转换为 GIF 文件。 以上就是广州蓝景实训部跟大家分享的客户端 JavaScript 将视频剪辑转换为 GIF 文件,更多前端技术知识可以关注广州蓝景。
学前端去广州蓝景怎么样?
广州蓝景技术分享 — 基于Docker的Vue前端自动化部署 Hello~~各位小伙伴,今天我们广州蓝景,继续和大家分享前端技术干货,基于Docker的Vue前端自动化部署。 前言 Docker的诞生 毫无疑问,DocKer成了近些年来最火热,甚至最具颠覆性的技术之一。首先,我们在传统项目简单描述一下,项目发布的基本流程。 本地开发+测试,没有问题的话,编译打包发布到测试环境 在测试环境中进行测试,测试完成后,发布到生产环境 在生产环境中进行最后的测试,如果没有问题,那么一切就OK了 如果你是从事了一年以上开发人员,我想100%的人都亲身经历过这样的事情 —— 在自己本地测试都没有问题,发布到测试环境、生产环境后,就出现问题了!搞得自己非常苦恼!非常纠结!到底是哪里出了问题呢?明明代码什么的都一样啊~ 那么在这种情况下,其实是有N种可能,但我这这里提出一个比较常见的原因,就是: 本地开发环境与测试环境、生产环境上的软件环境配置,可能出现不一致的情况,导致有些时候相同的代码在不同的环境下运行会出现问题。 存在问题:不同机器上的软件环境不一致。(比较核心的问题) 再列举几个实际开发中遇到的情况: 公司在阿里云买了一台新服务器,要想能正常发布项目等,前提是需要在服务器上重新安装一些软件环境(比如node、mysql、nginx等),在安装软件环境的过程中,很大几率会出现配置错误的情况;一些比较复杂的环境配置步骤会很多,很多人都记不清具体的步骤和命令,还得上网搜索...... 存在问题:软件环境的配置繁多、命令记不清楚。 像jdk、tomcat等基础的环境搭建,都已经很熟练了,每次有新机器的时候,都要重新搭建,这样就造成了重复性工作、效率低下、配置繁琐麻烦、易出错等情况。 存在问题:重复性搭建软件环境、效率低下。 当然也会有其他的问题,这里就不多做说明了。 因此,我们在开发完毕之后,如果我们要部署10个项目,那就需要找10部虚拟机,然后再给对应的10部虚拟机部署对应的10个环境,重复性的工作量就算了,但部署的过程中肯定会衍生出很多的问题,上述提到的问题只是其中的一部分,这样会导致项目的落地造成了极大的问题,效率上也及其的低效。因此Docker技术就帮我们解决了我上述的痛点。可以说是程序员的福音。接下来,我们简单了了解下docker的基本概念,了解下它是如何解决上述的问题和痛点的。 Docker本质上是一个采用虚拟化技术的容器,基于Linux容器进行再封装,使用户不用关心容器的管理,而简化应用操作。 传统的虚拟化是基于硬件实现的,如果要部署10个应用,则需要创建10个虚拟机,而Docker是基于操作系统做的虚拟化,也就是复用本地主机的操作系统,部署运营10个应用时只需要起10个隔离的应用即可。 我们之前需要在每台服务器去配置对应的环境,但docker的出现,我们只需要去docker的公开社区,通过docker命令去下载镜像环境即可。 通过启动容器命令就可以在你的服务器中使用了对应的环境,这就可以让开发者在不了解对应环境的安装命令的同时也可以使用环境。并且占用服务器的资源也会更少。 开发者只需在项目结构中添加对应的Dokcerfile文件,即可在服务器中实现代码和环境的容器,这样项目的环境会根据当前的Dockerfile配置对应的环境,解决了因环境的不同导致项目无法正常运行的问题。 当你熟练的使用了docker命令去部署一个项目之后,现在问题的核心在于,如果我们每新建一个项目就要去服务器中执行同样的命令去启动容器,启动项目。这样的操作算是一种重复的操作,例如10个项目,开发者需要在服务器中走10此同样的命令,这样的操作是十分致命的。 因此,接下来要做的,就是“让重复的事情自动化”!!! DevOps 就可以让“让重复的事情自动化”,它可以实现项目管理、自动化部署、自动化发布、自动化测试、容器云来实现持续集成、持续交付及持续部署。 Docker与Devops自动化部署部署实践 DevOps(Development和Operations的组合词)是一组过程、方法与系统的统称。 它实现持续集成、持续交付及持续部署。 持续集成: 在业界,我们通常将它称之为 CI ,这是一种开发、部署的实践。开发人员每天都将自己的更改推送到主分之中进行集成,通常情况下,这样的操作每天都会发生很多次。从更高的视角来看,CI 能使开发者更快的发现模块或功能中的错误。无论提交的大小,CI 能让整个团队看到每一个提交对整个项目的影响。当流程中出现问题的时候,通过 CI,可以准确地知道问题是由哪个提交造成的,也有可能从错误信息中看到是因为哪一行代码引起的。 持续交付、持续部署: 持续交互在业界被简称为 CD ,是指在自动完成所有事情过后,将通过的代码进行直接部署。作为整个流程的最后一个步骤,设置监控报警,来确保你所部署的服务正在顺利的运行。当出现问题是要第一时间通知开发者。了解了Devops之后,我们来看看具体的实现流程 Devops的具体流程 开发人员在本地完成代码开发后,提交到本地分支,利用docker模拟生产环境进行测试,测试通过后合并到远端的主分支; 2.master 主分支一旦更新后,触发持续集成软件进行打包集成(常见的集成工具: gitlab-vi,travis,或Jenkins)自动完成构建docker 镜像并push 推到远程仓库(docker cloud 或者企业自己的docker registry) 3.利用docker cloud等持续部署到web服务器。 4.配置发布服务器从仓库拉取镜像,run起来后,停止旧的版本。完成了一次自动集成部署。接下来,通过node制作了一个npm包,结合工蜂+腾讯云容器服务CODING实现一个自动化部署。让大家可以不懂任何底层原理的情况上,只需简单通过命令行操作,就可以实现项目自动化部署,体验技术的魅力。 流程图如下简单的描述流程 step1 通过命令bluej initVue 项目名称 部署域名 运行命令后,会从git仓库中获取VUE模板,此时项目的根目录文件中会多出三个文件,分别是Dockerfile.Jenkinsfile,nginx.conf三个文件,是为了后面的自动化部署作准备用的。 step2 bluej run 项目名 项目进入开发模式,即我们平时的开发模式,在这里该干什么就干什么了 step3 通过git命令将新建的项目这些都是git的基本操作了,就不过多的论述, 当联系远程仓库成功之后,进入step4 Step4 使用腾讯云的容器服务CODING,实现自动化部署配置 在实现部署之前,需要手动的创建一个制品库,制品库中存放着项目的镜像,并将权限设置为团队内部人员均可使用。在团队项目中新建一个构建计划。选择自定义构建过程。选择关联的工蜂项目并设置流水线的来源为项目本身自带的流水线设置后,点击确定和立即构建。接下来,就会按照自研流水线一步一步的执行自动化的部署。等待部署完成之后,即可访问之前输入域名地址进行访问。 同时,我们可以通过终端远程访问到服务器中容器运行情况,查看当前项目的容器是否正在运行,通过docker ps命令查看正在运行容器,而其中的bluej_test即是刚刚经过自动化部署完毕的项目。总结 1.通过自动化部署,规范了团队内部的开发流程 ,提升开发效率。 2.通过自动化部署,提升了服务器性能,对服务器资源利用的最大化,追求更高的利用率。 3.通过自动化部署,实时监控到每个项目的运行情况,及时的提醒开发人员做出相应的处理,避免项目中途出现死掉的状况。 4.目前产生前端工程 Docker 化的趋势,学习并且掌握这个技术有利于提高自身竞争力。 5.前端 Docker化 有利于前端开发工程师更专注于开发本身,弱化软件环境,降低项目部署难度。
广州蓝景分享—学习前端CSS常用代码大全(HTML+CSS) HTML+CSS 可以很方便的进行网页的排版布局,还能减少很多不必要的代码。 一、文本设置 font-size: 字号参数 font-style: 字体格式 font-weight: 字体粗细 颜色属性 color: 参数 注意使用网页安全色 二、超链接设置 text-decoration: 参数 主要用途是改变浏览器显示文字链接时的下划线。 参数取值范围: underline:为文字加下划线 overline:为文字加上划线 line-through:为文字加删除线 blink:使文字闪烁 none:不显示上述任何效果 三、背景 1、背景颜色 background-color: 参数 2、背景图片 background-image: url(URL) URL就是背景图片的存放路径,none表示无。 3、背景图片重复 background-repeat: 参数 参数取值范围 : no-repeat:不重复平铺背景图片 repeat-x:使图片只在水平方向上平铺 repeat-y:使图片只在垂直方向上平铺 如果不指定背景图片重复属性,浏览器默认的是背景图片向水平、垂直两个方向上平铺。 4、背景图片固定 背景图片固定控制背景图片是否随网页的滚动而滚动。如果不设置背景图片固定属性,浏览器默认背景图片随网页的滚动而滚动。为了避免过于花哨的背景图片在滚动时转移浏览者的注意力,一般都设为固定 background-attachment: 参数 参数取值范围: fixed:网页滚动时,背景图片相对于浏览器的窗口而言,固定不动 scroll:网页滚动时,背景图片相对于浏览器的窗口而言,一起滚动 四、区块 1、单词间距 word-spacing: 间隔距离 2、字母间距 letter-spacing: 字母间距 3、文本对齐 text-align: 参数 参数的取值: left:左对齐 right:右对齐 center:居中对齐 justify:相对左右对齐 4、垂直对齐 vertical-align: 参数 top:顶对齐 bottom:底对齐 text-top:相对文本顶对齐 text-bottom:相对文本底对齐 baseline:基准线对齐 middle:中心对齐 sub:以下标的形式显示 super:以上标的形式显示 5、文本缩进 text-indent: 缩进距离 12px相当于一个文字距离 6、空格 white-space: 参数 normal 正常 pre 保留 nowrap 不换行 7、显示样式 display: 参数 参数取值范围: block:块级元素,在对象前后都换行 inline:在对象前后都不换行 list-item:在对象前后都换行,增加了项目符号 none:无显示 五、方框 height 高度 width 宽度 padding 内边距 margin 外边距 float(浮动):可以让块级元素在一行中排列,例如横向菜单。 clear 清除浮动 六、边框 1、样式 border style 参数 边框样式的参数: none:无边框 dotted:边框为点线 dashed:边框为长短线 solid:边框为实线 double:边框为双线 2、宽度 border width 参数 3、颜色 border color 参数 七、列表 list-style-type 列表样式 不同浏览器的列表符可能不相同,可能会影响到网页,所以网页中的列表大多都是由背景图片显示。 控制用户界面的样式 八、鼠标 cursor:鼠标形状参数 CSS鼠标形状参数表: 鼠标形状:CSS代码 style="cursor:hand" 手形 style="cursor:crosshair" 十字形 style="cursor:text" 文本形 style="cursor:wait" 沙漏形 style="cursor:move" 十字箭头形: style="cursor:help" 问号形 style="cursor:e-resize" 右箭头形 style="cursor:n-resize" 上箭头形 style="cursor:nw-resize" 左上箭头形 style="cursor:w-resize" 左箭头形 style="cursor:s-resize" 下箭头形 style="cursor:se-resize" 右下箭头形 style="cursor:sw-resize" 左下箭头形 希望能帮助到正在学习前端技术的初学者。
蓝景小课堂 前端开发实用小技巧 1(上传按钮的样式穿透)
前端开发—学习技巧与方法(线下学习篇)3
前端开发—学习技巧与方法(线下学习篇)2
前端开发—学习技巧与方法(线下学习篇)1
零基础转行前端应该如何做 一
为什么其他程序员能做到月薪过万甚至更多,而你却不行?建议收藏
广州蓝景实训部 广州蓝景实训部--“好的经验得到传播” 1、商业开发项目+实战经验,从小白到专业 2、公司内训体系,小班制,每个人取长补短 3、个性化体验,做个有灵魂的工程师 4、定制化学习体系,一切为了就业 咨询前端技术学习:(电话)15013001692 (微信)philip_tan。地址:广州珠江新城CBD
程序员在面试前需要知道的几个小技巧,你了解的有多少呢? 建议收藏
广州蓝景实训部分享---零基础转行前端篇3
广州蓝景分享---零基础转行前端篇2
广州蓝景分享---零基础转行前端篇1
前端程序员就是API调用工程师吗?
前端学习 有广州蓝景(所有学员都是星辰大海)
[蓝景与你 “码”出未来] — 有灵魂 有态度 蓝景圣诞派对
圣诞活动即将开始了哦 热烈欢迎大家参与
蓝景与你 码出未来 圣诞派对 "圣"在有你 你知道吗?我们广州蓝景,有个传统活动,就是一年一度的圣诞节交换圣诞礼物 今年我们活动全面升级,在广州著名的live音乐酒吧191 speace举行, 美食、音乐、魔术、游戏,红包大奖送不停,无论你是老学员,还是新学员、甲方客户、我们都热烈的欢迎您的到来。 12月25日,下午2点,我们全体蓝景人约定你的到来!!
广州蓝景为各大高校赋能,开展《1+ X证书》Web前端辅导班 广州蓝景 &“1+X web前端开发” 早在2019年,1+X证书实施的第一年,广州蓝景获得广东水利电力职业学院诚意的邀请,为该校学生以实训周的形式,开展“1+X Web前端开发”考证冲刺班,针对众多学员的问题进行逐一解答和辅导。在开展“1+X web前端开发”辅导课程期间,获得学生们与校方领导的一致认可,同时广州蓝景对1+X web前端开发的课程,收获了丰富的教学经验。在整个实训阶段,学员的整体能力大幅度提升 广州蓝景 X 广东南华工商职业学院校企联手 共同育人,提升考证通过率 2020年疫情复学复工后,广州蓝景联合广东南华工商职业学院,开展“1+X web前端开发”中级课程,合计100多名学员积极报名,为了照顾大部分已经离校实习的学生,本次考证辅导课程,并以在线直播的形式开展课程。在线课程,也阻挡不了学生们的热情 各位同学 是否有听过“1+X”证书呢? 2019年,教育部、国家发展改革委、财政部、市场监管总局联合印发《关于在院校实施“学历证书+若干职业技能等级证书”制度试点方案》,部署启动“学历证书+若干职业技能等级证书”制度试点工作。 把学历证书与职业技能等级证书结合起来,探索实施1+X证书制度,是“职教20条”的重要改革部署,也是重大创新。“1”为学历证书,“X”为若干职业技能等级证书。职业技能等级证书是毕业生、社会成员职业技能水平的凭证,反映职业活动和个人职业生涯发展所需要的综合能力。 “1+X”Web前端人才标准 Web前端开发职业技能分为初、中、高三个等级 高级证书持有者具有复杂网页开发能力和架构规划能力; 中级证书持有者具有动态网页设计开发能力; 初级证书持有者具有静态网页开发能力。 广州蓝景打通技术开发&前端实训闭环帮助技术新手到专业人才的升级转型 作为专业的IT技术开发公司,同时拥有专业的前端实训课程,将实战中的开发经验,转化为优质的前端实训课程,打通技术开发&前端实训闭环,为广大的前端新手,提供专业的实训课程和就业指导。 本次通过高校实训课的合作进一步加深校企合作的信赖,增强了学生与企业之间的交流与了解,增加了同学们的实操经验、实现学校教育和企业需要“零距离”。也让同学们更加坚定努力学习的决心,坚定远大志向,为未来起航奠定了坚实的基础。前端实训就业,欢迎来广州蓝景实训部。 http://tieba.baidu.com/mo/q/checkurl?url=http%3A%2F%2Fwww.bluej.cn%2Fnewbluejsite%2Fhtml%2Findex.html&urlrefer=126bcd200c228796abb04a6fad1c7c3e 扫描下方二维码 关注我们 期待每周与你相遇 畅游代码世界 http://tieba.baidu.com/mo/q/checkurl?url=https%3A%2F%2Fu.wechat.com%2FECfsWT8qNcaKhCwPdhajj9E&urlrefer=78ebf7679c34ddfbd665284e2635ddc5
广州蓝景&广东食品药品职业学院(前端技术讲座)
广州蓝景&广州华商职业学院 (前端技术讲座)
闭包原理以及使用场景 前端面试,必问闭包 闭包有多重要?如果你是初入前端的朋友,我没有办法直观的告诉你闭包在实际开发中的无处不在,但是我可以告诉你,前端面试,必问闭包。面试官们常常用对闭包的了解程度来判定面试者的基础水平,保守估计,10个前端面试者,至少5个都死在闭包上。 讲解闭包时,我们先来看个例子 var n = 999; function f1() {console.log(n); } f1(); // 999 上面代码中,函数f1可以读取全局变量n。但是下面例子,函数外部无法读取函数内部声明的变量。 function f1() { var n = 999; } console.log(n) // Uncaught ReferenceError: n is not defined 上面代码中,函数f1内部声明的变量n,函数外是无法读取的. 如果有时需要得到函数内的局部变量。正常情况下,这是办不到的,只有通过变通方法才能实现。那就是在函数的内部,再定义一个函数。 function f1() { var n = 999; function f2() { console.log(n); // 999 } } 上面代码中,函数f2就在函数f1内部,这时f1内部的所有局部变量,对f2都是可见的。既然f2可以读取f1的局部变量,那么只要把f2作为返回值,我们不就可以在f1外部读取它的内部变量了吗! 闭包是什么闭包是一种特殊的对象。它由两部分组成。执行上下文(代号A),以及在该执行上下文中创建的函数(代号B)。当B执行时,如果访问了A中变量对象中的值,那么闭包就会产生。在大多数理解中,包括许多著名的书籍,文章里都以函数B的名字代指这里生成的闭包。而在chrome中,则以执行上下文A的函数名代指闭包。 我们可以对上面代码进行如下修改: function f1(){ var a = 999; function f2(){ console.log(a); } return f2; // f1返回了f2的引用 } var result = f1(); // result就是f2函数了 result(); // 执行result,全局作用域下没有a的定义, //但是函数闭包,能够把定义函数的时候的作用域一起记住,输出999 上面的例子,首先有执行上下文f1,在f1中定义了函数f2,而通过对外返回f2的方式让f2得以执行。当f2执行时,访问了f1内部的变量a。因此这个时候闭包产生 闭包就是函数f1,在上面的图中,红色箭头所指的正是闭包。其中Call Stack为当前的函数调用栈,Scope为当前正在被执行的函数的作用域链,Local为当前的局部变量。 JavaScript拥有自动的垃圾回收机制,关于垃圾回收机制,有一个重要的行为,那就是,当一个值,在内存中失去引用时,垃圾回收机制会根据特殊的算法找到它,并将其回收,释放内存。 而我们知道,函数的执行上下文,在执行完毕之后,生命周期结束,那么该函数的执行上下文就会失去引用。其占用的内存空间很快就会被垃圾回收器释放。可是闭包的存在,会阻止这一过程。 我们再来看个例子: var fn = null; function foo() { var a = 2; function innnerFoo() { console.log(a); } fn = innnerFoo; // 将 innnerFoo的引用,赋值给全局变量中的fn } function bar() { fn(); // 此处的保留的innerFoo的引用 } foo(); bar(); // 2 在上面的例子中,foo()执行完毕之后,按照常理,其执行环境生命周期会结束,所占内存被垃圾收集器释放。但是通过fn = innerFoo,函数innerFoo的引用被保留了下来,复制给了全局变量fn。这个行为,导致了foo的变量对象,也被保留了下来。于是,函数fn在函数bar内部执行时,依然可以访问这个被保留下来的变量对象。所以此刻仍然能够访问到变量a的值。 这样,我们就可以称foo为闭包。 所以,通过闭包,我们可以在其他的执行上下文中,访问到函数的内部变量。比如在上面的例子中,我们在函数bar的执行环境中访问到了函数foo的a变量。个人认为,从应用层面,这是闭包最重要的特性。利用这个特性,我们可以实现很多有意思的东西。 不过读者朋友们需要注意的是,虽然例子中的闭包被保存在了全局变量中,但是闭包的作用域链并不会发生任何改变。在闭包中,能访问到的变量,仍然是作用域链上能够查询到的变量。 对上面的例子稍作修改,如果我们在函数bar中声明一个变量c,并在闭包fn中试图访问该变量,运行结果会抛出错误。 var fn = null;function foo() { var a = 2; function innnerFoo() { console.log(c); // 在这里,试图访问函数bar中的c变量,会抛出错误 console.log(a); } fn = innnerFoo; // 将 innnerFoo的引用,赋值给全局变量中的fn}function bar() { var c = 100; fn(); // 此处的保留的innerFoo的引用}foo();bar();闭包形成的条件 函数嵌套 内部函数引用外部函数的局部变量 闭包的应用场景 初级前端了解以上闭包面试即可,以下内容属于中高级前端理解范围。大家可以暂时跳过,等学有一定基础积累,再回头看看。 除了面试,在实践中,闭包有两个非常重要的应用场景。分别是模块化与柯里化。 柯里化模块化 在我看来,模块是闭包最强大的一个应用场景。如果你是初学者,对于模块的了解可以暂时不用放在心上,因为理解模块需要更多的基础知识。但是如果你已经有了很多JavaScript的使用经验,在彻底了解了闭包之后,不妨借助本文介绍的作用域链与闭包的思路,重新理一理关于模块的知识。这对于我们理解各种各样的设计模式具有莫大的帮助。 (function () { var a = 10; var b = 20; function add(num1, num2) { var num1 = !!num1 ? num1 : a; var num2 = !!num2 ? num2 : b; return num1 + num2; } window.add = add;})();add(10, 20); 在上面的例子中,我使用函数自执行的方式,创建了一个模块。add是模块对外暴露的一个公共方法。而变量a,b被作为私有变量。为了验证自己有没有搞懂作用域链与闭包,这里留下一个经典的思考题,常常也会在面试中被问到。 利用闭包,修改下面的代码,让循环输出的结果依次为1, 2, 3, 4, 5 for (var i=1; i<=5; i++) { setTimeout( function timer() { console.log(i); }, i*1000 );}
广州蓝景&广东工贸职业学院 前端技术分享会
this的学习 解析器在调用函数时,每次都会向函数内部传递一个隐含的参数this, 这个隐含参数就是this, this指向的是一个对象, 这个对象我们称为函数执行的上下文对象 根据函数的调用方式的不同,this会指向不同的对象 1.以函数的形式调用时,this指向的是window 2.以方法的形式调用时,this就是调用方法的那个对象 3.当以构造函数的形式调用时,this就是新创建的那个对象 4.使用call()和apply()调用时,this就是call()和apply()第一个参数的对象 5.使用bind()()调用时,this就是bind()第一个参数的对象 *this 永远指向最后调用它的那个对象** functionfn(){ console.log(this);//window}fn(); 上面例子直接执行函数,这时的this指向window 下面再来个例子: function fn(){ console.log(this);}let obj = { name: '阿离王', fn: fn,};obj.fn(); 这时上面的例子打印的this 指向 obj对象了 let name = '全局name';function fn(){ console.log(this.name);}let obj = { name: '阿离王', fn:fn};let obj2 = { name: '张三', fn}fn(); // 全局nameobj.fn(); // 阿离王obj2.fn(); // 张三构造函数 使用new关键词调用的函数,是构造函数(constructor) 构造函数是专门用来创建对象的函数 构造函数就是一个普通的函数, 不同的是,构造函数习惯上函数名首字母大写(不成文规范) 构造函数和普通函数的区别就是调用方式的不同 普通函数是直接调用,而构造函数需要使用new关键字来调用 构造函数的执行流程: 1.立刻创建一个新的对象(在内存开辟一个新空间) 2.将新建的对象设置为函数中的this的指向新建的对象,在构造函数中可以使用this来引用新建的对象 3.逐行执行函数中的代码 4.将新建的对象作为返回值返回 使用同一个构造函数创建的对象,我们称为一类对象,也将一个构造函数称为一个类 我们将通过一个构造函数创建的对象,称为是该类的实例, new 一个构造函数,我们称为实例化一个对象出来 function Person(name, age){ this.name = name; this.age = age; this.sayName = function(){ console.log(this.name); }}let per = new Person('阿离王', 28);let per1 = new Person('张三', 20);let per2 = new Person('李四', 19);console.log(per);console.log(per1);console.log(per2.sayName());function Dog(){}let dog = new Dog();console.log(dog);call() 和 apply() 这两个方法都是函数对象的方法,需要通过函数来调用 当对函数调用call()和apply()都也会调用原函数 在调用call()和apply()时,第一个参数为一个对象 此时这个对象将会成为函数执行时的this function fn(a, b){ console.log(a, b); console.log(this);}let obj = {name: 'yu'};fn.call(obj, 1, 2);fn.apply(obj, [3, 4]);let obj2 = { name: '阿离王', sayName: function(){ console.log(this.name); }}let obj3 = {name: 'obj3'}obj2.sayName.call(obj3);bind() bind() 是创建一个新的函数,我们必须要手动去调用: 所以得写成bind()() function fn(a, b){ console.log(a, b); console.log(this);}let obj = {name: 'yu'};fn.call(obj, 1, 2);fn.apply(obj, [3, 4]);fn.bind(obj, 5, 6)(); 点击参考 this call() apply() bind()的详细介绍
我发表了一篇视频贴,大伙来看看吧~
浏览器渲染原理流程 前言 浏览器的内核是指支持浏览器运行的最核心的程序,分为两个部分的,一是渲染引擎,另一个是JS引擎。渲染引擎在不同的浏览器中也不是都相同的。目前市面上常见的浏览器内核可以分为这四种:Trident(IE)、Gecko(火狐)、Blink(Chrome、Opera)、Webkit(Safari)。这里面大家最耳熟能详的可能就是 Webkit 内核了,Webkit 内核是当下浏览器世界真正的霸主。 本文我们就以 Webkit 为例,对现代浏览器的渲染过程进行一个深度的剖析。 页面加载过程 在介绍浏览器渲染过程之前,我们简明扼要介绍下页面的加载过程,有助于更好理解后续渲染过程。 要点如下: 向浏览器输入网址 浏览器根据 DNS 服务器得到域名的 IP 地址 向这个 IP 的机器发送 HTTP 请求 服务器收到、处理并返回 HTTP 请求 浏览器接收到服务器返回的内容 例如在浏览器输入http://tieba.baidu.com/mo/q/checkurl?url=https%3A%2F%2Fwww.baidu.com&urlrefer=362457f588347b3b11bb6662a71492f5,然后经过 DNS 解析,http://tieba.baidu.com/mo/q/checkurl?url=http%3A%2F%2Fwww.baidu.com&urlrefer=d04a537000146ae20785a6d0d3533551对应的 IP 是14.215.177.38(不同时间、地点对应的 IP 可能会不同)。然后浏览器向该 IP 发送 HTTP 请求。 服务端接收到 HTTP 请求,然后经过计算(向不同的用户推送不同的内容),返回 HTTP 请求,返回的内容如下: 其实就是一堆 HMTL 格式的字符串,因为只有 HTML 格式浏览器才能正确解析,这是 W3C 标准的要求。接下来就是浏览器的渲染过程。 浏览器渲染过程从上面这个图上,我们可以看到,浏览器渲染过程如下: 解析HTML,生成DOM树,解析CSS,生成CSSOM树 将DOM树和CSSOM树结合,生成渲染树(Render Tree) Layout(回流):根据生成的渲染树,进行回流(Layout),得到节点的几何信息(位置,大小) Painting(重绘):根据渲染树以及回流得到的几何信息,得到节点的绝对像素 Display: 将像素发送给GPU,最后通过调用操作系统Native GUI的API绘制,展示在页面上。(这一步其实还有很多内容,比如会在GPU将多个合成层合并为同一个层,并展示在页面中。而css3硬件加速的原理则是新建合成层,这里我们不展开,之后有机会再写一篇博客来介绍) 渲染过程看起来也不复杂,让我们来具体了解下每一步具体做了什么。 构建DOM详细流程 浏览器会遵守一套步骤将HTML 文件转换为 DOM 树。宏观上,可以分为几个步骤:浏览器从磁盘或网络读取HTML的原始字节(字节数据),并根据文件的指定编码(例如 UTF-8)将它们转换成字符串。 在网络中传输的内容其实都是 0 和 1 这些字节数据。当浏览器接收到这些字节数据以后,它会将这些字节数据转换为字符串,也就是我们写的代码。 将字符串转换成Token,例如:、等。Token中会标识出当前Token是“开始标签”或是“结束标签”或着是“文本”等信息。 这时候你一定会有疑问,节点与节点之间的关系如何维护? 事实上,这就是Token要标识“起始标签”和“结束标签”等标识的作用。 例如“title”Token的起始标签和结束标签之间的节点肯定是属于“head”的子节点。上图给出了节点之间的关系,例如:“Hello”Token位于“title”开始标签与“title”结束标签之间,表明“Hello”Token是“title”Token的子节点。同理“title”Token是“head”Token的子节点。 生成节点对象并构建DOM 事实上,构建DOM的过程中,不是等所有Token都转换完成后再去生成节点对象,而是一边生成Token一边消耗Token来生成节点对象。换句话说,每个Token被生成后,会立刻消耗这个Token创建出节点对象。注意:带有结束标签标识的Token不会创建节点对象。 接下来我们举个例子,假设有段HTML文本: <html><head> <title>Web page parsing</title></head><body> <div> <h1>Web page parsing</h1> <p>This is an example Web page.</p> </div></body></html> 上面这段HTML会解析成这样:构建CSSOM详细流程 DOM会捕获页面的内容,但浏览器还需要知道页面如何展示,所以需要构建CSSOM。 构建CSSOM的过程与构建DOM的过程非常相似,当浏览器接收到一段CSS,浏览器首先要做的是识别出Token,然后构建节点并生成CSSOM。在这一过程中,浏览器会确定下每一个节点的样式到底是什么,并且这一过程其实是很消耗资源的。因为样式你可以自行设置给某个节点,也可以通过继承获得。在这一过程中,浏览器得递归 CSSOM 树,然后确定具体的元素到底是什么样式。 注意:CSS匹配HTML元素是一个相当复杂和有性能问题的事情。所以,DOM树要小,CSS尽量用id和class,千万不要过渡层叠下去。 构建渲染树 当我们生成 DOM 树和 CSSOM 树以后,就需要将这两棵树组合为渲染树。在这一过程中,不是简单的将两者合并就行了。渲染树只会包括需要显示的节点和这些节点的样式信息,如果某个节点是 display: none 的,那么就不会在渲染树中显示。 注意:渲染树只包含可见的节点 我们或许有个疑惑:浏览器如果渲染过程中遇到JS文件怎么处理? 渲染过程中,如果遇到<script>就停止渲染,执行 JS 代码。因为浏览器有GUI渲染线程与JS引擎线程,为了防止渲染出现不可预期的结果,这两个线程是互斥的关系。JavaScript的加载、解析与执行会阻塞DOM的构建,也就是说,在构建DOM时,HTML解析器若遇到了JavaScript,那么它会暂停构建DOM,将控制权移交给JavaScript引擎,等JavaScript引擎运行完毕,浏览器再从中断的地方恢复DOM构建。 也就是说,如果你想首屏渲染的越快,就越不应该在首屏就加载 JS 文件,这也是都建议将 script 标签放在 body 标签底部的原因。当然在当下,并不是说 script 标签必须放在底部,因为你可以给 script 标签添加 defer(延迟) 或者 async(异步) 属性(下文会介绍这两者的区别)。 JS文件不只是阻塞DOM的构建,它会导致CSSOM也阻塞DOM的构建。 原本DOM和CSSOM的构建是互不影响,井水不犯河水,但是一旦引入了JavaScript,CSSOM也开始阻塞DOM的构建,只有CSSOM构建完毕后,DOM再恢复DOM构建。 这是什么情况呢? 这是因为JavaScript不只是可以改DOM,它还可以更改样式,也就是它可以更改CSSOM。因为不完整的CSSOM是无法使用的,如果JavaScript想访问CSSOM并更改它,那么在执行JavaScript时,必须要能拿到完整的CSSOM。所以就导致了一个现象,如果浏览器尚未完成CSSOM的下载和构建,而我们却想在此时运行脚本,那么浏览器将延迟脚本执行和DOM构建,直至其完成CSSOM的下载和构建。也就是说,在这种情况下,浏览器会先下载和构建CSSOM,然后再执行JavaScript,最后在继续构建DOM。布局与绘制 当浏览器生成渲染树以后,就会根据渲染树来进行布局(也可以叫做回流)。这一阶段浏览器要做的事情是要弄清楚各个节点在页面中的确切位置和大小。通常这一行为也被称为“自动重排”。 布局流程的输出是一个“盒模型”,它会精确地捕获每个元素在视口内的确切位置和尺寸,所有相对测量值都将转换为屏幕上的绝对像素。 布局完成后,浏览器会立即发出“Paint Setup”和“Paint”事件,将渲染树转换成屏幕上的像素。 回流 前面我们通过构造渲染树,我们将可见DOM节点以及它对应的样式结合起来,可是我们还需要计算它们在设备视口(viewport)内的确切位置和大小,这个计算的阶段就是回流。 为了弄清每个对象在网站上的确切大小和位置,浏览器从渲染树的根节点开始遍历,我们可以以下面这个实例来表示: <!DOCTYPE html><html> <head> <meta name="viewport" content="width=device-width,initial-scale=1"> <title>Critial Path: Hello world!</title> </head> <body> <div style="width: 50%"> <div style="width: 50%">Hello world!</div> </div> </body></html> 我们可以看到,第一个div将节点的显示尺寸设置为视口宽度的50%,第二个div将其尺寸设置为父节点的50%。而在回流这个阶段,我们就需要根据视口具体的宽度,将其转为实际的像素值。(如下图)重绘 最终,我们通过构造渲染树和回流阶段,我们知道了哪些节点是可见的,以及可见节点的样式和具体的几何信息(位置、大小),那么我们就可以将渲染树的每个节点都转换为屏幕上的实际像素,这个阶段就叫做重绘节点。 既然知道了浏览器的渲染过程后,我们就来探讨下,何时会发生回流重绘。 何时发生回流重绘 我们前面知道了,回流这一阶段主要是计算节点的位置和几何信息,那么当页面布局和几何信息发生变化的时候,就需要回流。 比如以下情况发生回流: 根据改变的范围和程度,渲染树中或大或小的部分需要重新计算,有些改变会触发整个页面的重排,比如,滚动条出现的时候或者修改了根节点。 页面一开始渲染的时候(这肯定避免不了) 浏览器的窗口尺寸变化(因为回流是根据视口的大小来计算元素的位置和大小的) 添加或删除可见的DOM元素 元素的位置发生变化 元素的尺寸发生变化(包括外边距、内边框、边框大小、高度和宽度等) 内容发生变化,比如文本变化或图片被另一个不同尺寸的图片所替代。 元素字体大小变化 激活CSS伪类(例如::hover) 一些常用且会导致回流的属性和方法: clientWidth、clientHeight、clientTop、clientLeftoffsetWidth、offsetHeight、offsetTop、offsetLeftscrollWidth、scrollHeight、scrollTop、scrollLeftscrollIntoView()、scrollIntoViewIfNeeded()getComputedStyle()getBoundingClientRect()scrollTo() 以下情况发生重绘而不回流 当页面中元素样式的改变并不影响它在文档流中的位置时(例如:color、background-color、visibility等),浏览器会将新样式赋予给元素并重新绘制它,这个过程重绘而不回流。 注意:回流一定会触发重绘,而重绘不一定会回流 性能影响 回流比重绘的代价要更高。 有时即使仅仅回流一个单一的元素,它的父元素以及任何跟随它的元素也会产生回流。 浏览器优化机制 现代浏览器会对频繁的回流或重绘操作进行优化: 浏览器会维护一个队列,把所有引起回流和重绘的操作放入队列中,如果队列中的任务数量或者时间间隔达到一个阈值的,浏览器就会将队列清空,进行一次批处理,这样可以把多次回流和重绘变成一次。 当你访问以下属性或方法时,浏览器会立刻清空队列: clientWidth、clientHeight、clientTop、clientLeftoffsetWidth、offsetHeight、offsetTop、offsetLeftscrollWidth、scrollHeight、scrollTop、scrollLeftwidth、heightgetComputedStyle()getBoundingClientRect() 因为队列中可能会有影响到这些属性或方法返回值的操作,即使你希望获取的信息与队列中操作引发的改变无关,浏览器也会强行清空队列,确保你拿到的值是最精确的。 以上属性和方法都需要返回最新的布局信息,因此浏览器不得不清空队列,触发回流重绘来返回正确的值。因此,我们在修改样式的时候,最好避免使用上面列出的属性,他们都会刷新渲染队列。如果要使用它们,最好将值缓存起来。 减少回流和重绘 使用 transform 替代 top 使用 visibility 替换 display: none ,因为前者只会引起重绘,后者会引发回流(改变了布局) 不要把节点的属性值放在一个循环里当成循环里的变量。 不要使用 table 布局,可能很小的一个小改动会造成整个 table 的重新布局 动画实现的速度的选择,动画速度越快,回流次数越多,也可以选择使用 requestAnimationFrame CSS 选择符从右往左匹配查找,避免节点层级过多 将频繁重绘或者回流的节点设置为图层,图层能够阻止该节点的渲染行为影响别的节点。比如对于 video 标签来说,浏览器会自动将该节点变为图层。 最小化回流和重绘 由于回流和重绘可能代价比较昂贵,因此最好就是可以减少它的发生次数。为了减少发生次数,我们可以合并多次对DOM和样式的修改,然后一次处理掉。考虑这个例子 const el = document.getElementById('test');el.style.padding = '5px';el.style.borderLeft = '1px';el.style.borderRight = '2px'; 例子中,有三个样式属性被修改了,每一个都会影响元素的几何结构,引起回流。当然,大部分现代浏览器都对其做了优化,因此,只会触发一次重排。但是如果在旧版的浏览器或者在上面代码执行的时候,有其他代码访问了布局信息(上文中的会触发回流的布局信息),那么就会导致三次重排。 因此,我们可以合并所有的改变然后依次处理,比如我们可以采取以下的方式: 1.使用cssText const el = document.getElementById('test');el.style.cssText += 'border-left: 1px; border-right: 2px; padding: 5px;'; 2.使用class, 把css样式用个class包住,修改CSS的class.active{ border-left: 1px; border-right: 2px; padding: 5px; } const el = document.getElementById('test');el.className += ' active'; 批量修改DOM 当我们需要对DOM对一系列修改的时候,可以通过以下步骤减少回流重绘次数: 使元素脱离文档流 对其进行多次修改 将元素带回到文档中。 该过程的第一步和第三步可能会引起回流,但是经过第一步之后,对DOM的所有修改都不会引起回流,因为它已经不在渲染树了。 有三种方式可以让DOM脱离文档流: 隐藏元素,应用修改,重新显示 使用文档片段(document fragment)在当前DOM之外构建一个子树,再把它拷贝回文档。 将原始元素拷贝到一个脱离文档的节点中,修改节点后,再替换原始的元素。 下面来个例子演示下 我们要执行一段批量插入节点的代码: function appendDataToElement(appendToElement, data) { let li; for (let i = 0; i < data.length; i++) { li = document.createElement('li'); li.textContent = 'text'; appendToElement.appendChild(li); }}const ul = document.getElementById('list');appendDataToElement(ul, data); 如果我们直接这样执行的话,由于每次循环都会插入一个新的节点,会导致浏览器回流一次。 我们可以使用这三种方式进行优化: 隐藏元素,应用修改,重新显示 第一种方法:隐藏元素,这个会在展示和隐藏节点的时候,产生两次重绘 function appendDataToElement(appendToElement, data) { let li; for (let i = 0; i < data.length; i++) { li = document.createElement('li'); li.textContent = 'text'; appendToElement.appendChild(li); }}const ul = document.getElementById('list');ul.style.display = 'none';appendDataToElement(ul, data);ul.style.display = 'block'; 第二种:使用文档片段(document fragment)在当前DOM之外构建一个子树,再把它拷贝回文档 const ul = document.getElementById('list');const fragment = document.createDocumentFragment();appendDataToElement(fragment, data);ul.appendChild(fragment); 第三种:将原始元素拷贝到一个脱离文档的节点中,修改节点后,再替换原始的元素。 const ul = document.getElementById('list');const clone = ul.cloneNode(true);appendDataToElement(clone, data);ul.parentNode.replaceChild(clone, ul); 避免触发同步布局事件 上文我们说过,当我们访问元素的一些属性的时候,会导致浏览器强制清空队列,进行强制同步布局。举个例子,比如说我们想将一个p标签数组的宽度赋值为一个元素的宽度,我们可能写出这样的代码: function initP() { for (let i = 0; i < paragraphs.length; i++) { paragraphs[i].style.width = box.offsetWidth + 'px'; }} 这段代码看上去是没有什么问题,可是其实会造成很大的性能问题。在每次循环的时候,都读取了box的一个offsetWidth属性值,然后利用它来更新p标签的width属性。这就导致了每一次循环的时候,浏览器都必须先使上一次循环中的样式更新操作生效,才能响应本次循环的样式读取操作。每一次循环都会强制浏览器刷新队列。我们可以优化为: const width = box.offsetWidth;function initP() { for (let i = 0; i < paragraphs.length; i++) { paragraphs[i].style.width = width + 'px'; }} 对于复杂动画效果,使用绝对定位让其脱离文档流 对于复杂动画效果,由于会经常的引起回流重绘,因此,我们可以使用绝对定位,让它脱离文档流。否则会引起父元素以及后续元素频繁的回流。这个我们就直接上个例子。 打开这个例子后,我们可以打开控制台,控制台上会输出当前的帧数(虽然不准)。 从例子中,我们可以看到,帧数一直都没到60。这个时候,只要我们点击一下那个按钮,把这个元素设置为绝对定位,帧数就可以稳定60。 css3硬件加速(GPU加速) 比起考虑如何减少回流重绘,我们更期望的是,根本不要回流重绘。这个时候,css3硬件加速就闪亮登场啦!! 划重点:使用css3硬件加速,可以让transform、opacity、filters这些动画不会引起回流重绘 。但是对于动画的其它属性,比如background-color这些,还是会引起回流重绘的,不过它还是可以提升这些动画的性能。 如何使用css3硬件加速(GPU加速) 常见的触发硬件加速的css3属性: transform opacity filters Will-change css3硬件加速的坑 如果你为太多元素使用css3硬件加速,会导致内存占用较大,会有性能问题。 在GPU渲染字体会导致抗锯齿无效。这是因为GPU和CPU的算法不同。因此如果你不在动画结束的时候关闭硬件加速,会产生字体模糊。 几点补充说明 1.async和defer的作用是什么?有什么区别? 接下来我们对比下 defer 和 async 属性的区别:其中蓝色线代表JavaScript加载;红色线代表JavaScript执行;绿色线代表 HTML 解析。 1)情况1<script src="script.js"></script> 没有 defer 或 async,浏览器会立即加载并执行指定的脚本,也就是说不等待后续载入的文档元素,读到就加载并执行。 2)情况2 <script defer src="script.js"></script>(延迟执行) defer 属性表示延迟执行引入的 JavaScript,即这段 JavaScript 加载时 HTML 并未停止解析,这两个过程是并行的。整个 document 解析完毕且 defer-script 也加载完成之后(这两件事情的顺序无关),会执行所有由 defer-script 加载的 JavaScript 代码,然后触发 DOMContentLoaded 事件。 3)情况3<script async src="script.js"></script> (异步下载) async 属性表示异步执行引入的 JavaScript,与 defer 的区别在于,如果已经加载好,就会开始执行——无论此刻是 HTML 解析阶段还是 DOMContentLoaded 触发之后。需要注意的是,这种方式加载的 JavaScript 依然会阻塞 load 事件。换句话说,async-script 可能在 DOMContentLoaded 触发之前或之后执行,但一定在 load 触发之前执行。 defer 与相比普通 script,有两点区别:载入 JavaScript 文件时不阻塞 HTML 的解析,执行阶段被放到 HTML 标签解析完成之后。 在加载多个JS脚本的时候,async是无顺序的加载,而defer是有顺序的加载。 2.为什么操作 DOM 慢 把 DOM 和 JavaScript 各自想象成一个岛屿,它们之间用收费桥梁连接。——《高性能 JavaScript》 JS 是很快的,在 JS 中修改 DOM 对象也是很快的。在JS的世界里,一切是简单的、迅速的。但 DOM 操作并非 JS 一个人的独舞,而是两个模块之间的协作。 因为 DOM 是属于渲染引擎中的东西,而 JS 又是 JS 引擎中的东西。当我们用 JS 去操作 DOM 时,本质上是 JS 引擎和渲染引擎之间进行了“跨界交流”。这个“跨界交流”的实现并不简单,它依赖了桥接接口作为“桥梁”(如下图)。过“桥”要收费——这个开销本身就是不可忽略的。我们每操作一次 DOM(不管是为了修改还是仅仅为了访问其值),都要过一次“桥”。过“桥”的次数一多,就会产生比较明显的性能问题。因此“减少 DOM 操作”的建议,并非空穴来风。 性能优化策略 基于上面介绍的浏览器渲染原理,DOM 和 CSSOM 结构构建顺序,初始化可以对页面渲染做些优化,提升页面性能。 JS优化: <script> 标签加上 defer属性 和 async属性 用于在不阻塞页面文档解析的前提下,控制脚本的下载和执行。 defer属性: 用于开启新的线程下载脚本文件,并使脚本在文档解析完成后执行。 async属性: HTML5新增属性,用于异步下载脚本文件,下载完毕立即解释执行代码。 CSS优化:<link>标签的 rel属性 中的属性值设置为 preload 能够让你在你的HTML页面中可以指明哪些资源是在页面加载完成后即刻需要的,最优的配置加载顺序,提高渲染性能 总结 综上所述,我们得出这样的结论: 浏览器工作流程:构建DOM -> 构建CSSOM -> 构建渲染树 -> 布局 -> 绘制。 CSSOM会阻塞渲染,只有当CSSOM构建完毕后才会进入下一个阶段构建渲染树。 通常情况下DOM和CSSOM是并行构建的,但是当浏览器遇到一个不带defer或async属性的script标签时,DOM构建将暂停,如果此时又恰巧浏览器尚未完成CSSOM的下载和构建,由于JavaScript可以修改CSSOM,所以需要等CSSOM构建完毕后再执行JS,最后才重新DOM构建。 参考文章: 深入浅出浏览器渲染原理 你真的了解回流和重绘吗
setTimeout 和 setInterval 的定时时间深入研究 setInterval() - 间隔指定的毫秒数不停地执行指定的代码(一直执行)。 setTimeout() - 在指定的毫秒数后执行指定代码(只执行一次)。 使用setInterVal:下来用使用setTimeout模拟setInterval:看下正常情况下两者的区别: 看下正常情况下两者的区别:setInterval每个定时器之间的间隔是100ms,而setTimeout每隔100ms执行一次doStuff,所以每个定时器之间的间隔是100 + T(doStuff执行时间为T);这个T就是本文的关键了。 如果T可以忽略的话,两者的效果是基本相同的。 T <= 100时,setInterval定时器间隔100,setTimeout定时器间隔100+T。 如果T > 100,setTimeout依然如上图,两个定时器之间间隔100+T。 那么setInterval呢? 先看下图:在0ms时,定时器1开始进入宏任务队列;100ms时,定时器1开始执行doStuff1,队列为空,定时器2进入队列;200ms时,因为定时器2(doStuff1还没执行完)在队列中,所以定时器3被跳过。浏览器不会同时创建两个相同的间隔计时器。 300ms时,定时器2已经开始执行,队列为空,定时器4进入队列。以此类推~ 下面我们用代码验证下。T设置为140ms。 我们让定时器运行5次,按照上述理解,总运行时间应该是:100+5*140 = 800ms。 代码如下:可以看出定时器运行了5次,总时间的确为 100 + 140*5 = 800ms。 如果我们设置dead(250), 经过测试总时间为 100 + 250 * 5 = 1350;如果doStuff中的代码是异步的呢?比如像我们常用promise。140ms返回结果。代码如下:可以看出总时间是500ms,请求接口的异步代码并不会阻塞定时器。这个也很好理解,定时器中的同步代码会直接进入队列,异步代码注册事件,完成后进入队列;所以当异步代码注册事件后,这个定时器就执行完了,并不是等异步代码回来后这个定时器才算结束,不然5次定时器的总时间就是800ms了。 #根据以上代码效果总结 setInterval只是在特定时间点将代码推入队列,如果已有定时器在队列中,则会跳过。浏览器不会同时创建两个相同的间隔计时器。 setInterval设置定时时间小于函数体内的执行时间时候,则第一次执行定时时间后面的真正的定时时间应该是执行函数体的总时间。 setInterval中的异步代码不会阻塞创建新的定时器。
广州蓝景实训部-祝程序员们1024节日快乐
广州蓝景实训部-32班开班啦
广州蓝景实训部-31班开班啦
广州蓝景实训部-33班开班啦
1
下一页