关于前端框架:React内部是如何实现cache方法的

大家好,我卡颂。 前几天写的一篇介绍use这个新hook的文章中聊到React原生实现了一个缓存函数的办法 —— cache。 对于如下代码,被cache包裹的函数,当屡次调用时,如果传参不变,会始终返回缓存值: const cacheFn = cache(fn);cacheFn(1, 2, 3);// 不会执行fn,间接返回缓存值cacheFn(1, 2, 3);React内为什么须要cache办法呢?思考如下组件: const fetch = cache(fetchUserData);function User({id}) { const {name} = use(fetch(id)); return <p>{name}</p>;}User组件会依据用户id申请用户数据,并渲染用户名。 如果id扭转,那么fetch办法从新发动申请是失常逻辑。 然而,React组件常常render,如果在id不变的状况下,因为User组件render导致一直发动申请,显然是不合理的。 所以,这种状况下就须要cache办法。当id不变时,即便User组件重复render,fetch(id)都返回同一个值。 本文来聊聊cache的源码实现。 欢送退出人类高质量前端框架群,带飞 剖析实现思路整个办法实现一共有64行代码,首先咱们来剖析下实现要点。 如果参数不变,则应用缓存的值。这意味着咱们须要解决: 参数的程序举个例子,当参数程序变了,不应用缓存值: const cacheFn = cache(fn);cacheFn(1, 2, 3);// 不应用缓存值cacheFn(3, 2, 1);区别解决援用类型、原始类型参数举个例子,当同一地位的参数传递了同一个援用类型值,则返回缓存值: const cacheFn = cache(fn);const obj = {};cacheFn(1, obj, 3);// 返回缓存值cacheFn(1, obj, 3);当同一地位的参数传递了不同援用类型值,则不返回缓存值: const cacheFn = cache(fn);const obj = {};cacheFn(1, obj, 3);// 不返回缓存值cacheFn(1, {}, 3);缓存的垃圾回收缓存数据时,要留神缓存生效然而援用的数据没有开释造成的内存透露问题。 所以,对于援用类型数据,能够应用WeakMap保留。 对于原始类型数据,能够应用Map保留。 ...

October 27, 2022 · 1 min · jiezi

关于前端框架:自主搭建5个精品脚手架玩转前端提效

download:自主搭建5个精品脚手架,玩转前端提效1.写代码的目标是什么?一旦你走得足够远,称本人为程序员在某种程度上是一个限度职业生涯的行动。咱们来考虑一下:你遇到的很多问题,代码不肯定能解决。它们通过概念性的解决方案来解决,而后能够转换成代码。你的根本指标是解决业务问题。 2.解决商业问题的最佳编程语言是什么?假如咱们用汇编语言编码。您对模式进行了硬编码。而后你有C语言,在其中你能够形象修建。而后是抽象层次更高的语言:Python、Ruby、Java……列表越来越高。 形象的最高档次是什么?书面语。是的,咱们最相熟的语言——咱们的自然语言。 3.一个程序员最可贵的品质是什么?你有没有思考过有多少编程语言曾经死亡或者被诊断为行将死亡? Perl,Objective-C,Cobol,Pascal...他们以前都很酷。 你能够当先一种语言,当你切换到一种当先排行榜的新语言时——你又是一个初学者。这是一条永无止境的路,到了某个时候,你会精疲力竭。最终,这将导致你失去对软件开发的所有激情,并在开始最激动人心的局部之前退休。 与其学习总是被放弃、扭转甚至进行的新语法,不如把精力放在学习和设计解决方案以及正确与人沟通上。 我感觉把工夫花在人类心理学这种永恒的常识上会好很多,这样能力长于找到适合的人来施行解决方案。这个难题的所有其余局部都能够通过雇用善于编码的适合的人、为他们提供根本的策略领导、激励他们并让他们自在解决问题来解决。一切都是为了成果! 4.在感触到冒名顶替综合征的第一个症状后,什么才是正确的抉择?我的次要观点是,要成为一个胜利的程序员,你必须最终进行写代码。这听起来可能令人丧气甚至不安——因为咱们都喜爱编程。然而间接和计算机一起工作一段时间后,你会意识到无效的编程通常并不意味着写代码。不写代码,你可能会更胜利。 论断一旦你学会与电脑对话,它将随同你直到生命的止境。你永远不会遗记根本的编程概念,就像骑自行车一样。学习新的语法或框架可能须要一些工夫,但根本准则不会很快扭转。 在你作为程序员的日常生活中,你并不是从零开始写算法。你可能解决你背后的任何理论问题,但你解决不了的是人的问题:就像你的经理或共事一样,你是个彻头彻尾的混蛋。用代码解决不了这个问题吧?然而,这比应用一些愚昧的框架更能减弱你。

October 10, 2022 · 1 min · jiezi

关于前端框架:高级前端进阶必修自主打造高扩展的业务组件库

download:高级前端进阶必修:自主打造高扩大的业务组件库自建数据库可视化平台,在线治理数据库Bytebase简介Bytebase是一款面向开发者的数据库变更管理工具,目前在Github上已有3.6K+Star。它的次要个性如下: SQL审核:具备一站式SQL审核面板,可能直观地看到数据库所有变更记录。SQL倡导:能主动查看SQL语句规范,额定提供GitHub Action和API接入形式。SQL编辑器:可能在线治理及查看数据库表,反对语法的主动提醒。GitOps工作流:反对集成GitHub和GitLab,使用GitOps工作流进行数据库变更。备份复原:反对主动备份数据库及复原数据。 安装 首先咱们将在Linux下安装Bytebase,使用Docker来安装无疑是最便利的。 因为ByteBase对MySQL8的反对比较好,这里推荐安装MySQL8,首先下载MySQL8的Docker镜像; docker pull mysql:8复制代码 再使用如下命令运行MySQL8的容器; docker run -p 3506:3306 --name mysql8 \-v /mydata/mysql8/mysql-files:/var/lib/mysql-files \-v /mydata/mysql8/conf:/etc/mysql \-v /mydata/mysql8/log:/var/log/mysql \-v /mydata/mysql8/data:/var/lib/mysql \-e MYSQL_ROOT_PASSWORD=root \-d mysql:8复制代码 而后使用如下命令下载Bytebase的Docker镜像 docker pull bytebase/bytebase:1.3.1复制代码 下载胜利后,使用如下命令运行ByteBase容器; docker run --init \ --name bytebase \ --restart always \ --add-host host.docker.internal:192.168.3.105 \ --publish 5678:5678 \ --health-cmd "curl --fail http://localhost:5678/healthz || exit 1" \ --health-interval 5m \ --health-timeout 60s \ --volume /mydata/bytebase/data:/var/opt/bytebase \ -d bytebase/bytebase:1.3.1 \ --data /var/opt/bytebase \ --host http://localhost \ --port 5678 ...

September 14, 2022 · 1 min · jiezi

关于前端框架:大前端2022版全面升级完结无密内置文档资料

download:大前端2022版全面降级完结无密内置文档资料FutureTask源码深度剖析在JDK的FutureTask当中会使用到一个工具LockSupport,在正式介绍FutureTask之前咱们先熟悉一下这个工具。LockSupport次要是用于阻塞和唤醒线程的,它次要是通过包装UnSafe类,通过UnSafe类当中的方法进行实现的,他底层的方法是通过依赖JVM实现的。在LockSupport当中次要有以下三个方法: unpark(Thread thread))方法,这个方法可能给线程thread发放一个许可证,你可能通过多次调用这个方法给线程发放许可证,每次调用都会给线程发放一个许可证,然而这个许可证不能够进行累计,也就是说一个线程能够具备的最大的许可证的个数是1一个。 park()方法,这个线程会生产调用这个方法的线程一个许可证,因为线程的默认许可证的个数是0,如果调用一次那么许可证的数目就变成-1,当许可证的数目小于0的时候线程就会阻塞,因此如果线程从来没用调用unpark方法的话,那么在调用这个方法的时候会阻塞,如果线程在调用park方法之前,有线程调用unpark(thread)方法,给这个线程发放一个许可证的话,那么调用park方法就不会阻塞。 parkNanos(long nanos)方法,同park方法一样,nanos示意最长阻塞超时工夫,超时后park方法将主动返回,如果调用这个方法的线程有许可证的话也不会阻塞。 import java.util.concurrent.TimeUnit;import java.util.concurrent.locks.LockSupport; public class Demo { public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(() -> { LockSupport.park(); // 没有许可证 阻塞住这个线程 try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("阻塞实现");});thread.start();TimeUnit.SECONDS.sleep(2);LockSupport.unpark(thread); //给线程 thread 发放一个许可证System.out.println("线程启动");}} 复制代码下面代码的执行后果线程启动阻塞实现复制代码从下面代码咱们可能知道LockSupport.park()可能阻塞一个线程,因为如果没有阻塞的话必定会先打印阻塞实现,因为打印这句话的线程只休眠一秒,主线程休眠两秒。在源代码当中你可能会遇到UNSAFE.compareAndSwapXXX的代码,这行代码次要是进行原子交换操作CAS,比如:UNSAFE.compareAndSwapInt(this, stateOffset, NEW, CANCELLED)))复制代码下面的代码次要是将this对象当中的内存偏移地址为stateOffset的对象拿进去与NEW进行比较,如果等于NEW那就将这个值设置为CANCELLED,这整个操作是原子的(因为可能多个线程同时调用这个函数,因此需要保障操作是原子的),如果操作胜利返回true反之返回false。如果你目前不是很理解也没关系,只需要知道它是将对象this的内存偏移为stateOffset的值替换为CANCELLED就行,如果这个操作胜利返回true,不胜利返回false。 FutureTask回顾咱们首先来回顾一下FutureTask的编程步骤: 写一个类实现Callable接口。 @FunctionalInterfacepublic interface Callable<V> { /** * Computes a result, or throws an exception if unable to do so. * * @return computed result * @throws Exception if unable to compute a result */V call() throws Exception;}复制代码实现接口就实现call即可,可能看到这个函数是有返回值的,而FutureTask返回给咱们的值就是这个函数的返回值。 ...

August 8, 2022 · 1 min · jiezi

关于前端框架:不想当Window的Dialog不是一个好Modal弹窗翻身记

弹窗是咱们熟视无睹的一种交互方式,常常用到,但从没好好想过这种交互行为背地的意义... 弹窗是Windows的灵魂Windows的灵魂是什么?当然是Window,当方便快捷的多窗口进入人们视线的时候,大家无不为之惊呼太好用了!! 弹窗其实是一种多线程当你须要放弃当前任务运行,同时开启一个新工作时,就须要多线程。弹窗何尝不是一种交互畛域的多线程?它能够挂起以后的操作流,而后开拓一片全新的操作区域,让用户从新开始一条新的操作流,并且期待其实现后还能够从新返回之前的操作环境。 弹窗其实就是Page有的UI设计师厌恶弹窗,感觉不美观,我不晓得为什么?当你把一个弹窗放到最大,充斥整个视口,你会发现这不就是一个所谓的Page吗? 弹窗是一种keep-alive为了放弃以后的滚动地位,用户的操作环境等,最简略的方法就是应用弹窗,不销毁底下的Dom元素,这不就是keep-alive吗? 弹窗与WindowWindow?Dialog?Modal?傻傻分不清楚,我也分不清楚,但咱们也不必去杠,就是一个命名而已。咱们能够约定,Window特指那些重量级的弹窗,而Dialog和Modal特指轻量级弹窗。 咱们借用浏览器的Tab窗口来看: Window外面装的是独立的Page,而Dialog外面装的是一个独立的Fragment;Window外面不仅Dom元素是独立的,运行环境也是独立的,而Dialog只是Dom元素独立;Window领有独立的历史记录栈,能够后退/后退/刷新,而Dialog没有这些性能;没有Window的SPA是不残缺的咱们习惯用Single-Page-Application来模仿浏览器的多页,从而能够更自在的管制页面间的跳转体验,但始终不足一种对Window窗口的模仿。在多页中咱们能够一句话让一个页面在新窗口中关上,比方:<a href="xxx" target="_blank">或者window.open(xxx),然而在SPA中即使是操作一个Dialog都是绝对麻烦的事件,况且Dialog也不能算是Window。 实现虚构Window基于以上剖析,集体实现了一个根本能满足需要的虚构Window。 先看看成果:虚构Window 之所以说它是虚构Window,而非Dialog,理由如下: 它外面装的是独立的Page而非Fragment,仅依据Url就能够重建弹窗,例如http://admin-react-antd.eluxj... 它外面装的Page,不仅有独立的Dom构造,还有独立的全局Store,相似于实现与外界隔离的运行环境。 它自带独立的历史记录栈,基于它的每一个路由跳转都将主动造成一条历史记录。 它提供相似浏览器窗口的工具条:敞开/后退/刷新。如:文章列表 => 点击题目 => 点击作者 => 点击文章数。而后你能够顺次回退每一步操作,也可一次性全副敞开。 它提供窗口最大化、最小化按钮,如:文章详情,窗口左上角按钮;并反对默认最大化,如:创立文章 只需一句话即可关上新窗口,例如 <Link to="/article/list/index" action="push" target="window">:新窗口关上 <Link to="/article/list/index" action="push" target="page">:本窗口关上 Window中能够再开新窗口,最多可达10级,主动保护层级关系。 弹窗再弹弹窗体验不好?多层弹窗时自动隐藏上层弹窗,敞开下层弹窗主动复原上层弹窗,保障每一时刻始终之会呈现一层弹窗。 轻松实现是否keep-alive。keep-alive长处是用户体验好,毛病是太TM占资源(须要缓存所有Dom元素还有相干内存变量),当初应用虚构Windw,你想keep-alive你就在新窗口中关上,不想keep-alive就在原窗口中关上,随便管制。我的项目地址大家有什么意见、想法、改良思路,都欢送提供哦... 最初附上我的项目地址: React版本 github: https://github.com/hiisea/elu...gitee: https://gitee.com/hiisea/elux...Vue版本 github: https://github.com/hiisea/elu...gitee: https://gitee.com/hiisea/elux...

August 3, 2022 · 1 min · jiezi

关于前端框架:开发人员必须了解的-10-大前端开发工具

近几年里,前端技术倒退十分迅速。如果您是一个网络开发爱好者,那么您肯定晓得一个好的前端对商业运作的重要性。这里码匠将公布这篇前端开发工具指南,向读者介绍几款开发者罕用的前端开发工具,心愿能对您有所帮忙。 第一类:惯例前端开发工具React React 是目前十分风行的一个前端框架,寰球约 70% 的开发者都在应用。这个开源前端库容许开发者应用可复用代码无缝地构建应用程序。 劣势性能React 是速度的代名词,该前端框架可在不影响速度和响应性的前提下无效解决简单应用程序的更新。React 的模块化性能让开发者用更小的可复用代码文件取代密集的代码文件。React 的可扩展性很值得注意,开发人员能很容易地实现须要批改大量数据的大型项目。React 适应性很强,除了用于利用开发,用户也可将其利用于其余我的项目场景。Angular Angular 创立于 2009 年,其目标是通过预置模板、疾速代码生成和简略的 MVC 架构来帮忙开发者进行开发。Angular 还有宏大的社区反对,因而当开发者被卡住时能够去社区寻求解决方案。 劣势性能Angular 是一个跨平台前端开发工具,容许用户依据要求开发渐进式应用程序。Angular 有一个双向的数据绑定零碎,以确保视图层和模型层之间的同步。Angular 的 MVC 架构为开发者节俭了大量工夫,作为研发,你只须要把业务拆解成一个个MVC,剩下的交给 Angular 即可。Flutter Flutter 是一个来自谷歌公司的开源框架。Flutter 实用于开发跨平台的应用程序,其兼容性问题最小,还能帮助开发人员开发出更多响应式 UI。 劣势性能Flutter 是一个开源的前端开发框架,能以低成本高效益的形式实现用户的想法。Flutter 的热加载性能确保程序员对应用程序的 UI 所做的更改可能即时同步,节约了前端的开发工夫。Flutter 容许开发者通过组合合乎客户业务模式的不同 widget 来创立翻新的 UI。Flutter 带有谷歌 firebase 反对,简化了可扩大应用程序的开发。Bootstrap 马克-奥托在 2011 年搭建了这个框架,它有助于构建具体的、动静的网络应用。Bootstrap 也有一个弱小的开发者社区,能帮忙开发者解决各种问题。 劣势性能Bootstrap 是一个简略且有吸引力的前端开发框架,为开发者简化了开发过程。在 Bootstrap 上搭建一个应用程序相当容易,而且不会很费时。Bootstrap 有一套现成的模板,为开发者和商业用户的利用开发提供了便当。Bootstrap 包含预设的利用开发组件,如按钮、下拉菜单、导航、进度条和徽章。Vue.js 最受欢迎的前端开发工具之一 Vue.js 由作者尤雨溪于 2014 年公布。尤雨溪在 Google 任职期间,对 Angular 可借由材料系结来解决网页 DOM 的运作形式很感兴趣,并想以此为根底开发出一个性能类似但内容较笨重的框架。不久后 Vue 应运而生,该框架应用 MVVM 架构来开发交互式 Web 利用 UI,以联合 React 和 Angular 而闻名。 ...

June 27, 2022 · 1 min · jiezi

关于前端框架:前端主流布局系统进阶与实战完结无密

前端支流布局零碎进阶与实战|完结无密超清原画 残缺无密 包含所有视频课件以及源码 MP4格局 获取材料:网盘链接智能算法-粒子群算法 粒子群算法本節次要引見粒子群算法的改进思緒及戰略,首先回憶一下粒子群算法:一群扩散的鳥兒在隨機地飛行尋食,它們不知道食物所在的詳細地位,在尋食過程中,每只小鳥都會記载自己飛行地位。突然,其中某一只小鳥AA發現好多玉米,於是就喊:“快來,我這里有好多食物!”,然而,其余小鳥都只發現了零零星星的幾個玉米粒。於是,每個小鳥在飛行的時分就有了一個指導的方向(往小鳥A的地位飛),然而,每個小鳥都有不忍心放棄當前致力搜寻過的地位(小鳥們心想:我這里刚才也找到點玉米粒,再繼續找找說不定比妳還要多)。因此,它們最終決議分離自身的經歷和整個群體的經歷,調整自己的飛行速度和所在位置,不時地尋觅更加靠近食物的地位,最終使得群體匯集到食物地位。粒子群算法改进戰略2.1 選择思维從種群當選择出一個個體,將該個體的順應度與其它個體的順應度进行比擬,若優於某個體,則每次給該個體記一分。對每個個體反復這個過程。依據每個個體的分數,對所有個體进行排列。選择較好的前半部分個體,並復製它們,代替種群中較差的後半部分個體。在此過程中,最佳個體的順應度並未發作改動。評價:此種改进戰略收敛速度遲緩,相當於把差的粒子都交換了成優秀的粒子,导致所有粒子都很優秀,都比擬類似,大家朝著最優秀的粒子進步速度就遲緩了。越是差距大,越知道自己的致力方向,越是都很優秀,大家差距越小,晋升空間過小,進步遲緩。 2.2 雜交微粒群思维微粒群中的每個微粒被赋予一個雜交概率,由用戶給定,與順應值無關。 在每次迭代時,根據雜交概率選择肯定數量的微粒進入雜交池,池内的微粒隨機地兩兩雜交,產生雷同數目标子代,並用子代微粒取代父代微粒,以堅持種群的微粒數目不變。 微粒雜交計算形式請參考如下公式: 2.3 變異思维測試所有粒子與當前最優的間隔,當間隔小於肯定的數值的時分,能夠拿出所有粒子的一個百分比(如10%,需依據標題自行必定)的粒子进行隨機初始化,讓這些粒子从新尋觅最優值(給一些粒子隨機赋予新的地位,豐厚粒子群)。 評價:此改进思维能夠克製“早熟”(防止收敛過快而墮入局部最優解) 2.4 小生境改进思维小生境的简單概述,自然界中经常特徵、性狀類似的物種相聚在一起,並在同類中交配繁殖後代。遺傳算法中的小生境思维次要是將每一代個體劃分爲若幹類,每類選出優秀個體組成一個種群; 基於動態鄰域的改进微粒群算法。 依據微粒的下標將微粒群體宰割成若幹個相鄰的區域,而不論它們在空間地位上如何。每次迭代時,種群中一個微粒到其它微粒之間的間隔能夠被算出來,並記载下最大間隔dmaxdmax,然後計算每一個粒子的 ‖−‖/max‖xa−xb‖/dmax,依據計算出的結果選择相鄰的微粒,應用較小比值和較大比值作爲選择根據。公式中的‖−‖‖xa−xb‖示意粒子aa與粒子bb的間隔。

May 6, 2022 · 1 min · jiezi

关于前端框架:微服务平台下基于-GraphQL-构建-BFF-的思考

写在结尾的局部,本文的契机是最近咱们组共事在客户端实现了一套 Redux,对某个业务域的性能进行重构设计,iOS、Android 都遵循这套规定,即 Redux。为什么须要客户端去实现一套 Redux?商品模块业务逻辑十分负责,商品根底信息十分多,比方多规格、多单位、价格、库存等信息,还有对应的门店、网店模型,还有各种行业能力开关管制,晚期的实现有 Rx 的角色,导致代码逻辑较为简单,数据流动比拟乱。架构设计、代码保护各方面来看都不是很优雅,加上最近有大的业务调整,中台的同学用 Redux + 单向数据流的形式重构了业务。 另一个下线常常申请网关数据,iOS、Android 各自去申明 DTO Model,而后解析数据,生成客户端须要的数据,这样“反复”的行为常常产生,所以索性用 TS + 脚本,对立做掉了网关数据模型主动生成 iOS、Android 模型的能力。然而在探讨框架设计的时候回类比 React Redux、Flutter Fish Redux、Vuex 等,还会聊到单向数据流、双向数据流,然而有些同学的了解就是谬误的。所以本文第一局部「纠错题」局部就是讲清楚前端几个要害概念。 前面的局部依照逻辑程序讲一下:微前端 -> BFF/网关 -> 微服务。 一、纠错题1. Vue 是双向数据流吗? 如果答复“是”,那么你应该没有搞清楚“双向数据流”与“双向绑定”这2个概念。其实,精确来说两者不是一个维度的货色,单向数据流也能够实现双向绑定。 其实,你要是认真看过 Vue 的官网文档,那么官网就曾经阐明 Vue 其实是 One-Way Data Flow。上面这段话来自官网 首先明确下,咱们说的数据流就是组件之间的数据流动。Vue 中所有的 props 都使得其父子 props 之间造成一个单向上行绑定:父级 props 的更新回向下流动到子组件中,但反过来不行,这样避免从子组件意外更改其父组件的状态,从而导致你的利用数据流难以了解。 此外,每次父级组件产生变更时,子组件中所有的 props 都将会被刷新为最新的值,这意味着你不应该在子组件内去批改 prop,如果你这么做了,浏览器会在控制台中输入正告。Vue 和 React 批改 props 报错如下: 2. React 是 MVVM 吗?不间接答复这个问题,咱们先来聊几个概念,这几个概念弄清楚了,问题的答案也就跃然纸上了。 2.1 单向绑定、双向绑定的区别?讲 MVVM 肯定要讲 binder 这个角色。也就是单向绑定和双向绑定。 ...

November 30, 2021 · 7 min · jiezi

关于前端框架:华为云官网负责人明哥我们是如何做到门面不倒8个月挑战业界翘楚

本文分享自华为云社区《华为云官网负责人明哥:咱们是如何做到门面不倒,8个月挑战业界翘楚?》,原文作者:华为云社区精选 。 4月的一个周五黄昏,刚刚完结一场语音会议的明哥,拿起桌上的咖啡,一口灌了上来。同时,翻了翻摊在右手边的笔记本,思考行将抛给他的一些问题。 在华为曾经工作第15个年头的他,目前是华为云官网研发团队的技术负责人,看护着华为云对外的“门面”。 作为技术管理者,明哥有个小习惯,“每天给本人留一些静默工夫,在这段时间内,尽量不解决邮件、工作信息,可能做一些代码开发、review、技术钻研的工作。” 他还习惯把事务性的工作都安顿在前半周,后半周能有绝对残缺的工夫,和团队的架构师、设计师零碎探讨比拟大的技术计划。 在钻研技术这块,明哥喜爱往下走,去看它的底层运行机制,它的源码。他也是 “一万个小时定律”的拥趸者,始终深信积攒足够的工夫和精力,肯定能在技术上有所建树,举一反三。 所以,他能和团队仅用半年多的工夫,实现一次简直不可能的挑战。 再难,官网“门面”不能倒在华为南京研究所露天长梯的二层平台上,始终竖着一块海报板:二战中被打得像筛子一样、浑身弹孔累累的伊尔2飞机,仍然保持航行,终于平安返回。 两年前的下午,明哥双手环胸站在办公室的落地窗前,紧紧盯着这块海报,思路却停在十分钟前接到的工作上:他的团队须要在无限的工夫内,实现官网内容生产平台的全副自研重构,且达到业界当先的程度。 这是一次走出技术舒服圈的挑战,放弃他们十分相熟的技术架构,所有从头开始,好比明明有一条高速公路通往起点,然而你不能走,你得本人新建一条。 这期间,华为云官网团队既要保障日常业务的失常运行,循序渐进解决各种业务需要,又要抽调出足够的人手搭建新的内容平台,工夫紧、人手少、工作重。 在一直的技术研究,重写代码,验证测试后,我的项目的最小可用版本实现了showcase,彼时大家都很有成就感,也感觉终于能缓一缓了。然而一个更紧急的工作再次抛向他们:为了疾速催熟产品,接下来的大促期间将间接应用自研零碎。 此时离大促还有两个月,开发团队除了要分出一部分兵力生产页面(为了确保用户体验,页面要全新设计),还要补齐高并发、高可用、平安可信等产品化所必须的能力。通常,这样的能力个别至多须要3到6个月,能力打磨欠缺的差不多。 明哥和团队只有破釜沉舟,那段时间里,工作板上写满了被拆分的工作细节,新的计划一直笼罩旧的版本,会议室里坐阵的技术专家走了一波,又新来一波……大家回绝斗争,一门心思埋头往前冲。 比方,为了保障生产进去的页面在任何状况下都不能丢,设计团队翻阅了大量材料,与平安、可用性、性能专家屡次探讨和原型验证,而后抉择了最‘冗余’的计划,最终胜利应答屡次突发状况,禁受住了大促的考验。 历时8个月,从我的项目启动到第一个基于自研的内容生产页面诞生,官网团队交出了一份丑陋的成绩单。 “挑战十分大,但咱们胜利了。” 与此同时,他们还“顺带”开发了一个PQP页面品质平台,负责主动查看页面上线前的内容品质,包含页面的404、敏感字词、中英文单词的拼写、图标的设计元素是否符合规范等等。 从接手华为云官网开始,品质就是悬在明哥头上的达摩克利斯之剑。用他的话说,“品质这个货色,不出问题的时候大家不会感觉多重要,凡是产生问题,就会成为人心所向,所谓善战之将无赫赫之功。” 如何保障页面品质稳固,这一点往往是不少前端技术人员漠视的。“咱们找征询公司,合作伙伴问了一圈,大家都没有这样的工具,更多的是靠流程保障,比方发现问题告诉oncall,再逐层找到负责人。尽管管理手段可能运行上来,但效率太低了。” 所以,将这种“人拉肩扛”的问题解决形式,转化为工具能力,做成平台去赋能,再贯通到整个页面的公布流程,是一件成就感与挑战并存的事件。 以后,PQP平台已在华为外部“开源”,包含华为官网在内的80多个网站都曾经接入,用于看护网站的内容品质。 谈及品质,不仅是页面内容的品质,还有官网稳定性的品质。试想,12306的每一次解体,前面是多少用户的吐槽骂声。 为了保护华为云官网的稳定性,他们也针对高可用做了多层保障,比方多正本的容灾备份,数据多活等等,在寰球4个地区的6个机房都安置了华为云官网的服务器,并且洽购了4家不同的CDN厂商躲避可能呈现的任何主客观危险。构建多个逃生通道,一键实现流量的疾速切换。就像剥洋葱一样,剥开一层外面仍然保障完好无缺。 “华为云官网是咱们的门面,控制台、后盾服务或者能够挂,但官网就像上甘岭的那面旗号,哪怕是个光杆司令,我也不能倒,肯定要竖在那里。” 云原生藏在业务里门面不能倒,为了这个指标,华为云官网的架构以及生产公布流程也在一直优化欠缺中。 以前端框架为例,React性能弱小且灵便,Angular有丰盛的组件,Vue简洁易构建,选起来颇有些乱花渐欲迷人眼。 明哥也曾陷入抉择何种技术框架的纠结中,团队通过一番探讨,抉择了一个折中的形式——他们和web能力核心定下准则:根底能力团队保护一套支流技术框架和组件库,各业务团队有本人的选择权,能够间接应用,也能够依据须要抉择其余技术栈,但外围是听从对立的设计规范,达到即便不同技术栈生产的页面也能让用户无感知差别的成果。 正所谓好马配好鞍,让开发人员依据各自看护的业务个性找到最匹配的框架。 但问题随之而来,如何将这些新、老技术栈,以及不同技术框架生产的页面放在一起出现给用户? 华为云引入了微前端框架,让各个小团队,不同的技术栈都能共生。 微前端的目标是低耦合,它把各模块之间的影响降到最低,各模块能按需应用不同的技术栈,从而升高技术栈切换的老本,确保产品平滑过渡,防止一刀切带来的品质危险。 同时,所有的服务都部署在容器里的,所有皆代码。诸如应用程序、中间件、底层操作系统都被打包成规范的包,不论在什么环境,什么时候部署,模块都是一样的,不会呈现因为零碎、中间件版本、配置不统一引发的研发环境和生产环境状态不同的状况。这也是继续交付、疾速迭代的根底。 从人拉肩抗的低效率开发,到现在规范的页面公布流程,华为云官网的架构也进入到一个新的阶段:后盾采纳微服务架构,前端采纳微前端架构,页面上线恪守规范的DevOps流程,化繁为简,充分利用技术的个性,破除理论业务的瓶颈。 举个例子,以前的网站开发不论是页面性能,还是页面内容的变动,都绕不开发人员,网页上任何一个轻微的变动都得去批改html代码或者CSS脚本。这种状况下,轻易批改一个字,开发需要排下来,小半个月过来了。 为了让大家都能失去“解脱”,所以有了页面生产平台,能够让业务人员自助实现页面批改;有了可视化搭建,拖拽组件即可实现所见即所得的网页制作;有了零碎的内容品质检测平台,可能保障页面的平安上线。通过IT化,让所有上线动作都高效可控,买通官网内容DevOps的最初一环。 这也是明哥对于云原生的了解,“云原生自身并不能算一套架构,它更像是一个定义,一套方法论。 关上来看,云原生无非这几个要害元素:微服务、DevOps、继续交付、容器化。” 目前,DevOps方面,华为云有一套对立的公布流水线平台,所有服务均通过这个平台公布到生产环境;继续交付方面,华为云官网有65%左右的个性是通过按个性独立公布的,每周都会有几百个个性公布到生产环境上。 让子弹再飞一会儿康威定律里曾提到,组织的架构决定了整体的技术架构。因为华为云的前端和后端组织绝对拆散,单方各司其职,技术沟通中难免会产生一些小的摩擦。不过,以后端技术浪潮汹涌而来之时,它也在试图用技术去弥合人为起因造成的各种沟通问题。 以Node.js为例,艰深点说它是运行在服务端的JavaScript,能够让懂JS的前端人员写出简略的后端服务,实现一些接口的拼装。“通过Node.js,如果一个程序员针对一个简略的需要,从前端到后端都由他本人来实现,因为省去沟通老本以及同步版本公布的动作,效率能晋升30%。” 明哥示意,这就是咱们常说的“大前端”、“全栈开发者”。而全栈能力就是消解一些组织团队互相配合产生的损耗,缩小损耗,天然能够给开发效率、模式带来质的晋升。 谈到开发效率的晋升,时下大火的Serverless正在掀起一场云计算畛域的反动,这场风暴也波及到了前端,对于此,明哥显得审慎很多。 Serverless勾画了一个不须要搭建环境、部署中间件,没有特定应用场景、业务类型,只需部署代码的世界。这是技术人员的“乌托邦”,但明哥认为以后的Serverless技术有肯定的局限性。开发团队不可能只应用一种技术或者组件,而不少技术或者框架,是须要在中间件、操作系统层面进行剖析调优工作,Serverless目前没有达到这个灵活性和适配性。 华为云官网团队也尝试过利用Serverless进步开发效率,比方把一些后盾执行不敏感、可用性要求较低的服务部署下来,再通过定时器触发,也能达到肯定成果。然而只有波及到全场景,尤其是多部件的解决方案,就不会思考首选Serverless服务。 “可能我比拟审慎,有先进或者新的技术,习惯性察看一阵子,让子弹再飞一会, 技术成熟稳固后再跟上,那个时候也不晚。” 明哥在技术栈抉择这条路上也走过不少弯路,他认为,前端团队抉择技术栈肯定要结合实际业务需要,再去察看技术栈的生态是不是继续演进中,随声附和、好高骛远不可取,如果没有适合的,宁愿自研也好过斗争。 冲破技术标签,视线决定高度回望前端技术的迭代,能够说是瞬息万变,新的框架、组件库层出不穷,新的编程语言一波波袭来…… 涉猎不同技术栈的明哥始终在考虑,技术的目标是什么?在建设华为云官网的过程中,他仿佛找到了答案。 以JAVA为首的后端技术栈,在几十年的迭代中,无论是技术语言,还是框架都趋于稳定。相较之下,前端还朝着技术成熟曲线的峰顶狂奔中,将来也会逐步从百花齐放过渡到一两个成熟稳固框架一统江山,一步步补全整个生态的阶段。 目前一些支流框架实质上也是大同小异,抉择一个畛域或者技术栈深耕,愈往下探,愈会发现其中的一致性法则。 大浪淘沙中,明哥认为比拟有后劲和摸索空间的三个技术方向是沉迷式、智能化以及低码化。 首先是沉迷式的成果,所见即所得的前端正在谋求更丰盛的展示和互动模式。比方工业制作畛域的仿真模仿,能够对孪生的数字模型进行各种测试验证。同样,在前端畛域,也能把产品可视化地出现在网站上,让用户直观地感知解决方案的运作模式。 说到这里,他在空气中比划了一下,“你设想把后盾看不见摸不着的一些组网解决方案搬到前台,计划中的流程、数据流动都是能够看失去的,很神奇, 但也十分考验后端数据和前端渲染能力的联合,不过咱们正在致力。” 第二个是智能化,一方面华为云官网团队会在搜寻和举荐中进一步优化智能算法和策略,达到精准的千人千面智能化举荐,晋升用户的注册转化率;另一方面,团队会在内容的智能生产方面,包含文章、图片、广告等,做出更多的摸索,帮助经营人员、业务人员生产出更高质量的内容。 第三个方向是低码化,当初少数业务人员能够自主生产简略的页面,波及一些简单页面才有开发人员染指。当前,无论是面向经营人员,还是最终用户,越来越多的页面、接口、流程都会通过低码化或者无码化的形式实现。 前端新技术的呈现,最终目标还是为了可能响应业务,疾速地解决生产、经营的需要,这也是所有技术都在摸索的方向。 到了这个阶段,大前端的领域也在裁减,明哥也更习惯站在架构师的角度去看背后出现的这些网页,察看它们背地的一系列逻辑。“凡是波及到用户可感知的内容,其实都是大前端要关注的,对于前端人员来说,前端不仅是一个技术,它更像是一个目标。” 最开始,前端这个概念在业界比拟含糊,前端人员都自嘲“切图仔”,也没有当初风行的三大框架,混沌初开,大家都摸着石头过河。 这个时代曾经一去不复还,现在的前端人员,技术是根底,在此之上的思维和视线则决定了技术的高度。 “比方大家经常在论坛上为哪个编程语言最好而争得面红耳赤。其实,囿于一个技术的优劣,就是在给本人贴标签。就像有的前端人员会纠结技术路线,认为写页面看不到倒退空间,这是把本人困在‘前端’的标签里。” “如果你的定位是一个简略的开发,一项技能足矣。但想要成长,得学会跳出那个圈子,换种思路,比方以进步用户体验为指标,能够学的技术就不只是某一个框架或语言。在此过程中,将本身的技术能力和定位从开发人员向架构师,乃至CTO的规范去晋升。” 心中有教堂,月亮和六便士,都能够领有。 福利工夫到:欢送大家到原文(https://bbs.huaweicloud.com/b... 福利一: 看完华为云官网的业务实际,以及明哥对前端技术的思考,如果你也有业务或者技术上的纳闷,在评论区留言,明哥将空降评论区,现场答疑解惑。 ...

May 13, 2021 · 1 min · jiezi

9个项目助你在2020年成为前端大师

原文链接:https://dev.to/simonholdorf/9... DEV的年度热文,读完觉得不错,所以翻译出来供大家参考,个人水平有限,文中可能会有一些翻译错误,可以在评论区指正。 本篇文章一共涉及了9个流行的框架/库,没有具体的介绍使用方法,而是给了一些非常棒的实战教程。 初学者(也许一些有经验的开发者也是一样)在读完官方文档,想写一个项目练手的时候不知道做什么项目好,或是有想法,但是无从下手。那么这篇文章将会给你带来很大的帮助。 导读无论你是编程新手还是经验丰富的开发人员。在这个行业中,我们不得不一直学习新概念和新语言或是框架,才能跟上快速变化。以React为例 —— FaceBook 四年前开源,现在它已经成为了全球JS开发者的首选。但是与此同时,Vue 和 Angular 也有自己的追求者。然后是 Svelte,Next 和 Nuxt.js,Gatsby,Gridsome,quasar 等等,如果你想成为专业的 JavaScript 开发人员,你在使用自己熟悉的框架进行开发的同时,还需要对不同的框架和库有一些了解。 为了帮助你在2020年成为一个前端大神,我收集了9个使用了不同JS框架/库的项目,你可以去构建或者将他们加入到自己未来的开发计划中。记住,没什么比实际开发一个项目更有帮助。所以,不要犹豫,试着去开发一下。 1. 使用React(with hooks)构建一个电影搜索应用首先,你可以使用React构建一个电影搜索应用。展示如下: 你将学到什么?构建这个项目,你可以使用较新的 Hook API 来提升你的 React 技能。示例项目使用了React组件,很多 hooks 以及一些外部的 API,当然还有一些CSS样式。 技术栈/点React(Hooks)create-react-appJSXCSS你可以在这里看到这个示例项目:https://www.freecodecamp.org/... 2.使用Vue构建一个聊天应用另外一个要介绍给你的很棒的项目是使用Vue构建的聊天应用程序。展示如下: 你将学到什么?您将学习到如何从头开始设置Vue应用,创建组件,处理状态,创建路由,连接到第三方服务,甚至是处理身份验证。 技术栈/点VueVuexVue RouterVue CLIPusherCSS这真的是一个非常棒的项目,不管是用来学习Vue或者是提升现有的技能,以应对2020年的发展。你可以查看这个教程: https://www.sitepoint.com/pus... 3. 使用Augular8构建一款漂亮的天气应用此示例将帮助你使用 Google 的 Angular 8 来构建一块漂亮的天气应用程序: 你将学到什么?该项目将教你一些宝贵的技能,例如从头开始创建应用,从设计到开发,一直到生产就绪部署。 技术栈/点Angular 8FirebaseSSR网络布局和Flexbox移动端友好 && 响应式布局深色模式漂亮的用户界面对于这个综合项目,我真正喜欢的是,不是孤立地学习东西,而是从设计到最终部署的整个开发过程。 https://medium.com/@hamedbaat... 4. 使用 Svelte 构建一个 To-Do 应用与React,Vue和Angular相比,Svelte 还很新,但仍是热门之一。好的,To-Do应用不一定是那里最热门的项目,但这确实可以帮助你提高Svelte技能,如下: 你将学到什么?本教程将向你展示如何从头到尾使用Svelte3制作应用。 它利用了组件,样式和事件处理程序。 技术栈/点Svelte 3ComponentsCSSES6语法Svelte 没有太多优秀的入门项目,这个是我觉得不错的一个上手项目:https://medium.com/codingthes... ...

November 5, 2019 · 1 min · jiezi

React实战学习必经之路JSX语法2

React必修技能JSX本篇我们来了解React的JSX语法,在此之前,我们先安装React。 这里需要注意两点: 1.第一点记得安装node,地址:https://nodejs.org/en/ 使用lts版本。 2.安装脚手架,地址:https://github.com/facebookin...。 步骤1.安装脚手架 npm install -g create-react-app2.构建项目 create-react-app hello-react3.启动项目 输入 cd hello-react yarn start看到界面,建立样板项目完成。 注意1.npm 有时候慢,多等会。 2.可以使用gitbash命令行,比cmd好用太多。 地址:https://gitforwindows.org/ 初识JSX在安装完毕以后让我们回到今天的主题,React的JSX语法。 我本身是很反对动不动就搞一个语言或者推倒一个东西重来的,一来是学习成本,二来是项目积淀清零。 但是我很喜欢JSX,这并不矛盾,因为它确实挺好用。 我们看看JSX到底是怎么回事。 打开App.js,删掉无用的东西变成小纯洁,模仿之前原生星星组件写法。 import React, { Component } from 'react'import ReactDOM from 'react-dom'class RatingStar extends Component { render () { return ( <div id ="box"> <h1>我是星星评分组件</h1> </div> ) }}ReactDOM.render( <RatingStar />, document.getElementById('root'))export default RatingStar;这里的 Component就是相当于我们自己写那个component.js,react就是实现virtualDOM什么的东西。 ReactDOM就相当于帮我们把我们要渲染的东西扔到root上面,粗略类比mounted方法(不是,但是先这么理解就好。 那么重点来了,return 小括号里面的四不像是什么? 你如果是一个字符串我能理解,你如果是一个对象我也能理解,可是你是JS里面X html 就不好理解了。 ...

November 2, 2019 · 3 min · jiezi

Vue原理Vue源码阅读总结大会-终

写文章不容易,点个赞呗兄弟 专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧 【Vue原理】Vue源码阅读总结大会 - 终 终于啊终于啊,把 Vue 系列的文章发完了了,如释重负的感jio啊,今天就打算总结下,我这段时间来的历程和收获,本文纯吹水,没有技术含量,各位客官权当娱乐消遣也无谓,来收集个表情包也是可以的 学习源码用时花了3个多月读源码,花了六个月 写文章,花了九个月发文章 耗时如下 从 2018 - 9 - 10 开始读源码 然后从 2018 - 12 - 5 开始写文章 然后 2019 - 6 - 2 写完所有文章 然后 2019 - 8 - 19 发完所有文章(也就是今天) 我不是写了就马上发的,我写完一篇文章之后,要等一段时间之后,再重新审核一遍再发送,这样一来,既可以发现遗漏的错误,也可能可以收获不同的心得 你以为这时间算长的了吗?? 其实如果只用晚上下班回去的时间写的话......写文章估计得用一年......我不仅内容追求简单,可以让我们在以后忘记的时候迅速捡回来,我还要追求排版好看(鳖跟我说难看好不喽,我花了很多心思的哈哈),因为我知道排版难看的文章一秒都不想爱看下去.... 然后我为什么只用半年呢,我.....不好说....哈哈哈 不过也算很长了,历时这么长,真的是非常难熬的,在 坚持和 放弃中 摇摆不定,57 篇文章真不是搞笑的啊,差点崩溃了 幸好给自己定的奖励,一路撑着我走到现在 比如写完这个系列,我打算给自己买双鞋子..... 咳咳,回到正题 阅读源码不难,难的是深度阅读源码 ...

October 14, 2019 · 1 min · jiezi

免费angular8高级实战教程网易云音乐pc端

自制angular8实战教程,先上链接: 网易云课堂:https://study.163.com/course/...B站:https://www.bilibili.com/vide...历时个把月,本想出个单一功能的教程,没想一开始就控制不住了,到最后时长竟高达30多个小时, 为什么是angular?angular是我的第一个框架,所谓先入为主,即使工作中怕是再难用上,也不会把它丢掉,而且angular用户是痛苦的,至少在国内,不论是文档、生态、百度、教程等都全面被vue和react压制,并非angular技不如人,只因google太任性。本教程也算是为推进angular做点贡献吧,这应该是前端框架中,最给力的免费教程了。 做什么?用angular8仿造网易云音乐pc端的部分功能,包括:歌单、歌曲、歌手和会员的登录注册等,并实现网易云核心的播放器功能。 能学到什么?主技术栈:angular8 + ngrx8 + ng-zorro + material/cdk,包括但不限于: ng-template,ng-content,ng-containerng模块化设计proxy,http拦截器依赖注入自定义指令和管道自定义表单控件动态组件各种rxjs操作符material/cdk变更检测策略ngrx8...课程特色?本课程全程都在实战,在开发过程中会尽力覆盖ng的各种api,项目的模块化、目录设计和组件化等都是以真实项目标准来做的,可复用到日常工作的各项目中去,代码极度精简,期间更有徒手造轮子的过程,是一门学习框架和提升基本功的双休课程。源码也分好了章节上传到github:master分支是最终完成的代码https://github.com/lycHub/ng-wyy 需要的基本知识?typescriptrxjsangular基本api的使用(重要)学完后能达到什么水平?由于本课程会尽可能多的使用angular高级api,如果能完全掌握,那在使用层面已经非常优秀了。完全可以独自用angular胜任网易云音乐官网这种难度的项目 教授方式?手写每行ts和html,样式部分复制做好的。 很遗憾无法上传到慕课网,因为实现没有做好功课,推荐大家去网易云课堂或51cto(待上)观看,记的好评哟: 网易云课堂:https://study.163.com/course/...B站:https://www.bilibili.com/vide...

October 8, 2019 · 1 min · jiezi

React实战为什么会出现React0

最开的网页只有文字,也就html部分,随着互联网发展,网页内容的丰富多彩,网页不仅仅限于浏览,而是需要互动。为解决这个问题,先后出现了js和css。 解决这个问题的思路,各有利弊: 1.优点 css很灵活,程序员可以有能力随心所欲的控制网页。 JS很强大,可以随心所欲的控制用户的行为。 分工明确,更容易专业化,符合社会趋势,越来越精细化。 2.缺点 分工必然会涉及到协作,协作必然会存在这样的问题:怎么让沟通和切换工作场景状态保持一致。 举个现实的例子,你把一个前端项目分给三个人做,一个人切图,一个人数据交互,一个写后台。那么如果切图的人改网页上的东西,一定会影响另外两个人,怎么保证三个人修改页面状态一致,这就是个问题。 根本性的解决方案 web components 解决问题的思路: 让网页由一个个积木组成,每一个积木通过良好的定义规定好接口和规范,以及统一的实现。这个本质上和我们用同一个型号的砖头可以盖出不同的房子没什么两样。 目前问题: 兼容性不好、思路不成熟、不好用 普遍的解决方案 1.mvc方案 MVC很简单,还是拿刚才的三个人工作举例: 现在写数据交互的人说了,切图的你啥也不用想,你就按照我们商定的标准切图,别瞎搞。后台你也是,我想要啥数据你就给我啥格式,别自己瞎搞,我统一去合并文件。 这就是MVC思想,纯切图的就是view,写后台搞数据的就是model,写数据交互让前后台对应起来的就是c。 MVC的初衷是好的, MVC要实现的目标是将软件用户界面和业务逻辑分离以使代码可扩展性、可复用性、可维护性、灵活性加强。 结果有两个问题: 1.很多程序员将MVC当成了三层架构在用,写出来的东西既不是三成架构也不是MVC,倒是像一个什么都不是的四不像。三层架构的核心思想是面向接口编程和各层之间的解耦和可替换性,MVC并不具备这样的思想和能力,很多人误解了它。 2.即使不误解,这样做也有问题从我们的例子可以看出来,C端业务过重,一来造成混乱,二来对C端业务能力要求也高。Controller会越来越难维护 3.手动管理各个过程很费劲。 举个例子: <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <script> window.onload = function(){ var oUl = document.getElementById('ul1'); var aPrev = document.querySelectorAll('.prev'); var aNext = document.querySelectorAll('.next'); for(var i=0;i<aPrev.length;i++){ aPrev[i].onclick = function(){ var oParent = this.parentNode; var oPrev = oParent.previousElementSibling || oParent.previousSibling; if(oParent == oUl.children[0]){ alert('到头了!'); return; } oUl.insertBefore(oParent,oPrev); } } for(var i=0;i<aNext.length;i++){ aNext[i].onclick = function(){ var oParent = this.parentNode; var oNext = oParent.nextElementSibling || oParent.nextSibling; var oNext2 = oNext.nextElementSibling || oNext.nextSibling; if(oParent == oUl.children[oUl.children.length-1]){ alert('到底了!'); return; } oUl.insertBefore(oParent,oNext2); } } }; </script></head><body> <ul id="ul1"> <li> <span>1、111111111</span> <a href="javascript:;" class='prev'>上移</a> <a href="javascript:;" class='next'>下移</a> </li> <li> <span>2、222222222</span> <a href="javascript:;" class='prev'>上移</a> <a href="javascript:;" class='next'>下移</a> </li> <li> <span>3、3333333333</span> <a href="javascript:;" class='prev'>上移</a> <a href="javascript:;" class='next'>下移</a> </li> <li> <span>4、4444444444</span> <a href="javascript:;" class='prev'>上移</a> <a href="javascript:;" class='next'>下移</a> </li> <li> <span>5、5555555555</span> <a href="javascript:;" class='prev'>上移</a> <a href="javascript:;" class='next'>下移</a> </li> </ul></body></html>效果: ...

October 4, 2019 · 2 min · jiezi

目前流行前端几大UI框架

在前端项目开发过程中,总是会引入一些UI框架,已为方便自己的使用,很多大公司都有自己的一套UI框架,下面就是最近经常使用并且很流行的UI框架。 一.Mint UI Mint UI是 饿了么团队开发基于vue .js的移动端UI框架,它包含丰富的 CSS 和 JS 组件,能够满足日常的移动端开发需要。官网:https://mint-ui.github.io/#!/...Github: https://github.com/ElemeFE/mi... 二.WeUIWeUI 是一套同微信原生视觉体验一致的基础样式库,由微信官方设计团队为微信内网页和微信小程序量身设计,令用户的使用感知更加统一。包含button、cell、dialog、toast、article、icon等各式元素。 官网地址:https://weui.io/Github: https://github.com/weui/weui.git 三.cube-ui cube-ui 是滴滴团队开发的基于 Vue.js 实现的精致移动端组件库。支持按需引入和后编译,轻量灵活;扩展性强,可以方便地基于现有组件实现二次开发。 官网地址:https://didi.github.io/cube-u...Github: https://github.com/didi/cube-ui/ 四.iView UI iview ui是一个强大的ui库基于vue,有很多实用的基础组件比elementui的组件更丰富,主要服务于 PC 界面的中后台产品。使用单文件的 Vue 组件化开发模式 基于 npm + webpack + babel 开发,支持 ES2015 高质量、功能丰富 友好的 API ,自由灵活地使用空间。 官网地址:https://www.iviewui.comGithub: https://github.com/TalkingDat... 五.layui layui是一款采用自身模块规范编写的前端 UI 框架,遵循原生 HTML/CSS/JS 的书写与组织形式,门槛极低,拿来即用。其外在极简,却又不失饱满的内在,体积轻盈,组件丰盈,从核心代码到 API 的每一处细节都经过精心雕琢,非常适合界面的快速开发。官网地址:https://www.layui.comGithub: https://github.com/sentsin/la... 六. ElementUI Element是饿了么前端开源维护的Vue UI组件库,组件齐全,基本涵盖后台所需的所有组件,文档讲解详细,例子也很丰富。 主要用于开发PC端的页面,是一个质量比较高的Vue UI组件库。官网地址:http://element-cn.eleme.io/#/...Github: https://github.com/ElementUI/... ...

September 20, 2019 · 1 min · jiezi

可以免费自学编程的12个网站

很多人包括一些企业家,和市场营销人员都认为学习编程对一个人走向成功十分有帮助。在过去的一年里,我一直在学习编程。它有助我成为一个更好的创业者,我甚至可以提供一些帮助,当我的团队需要解决一些bug的时候。 现在,如果你想学编程的话,这12个网站可以帮助你,它们都提供了免费的课程供你学习,因此你无需担心费用的问题。 1.Codecademy 其中,Codecademy是最受欢迎的免费编程学习网站之一。事实上,已经有超过2400万人通过这家公司的教育模式学会了编程。Codecademy开设的课程有HTML&CSS,JavaScript,jQuery的,PHP,Python和Ruby。 2.Coursera Coursera成立于2012年,如今已经成长为一个主要以营利为目的的技术教育公司,现提供来自119家机构的超过1000门课程。如果你想要获得证书,可能需要为一定的课程付费,这里也有一些来自不同大学的免费编程课程,如华盛顿大学,斯坦福大学,多伦多大学和范德比尔特大学等。 3.edX edX是另一个领先的在线学习平台,重点是它不是以营利为目的,而是开源的。edX是由美国哈佛大学和麻省理工学院于2012年联合创办的,所以你将会在这里学习到先进的技术和理论。如今,edX已涵盖了60所学校。此外在这里,你应该不会错过哈佛大学的计算机科学导论的,免费的哦。 4.Udemy Udemy成立于2010年,是一个在线学习平台,可以帮助你改善或学习工作技能。虽然有部分课程需要付费,但也有大量的免费编程学习课程,通过视频讲授。 5.aGupieWare 一个独立APP开发者从美国的一些领先机构调查了计算机科学程序,然后基于斯坦福大学,麻省理工学院,卡耐基梅隆大学伯克利分校和哥伦比亚大学提供的免费课程,它创建了一个类似的课程。该程序分为15个课程:3个入门课程,7门核心课程和5个选修课程。 对有潜力的程序员来说,这简直是一个完美的入门程序。(本段感觉译的不是很好,有其他见解还请赐教~) 6.GitHub 这个我想大多数开发者都知道就不多说了,作为开源代码库以及版本控制系统,Github拥有140多万开发者用户。 7.MIT Open Courseware 如果你已经学过基础知识,并向更深入的层次探索,比如探究背后的编码理论等,麻省理工学院提供了包括如计算机科学导论,Java编程介绍和C语言编程等免费课件的网站,值得好好利用一下。 8.Hack.pledge() 这是一个开发者社区,其中聚集了一些知名度比较高的开发者,如Bram Cohen,BitTorrent的创始人。在那里,你可以从一些大牛那里学习到一些编程技巧。 9.Code Avengers Code Avengers提供了很多有趣的和互动性的程序设计课程,教你如何编写游戏,应用程序,以及如何使用JavaScript,HTML和CSS创建网站。每门课程只需要12个小时即可完成,并且支持英语,俄语,荷兰语,西班牙语,意大利语,土耳其语和葡萄牙语。 10.Khan Academy Khan Academy由教育家萨尔曼汗创建于2006年,是其免费在线学习机构之一。这里提供一步一步的视频教程,你可以在这里学习如何使用JavaScript和ProcessingJS编写动画,游戏等,或者学习如何使用HTML和CSS创建网页。 11.Free Code Camp 在这里,你可以学习HTML5,CSS3,JavaScript,数据库,DevTools,Node.js,Angular.js和Agile的知识。你甚至免费创建自己的应用,以锻炼自己的编程技巧。总之,在这里你可以学习到真正的技能,并且对你解决实际中的问题十分有帮助。 12.HTML5 Rocks 为对抗苹果的HTML 5,谷歌于2010年推出该项目。该网站提供了大量的教程,资源以及最新的HTML5更新。它是开源的,因此开发人员可以尽情使用HTML5代码。由于这比大多数课程都要先进,因此在这里你肯定会比没来之前获得更多的知识和经验。 学习编程常常需要看一些比较昂贵的书籍和课程,但是现在由于互联网的发展,市场上有很多可以免费学习编程的网站,所以费用问题就不用太担心了。 我强烈建议每个企业家都能去学学代码,这对于你成为一个成功的企业家非常有帮助。 在entrepreneur看到这篇文章,也许会对大家有帮助,就拿来翻译一下,需要的朋友可以看一下。有不足之处还请指正,多谢。 写在最最后本文为译文,所以文章里列举的学编程网站均是来自国外,可能对一些英文不是很好的小伙伴来说用起来有点儿吃力,其实国内也有一些编程学习网站,像网易云课堂、慕课网、极客学院等,大家可以参考一下。但是,如果英文不错的话,Aylee还是建议大家多看看国外的网站,毕竟代码都是用英文写的吧是吧哈哈~开个玩笑啦~~总之,希望本文对大家会有所帮助吧。 原文:12 Sites That Will Teach You Coding for Free编译作者:Aylee姓Liu

September 11, 2019 · 1 min · jiezi

前端这些年我到底经历了什么上

正文前端路漫漫,吾将上下而求索。故事应该从 2011 年说起,那时我刚上大学,读的是软件工程专业。 那时大学里这种专业的课除了数学和英语外,基本上都是些计算机语言的课程,比如《C#语言程序设计基础》、《面向对象程序设计》等。 说实话可能骨子里就是做前端的命吧(虽然当时并不知道前端是什么),我对这些后台编程语言并不感兴趣。 因此大一大二过去了,我对自己的人生毫无规划可言,对自己将来毕业后能做什么也是一片迷茫,基本上这两年除了在学生组织里混迹外也别无是处了。 到 2013 年的时候情况突然发生了转变,可能上大三了吧(比较闲),更想通过课余时间来赚点钱,和其他找兼职的同学不一样的是,我开始接触到了“网赚”,百度搜索“用电脑赚钱”的时候很容易就被各种广告和推广给吸引了。 那时还付费买了课程和资料学习,虽然基本没有赚到钱吧,但是不得不说我从课程里开始学会了如何建站(当时的大部分网站都是 php 写的)以及做 SEO,开始学用 WordPress 搭建博客,用 Discuz 搭建论坛,因此也购买了服务器申请了好几个域名,感觉那时能有一个属于自己的博客是种很 Nice 的体验。 当然当用惯了各种 WordPress 的模版后也就腻了,想玩出自己的花样来,于是就开始改它的源码,这时候我才开始接触 HTML 和 CSS,当时感觉用 CSS 写一个动态效果(还称不上动画)是件很牛逼的事情。 为了能尽快打造出令我满意的个人博客,我开始借助学校图书馆学习相关的技术,我还清楚的记得我借的第一本前端的书籍是李刚的《疯狂 HTML + CSS + JavaScript》,很厚的一本书。 那时候图书馆里称得上前端的书籍大多是什么《网页设计与网站建设从入门到精通》、《网页制作与网站建设实战大全》等,还有一本比较经典的就是 《锋利的 jQuery》,现在京东上还有的卖。 就是这些书伴我度过了大三和大四上学期。不管上什么课,我都会占据教室的后排位置(最佳打酱油位置)摊一本前端的书在那边看。因此我大学的毕业设计也是和前端相关的一个个人博客系统的搭建,主要亮点就在于博客前端部分的各种动画和样式。 其实当时毕业设计偏前端的估计寥寥无几吧,答辩评审老师他们也无从问答,毕竟学校里就没有专业的前端老师,索性他们发现了我的兴趣所在,也便让我过了,这也得益于我找到了自己的第一份实习工作。 我大四实习是在一家规模 100 人左右的网络公司,住的是 4 人宿舍,工资很低,2000 一个月,但是发的是现金,因此每到发工资的时候拿到一叠毛爷爷心里也是美滋滋的。因为是实习,当时面试我的前端主要看重了我很强的学习能力和在这方面的兴趣,对经验要求也没有太高。 虽然在这家公司实习仅仅干了3个多月(从 2014 年底到 2015 年初),但是很充实,很有激情。每天晚上在宿舍和其他几个实习伙伴在一起都能畅谈技术和人生,周六周日也都会去公司主动加班或学习。 那时候,公司前端的主要任务就是用 HTML 和 CSS(用的是 Kendo UI 和 less) 画页面,处理各种浏览器的兼容性问题,JS 对于我一个实习生来说基本接触不到,大多都是后台负责写的(基本都是 jQuery)。 离开公司的主要原因还是希望自己能趁着年轻气盛去大公司瞧瞧,因此我离职那天还给组长发了封邮件,以感谢她的悉心栽培和指导。 此后,我便开始向我期望的公司和目标迈进,此时我简历中的写的技能是这样的: ...

September 9, 2019 · 1 min · jiezi

Vue原理Diff-白话版

写文章不容易,点个赞呗兄弟 专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧 【Vue原理】Diff - 白话版 终于到了最后一块内容了!今天我们就来简单概括一下 Diff,内容一点都不多哦,全是图片 Diff 作为 Vue 比较重要的一部分内容,还是非常有必要深入了解的 此篇文章就会分几块内容进行简单阐述,不会出现任何的源码,只是为了帮助大家建立一种思路,了解下 Diff 的大概内容 1、Diff 的作用2、Diff 的做法3、Diff 的比较逻辑4、简单的例子下面就开始我们的正文 Diff 作用Diff 的出现,就会为了减少更新量,找到最小差异部分DOM,只更新差异部分DOM就好了 这样消耗就会小一些 数据变化一下,没必要把其他没有涉及的没有变化的DOM 也替换了 Diff 做法Vue 只会对新旧节点中 父节点是相同节点 的 那一层子节点 进行比较 也可以说成是 只有两个新旧节点是相同节点的时候,才会去比较他们各自的子节点 最大的根节点一开始可以直接比较 这也叫做 同层级比较,并不需要递归,虽然好像降低了一些复用性,也是为了避免过度优化,是一种很高效的 Diff 算法 新旧节点是什么所有的 新旧节点 指的都是 Vnode 节点,Vue 只会比较 Vnode 节点,而不是比较 DOM 因为 Vnode 是 JS 对象,不受平台限制,所以以它作为比较基础,代码逻辑后期不需要改动 拿到比较结果后,根据不同平台调用相应的方法进行处理就好了 想了解 Vnode 更多信息可以转到这篇文章 VNode - 源码版 ...

September 9, 2019 · 2 min · jiezi

Vue实战如何实现商品评价栏目14

这篇我们来实现商品评价栏目。 完成评分组件的结构: 我们先来设置一个ratings容器,还是熟悉的老情况,ratings-wrapper的高度可能会超过ratings,这时候我们肯定会让ratings内出现滚动条的,ref也是老朋友了,配合Bscroll实现ratings-wrapper的滚动。 接着我们通过overview来构建商家评分内容。 最后评论的内容content部分,我们通过点击全部,有图,点评,来显示不同的内容,content这个部分就是我们经常遇到的俗称选项卡。 <div class="ratings" ref="ratingView"> <div class="ratings-wrapper"> <div class="overview"> <div class="overview-left"> <div class="comment-score"> <p class="score">{{ratings.comment_score}}</p> <p class="text">商家评分</p> </div> <div class="other-score"> <div class="quality-score item"> <span class="text">口味</span> <span class="score">{{ratings.quality_score}}</span> </div> <div class="pack-score item"> <span class="text">包装</span> <span class="score">{{ratings.pack_score}}</span> </div> </div> </div> <div class="overview-right"> <div class="delivery-score"> <p class="score">{{ratings.delivery_score}}</p> <p class="text">配送评分</p> </div> </div> </div> <div class="content"> <div class="rating-select" v-if="ratings.tab"> <span class="item" @click="selectTypeFn(2)" :class="{'active':selectType==2}" >{{ratings.tab[0].comment_score_title}}</span> <span class="item" @click="selectTypeFn(1)" :class="{'active':selectType==1}" >{{ratings.tab[1].comment_score_title}}</span> <span class="item" @click="selectTypeFn(0)" :class="{'active':selectType==0}"> <img src="./icon_sub_tab_dp_normal@2x.png" v-show="selectType!=0"> <img src="./icon_sub_tab_dp_highlighted@2x.png" v-show="selectType==0"> {{ratings.tab[2].comment_score_title}} </span> </div> <div class="labels-view"> <span v-for="item in ratings.labels" class="item" :class="{'highligh':item.label_star>0}" >{{item.content}}{{item.label_count}}</span> </div> <ul class="rating-list"> <li v-for="comment in selectComments" class="comment-item"> <div class="comment-header"> <img :src="comment.user_pic_url" v-if="comment.user_pic_url"> <img src="./anonymity.png" v-if="!comment.user_pic_url"> </div> <div class="comment-main"> <div class="user">{{comment.user_name}}</div> <div class="time">{{fotmatDate(comment.comment_time)}}</div> <div class="star-wrapper"> <span class="text">评分</span> </div> <div class="c_content" v-html="commentStr(comment.comment)"></div> <div class="img-wrapper" v-if="comment.comment_pics.length"> <img v-for="item in comment.comment_pics" :src="item.thumbnail_url"> </div> </div> </li> </ul> </div> </div> </div></template>获取评分数据 ...

September 8, 2019 · 5 min · jiezi

Vue原理Render-源码版-之-主要-Render

写文章不容易,点个赞呗兄弟 专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧 【Vue原理】Render - 源码版 之 主要 Render compile 我们已经讲了九篇的内容了,终于走到了 render,今天就来给自己记录下渲染三部曲的第二部,render,咦,render 内容不多的,就两篇文章哈哈哈 噔噔噔噔 render 的作用大家应该清楚 就是 执行 compile 生成的 render函数,然后得到返回的 vnode 节点 比如现在存在这个简单的模板 经过 compile 之后,解析成了对应的 render 函数,如下 function render() { with(this) { return _c('div', { attrs: { "data": 111 } }, [_v(111)]) }}看着这个莫名其妙的 render 函数,里面都是些什么东西? 不怕,主要是出现了两个函数,我们要探索的就是这两个东西 _c , _v 这个两个函数的作用,都是创建 Vnode,但是创建的过程不一样 并且 render 函数执行的时候,会绑定上 模板对应的实例 为上下文对象 ...

August 28, 2019 · 3 min · jiezi

可能是你见过最完善的微前端解决方案

原文链接:https://zhuanlan.zhihu.com/p/... Techniques, strategies and recipes for building a modern web app with multiple teams using different JavaScript frameworks. — Micro Frontends前言TL;DR 想跳过技术细节直接看怎么实践的同学可以拖到文章底部,直接看最后一节。 目前社区有很多关于微前端架构的介绍,但大多停留在概念介绍的阶段。而本文会就某一个具体的类型场景,着重介绍微前端架构可以带来什么价值以及具体实践过程中需要关注的技术决策,并辅以具体代码,从而能真正意义上帮助你构建一个生产可用的微前端架构系统。 而对于微前端的概念感兴趣或不熟悉的同学,可以通过搜索引擎来获取更多信息,如 知乎上的相关内容, 本文不再做过多介绍。 两个月前 Twitter 曾爆发过关于微前端的“热烈”讨论,参与大佬众多(Dan、Larkin 等),对“事件”本身我们今天不做过多评论(后面可能会写篇文章来回顾一下),有兴趣的同学可以通过这篇文章了解一二。 微前端的价值微前端架构具备以下几个核心价值: 技术栈无关主框架不限制接入应用的技术栈,子应用具备完全自主权独立开发、独立部署子应用仓库独立,前后端可独立开发,部署完成后主框架自动完成同步更新独立运行时每个子应用之间状态隔离,运行时状态不共享微前端架构旨在解决单体应用在一个相对长的时间跨度下,由于参与的人员、团队的增多、变迁,从一个普通应用演变成一个巨石应用(Frontend Monolith)后,随之而来的应用不可维护的问题。这类问题在企业级 Web 应用中尤其常见。 针对中后台应用的解决方案中后台应用由于其应用生命周期长(动辄 3+ 年)等特点,最后演变成一个巨石应用的概率往往高于其他类型的 web 应用。而从技术实现角度,微前端架构解决方案大概分为两类场景: 单实例:即同一时刻,只有一个子应用被展示,子应用具备一个完整的应用生命周期。通常基于 url 的变化来做子应用的切换。多实例:同一时刻可展示多个子应用。通常使用 Web Components 方案来做子应用封装,子应用更像是一个业务组件而不是应用。本文将着重介绍单实例场景下的微前端架构实践方案(基于 single-spa),因为这个场景更贴近大部分中后台应用。 行业现状传统的云控制台应用,几乎都会面临业务快速发展之后,单体应用进化成巨石应用的问题。为了解决产品研发之间各种耦合的问题,大部分企业也都会有自己的解决方案。笔者于17年底,针对国内外几个著名的云产品控制台,做过这样一个技术调研: 产品架构(截止 2017-12)实现技术google cloud纯 SPA主 portal angularjs,部分页面 angular(ng2)。aws纯 MPA 架构首页基于 angularjs。各系统独立域名。七牛SPA & MPA 混合架构入口 dashboard 及 个人中心模块为 spa,使用同一 portal 模块(AngularJs(1.5.10) + webpack)。其他模块自治,或使用不同版本 portal,或使用其他技术栈。又拍云纯 SPA 架构基于 angularjs 1.6.6 + ui-bootstrap。控制台内容较简单。ucloud纯 SPA 架构angularjs 1.3.12MPA 方案的优点在于 部署简单、各应用之间硬隔离,天生具备技术栈无关、独立开发、独立部署的特性。缺点则也很明显,应用之间切换会造成浏览器重刷,由于产品域名之间相互跳转,流程体验上会存在断点。 ...

August 20, 2019 · 3 min · jiezi

React-Fiber架构可控的调用栈

React 16采用新的Fiber架构对React进行完全重写,同时保持向后兼容。 动机:concurrent renderingconcurrent rendering 又叫 async rendering,主要包含2个特性: time slicing(分片) 为了让浏览器保持60fps,因此渲染一帧需要在16.67ms内完成,否则会造成“卡顿”time slicing将渲染工作切分,从而保证JavaScript的执行不会造成卡顿另一个功能是,将渲染工作按重要性来排序,提高时间敏感(time-sensitive)渲染的优先级(比如text input)suspense 让任何一个组件能够暂停渲染,等待数据的获取(比如懒加载组件、比如网络请求数据)这两个特性的关键前提是:React的渲染能够被中止(interrupt)、恢复。这就是为什么我们需要fiber架构了。 背景:JavaScript的执行模型:call stack首先,我们先解释,为什么过去的架构无法支持渲染中止。 JavaScript原生的执行模型:通过调用栈来管理函数执行状态。 其中每个栈帧表示一个工作单元(a unit of work),存储了函数调用的返回指针、当前函数、调用参数、局部变量等信息。因为JavaScript的执行栈是由引擎管理的,执行栈一旦开始,就会一直执行,直到执行栈清空。无法按需中止。 这与React有什么关系呢?React将视图看做函数调用的结果: View = Component(Data)Component会递归调用其他的Component。页面复杂的话,这个调用栈会很深,导致UI变卡。在React Fiber之前,React的渲染就是使用原生执行栈来管理组件树的递归渲染。这意味着,整颗组件树的渲染必须一次性完成,工作无法被分片。因此,react需要另一种可控的执行模型,让react来管理工作的调度。 React Fiber架构:可控的“调用栈”React Fiber架构就是用JavaScript来实现的执行模型。可以将它比作由react管理的“调用栈”,一个fiber与一个函数栈帧非常类似,它们都表示一个工作单元(a unit of work)。一个组件实例对应一个Fiber。 函数栈帧fiber返回指针父组件当前函数当前组件调用参数props局部变量stateReact Fiber的构造函数源码 React Fiber与调用栈的区别: React Fiber是链表结构,过去的递归调用变成了对fiber的链表遍历。fiber不仅有return指针,还有child、sibling指针,有这三个指针的链表就能够实现深度优先遍历(其实scheduler还能够更加灵活地调度,使得react能够优先执行重要组件的渲染)。fiber与调用栈的另一个区别是,栈帧在函数返回以后就销毁了,而fiber会在渲染结束以后继续存在,保存组件实例的信息。React Fiber是使用JavaScript实现的,这意味着它的底层依然是JavaScript调用栈。Fiber其实是计算机科学中早已存在的概念。Fiber的英文含义就是“纤维”,意指比Thread更细的线,寓意它是比线程(Thread)控制得更精密的执行模型。fiber是协作的(cooperatively)、可控的。一个fiber执行完自己的工作以后,会主动让出控制权,不会主宰(dominate)整个程序的执行。 协程(Coroutines)基本是相同的概念,它们的区别微乎其微。说白了,React Fiber就是用JavaScript重新实现了一个协程模型。话说回来,generator函数也能够主动让出程序控制权(generator函数本质就是协程),用它也能够做到concurrent rendering。为什么react不使用generator函数而是重新实现协程,应该是因为后者能够更加灵活吧,比如generator函数不支持回到之前的yield状态,而fiber支持从任意一个fiber节点重新开始渲染。与Fiber相反,调用栈模型则不可控、不协作(non-cooperatively)。如果函数不断地递归调用,那么会完全主宰整个程序,后续的工作(比如浏览器paint)必须等待它执行完成。 摘自React Fiber是什么: 在React Fiber中,一次更新过程会分成多个分片完成,所以完全有可能一个更新任务还没有完成,就被另一个更高优先级的更新过程打断,这时候,优先级高的更新任务会优先处理完,而低优先级更新任务所做的工作则会完全作废,然后等待机会重头再来。因为一个更新过程可能被打断,所以React Fiber一个更新过程被分为两个阶段(Phase):第一个阶段Reconciliation Phase和第二阶段Commit Phase。在第一阶段Reconciliation Phase,React Fiber会找出需要更新哪些DOM,这个阶段是可以被打断的;但是到了第二阶段Commit Phase,那就一鼓作气把DOM更新完,绝不会被打断。生命周期示意图: 参考资料Algebraic effects, Fibers, Coroutines...React Fiber ArchitectureInside Fiber: in-depth overview of the new reconciliation algorithm in React ...

August 18, 2019 · 1 min · jiezi

Vue实战购物车详情页面的实现11

上次我们为商品分类菜单添加了显示购物数量,这篇我们继续推进项目,来实现购物车的详情页面,在开始之前我们先看它在页面中的样子: 如上所示,此页面包含了购物列表,而它由商品名称,单价,增减商品功能构成,增减商品功能我们在商品列表中实现过,那么我们现在可以进行复用。 搭出购物车结构 我们将购物车底部构建出来, <templete><div class="shopcart" :class="{'highligh':totalCount>0}"> <div class="shopcart-wrapper"> </div></div></templete>老情况,在templete模板下的shopcart-wrapper内完成底部购物车一栏: 1 count大于0.让它打开 <!-- 左=>内容包含购物车icon 金额 配送费 --> <div class="content-left"> <div class="logo-wrapper" :class="{'highligh':totalCount>0}" @click="toggleList"> <span class="icon-shopping_cart logo" :class="{'highligh':totalCount>0}"></span> <i class="num" v-show="totalCount">{{totalCount}}</i> </div> <div class="desc-wrapper"> <p class="total-price" v-show="totalPrice">¥{{totalPrice}}</p> <p class="tip" :class="{'highligh':totalCount>0}">另需{{poiInfo.shipping_fee_tip}}</p> </div> </div> <!-- 去结算 --> <div class="content-right" :class="{'highligh':totalCount>0}"> {{payStr}} </div>搭建所选商品列表 如图所示,我们分好结构,紧接着搭建所选商品的列表 所选商品的列表 shopcart-list默认隐藏的,也就是说我们在没有选择食品的时候,点击购物车它不会展开。 1.list-hearder,左右结构包括1号口袋与清空购物车2.list-content 列表,存放我们选择的食物2.1左边是我们的食物名字,商品描述;右侧是数量,加减商品的组件。<div class="shopcart-list" v-show="listShow" :class="{'show':listShow}"> <!--列表顶部满减信息--> <div class="list-top" v-if="poiInfo.discounts2"> {{poiInfo.discounts2[0].info}} </div> <!--1号口袋 清空功能--> <div class="list-header"> <h3 class="title">1号口袋</h3> <div class="empty" @click="emptyFn"> <img src="./ash_bin.png" /> <span>清空购物车</span> </div> </div> <!--所选商品列表--> <div class="list-content" ref='listContent'> <ul> <li class="food-item" v-for="food in selectFoods"> <div class="desc-wrapper"> <!--左侧--> <div class="desc-left"> <!--所选商品名字--> <p class="name">{{food.name}}</p> <!--所选商品描述 unit 例 des 霆锋苦辣鸡腿堡1个--> <p class="unit" v-show="!food.description">{{food.unit}}</p> <p class="description" v-show="food.description">{{food.description}}</p> </div> <!--商品单价--> <div class="desc-right"> <span class="price">¥{{food.min_price}}</span> </div> </div> <!--复用商品增减组件 Cartcontrol--> <div class="cartcontrol-wrapper"> <Cartcontrol :food='food'></Cartcontrol> </div> </li> </ul> </div> <div class="list-bottom"></div> </div>加入遮罩层 ...

August 17, 2019 · 4 min · jiezi

Vue原理NextTick-源码版-之-宏微任务的抉择

写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧 【Vue原理】NextTick - 源码版 之 宏微任务的抉择 nextTick 已经写了三篇文章啦,这是最后一篇源码版,没看过的童鞋可以看看白话版简单了解下拉 【Vue原理】NextTick - 白话版 简单了解下NextTick 在前面的文章 NextTick-源码版之独立自身 中 埋下过两个问题 1、Vue 在哪里使用到了 宏任务和 微任务2、Vue 为什么需要 宏任务 和微任务今天的任务就是解决这两个问题!!! 在这里,大家肯定必须一定要了解了 宏任务和 微任务的哈,这两个东西不赘述了 首先,第一个问题就是宏微任务的使用场景场景 宏微任务的使用场景1、Vue 一般情况下使用的是微任务 2、在绑定DOM 事件的时候,会使用宏任务。 这么讲,有点笼统,准确地说,应该是 事件回调执行过程中,在JS 主线程为空之后,异步代码执行之前,所有通过 nextTick 注册的异步代码都是用宏任务。 来看看绑定DOM 事件的源码 通过 addEventListener 给 DOM 绑定事件 function add$1(event, handler) { handler = withMacroTask(handler); target$1.addEventListener(event, handler);}function withMacroTask(fn) { return fn._withTask || (fn._withTask = function() { useMacroTask = true; var res = fn.apply(null, arguments); useMacroTask = false; return res })}你看到了,把原先DOM 事件的回调包装了一遍,然后通过设置 useMacroTask 来控制注册宏任务 ...

July 15, 2019 · 2 min · jiezi

Vue原理NextTick-源码版-之-服务Vue

写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧 【Vue原理】NextTick - 源码版 之 服务Vue 初次看的兄弟可以先看 【Vue原理】NextTick - 白话版 简单了解下NextTick 好的,今天,就来详细记录 Vue 和 nextTick 的那些事 nextTick 在 Vue 中,最重要的就是~~~ 协助 Vue 进行更新操作! 上篇文章 NextTick-源码版之独立自身 提到过,nextTick 帮助 Vue 避免频繁的更新,这里简单提一下, 每次修改数据,都会触发数据的依赖更新 也就是说数据被修改的时候,会调用一遍【引用这个数据的实例】的更新函数 那么,按道理来说,修改3次,就应该调用3遍更新函数,但是实际上只会调用一遍 比如我们使用 watch 监听 data(data 便收集了 watch 的 watcher,监听回调就是更新函数) 结果就是只打印一次 至于依赖更新,可以看下面的文章 依赖更新 - 源码版 其实,修改数据能够只更新一次,不止是 nextTick 起了作用,Vue 也做了其他处理,比如过滤实例,清空队列等等,下面就来说一下 一切先从【实例更新函数】开始 ...

July 15, 2019 · 3 min · jiezi

Vue原理NextTick-源码版-之-独立自身

写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧 【Vue原理】NextTick - 源码版 之 独立自身 好的,今天到了 nextTick 的环节,之前我看的版本是 2.5.17,然后瞄了一眼 2.6 的,发现对于 nextTick 修改了 少部分内容,但是不太大,所以就一起记录下来 (如果改太多,就懒得看了.....反正了解一个思想以及实现思路就行了) nextTick 是一个在 Vue 中比较独立的东西,可以直接拿出来为你的项目服务 nextTick 涉及的点,就下面这些 1、任务队列callbacks2、任务队列执行函数 flushCallbacks3、控制(宏任务,微任务)注册标志位 pending4、宏任务,微任务没看懂?没关系,后面会慢慢说 这篇先讲 nextTick 自身,下篇再讲 nextTick 和 Vue 的关联 接下来就是一个个去详细记录了 宏任务,微任务这个知识点,很重要,也不算太简单,在网上也能找到很多很好的讲解,比如下面这篇文章,在这里不会特别解释这两个,毕竟主题不是这个 https://juejin.im/post/59e85e... 宏微任务的下面总结也是个人理解,有错尽管骂我 那么这里就先记录一下相关的结论 1、宏任务和微任务都是异步2、宏任务和微任务会被注册到两个不同的队列中3、宏任务队列不是一次性清空执行,而是执行一个宏任务时,然后去清空执行一列微任务队列接着再执行下一个宏任务.....循环往复,直到所有队列都为空 什么是一个宏任务比如 一个 setTimeout 就是一个宏任务,两个 setTimeout 就是两个宏任务 例子说明执行顺序比如现在,宏任务队列中有两个 setTimeout,微任务队列中有两个 Promise 假设现在正在执行第一个宏任务 setTimeout,执行完之后,会开始清空执行 微任务队列 于是开始执行了两个Promise 结束之后,接着执行 另一个宏任务, setTimeout 以前我以为是 宏任务队列执行完,再执行微任务队列,发现不是,很受伤,都是了解 nextTick 源码让我有机会重新了解了一遍 这个知识点 ...

July 15, 2019 · 2 min · jiezi

Vue原理NextTick-白话版

写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧 【Vue原理】NextTick - 白话版 nextTick 是 Vue 中比较重要的一部分,源码独立而简短,稍作修改就可以拿出来为你的项目服务, 我已经有在项目中使用了 想必大家写 Vue 项目的时候,应该也有使用过 nextTick 一般我是用在数据渲染完毕之后执行某些操作 this.list =[xx,xx,xx]this.$nextTick(()=>{ this.isLoading=false})nextTick 按我的理解,就是设置一个回调,用于异步执行 异步执行,比如,就是把你设置的回调放在 setTimeout 中执行,这样就算异步了,等待当时同步代码执行完毕再执行 但是,每设置一个 nextTick 就新建一个 setTimeout 又不实际, 毕竟一个 setTimeout 是异步,两个setTimeout 也是异步,两个都要等在 同步代码执行完毕之后才执行 那我直接只设置一个 setTimeout 不就好了 那一个 setTimeout 怎么执行多个回调呢?1 存在 回调数组 里。每次调用 nextTick,便往数组里面 push 设置的回调 2 只注册一个 setTimeout,时间为0,用于遍历 回调数组,然后逐个执行子项 3 同步代码执行完毕,setTimeout 自然会执行 Vue 不止使用 setTimeoutVue的 nextTick 也是只用setTimeout 吗,不是的,这里便会涉及到 javascript 的 宏微任务 ...

July 15, 2019 · 1 min · jiezi

Vue实战菜单栏商品展示数据交互8

上篇我们将菜单栏,商品展示结构完成,本次我们为这两个部分接入数据。 菜单栏接入数据 导入组件,定义需要的数据格式 <script>// 导入BScroll 滚动组件import BScroll from "better-scroll";// 导入Food 商品页面import Food from "components/Food/Food";export default { data() { //准备需要的数据,初始化组件。 return { container: {}, goods: [], poiInfo: {}, listHeight: [], scrollY: 0, menuScroll: {}, foodScroll: {}, selectedFood: {} }; }, //引用组件 components: { BScroll, Food }};</script>通过created钩子发起请求获取数据 之前我们说过在created钩子,mounted钩子内可以发起请求,请求需要的数据。本次我们在created钩子内发起get请求,获取数据 created() { //通过that改变.then中的this指向 var that = this; // 通过axios发起get请求 this.$axios .get("/api/goods") .then(function(response) { // 获取到数据 var dataSource = response.data; if (dataSource.code == 0) { that.container = dataSource.data.container_operation_source; that.goods = dataSource.data.food_spu_tags; that.poiInfo = dataSource.data.poi_info; // 调用滚动的初始化方法 // that.initScroll(); // 开始时,DOM元素没有渲染,即高度是问题 // 在获取到数据后,并DOM已经被渲染,表示列表高度是没问题的 // nextTick that.$nextTick(() => { // DOM已经更新 that.initScroll(); // 计算分类区间高度 that.calculateHeight(); }); } }) .catch(function(error) { // 出错处理 console.log(error); }); },注意$nextTick()用法,在dom更新后。我们执行初始化滚动函数。 ...

July 13, 2019 · 2 min · jiezi

Vue原理Event-源码版-之-绑定组件自定义事件

写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧 【Vue原理】Event - 源码版 之 绑定组件自定义事件 组件自定义事件其实是我最感兴趣的,我当时花了好多时间去探索的哈哈哈,探索完了之后,发现很简单的,可以先看下白话版了解下 【Vue原理】Event - 白话版 我当时脑海中就几个问题,我很想弄懂啊 1、父给子绑定的事件,存放在父组件还是子组件?2、父给子绑定自定义事件,子组件为什么可以触发?3、子组件触发事件后,是怎么调用绑定的 父组件的方法的?看看当时做的笔记时间,已经过了好久了啊 笔记看着很混乱,所以下定决心写文章,写得详详细细的,然自己一眼就明白,而且怕以后自己忘记 首先肯定是为自己服务的,只是顺便分享给大家,能帮到别人少走弯路而已哈哈哈 好的,不多说了,马上进入主题~~看完文章的欢迎到下面投票啊啊啊啊 怎么解析同样,一个给组件绑定自定义事件的模板 然后template 解析成下面的渲染函数 渲染函数执行,生成这样的组件外壳VNode 还可以打印组件实例看一下 你可以看到,绑定的自定义事件,存在了 组件外壳VNode的 componentOptions.listeners 中 等下! 渲染函数中,明明把事件解析放在 on 的啊,怎么到 listeners了 这里记录一下哈 当 _c('test') 执行的时候,因为是组件,所以内部会特别调用 createComponent 去生成组件的VNode 而这个VNode 是外壳VNode 下面的源码中可以很清楚的看到 1、把 on 赋值给了 listeners 2、listeners 传给了 VNode 构造函数,保存到了 vnode.componentOptions function createComponent( Ctor, data, context, children, tag) { var listeners = data.on; data.on = data.nativeOn; var vnode = new VNode( tag, // 组件名字 data, undefined, undefined, undefined, context, // 下面这个对象就存在 vnode.componentOptions { listeners: listeners, } ); return vnode}function VNode( tag, data, children, text, elm, context, componentOptions ) { this.componentOptions = componentOptions;};想了解多一点Vnode可以看 VNode - 源码版 ...

July 10, 2019 · 2 min · jiezi

Vue原理Event-源码版-之-自定义事件

写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧 【Vue原理】Event - 源码版 之 自定义事件 Vue 的自定义事件很简单,就是使用 观察者模式 进行事件的监听和分发 Vue 封装的这个观察者模式,可以说是很完善了,这个可以独立抽取出来的在其他项目中使用的代码,只需要做一点点改动,把事件存储器换个地方(Vue 放在了实例上) 我经常在项目中使用,就是为了解耦或者解决一些异步的问题 今天来详细探索 Vue 的 自定义事件 首先,Vue 的事件存储器放在那里? 没错,放在 vm._events 中 你看,比如你这样监听事件 看到实例上保存了你的事件 1、事件存储器vm._events 看下这个事件存储器在哪里生成的 首先,实例在初始化的时候,给实例增加一个事件存储器 _events Vue.prototype._init = function(options) { initEvents(vm); //...初始化选项数据,解析模板,挂载dom等}function initEvents(vm) { vm._events = Object.create(null);}以后,所有这个实例监听的事件,就都存在这里了 那么,接下来就来看 自定义事件的源码了 下面的源码比较不太属于 Vue 的内容,比较独立,很实用,相信大家也都看得懂,这里主要起一个记录的作用 下面会有四个函数绑定事件,$on 一次性绑定事件,$once 触发事件,$emit 解绑事件,$off 2、$on注册事件,接收 事件名和回调,很清楚了,都能看得懂 Vue.prototype.$on = function(event, fn) { var vm = this; if (Array.isArray(event)) { for (var i = 0,l = event.length; i < l; i++) { this.$on(event[i], fn); } } else { (vm._events[event] || (vm._events[event] = [])).push(fn); } // 为了链式调用 return vm};3、$once单次注册。只监听一次,触发之后马上销毁 ...

July 10, 2019 · 2 min · jiezi

Vue原理Event-源码版-之-绑定标签DOM事件

写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧 【Vue原理】Event - 源码版 之 绑定标签DOM事件 这里的绑定DOM事件,是指绑定原生标签的DOM 事件 因为组件也是可以绑定原生DOM事件的,不过并不是在原生标签上绑定,而是直接在组件上绑定的,这部分内容会其他文章说明 或者你可以看看白话版先了解下Event 【Vue原理】Event - 白话版 怎么解析由于解析不是本内容的重点,所以在这里就不谈怎么解析的了,只说一个结果就好了 现在有这么一个模板 模板被解析成这样的渲染函数 渲染函数执行之后,得到这样的 VNode 你可以看到,事件被存放到了 vnode.data 上 Vnode 有疑惑的可以看介里 【Vue原理】VNode - 源码版 怎么绑定既然模板已经被解析完成了,下一步就是开始绑定了 好的,继续来走流程 在 template 解析得到 Vnode 之后,下面就会进行DOM生成挂载 而绑定事件,就发生在开始挂载,创建DOM 之后 的阶段 挂载时从 Vue.prototype._update 这个函数开始的 挂载的流程,可以看看这篇文章 从模板到DOM的简要流程 1、开始挂载VNode创建完毕,传入 Vue.prototype._update 这个方法中,进行比对新旧VNode 然后生成DOM挂载页面 其中需要生成DOM,调用的方法是 createElm 2、创建DOM创建DOM,在Vue 中调用的是 createElm 这个方法 看过以前的文章的,都知道这个函数的作用是 ...

July 10, 2019 · 2 min · jiezi

Vue原理Event-源码版-之-绑定组件DOM事件

写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧 【Vue原理】Event - 源码版 之 绑定组件DOM事件 上一篇已经说了绑定正常标签的原生事件,今天是组件的原生事件,两者最终作用是一样的,但是过程有少少不同 最近更新快是因为文章早就写完了,只是定时发而已哈哈 官网已经说明,要是想在组件上绑定原生事件,需要加上 native 修饰符 怎么解析这里一样,解析不是本文重点,只给出结果 现在,我有这么一个模板 被解析成这样的渲染函数然后生成这样的 VNode 这个VNode 是 外壳vnode,我们已经知道外壳 vnode 是保存 父子组件关联的数据 比如 props,事件之类的 所以你在组件上绑定的原生事件,自然而然就是保存在 外壳vnode 上啦 绑定在 外壳vnode 的数据,是要在解析组件内部模板时,才会拿出来使用的 然后! 你可以看到,nativeOn 和 on 都保存有事件 其实解析的时候,只保存在了 nativeOn,on 是 后面 从 nativeOn 直接赋值过去的 打印组件实例可以看到 耶!Vnode 相关又可以看这篇哦 【Vue原理】VNode - 源码版 怎么绑定绑定的流程千篇一律,但是有少少出入 可以参考我这篇,绑定原生事件的文章 【Vue原理】Event - 源码版 之 绑定标签DOM事件 ...

July 10, 2019 · 2 min · jiezi

Vue原理Event-白话版

写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧 【Vue原理】Event - 白话版 Vue 事件是我最感兴趣的东西之一,一直想研究玩玩 特别是组件自定义事件,很想知道,给子组件绑定自定义事件,子组件是怎么触发的 巴拉巴拉的 开始正文了 在 Vue 中,事件大概分为 4 种 1、自定义事件 2、DOM 事件 3、组件DOM 事件 4、组件自定义事件 然后再细分,就只有两种 1、自定义事件 2、DOM 事件 下面就来粗略说一下事件 自定义事件自定义事件 是使用观察者模式建立起来的一种事件机制 分为 个人使用 和 组件使用 自定义事件主要由下面两部分构成 1、有事件存储器 2、绑定事件,触发事件,解绑事件三个函数 在 Vue 中,每个实例都会添加一个属性_events,用来存放本实例上注册的自定义事件 _events 就是 事件存储器,是一个对象 属性名是 事件名,属性值是事件回调 个人调用自定义事件我也经常在项目中使用到 自定义事件,像下面这样 没错,$on 就是注册事件,$emit 是 触发事件,$off 就是 解绑事件 在哪个实例上注册的事件,事件就属于哪个实例,正常情况下,你是不可能能触发别的实例上的事件的 而给组件绑定自定义事件 是怎么样的呢? 结果也是一样的,前面的解析处理可以不管,最后同样是使用内置的 $on 方法去注册事件 ...

July 10, 2019 · 1 min · jiezi

Vue原理Component-源码版-之-挂载组件DOM

写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧 【Vue原理】Component - 源码版 之 挂载组件DOM 由这篇文章 从模板到DOM的简要流程 我们知道,在生成 VNode 之后,下一步就是根据 VNode 生成DOM然后挂载了 在本文开始之前你可以先看 Component - 白话版 先整体了解下component 现在开始我们的正文 上一篇文章 Component - 创建组件VNode ,我们已经说到了 【页面模板解析成 VNode 树】的步骤 那今天就就到了 【页面VNode生成DOM挂载】 了 等等,今天说的不是 Component 挂载DOM 吗?跟页面Vnode 有什么关系??是啊,component 的挂载肯定是跟着父页面的啊,你自己挂?自挂东南枝吗? 好了,废话不说,马上开始 前言预告这篇 从模板到DOM的简要流程 已经说过下面的步骤 1vm._render 执行得到 页面VNode 2vm._update 拿到 页面VNode ,会开始 patch,不断比对 【旧VNode 和 刚拿到的新VNode】 对比完之后,会调用一个 createElm 的方法去创建DOM,然后插入页面 那现在,我们就从 createElm 这个方法突破,前面的流程跟本内容无关,一律略过 ...

July 9, 2019 · 2 min · jiezi

Vue原理Component-源码版-之-创建组件VNode

写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧 【Vue原理】Component - 源码版 之 创建组件VNode 今天就要开启我们 Component 探索之旅,旅途有点长,各位请坐好,不要睡着了 内容的主题是,Component 的创建过程,从调用 component,到 component 挂载,到底经历了什么?欢迎来到 component 的内心世界 建议可以先看看白话版 Component - 白话版 Component 创建,我主要分了两个流程 1、创建 组件 VNode2、挂载 组件 DOM每个流程涉及源码都很多,所以每个流程写一篇文章。没错了,今天讲的就是 创建组件 VNode 场景设置首先,我们假定现在有这么一个模板,使用了 test 组件 然后页面噼里啪啦执行到了 准备挂载DOM 的步骤(之前的部分跟本主题无关,跳过) 然后页面准备执行渲染函数 render,嗯,就是执行上面模板生成的渲染函数,如下 没有错,我们的 Vue 已经走到了这一步,那么我们的突破口是什么? 没错,就是 _c function _createElement( context, tag, data, children) { var vnode; var options = context.$options // 从父组件选项上拿到 对应的组件的选项 var Ctor = options.components[tag] if (正常的HTML标签) { ....直接新建VNode } else if ( Ctor ) { vnode = createComponent( Ctor, data, context, children, tag ); } return vnode}今天讲的是 component,跳过其他,直接走到 第二个 if,嗯,他调用了一个 createComponent ...

July 9, 2019 · 2 min · jiezi

Vue原理从模板到DOM的简要流程

写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧 【Vue原理】从模板到DOM的简要流程 今天的计划是,探索Vue模板挂载到页面是怎么样的一个流程,内容是指 正常 HTML 标签的模板挂载,这部分内容很重要。 而这部分内容也是为了 讲解 Component 作为铺垫,因为到最后 Component 必然也是作为一个正常标签去挂载,所以先把这部分抽出来讲 首先,这个流程,个人认为可以分为两大部分,分别是 init 和 mount 顾名思义,init 必定是和初始化有关,mount 和 挂载DOM 有关 Init首先,当你开始调用 Vue 的时候,比如这样 // jsnew Vue({ el: document.getElementsByTagName("div")[0],})// html,够简洁了吧<div></div>那么,先进入的肯定是 Vue 这个构造函数,呈上来! function Vue(options) { this._init(options);}Vue.prototype._init = function(options) { // 初始化 选项,computed,data 之类的 // 初始化实例,给实例绑定些方法 // 触发 beforeCreated,created 钩子}这个 _init 方法,是构建Vue 实例的时候调用的,而创建Vue 实例,并非只有通过 new Vue 创建,有可能是 Vue 内部创建的,比如 component ...

July 8, 2019 · 2 min · jiezi

Vue原理Component-白话版

写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧 【Vue原理】Component - 白话版 component 从模板上使用到挂载到页面上,到底经历了一个怎么样的流程??里面到底掺杂了什么神奇的东西,母猪为何半夜惨叫,这一切的背后,究竟是.... component 从模板上使用到挂载到页面上,到底经历了一个怎么样的流程??里面到底掺杂了什么神奇的东西,母猪为何半夜惨叫,这一切的背后,究竟是.... 好吧,马上进入主题,component 挂载流程 好了,既然说的是 component,那么其他的无关步骤自然是要略过的 总的说起来,component 创建流程,就两个步骤 1、创建 component 外壳VNode2、挂载 component dom我们一步一步来说 创建组件vnode“这里说的组件vnode,是外壳vnode,不懂下面会说” 现在有一个页面A 使用是了 test 组件 然后页面被解析成了一个渲染函数 现在要开始执行页面A渲染函数,这个渲染函数执行得到 【模板对应的 VNode】 其中 _c 的作用就是,根据传入的参数,构造相应的 VNode 执行到 _c('test'),需要构建一个标签为 test 的 vnode,但是发现,诶?test 不是一个正常的 html 标签啊 于是送去非正常标签研究院进行研究 ,哈哈,就是去做一些特殊处理 做的是什么呢? 1、构建组件的构造函数,处理父组件给子组件绑定的数据,比如 props,事件,slot 等等 2、创建组件外壳VNode,就是下面这个 相信大家应该清楚什么是外壳节点了,细节可以跳到下文相关内容看 VNode - 源码版 ...

July 8, 2019 · 1 min · jiezi

Vue原理VNode-源码版

写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧 【Vue原理】VNode - 源码版 今天就来探索 VNode 的源码,VNode 是 Vue2 渲染机制中很重要的一部分,是深入Vue 必须了解的部分 我们以4个问题来开始我们的探索 1、vnode 是什么及其作用2、vnode 什么时候生成3、vnode 怎么生成4、vnode 存放什么信息5、vnode 存放在哪里文章很长,看之前值做好准备 VNode是什么及作用首先,第一个问题已经很烂了,网上有很多相关的内容,为了内容的完整性,所以也放上来哈哈。 VNode 表示 虚拟节点 Virtual DOM,为什么叫虚拟节点呢,因为不是真的 DOM 节点。 他只是用 javascript 对象来描述真实 DOM,这么描述,把DOM标签,属性,内容都变成 对象的属性 就像用 JavaScript 对象描述一个人一样 {sex:'男', name:'神仙朱', salary:5000,children:null}过程就是,把你的 template 模板 描述成 VNode,然后一系列操作之后通过 VNode 形成真实DOM进行挂载 是什么? JavaScript 对象 什么用? 1、兼容性强,不受执行环境的影响。VNode 因为是 JS 对象,不管 Node 还是 浏览器,都可以统一操作, 从而获得了服务端渲染、原生渲染、手写渲染函数等能力 ...

July 7, 2019 · 3 min · jiezi

Vue原理生命周期-源码版

写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧 【Vue原理】生命周期 - 源码版 好的!今天探索Vue的生命周期,鉴于生命周期这个东西很简单,所以直接写源码版了 简单到什么程度呢,就是直接执行你的 created 什么的,只是分在什么时候执行而已 但是!我们仍然要分两个问题,理清思路方便记忆 1、生命钩子怎么触发2、生命钩子在什么时候触发钩子怎么触发首先,我设置了下面的例子 那么 el 和 created 就是你传入 Vue 的自定义选项啦 1、把所有同类钩子先合并成数组,然后存放在 vm.$options 这个点跟 mixins 有关,可以看这篇下对钩子的合并处理 【Vue原理】Mixins - 源码版 合并,主要是为了把全局设置的钩子和 组件自定义的钩子合并起来,就算你没有全局钩子,也要存在数组里面,比如 created 是下面 vm.$options={ created:[fn,fn,fn...]}2、初始化设置一些标志位,表明是否已经完成某种钩子 function initLifecycle(vm) { vm._isMounted = false; vm._isDestroyed = false; vm._isBeingDestroyed = false;}这个函数会在 beforeCreated 钩子触发前调用,在 Vue.prototype._init 中,下个问题源码有显示。其中的标志位什么时候设置呢,是在相应的钩子触发之后,具体看下面源码 3怎么执行钩子呢 没错,就是下面这个函数 function callHook(vm, hook) { // 是自己传入的 created 等回调 var handlers = vm.$options[hook]; if (handlers) { for (var i = 0,j = handlers.length; i < j; i++) { handlers[i].call(vm); } }}那是怎么用呢? ...

July 7, 2019 · 1 min · jiezi

Vue原理Computed-源码版

写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧 【Vue原理】Computed - 源码版 今天要记录 computed 的源码,有时候想,理解不就好了吗,为什么要记录一遍源码。现在终于想通了 过了一段时间之后,你就会忘记你的所谓理解是怎么来的 “哎,为什么会这么做,关系为什么是这样,我c....” 于是,记录并简化源码,就有助我们迅速找到根源,解决我们的疑惑,还能加强我们的理解 好吧 嗯,这篇文章很长很详细哦,做好阅读的准备,唔该 我们重点说明,几个问题的源码实现 1、computed 的 月老身份的来源2、computed 怎么计算3、computed 的缓存是怎么做的4、computed 什么时候初始化5、computed 是怎么可以直接使用实例访问到的问题不会按顺序解析,因为这些问题会互相关联,在探索源码的过程中,你自然会得到答案 首先,从这个问题开始我们今天的探索之旅,请看源码 什么时候初始化function Vue(){ ... 其他处理 initState(this) ...解析模板,生成DOM 插入页面}function initState(vm) { var opts = vm.$options; if (opts.computed) { initComputed(vm, opts.computed); } .....}没错,当你调用 Vue 创建实例过程中,会去处理各种选项,其中包括处理 computed 处理 computed 的方法是 initComputed,下面就呈上 源码 initComputedfunction initComputed(vm, computed) { var watchers = vm._computedWatchers = Object.create(null); for (var key in computed) { var userDef = computed[key]; var getter = typeof userDef === 'function' ? userDef: userDef.get; // 每个 computed 都创建一个 watcher // watcher 用来存储计算值,判断是否需要重新计算 watchers[key] = new Watcher(vm, getter, { lazy: true }); // 判断是否有重名的属性 if (! (key in vm)) { defineComputed(vm, key, userDef); } }}initComputed 这段代码做了几件事 ...

July 6, 2019 · 3 min · jiezi

Vue原理Props-源码版

写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧 【Vue原理】Props - 源码版 今天记录 Props 源码流程,哎,这东西,就算是研究过了,也真是会随着时间慢慢忘记的。 幸好我做了详细的文章,忘记了什么的,回忆起来必然是很快的。 好的,回到正题,Props 请你在读这篇之前,先去看看我的白话版 【Vue原理】Props - 白话版 在上面这篇文章中,也已经清楚地解决了一个问题 父组件 如何 把数据 当做 props 传给子组件 所以今天,我们只需记录 Props 的处理流程源码即可 初始化在创建Vue实例的过程中,会调用 initState 处理options,比如 props,computed,watch 等 只要你 new Vue 创建实例之后,很快就会处理options function Vue(){ ... 其他处理 initState(this) ...解析模板,生成DOM 插入页面}function initState(vm) { var opts = vm.$options; if (opts.props) { initProps(vm, opts.props); } ... 处理 computed,watch,methods 等其他options}initProps你看到处理 Props ,主要用到了一个方法 initProps,他就是本场的焦点了,让我们来采访下源码本码 ...

July 6, 2019 · 2 min · jiezi

Vue原理Watch-源码版

写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧 【Vue原理】Watch - 源码版 今天继续探索 Watch 源码,废话不多说了 带着我的几个疑问开始 1、什么时候初始化2、怎么确定监听哪些值3、深度监听怎么回事4、怎么触发我的函数这些问题的答案会掺杂在源码的解析中,我发现这几篇的写作套路都差不多..... 也可以看查一下我的白话版 什么时候初始化首先,从这个问题开始我们今天的探索之旅,请看源码 function Vue(){ ... 其他处理 initState(this) ...解析模板,生成DOM 插入页面}function initState(vm) { ...处理 data,props,computed 等数据 if (opts.watch) { initWatch(this, vm.$options.watch); }}没错,当你调用 Vue 创建实例过程中,会去处理各种选项,其中包括处理 watch initWatch处理 watch的方法是 initWatch,下面就呈上 源码 function initWatch(vm, watch) { for (var key in watch) { var watchOpt = watch[key]; createWatcher(vm, key, handler); }}然后这段源码并没有做什么惊天地泣鬼神的事情,只是简简单单的一个遍历,然后每个watch 都使用一个叫什么 createWatcher 的东西去处理 ...

July 6, 2019 · 2 min · jiezi

Vue原理Mixins-源码版

写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧 【Vue原理】Mixins - 源码版 今天探索的是 mixins 的源码,mixins 根据不同的选项类型会做不同的处理 篇幅会有些长,你知道的,有很多种选项类型的嘛,但不是很难。只是涉及源码难免会有些烦, 不过这篇文章也不是给你直接看的,是为了可以让你学习源码的时候提供微薄帮助而已 如果不想看源码的,可以看我的白话版 【Vue原理】Mixin - 白话版 我们也是要带着两个问题开始 1、什么时候开始合并 2、怎么合并 如果你觉得排版难看,请点击下面原文链接 或者 关注公众号【神仙朱】 什么时候合并合并分为两种 1、全局mixin 和 基础全局options 合并 这个过程是先于你调用 Vue 时发生的,也是必须是先发生的。这样mixin 才能合并上你的自定义 options Vue.mixin = function(mixin) { this.options = mergeOptions( this.options, mixin ); return this};基础全局options 是什么? 就是 components,directives,filters 这三个,一开始就给设置在了 Vue.options 上。所以这三个是最先存在全局options Vue.options = Object.create(null);['component','directive','filter'].forEach(function(type) { Vue.options[type + 's'] = Object.create(null);});这一步,是调用 Vue.mixin 的时候就马上合并了,然后这一步完成 以后,举个栗子 ...

July 6, 2019 · 3 min · jiezi

Vue原理依赖更新-源码版

写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧 【Vue原理】依赖更新 - 源码版 如果对依赖收集完全没有概念的同学,可以先看我这篇白话版 响应式原理 - 白话版 我们已经讲过了 依赖收集 【Vue原理】依赖收集 - 源码版之基本数据类型 【Vue原理】依赖收集 - 源码版之引用数据类型 现在就要看依赖更新了哈哈哈,毕竟收集完是要更新的嘛 其实依赖更新挺简单的,就是两步 修改属性值通知保存的依赖进行更新重点只需要看 Object.defineProperty 设置的set 函数,当给数据重赋新值的时候,自然会触发 set 函数,完成依赖更新 function defineReactive(obj, key, val) { var dep = new Dep(); var childOb = observe(val); Object.defineProperty(obj, key, { get(){ ... 属性被读取,完成依赖收集 // 返回闭包值 return val }, set(newVal) { // 值没有变化 if (newVal ===val) return // 修改闭包值 val = newVal; // 如果属性已经存在过,设置新值的时候,会重新调用一遍 childOb = observe(newVal); // 触发更新 dep.notify(); } }); }依赖更新重点就重在 通知更新 ...

July 6, 2019 · 2 min · jiezi

Vue组件库工程探索与实践构建工具篇

我们团队近期发布了移动端 Vue 组件库 NutUI 的 2.0 版,2.0 不是 1.0 的升级,而是一个全新的组件库。从 1.0 到 2.0 一路走来,我们积累了一些 Vue 组件库的开发经验,接下来的一段时间,我们将以系列文章的形式与大家进行分享,欢迎大家关注。作为《Vue组件库工程探索与实践》系列文章开篇之作,我们从“盘古开天地”说起吧。 从当年的静态页面到如今的 Web App,前端工程越来越复杂,对于一个稍大些的前端项目来说,代码都写在一起难以维护,团队分工协作也成问题。根据软件工程领域的经验,解决这些问题的一个可行思路就是代码的模块化,即对代码按功能模块进行分拆,封装成组件,而反过来讲,组件就是指能完成某个特定功能的独立的、可重用的代码块。 把一个大的应用分解成若干小的组件,而每个组件只需要关注于某个小范围的特定功能,但是把组件组合起来,就能构成一个功能庞大的应用。组件化的网页开发也是如此,就像搭积木,各个组件拼接在一起就组成了一个完整的页面。 组件化开发可大大降低代码耦合度、提高代码的可维护性和开发效率,同时有利于团队分工协作和降低开发成本。这种开发模式已日渐流行起来。 当前,前端开发领域最流行的三大框架 Vue、React、Angular 都推崇组件化开发,组件是这些框架中极为重要的概念和功能。 以 Vue.js 来说,组件 (Component) 可以说是其最强大的功能,它可以扩展 HTML 元素,封装可重用的代码。Vue.js 的组件系统让我们可以用这些独立可复用的小组件来构建大型 Vue 应用,几乎任意类型的 Vue 应用的界面都可以抽象为一个组件树。 如果我们把日常应用开发中常用的组件累积起来,后续的项目就可以复用这些组件,这对提高开发效率、降低开发成本有重要意义。 因此,一个前端团队拥有一个常用框架的组件库是十分必要的。 模块化与构建工具组件库自身就是一个大的工程,需要按照模块化开发思想进行模块划分。通常,在一个组件库里,组件、组件的样式文件、配置文件…都是模块,而最终我们需要把这些模块组合成一个完整的组件库文件,承担这种组装工作的就是打包构建工具。当下主流的库构建工具主要有 Rollup 和 Webpack 等。在说这些模块打包构建工具之前,我们先来了解一下目前主流的 JavaScript 模块化方案。 JavaScript 语言一直以来饱受诟病的一个地方就是它的语言标准里没有模块(module)体系,这对开发大型的、复杂的项目形成了巨大障碍。直到 ES6 时期,才在语言标准层面实现模块功能(ES6 Module)。在 ES6 之前,业界流行的是社区制定的一些模块加载方案,如 CommonJS 和 AMD 。而 ES6 Module 作为官方规范,且浏览器端和服务器端通用,未来一定会一统天下,但由于 ES6 Module 来的太晚,受限于兼容性等因素,可以预见的是今后一段时期内,多种模块化方案仍会共存。 ES6 Modue 规范:JavaScript 语言标准模块化方案,浏览器和服务器通用,模块功能主要由 export 和 import 两个命令构成。export 用于定义模块的对外接口,import 用于输入其他模块提供的功能。CommonJS 规范:主要用于服务端的 JavaScript 模块化方案,Node.js 采用的就是这种方案,所以各种 Node.js 环境的前端构建工具都支持该规范。CommonJS 规范规定通过 require 命令加载其他模块,通过 module.exports 或者 exports 对外暴露接口。AMD 规范:全称是 Asynchronous Modules Definition,异步模块定义规范,一种更主要用于浏览器端的 JavaScript 模块化方案,该方案的代表实现者是 RequireJS,通过 define 方法定义模块,通过 require 方法加载模块。一些“上年纪”的国内前端老艺人们可能还会提到 CMD 规范,它是 SeaJS 在推广过程中对模块定义的规范化产出,只是 SeaJS 并未实现国际化,且项目在2015年就已宣布停止维护了,算不上当前主流模块化方案。介绍完主流模块化规范,我们再回过头来看 Rollup 和 Webpack 这两个模块打包构建工具。 ...

July 5, 2019 · 3 min · jiezi

Vue原理依赖收集-源码版之基本数据类型

写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧 【Vue原理】依赖收集 - 源码版之基本数据类型 如果对依赖收集完全没有概念的同学,可以先看我这篇 【Vue原理】响应式原理 - 白话版 依赖收集,主要是为了解决一个问题,什么问题呢? 首先,我们都知道,Vue 的数据是响应式更新的,一旦数据改变了,那么相应使用到 数据的地方也会跟着改变。 那么问题来了,数据改变的时候,Vue 怎么知道,去让那些使用到数据的地方也改变呢? 这就是依赖收集要解决的问题! 他是怎么解决的?简单说就是把依赖了数据的地方,给集中收集起来以便变化后通知!我们今天来看源码的流程 首先,响应式更新,分为两步,依赖收集和依赖更新 今天讲的是依赖收集,如何去收集 使用了数据的地方 依赖收集,又分为两个流程 1、数据初始化流程 2、依赖收集流程 当前篇,先以基本数据类型为例讲解,因为 基本数据和 引用数据 在处理上有很大的不同,引用类型需要理解的东西更多更复杂,所以需要循序渐进,分两篇描述 数据初始化流程首先,在实例初始化的时候,需要对数据进行响应式处理,也就是给每个属性都使用 Object.defineProperty 处理 处理的流程是怎么样的呢? 1、实例初始化中,调用 initState 处理部分选项数据,initData 用于处理选项 data Vue.prototype._init=function(){ ... initState(this) ...}function initState(vm) { var opts = vm.$options; ... props,computed,watch 等选项处理 if (opts.data) { initData(vm); }};2、initData 遍历 data,definedReactive 处理每个属性 ...

July 5, 2019 · 2 min · jiezi

Vue原理依赖收集-源码版之引用数据类型

写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧 【Vue原理】依赖收集 - 源码版之引用数据类型 上一篇,我们已经分析过了 基础数据类型的 依赖收集 【Vue原理】依赖收集 - 源码版之基本数据类型 这一篇内容是针对 引用数据类型的数据的 依赖收集分析,因为引用类型数据要复杂些,必须分开写 文章很长,高能预警,做好准备耐下心好,肯定还是有点收获的 但是两个类型的数据的处理,又有很多重复的地方,所以打算只写一些差异性的地方就好了,否则显得废话很多 两个步骤,都有不同的地方 1、数据初始化 2、依赖收集 数据初始化流程如果数据类型是引用类型,需要对数据进行额外的处理。 处理又分了 对象 和 数组 两种,会分开来讲 1对象 1、遍历对象的每个属性,同样设置响应式,假设属性都是基本类型,处理流程跟上一篇一样 2、每个数据对象会增加一个 ob 属性 比如设置一个 child 的数据对象 下图,你可以看到 child 对象处理之后添加了一个 ob 属性 ob_ 属性有什么用啊? 你可以观察到,__ob__ 有一个 dep 属性,这个 dep 是不是有点属性,是的,在上一篇基础数据类型中讲过 那么这个 ob 属性有什么用啊? 你可以观察到,__ob__ 有一个 dep 属性,这个 dep 是不是有点属性,是的,在上一篇基础数据类型中讲过 ...

July 5, 2019 · 3 min · jiezi

Vue原理Slot-源码版之作用域插槽

写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧 【Vue原理】Slot - 源码版之作用域插槽 今天探索Slot的另一部分,作用域插槽。 首先,设置一个模板例子 把子组件的 child 传给 插槽 父组件会解析成下面的渲染函数 with(this) { return _c('div', {}, [_c('test', { scopedSlots: _u([{ key: "default", fn: function(slotProps) { return [ "我是放在组件的 slot :"+slotProps ] } }]) })], 1)}其中,_u 是 resolveScopedSlots,Vue会给每个实例都注册一个 _u 方法。 作用主要是把数组变成对象map并返回 看下 resolveScopedSlots 源码 给每个实例注册 _u function resolveScopedSlots(fns, res) { res = res || {}; for (var i = 0; i < fns.length; i++) { res[fns[i].key] = fns[i].fn; } return res}把传入的数组组装成对象,像是下面这样 ...

July 4, 2019 · 2 min · jiezi

Vue原理Slot-源码版之普通插槽

写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧 【Vue原理】Slot - 源码版之普通插槽 今天我们来解读Slot 的源码啦。我们都知道 Slot 分为 普通Slot 和 作用域Slot,两个内容都很多,所以分两部分进行讲述。 今天讲的是普通Slot! 其实普通Slot,表示默认Slot和 具名Slot,只是他们的处理方式都差不多,就只是是否有自定义名字而已,所以,表示一种类型。 而我们就以默认Slot为例去探索,让我们先设置一个模板例子 父组件模板 test 组件被定义在父组件中 new Vue({ el: document.getElementsByTagName("div")[0], components: { test: { template: ` <main> 我在子组件里面 <slot></slot> </main> ` } }, data() { return { name: 11 } }})分两个问题去看 1、插槽内容怎么解析 2、插槽如何插子页面 插槽内容怎么解析插槽的作用域,是父实例。就是说,普通插槽的变量,都是从父实例上获取的,比如上面例子插槽内的name 根据上面的例子,父组件被解析成下面的渲染函数 with(this) { return _c('div', {}, [ _c('test', [ "我是放在组件的 slot " + name ]) ], 1)}父渲染函数执行时,会绑定父实例为执行作用域,根据 with 的作用,test 的 slot 内的变量name,就会访问父实例上的name。 ...

July 4, 2019 · 2 min · jiezi

Vue原理Directives-源码版

写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧 【Vue原理】Directives - 源码版 咦,上一篇我们已经讲过白话版啦,主要的逻辑大家应该也清楚了的,今天我们就直接开干源码。有兴趣读源码的同学,希望对你们有帮助哦~ 没看过白话版的,还是先别看源码版了,那么多代码看了估计会懵逼... 首先,上一篇说过,Vue 会在DOM 创建之后,插入父节点之前。对DOM绑定的事件和属性等进行处理,其中包含指令。 Vue 有专门的方法来处理指令,这个方法是 updateDirectives,其作用,获取指令钩子,和对不同钩子进行不同处理。 updateDirectives 的源码不是很短,其中还涉及其他方法,不打算一次性放出来,打算一块一块分解地讲,所以 源码会被我分成很多块 今天我们以两个问题开始 1、怎么获取到设置的指令钩子 2、内部怎么调用钩子函数 还有,模板上指令会被解析成数组,比如下面这个模板 会被解析成下面的渲染函数,看下其中的 directives,这就是指令被解析成的终极形态了。下面 updateDirectives 方法处理指令,处理的就是这个数组 with(this) { return _c('div', { directives: [{ name: "test", rawName: "v-test" },{ name: "test2", rawName: "v-test2" }] })}怎么获取设置的指令钩子在 updateDirectives 中,处理的是指令的钩子,那么第一步肯定是要先获取钩子啊,不要处理个锤子。 function updateDirectives(oldVnode, vnode) { // 获取旧节点的指令 var oldDirs = normalizeDirectives$1( oldVnode.data.directives, oldVnode.context); // 获取新节点的指令 var newDirs = normalizeDirectives$1( vnode.data.directives, vnode.context); }你也看到了,上面的源码中有一个 normalizeDirectives$1,他就是获取钩子的幕后黑手。 ...

July 3, 2019 · 3 min · jiezi

Vue原理Slot-白话版

写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧 【Vue原理】Slot - 白话版 插槽作为组件一个重要的部分,在项目中也是经常会用到的,起到自定义组件的作用。 插槽,按照处理方式,我觉得大概可以分为两种,一种是普通插槽,一种是作用域插槽 普通插槽,就是不给名字的默认插槽和 具名插槽 作用域插槽,就是使用子作用域数据的插槽 接下来,我们简单解释一下插槽的处理流程,主要理解思想和流程 普通插槽下面以默认插槽来说,就是不给名字的插槽 父组件中使用组件 test,并且使用插槽 子组件 test 模板 1、父组件先解析,把 test 当做子元素处理,把 插槽当做 test 的子元素处理,生成这样的节点 { tag: "div", children: [{ tag: "test", children: ['插入slot 中'] }]}插槽的节点就是上面的 ['插入slot 中']2、子组件解析,slot 作为一个占位符,会被解析成一个函数 大概意思像 解析成下面 { tag: "main", children: [ '我在子组件里面', _t('default') ]}3、这个 _t 函数,传入 'default ' 参数并执行 因为这里不给名字,默认插槽,所以是default,如果给了名字,就传入你的名字 这个函数的作用,是把第一步解析得到的插槽节点拿到,然后返回 那么子组件的节点就完整了,插槽也成功认了爹 ...

July 3, 2019 · 1 min · jiezi

Vue原理Directive-白话版

写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧 【Vue原理】Directive - 白话版 今天是除夕啦,大家新年快乐!身体健康!2019多多开花,耶,嗨起来! 言归正传! 我相信应该大家应该都使用过 Directive 指令,有时为了能够直接操作DOM,而指令中最重要的莫过于是 钩子函数了,指令一共有五个钩子函数,他们不会在不同的阶段触发,文档也已经说明 当然了,其实你只要了解它是什么时候触发的,就完全可以用在项目。但是我们是不会满足于此的,我要知道他是怎么触发的,怎么调用到我设置的钩子的 今天,我们就来简单说一下这几个钩子都是怎么被调用的 你能相信我写 Directive 花了一个星期啊,不是有多难,而是我不知道怎么下手写啊,根本不知道怎么描述会简单好了解,吐血 钩子如何调用首先,Vue 在绑定了指令的DOM 创建之后,插入页面之前,对一些DOM 本身的事件或者属性等进行处理。 其中,就包含对本DOM的所有指令进行处理 怎么处理呢?每一个钩子函数都不一样,所以我们会分不同钩子说明 首先,处理时,Vue 要判断哪些指令是新的还是旧的 所以需要比较 旧节点上的指令 和 新节点上的指令 比如 新指令比旧指令 多了一个指令 ,如下 // 新指令 newDir 如下newDir={ "v-test":{ name: "test", rawName: "v-test" }, "v-test2":{ name:"test2" rawName:"v-test2" } }// 旧指令 oldDir 如下,少了一个 v-test2oldDir={ "v-test":{ name: "test", rawName: "v-test" } }如果是新指令 ...

July 1, 2019 · 2 min · jiezi

Vue原理VModel-源码版-之-select-详解

写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧 【Vue原理】VModel - 源码版 之 select 详解 今天我们来看看 v-model 处理 select 有什么特殊的地方 前面已经有三篇说明VModel了 【Vue原理】VModel - 白话版 【Vue原理】VModel - 源码版 之 表单元素绑定流程 【Vue原理】VModel - 源码版之input详解 通过第一篇源码分享,我们就知道 Vue是通过 设置 select 的 selectedIndex 来控制选项的, 哈哈,现在我们就是来分析到底是怎么设置 selectedIndex 的 好的,我们一定要带着问题进行学习,这样学完才有用 1、Vue 如何设置 selectedIndex2、Vue 在哪里设置 selectedIndexVue 如何设置 selectedIndexVue 是通过 一个 setSelected 的方法专门来设置 selectedIndex 的,我们来看下源码 function setSelected(el, binding, vm) { var selected, option; for (var i = 0, l = el.options.length; i < l; i++) { option = el.options[i]; if (isMultiple) { selected = value.indexOf(option) > -1; if (option.selected !== selected) { option.selected = selected; } } else { if (option.value == value) { if (el.selectedIndex !== i) { el.selectedIndex = i; } return } } } if (!isMultiple) { el.selectedIndex = -1; }}两处会修改 selectedIndex 的地方我已经加红加粗 ...

July 1, 2019 · 3 min · jiezi

Vue原理Mixin-白话版

写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧 【Vue原理】Mixin - 白话版 今天我们用白话文解读 mixin 的工作原理,轻松快速理解 mixin 内部工作原理。你说,你只懂怎么用的,却不懂他内部是怎么工作的,这样也不太行。 而我觉得现在出一个 白话版,是让大家有兴趣去研究源码的时候,可以提前理清一下思路。暂时没时间了解源码的,也可以先了解下内部流程,对解决一些奇奇怪怪的问题也是很有作用的 mixins 我觉得可能大家很少用把,但是这个东西真的非常有用。相当于封装,提取公共部分。 显然,今天我不是来教大家怎么用的,怎么用看文档就好了,我是讲解 生命的真谛 内部的工作原理。如果不懂用的,请去官网看怎么用,兄弟 mixin不难,就是有点绕,今天我们探索两个问题 1、什么时候合并 2、怎么合并 什么时候合并什么是全局选项? 就是 全局组件,全局过滤器,全局指令,全局mixin 1、Vue.component 注册的 【全局组件】2、Vue.filter 注册的 【全局过滤器】3、Vue.directive 注册的 【全局指令】4、Vue.mixin 注册的 【全局mixin】 也就是说,你全局注册的选项,其实会被传递引用到你的每个组件中,这样,全局选项 和 组件选项 就会合并起来,之后组件便能访问到全局选项,然后你就可以在组件内使用全局注册的选项,比如使用 全局过滤器 其实就是像你在 自己的组件声明 components 一样,只是全局注册的话,Vue 背后偷偷给你的每个组件 都合并多一个全局选项的引用 但是为了保证全局选项不被污染,又不可能每个组件都深度克隆一份全局选项导致开销过大,所以会根据不同的选项,做不同的处理。下面会详细讲解 怎么合并权重1、组件选项2、组件 - mixin3、组件 - mixin - mixin4、.....省略无数可能存在的嵌套 mixinx、全局 选项 权重 从 1 到最后 依次减少 ...

July 1, 2019 · 2 min · jiezi

Vue原理Watch-白话版

写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧 【Vue原理】Watch - 白话版 今天我们用白话文解读 watch 的工作原理,轻松快速理解 watch 内部工作原理。你说,你只懂怎么用的,却不懂他内部是怎么工作的,这样能有什么用? 近期有篇 《停止学习框架》很火,其实本意不是让我们不要学框架,而是让我们不要只停留在框架表面,我们要学会深入,以一敌十,让我们也要学会框架的抽象能力 watch 想必大家用得应该也挺多的,用得也很顺,如果你顺便花一点点时间了解一下内部的工作原理,相信肯定会对你的工作有事半功倍的效果 watch 的工作原理其实挺简单的,如果你有看过我之前讲解其他选项的文章,你可以一下子就明白 watch 是如何工作的,所以这边文章我也✍得很快 根据 watch 的 api,我们需要了解三个地方 1、监听的数据改变的时,watch 如何工作 2、设置 immediate 时,watch 如何工作 3、设置了 deep 时,watch 如何工作 简述 响应式Vue 会把数据设置响应式,既是设置他的 get 和 set 当 数据被读取,get 被触发,然后收集到读取他的东西,保存到依赖收集器 当 数据被改变,set 被触发,然后通知曾经读取他的东西进行更新 如果你不了解,可以查看下 以前的文章 【Vue原理】响应式原理 - 白话版 监听的数据改变的时,watch 如何工作watch 在一开始初始化的时候,会 读取 一遍 监听的数据的值,于是,此时 那个数据就收集到 watch 的 watcher 了 ...

July 1, 2019 · 2 min · jiezi

Vue原理代理-Data-源码版

写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧 【Vue原理】代理 Data - 源码版 写这篇文章,我就是为了记录我对 Data 的一个疑问的探索,很简短 data 的数据是怎么可以通过实例直接访问的? 第一想法,或许是,遍历逐个复制? 但是其实并不是,这里涉及的一个词,叫 【代理】 怎么代理呢?听我慢慢说,抓住逐个疑问,跟着我慢慢探索 初始化数据实例使用 initData 初始化数据,如下 function initData(vm) { var data = vm.$options.data; var keys = Object.keys(data); var i = keys.length; data = vm._data = ( typeof data === 'function' ? data.call(vm) : data ) || {}; while (i--) { var key = keys[i]; if (只要不是_和$开头的属性) { proxy(vm, "_data", key); } } }首先,拿到 data 数据,如果data 是函数,就执行拿到返回值,否则直接拿设置的对象data ...

July 1, 2019 · 1 min · jiezi

Vue原理VModel-源码版-之-表单元素绑定流程

写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧 【Vue原理】VModel - 源码版 之 表单元素绑定流程 今天讲解 v-model 的源码版。首先,兄弟,容我先说几句 v-model 涉及源码很多,篇幅很长,我都已经分了上下 三篇了,依然这么长,但是其实内容都差不多一样,但是我还是毫无保留地给你了。你知道我这篇文章写了多久,一个多星期啊,不是研究多久啊,是写啊写啊,不停地修修改改,一直在想如何才能讲明白 如果你做好了十足的学习准备,会对你事半功倍,如果你只是看看,请看白话版吧,不然估计会越看越烦..... 如果你看过白话版,估计你会了解今天内容的大概,也能很快就入戏 今天讲解不同表单元素的Vue是如何处理的,表单元素有 input、textarea、select、checkbox、radio 五大种 所以,我们把每个表单元素当做一个模块,然后每个模块解决三个问题的流程,来开始我们今天的表演 1、v-model 如何绑定表单值 2、v-model 如何绑定事件 4、v-model 如何双向更新 TIP 下面所有涉及到的源码,为了方便理解,都是简化过的,因为源码太长,所以只保留主要思想,去掉了很多兼容处理以及错误处理 v-model 指令的处理我们现在假设模板的解析已经到了 解析 v-model 的部分.... Vue 会调用 model 方法 来解析 v-model ,这个方法里面,针对不同的表单元素,再调用不同的专属方法进行深度解析 function model(el, dir) { var value = dir.value; var tag = el.tag; var type = el.attrsMap.type; if (tag === 'select') { genSelect(el, value); } else if (tag === 'input' && type === 'checkbox') { genCheckboxModel(el, value); } else if (tag === 'input' && type === 'radio') { genRadioModel(el, value); } else if (tag === 'input' || tag === 'textarea') { genDefaultModel(el, value); }}你也看到了,上面每种表单元素都会使用一个方法来特殊照顾,不过这些方法,作用大致一样 ...

July 1, 2019 · 5 min · jiezi

Vue原理VModel-源码版之input详解

写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧 【Vue原理】VModel - 源码版之input详解 上一篇文章,我们大概讲了所有表单元素的双绑原理,但是仍然有两个特殊的表单元素,是要多更多处理的,也不可能放在一篇文章说完,今天,我们说的是 input 的特殊处理的地方 而 input 有什么特殊处理的地方呢? 1、预输入延迟更新 2、range 类型的 input 3、v-model.lazy 4、v-model.trim、v-model-number 预输入延迟更新先来看看Vue 给 input 正常绑定的回调 input: function($event) { if ($event.target.composing) return; name = $event.target.value}看到我标红的地方,这句话就是完成预输入延迟更新的重点 当composing=true时,事件回调不会走到下面的更新操作,而 Vue 正式通过这个标志位,判断现在是否是预输入而确定是否需要实时更新 首先,Vue 会为 input 或者 textarea 绑定以下事件 compositionstartcompositionendchange开始讲解这三个事件了 1、compositionstart 首先,compositionstart 会在 input 事件触发之前 触发 but!你打一些直接输入的字符,是不会触发 compositionstart 的,只会触发 input 只有打预输入的字符才会触发,比如 输入拼音,不行看图 输入普通字符 预输入延迟更新下,输入拼音 取消预输入延迟更新,输入拼音 ...

July 1, 2019 · 3 min · jiezi

Vue原理VModel-白话版

写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧 【Vue原理】VModel - 白话版 说到 Vue,感觉第一印象就是双向绑定,所以v-model键值是Vue印象的半壁江山啊,这么重要的东西,你好歹要知道是怎么实现的吧 我们今天就来讲解双向绑定的工作原理,你应该知道,双向绑定就是通过绑定 dom 事件来实现的,可是,怎么绑定的事件,绑定什么事件? 首先,双向绑定,我个人认为应该分为 初始化绑定 和 双向更新 两part。 初始化绑定,就是初始化时给表单元素绑定值,绑定事件,为双向更新做准备 双向更新,就是任意一边变化,同时能让另一个边更新 双向更新那是后话,只有一开始时成功执行绑定操作才会有之后 双向更新这个东西,所以,今天按顺序来了解两个part,从四个问题开始 1、v-model 怎么给表单绑定数据 2、v-model 绑定什么事件 3、v-model 怎么绑定事件 4、v-model 如何进行双向更新 TIP v-model 还可以用在 自定义组件上,但是很明显,这次我们先不讲这一块,而是先将正常的表单使用 先看结论我们先以 input text 类型讲解,对于其他的表单元素,流程都差不多,只是中间涉及的内容不同。所以就先讲个例子,然后具体在源码版全部一起说 1、怎么赋值?v-model 绑定的数据赋值给表单元素的 value 属性 2、怎么绑定事件?解析不同表单元素,配置相应的事件名和事件回调,在插入dom之前,addEventListener 绑定上事件 3、怎么双绑?外部变化,触发事件回调,event.target.value 赋值给model绑定的数据;内部变化,修改表单元素属性 value 看完结论,有点懵?我们来看看具体的内容,结果导向来进行学习 下面的讲解以下面这个为例 v-model 怎么给表单绑定数据获取值流程 首先,上面例子解析后的渲染函数是下面这样(已简化,只保留表单值相关) (function(){ with(this){ return _c('div',[ _c('input', domProps:{"value":name} ) ]) }})1、这个渲染函数是没有执行的 匿名函数。执行的时候,会绑定上下文对象为 组件实例 ...

July 1, 2019 · 2 min · jiezi

Vue原理Filters-源码版

写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧 【Vue原理】Filters - 源码版 嗨,又到周末啦,又可以睡懒觉了,冬天睡懒觉真苏胡,但是我明天要无名加班 好吧,今天我们 解读 filter 的工作原理(话题转得真是生硬),filter 其实实现也是非常简单的,所以这里直接出源码版了十分钟就能大概了解这个 filter 了吧好吧,今天的研究,我们仍然要带着问题去学习 1、页面的 filter 解析成什么2、设置的 filter 如何被调用 场景设置下面的讲解会以下面例子 作为讲解模板 这里有一个过滤器 all,用来过滤 parentName <div>{{parentName|all }}</div>new Vue({ el:document.getElementsByTagName("div")[0], data(){ return { parentName:111 } }, filters:{ all(){ return "我是过滤器" } }})页面的 filter 解析成什么首先,上面的例子会被解析成下面的渲染函数 (function() { with(this) { return _c('div',[ _v(_s(_f("all")(parentName))) ]) }}这段代码继续解释下 1、_c 是渲染组件的函数,这里会渲染出根组件 2、这是匿名自执行函数,后面渲染的时候调用,会 绑定当前实例为作用域 3、with 的作用是,绑定大括号内代码的 变量访问作用域,所以里面的所有变量都会从 实例上获取 ...

June 28, 2019 · 2 min · jiezi

Vue原理Methods-源码版

写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧 【Vue原理】Methods - 源码版 今天我们解读 methods 的源码,其实 methods 挺简单的,所以就不打算出白话版了,但是 methods 里面让我重新认识到这一个重要的知识点,是我是我,可能你们已经掌握了哈哈 methods 怎么使用实例访问?methods 简单到什么程度呢,估计你用脚都能想得到 那么现在的问题怎么解答 "遍历 methods 这个对象,然后逐个复制到 实例上?" 没错,你猜对了,的确是逐个复制,简化源码是这么写的 function initMethods(vm, methods) { for (var key in methods) { vm[key] = methods[key] == null ? noop : bind(methods[key], vm); }} methods 如何固定作用域的其实 methods 的固定作用域的唯一重点就是 bind 了,bind 相信大家也都用过 bind 是固定函数作用域的,说实在的,之前我还真不太用 bind 这个东西,就知道可以绑定作用域,我觉得我会 call 和 apply 就行了,现在后悔了,发现用处太大了 ...

June 28, 2019 · 1 min · jiezi

Vue原理Props-白话版

写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧 如果你觉得排版难看,请点击下面公众号链接 【Vue原理】Props - 白话版 今天我们用白话文解读props 的工作原理 props 真的挺有用的,作为父传子的载体,大家肯定都用过,但是你有没有想过,Props 到底是怎么工作的? 希望先看下 响应式原理,对 props 理解很有帮助 响应式原理 开篇之前,我提出三个问题 1、父组件 怎么传值给 子组件的 props 2、子组件如何读取props 3、父组件 data 更新,子组件的props 如何更新 今天我们带着三个问题去开始我们的讲解 明白这三个问题,那么相信你也就理解了 props 的工作原理 场景设置现在我们有一个这样的 根组件 A 和 他的 子组件 testb 根组件A 会 把 parentName 传给 子组件 testb 的 props 根组件A 也是 组件testb 的 父组件 <div class="a" > <testb :child-name="parentName" ></testb></div>new Vue({ el:".a", name:"A", components:{ testb:{ props:{ childName:"" }, template: '<p>父组件传入的 props 的值 {{childName}}</p>', } }, data(){ return { parentName:"我是父组件" } },})按照上面的例子,开始我们的问题解析 ...

June 28, 2019 · 2 min · jiezi

Vue原理月老Computed-白话版

写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧 【Vue原理】月老Computed - 白话版 今天我们用白话文解读 computed 的工作原理,轻松快速理解 computed 内部工作原理。因为如果你不懂原理,有时候做项目,碰到奇怪的问题,真的不知道怎么回事 要理解 computed 的工作原理,只需要理解下面三个问题 1、computed 也是响应式的 2、computed 如何控制缓存 3、依赖的 data 改变了,computed 如何更新 开始我们今天的讲解,希望你认真看完会有收获 “必须有收获谢谢,不然我不白写了吗兄弟” 在这里,我先告诉你,computed 其实是一个 月老,专门牵线 computed 也是响应式的什么是响应式,不知道的童鞋请参考我以前的文章 【Vue原理】响应式原理 - 白话版 简单地说你给 computed 设置的 get 和 set 函数,会跟 Object.defineProperty 关联起来 所以 Vue 能捕捉到 读取computed 和 赋值computed 的操作 读取computed 时,会执行你设置的 get 函数,但是并没有这么简单,因为还有一层缓存的操作 赋值 computed 时,会执行你设置的 set 函数,这个就比较简单,会直接把 set 赋值给 Object.defineProperty - set ...

June 28, 2019 · 2 min · jiezi

Vue原理看Vue源码不会调试不行啊

写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧 【Vue原理】学会调试Vue源码 调试是程序猿必备的技能,如果你不会调试,你的下场就是......... 嗯,一样可以拿高工资............ 不过据我了解,连张鑫旭大佬都喜欢使用 console.log 调试,但是你以为别人不用,你就以为别人不会吗,你真的太天真了.... 下面的评论也是...看来同是天涯沦落人 我在项目中也是使用 console.log 调试啊,但是阅读源码不一样啊,你试试一直用 console.log 调试,搞不死你 好了,进入正题.... 进入VSCode调试界面是是是,让我们把眼睛移动到 VSCODE 左边活动栏 这边 1、有个小虫 ,点击进入调试界面 2、或者按快捷键, ctrl+shift+D 错!其实第一步你应该打开 VSCode 创建调试配置文件点击这个小设置按钮 之后,会弹窗,让你选择调试的类型,我们选择 Chrome ,骚年 Duang 的一声,你会发现自动生成了一个配置文件 然后这一步你就成功了呗....到下一步了 配置调试配置文件配置文件有很多选项,我只给出最简单的可以使用的版本 { "version": "0.2.0", "configurations": [ { "type": "chrome", "request": "launch", "name": "调试 Vue 的调用走向", "file": "${workspaceRoot}/index.html", } ]}解释一下 ( • • )✧ ...

June 27, 2019 · 2 min · jiezi

Vue原理响应式原理-白话版

写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧 【Vue原理】响应式原理 - 白话版 本文打算 白话文的形式讲解 Vue 的响应式系统原理,尽量不涉及源码。 只阐述工作流程,不想内容过多过于繁杂,导致大家会没有什么阅读的兴趣。 所以我今后打算把每一个内容分成 白话版和 源码版。 白话版,就是让大家不用花费太多脑力,不用消耗太多时间,就能轻松地看完并大致了解内容。 有时间精力的人可以阅读源码版 ,然后自己参考源码,来进行研究学习。有什么错误的地方,感谢大家能够指出 响应式系统我们都知道,只要在 Vue 实例中声明过的数据,那么这个数据就是响应式的。 什么是响应式,也即是说,数据发生改变的时候,视图会重新渲染,匹配更新为最新的值。 也正是因为这个系统,让我们可以脱离界面的束缚,只需要操作数据。 我们可以问出下面三个问题 1、Vue 是怎么知道数据改变? 2、Vue 在数据改变时,怎么知道通知哪些视图更新? 3、Vue 在数据改变时,视图怎么知道什么时候更新? 现在,我将会讲解三个重要的概念 Object.defineProperty,依赖收集,依赖更新 Object.defineProperty这个方法,是 Vue 响应式系统的精髓,骨髓,脑髓 使用 Object.defineProperty 可以为对象中的每一个属性,设置 get 和 set 方法 Object.defineProperty 可以为属性设置很多特性,例如 configurable,enumerable,但是现在不过多解释,重点只放在 get 和 set 那么 get 和 set 方法有什么用? get 值是一个函数,当属性被访问时,会触发 get 函数 set 值同样是一个函数,当属性被赋值时,会触发 set 函数 ...

June 27, 2019 · 2 min · jiezi

前端应用框架-NoahV

NoahV是一个基于vue开发的前端应用框架,主要适用于运维监控、数据分析等场景,同时也适用于常见的后台管理系统。 主要包含如下特性: 上手简单,提供常用的各类组件,快速开始前端开发导航栏、页面布局JSON配置,无需代码逻辑开发API数据模拟和API数据代理,让前后端不再耦合,联调更简单,效率更高数据仪表盘功能,满足常见的数据可视化需求,助力数据分析场景实用的脚手架功能,项目初始化、模板复用、模拟数据创建、调试server启动、框架升级一行命令通通搞定 如果你想要快速搭建前端界面,你可以放心使用NoahV框架,NoahV框架主要为提升前端开发效率,降低前端开发门槛而生。 文档:https://www.noahv.org/ Github:https://github.com/baidu/NoahV

June 25, 2019 · 1 min · jiezi

使用Docker部署Node应用

上篇《前端也要学Docker啊!》介绍了 Docker 及它的三个主要概念:Image(镜像)、Container(容器)、Registry(仓库) 以及Docker安装。 本篇我们来动手实践:在本地创建一个自己的镜像(Node应用),使用该镜像创建容器并执行容器中的Node应用。 创建一个Node项目在根目录创建index.js //index.jsconst Koa = require('koa');const app = new Koa();app.use(async ctx => { ctx.body = 'Hello Docker O(∩_∩)O~~';});app.listen(3000);创建 Docker 镜像需要用到 docker build命令,而docker build命令又是根据 Dockerfile 配置文件来构建镜像,所以我们要在项目根目录创建一个 Dockerfile 文件: #DockerfileFROM node:10.13-alpine #项目的基础依赖MAINTAINER chenLong #项目维护者COPY . . #将本机根目录所有文件拷贝到容器的根目录下 这个可以根据喜好调节路径EXPOSE 3000 #容器对外暴露的端口RUN npm i #安装node依赖CMD npm start #在容器环境里执行的命令你可以到 Docker 官网查看详细的Dockfile说明 构建镜像上面 Node 代码已经完成了,我们使用 yarn init -y或 npm init -y 完成package.json初始化,然后安装一个koa依赖:执行yarn add koa或 npm i koa。 然后我们在本地跑一下 node 程序:node index.js,打开浏览器输入 localhost:3000 ,可以看到浏览器中成功显示了 Hello Docker O(∩_∩)O~~ 。 ...

June 18, 2019 · 1 min · jiezi

Vue实战项目数据交互axios4

1.axios地址 https://github.com/axios/axios 2.安装 npm install axios3.使用 1.用在哪? main.js 全局2.什么时候用? 生命周期:https://cn.vuejs.org/v2/guide/instance.html created 的时候 发起异步请求获取数据3.怎么用 数据从哪里来: 1.美团API接口 , 优点:贴近工作情形缺点:官方接口申请有门槛,手续繁琐,限制多,速度慢,非官方不稳定,容易失效2.自己搭建线上服务器数据 优点:贴近工作情形,自己造数据想怎玩怎么玩缺点:写JSON麻烦,不稳定,速度慢,域名空间收费,繁琐3.搭建本地模拟数据 优点:贴近工作情形,自己造数据想怎玩怎么玩,访问速度快,稳定缺点:写JSON、配置服务器麻烦(如使用express或者mockjs)结论:为了工作使用1,为了灵活性使用3,自己有现成的服务器或空间,从2、3中选的话,选择2 我们使用1,按照工作标准来: 美团外卖官方接口: http://developer.waimai.meitu... GET例子: axios.get('/user?ID=12345') .then(function (response) { // handle success console.log(response); }) .catch(function (error) { // handle error console.log(error); }) .then(function () { // always executed });POST例子: axios.post('/user', { firstName: 'Fred', lastName: 'Flintstone' }) .then(function (response) { console.log(response); }) .catch(function (error) { console.log(error); });总 结 本文我们从安装,到axios,让我们对vue的数据相关的技术点有了一定了解,我们将一步步完善项目,丰富我们的技术经验。 ...

June 17, 2019 · 1 min · jiezi

使用imbaio框架得到比-vue-快50倍的性能基准

我是标题党吗?是,但也不是。以图为证。 上图表示了vue, react 以及 imba 在 todo 这个项目中拥有60个 todoItem 不同进行 crud 操作的表现。可以看到 imba 达到了每秒操作5w次以上。如果你也想试一试该测试,可以访问 Todos Bench。测试使用的是 Benchmark.js。 imba 简单介绍imba 是一种新的编程语言,可以编译为高性能的 JavaScript。可以直接用于 Web 编程(服务端与客户端)开发。 下面是语法: // 自定义标签tag App // 属性 prop items // 方法定义 def addItem if @input.value items.push(title: @input.value) @input.value = "" def toggleItem item item:completed = !item:completed// 挂载 Imba.mount(element, into) // 如果没有第二个参数,默认挂载到 document.body 上面Imba.mount <App.vbox items=[] -> <form.bar :submit.prevent.addItem> <input@input> <button> 'add' <ul> for item in items <li .done=item:completed :tap.toggleItem(item)> item:title可以看出作者喜欢 ruby 以及 pug与,偏向于缩进类风格(个人并不是很喜欢这种语法风格)。具体语法可以参考 imba 文档。当然了,因为可以编译成js,所以服务端编译成 js 进行node开发也是可以实现的。 ...

June 17, 2019 · 3 min · jiezi

阿里云前端技术周刊第九期

作者:灵沼校对:染陌 知乎:阿里云中台前端/全栈团队专栏Github:阿里云前端技术周刊 给我们投稿:传送门参与交流:传送门 前端速报 拒绝重复造轮子!GitHub推出新功能repository template,帮助开发者在所有项目中重用代码WHATWG 击败 W3C,赢得 HTML 和 DOM 的控制权,了解一下为React Hooks编写类似Redux的简单中间件,戳我看看吧趣前端 Service worker mindset 当我们在谈论service worker的时候,我们在谈论什么?应该掌握的思维模式有哪些? 除了这篇文章,这里还提供了一个非常精美生动的免费游戏来帮助你快速掌握service worker。 The economics of package management 对于前端工程师来说npm已经跟吃饭喝水一样不可或缺,但如果有朝一日npm关闭服务呢?或者还记得当时的left-pad事件么?某种程度上,npm「并不爱你」。 这是npm, Inc的前CTO, C.J.Silverio在JSConfEUwatch上演讲的文字版,不仅叙述了js包管理生态的历史,也表达出对npm的所有权为公司控制的担忧。这也是她为何投入新的包管理器Entropic开发的原因(可允许多个registry共存互通)。 编者推荐 走进身份管理 身份管理对企业信息安全至关重要,本文从概念、需求、管理等几个角度阐述身份管理的要点并提供系统化解决方案(Authing)。 History of Node.js on a Timeline Node.js十周年系列文章回顾,包含很多标志性的事件。 Yup Yup是一个Javascript object schema验证库。API和风格参考了Joi(nodejs流行的对象验证库),但是相较Joi更轻量、体积更小,适合用于浏览器端。完全声明式的验证规则、并可描述对象属性之间的级联验证,可用于表单验证,流行的react表单库Formik就内置了对Yup的支持。 跨界碰撞 比个手势,AI就识别出Emoji,浏览器运行无压力,体验一下 大疆发布首款教育机器人机甲大师 RoboMaster S1,启蒙编程思维,程序员从娃娃抓起 关于我们我们是阿里云智能中台体验技术团队。详情 如有兴趣加入我们,简历/沟通请至:lingzhao.sd@alibaba-inc.com

June 16, 2019 · 1 min · jiezi

Svelte-3-快速开发指南对比React

翻译:疯狂的技术宅原文:https://www.valentinog.com/bl... 未经允许严禁转载 什么是Svelte?Svelte 是由 Rich Harris 创建的 JavaScript UI 库。 Rich 认为 virtual DOM 带来了额外开销,并提出了 Svelte,现在它正处于第三版的状态。 但是你为什么要学习Svelte?而不是 React 或 Vue?嗯,它有一些有趣的卖点: Svelte是编译器,而不是像 React 或 Vue 这样的依赖项Svelte 似乎需要更少的代码,用 React 开发同样的功能代码量大约会多 40% (来源:Rich Harris)Svelte 没有 virtual DOM,它会被编译成最小的 “vanilla” JavaScript,并且看起来比其他库性能更好在下面的教程中,我更关注 Svelte 3 的核心概念。 不管怎样,不要过分的去追逐潮流。 Svelte 3 确实很有趣,虽然它在一些细节上还比较粗糙。你可以通过本教程来试试 Svelte 3 的水到底有多深,并形成你自己的观点。 请慢慢享用。 本指南适用于哪些人(要求)如果你对以下内容有基本的了解,那么学习本教程就没有问题: HTML、CSS 和 JavaScript (ES6+)import 和 export 语法(ES6模块)async/await 语法组件等概念fetch API如果你是前端初学者,那么这个教程对你来说也许太过分了。但是不要绝望,先学习以下资源然后再回来。 如果你需要学习 ES6模块,请查看 JavaScript 中关于 import 和 export 语句的文档。还有优秀的文章 ES6 Modules in depth。 ...

June 14, 2019 · 9 min · jiezi

阿里云前端技术周刊第七期

作者:联民 校对:染陌 知乎:阿里云中台前端/全栈团队专栏 Github:阿里云前端技术周刊 给我们投稿:传送门 参与交流:传送门 前端速报 微软官方放出针对 Mac OS 用户的 Microsoft Edge Canary 预览版本,它基于开源的 Chromium 打造, 在 Tab 切换和媒体播放上, 针对 Touch Bar 做了特定支持,下载地址 Node 12.3.0 发布, 添加 --experimental-wasm-modules 特性,支持通过 import './module.wasm' 引入 WebAssembly 模块,查看链接Express 发布了近 7 个月以来的新版本 4.17.0,新增了 2 个小特性。express.raw 和 express.text, 查看链接编者推荐 PixiJS V5发布跨设备的快速轻量2D引擎库。 让你可以不需要关心 WebGL 深入知识也能充分发挥硬件加速的优势,创建各种 2d 效果。 PixiJS 有很多有趣的 DEMO , 也可以自己尝试编写一个例子。 Understanding WebViews提到互联网内世界的入口的时候,我们通常想到的是 Web 浏览器。这是一篇揭露浏览器内核 Webview 神秘面纱的文章, 介绍了它与 App 的共存关系和各类使用场景举例,是一篇科普类的好文 Linear Algebra with JavaScript (墙)这是一组系列文章, 作者使用 JS 编写了关于向量、线性变换、矩阵、线性方程等线性代数知识的大量 DEMO , 探索线性代数的奥妙。通过 JS 学习线性代数,是一个很有趣新颖的切入点。 ...

June 9, 2019 · 1 min · jiezi

Vue核心50讲-第一回Vue-与-MVVM-之间那些事儿

书接上文,上一回咱们说到了如今的前端江湖早已是框架三分天下的格局。接下来,咱们就要说到主角 Vue 了。在说真正的 Vue 内容之前,咱们还要先来说说 Vue 与 MVVM 之间的那些事儿。 什么是Vue想要近距离了解什么是 Vue,其实特别简单。咱们只需要访问 Vue的官方网站 就可以了。 映入眼帘的,咱们可以看到说 Vue 是渐进式 JavaScript 框架,英文叫做“Progressive JavaScript Framework”。当然,你现在并不需要知道什么是渐进式 JavaScript 框架。那么接下来,咱们再进一步看看 Vue 的官方是如何来介绍自己的呢。 Vue (读音 /vju/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。关于这个定义咱们不做过多的解释了,因为就算解释估计你现在也很难知道它在说个啥!如果你不满足,那我们就再看一个 Vue 官方提供的视频吧。 Vue的特点根据 Vue 的官方提供的信息,咱们可以看到 Vue 主要的特点集中在三点: 易用:只要你掌握了 HTML、CSS 和 JavaScript,就可以直接来学习 Vue 框架了。灵活:Vue 提供一个核心库,类似于 jQuery。依赖自身不断繁荣的生态系统,类似于 jQuery 的插件库一样,可以在一个库和一套完整框架之间自如伸缩。高效:核心库文件压缩之后只有 20KB,远比 jQuery 的压缩版文件小得多。并且还提供超快的虚拟 DOM。总体来讲,Vue 的官方就是告诉你,用我这个框架要求很低,会HTML、CSS 和 JavaScript 就可以了。而且,我这个框架的核心库 Vue.js 文件很小,你使用的时候不会对你现在的项目造成多大的影响。裤裆里着火,当然了!我们现在也没用 Vue 来开发个网页,所以它所谓的优势对于咱们现阶段来说只不过自说自话而已。是不是这个样子,咱们还需要在具体的案例中体验。 什么是 MVVM关于 Vue 咱们说了这么多,接下来再来说说关于 MVVM 吧。MVVM 呢,其实是一种开发模式。当然,这么说估计你也是一脸懵逼的。心急吃不了热豆腐,且听我慢慢道来~MVVM 其实表示的是 View-ViewModel-Model,就是视图层-视图模型层-模型层。View是作为视图层,简单来说可以把它理解为HTML页面;Model 是作为模型层,它是负责处理业务逻辑以及和服务器端进行交互的;ViewModel 是作为视图模型层,也就是 Vue 框架所起到的作用了,主要是作为 View 层和 Model 层之间的通信桥梁。 ...

June 6, 2019 · 1 min · jiezi

Gatsby极速入门添加博客内容页4

1.查数据注意,这里跟前面不一样了,我用gatsby-node.js这个文件去提供数据,没有什么为什么,规定,照做就好。 const path = require("path");exports.createPages = ({ actions, graphql }) => { const { createPage } = actions const blogPostTemplate = path.resolve(`src/templates/blogPost.js`) return graphql(` { allMarkdownRemark{ edges { node { frontmatter { path, title, tags } } } } } `).then(result => { if (result.errors) { return Promise.reject(result.errors) } const posts = result.data.allMarkdownRemark.edges; createTagPages(createPage, posts); posts.forEach(({ node }, index) => { const path = node.frontmatter.path; const title = node.frontmatter.title; createPage({ title, path, component: blogPostTemplate, context: { pathSlug: path }, }) }) })}很清晰明显,这里就说一点我传递了一个参数,pathSlug到内容页。 ...

May 30, 2019 · 1 min · jiezi

前端开发工程师找工作应该选择大公司还是小公司

大公司做开发有面子,小公司做开发有金子?小公司做开发是否更容易拿到offer?大公司做开发是否会接触到最前沿的项目?大小公司做开发,职业发展前景如何?金三银四虽然过去了,但现在来谈这些问题还不迟,如果你准备在下半年就业,那今天教你如何选择就业公司,帮助自己打好职业发展的黄金五年第一枪! 其实上面这些问题都是我在这个行业那么多年,很多学员或者朋友经常问我的,那么我今天就来给大家讲一下这些问题,分析一下大小公司的优劣,以及大家在就业选择的时候需要注意的一些地方。 薪资福利比较待遇和福利,这个是大家最关心的,先来聊最刺激的。 大公司国内的大公司,肯定就是选择一二线的互联网公司了。第一梯队:BAT(百度,阿里,腾讯),开发工程师心之所向,技术天堂。第二梯队:网易,美团,字节跳动,滴滴,360,京东,爱奇艺,小米,携程等等这些发展不错的互联网公司 一线公司的特点就是钱多,公司运行稳定,福利待遇充足,比如说腾讯的底薪比同行的高出30%,员工餐厅、咖啡厅,健身房,K歌跳舞打游戏,住房福利等等。还有阿里的也是高薪,住房+教育福利,特色食堂,各种娱乐活动。百度offer直接就是14.6个月薪水,小吃点心送到工位,员工宿舍,健康保障。 小公司但是小公司的福利你以为就会很差吗?国内很多小公司福利都还是不错的,招起开发人员来也是从不“手软”,同水平技术能力,基本的开发工资待遇底薪都会和BAT相当 。虽然说整体的福利不能完全和打公司媲美,但是也是能别出心裁,努力表现出来对员工的关怀的。比如说餐补等各种补助,健身房,员工饭堂咖啡厅,节假日问候礼等等。 如果你运气好,找对了公司,正好是在小公司蓬勃发展的时期加入了公司,到上市的时候,你还能以创始员工身份获得丰厚的股份收益。这就是很多人做开发的人说的:选对startup,一夜致富不是梦。 学习机会比较大公司在大公司做开发,你能学习到规范化的工作流程和职业技能的系统性培养。这可以帮助你培养出高效良好的工作习惯。而一旦掌握了正确的工作习惯,以后无论做什么岗位都能快速上手。打下这样良好的基础,在开发工程师黄金5年里是非常重要的。此外,大公司的员工可获得更多的知识和学习资源。比如,不同于小公司,大公司一般都有非常完善的代码文档和 CodeBase。千万不要小看好像流水账一样的记录内容,这可都是宝贵的学习资源。比如阿里就有专门的java开发规范手册。通过查看这些文档,开发工程师可以从中了解到某个专业的复杂的企业级项目,以及它每一部分的设计思路、每一段代码的具体作用。又比如,很多大公司都会有内部培训课程,manager会根据不同员工的工作需求,建议他们去上不同的课程,汲取新的技术知识。 小公司在学习机会上,小公司就没办法有大公司那么多的积累了,但是小公司最大的优势就没有边界的“野蛮生长”。在小公司,你会接触到各种各样的任务,获得来自不同岗位、不同业务的工作经验和锻炼机会,直接可以锻炼你的能力广度。不像大公司里,一年到头见不到大老板几面,在国内很多小公司都是扁平化管理,所以你可以在小公司随时随地与“高层”零距离接触。这就意味着,你有更多的机会和“大牛”一起工作,并从中获得更多高级的工作经验。而这样的机会,很多人要在大公司待4、5年才有可能获得。 职业发展比较作为一名开发工程师,你所参与开发企业项目的影响力和参与度都是很重要的,这个是可以直接影响你的职业发展的。大公司在大公司,由于公司规模大,厉害的人也多,你的影响力和参与度十分有限。而且,因为公司体系完整,分工明确,你很有可能会日复一日的做相同的工作,有种“螺丝钉”的感觉。有时候,即使你有很好的想法,可是因为大公司有很长的决策流程和复杂的人事制度,所以等你的想法层层上报,时机可能已经过去了。普遍的说,刚进大公司的新人,起码要花3个月的时间才能熟悉公司的流程,适应规则。一点点来,慢慢的才能产生实际的参与度和影响力。 不过也有好处,大公司的企业项目远超小公司的庞大项目,在未来,如果你有机会参与到这些复杂的问题,也能接触到有社会影响力的大项目,大公司会作为你升级个人能力很好的一个平台。从这个角度来看,大概是的长期发展上限会比小公司高一些。 但是,并不是所有的“螺丝钉”都能熬出头,想要达到这样一个上限,不断地学习和长期的个人努力是必不可少的。因此,很多开发工程师在大公司待了一段时间后,就会跳槽去小公司寻找突破点。小公司在小公司里,由于公司规模不大,很多时候需要一个人做更多的事情,参与的事情多了,一开始的个人影响力和上升空间会更大一些。举个例子,小公司没有像大公司那样,有非常完整的代码文档,做开发工程师,不仅要学会"拧螺丝",还要学会自己摸索着“造轮子”、“造发动机”,最后“造一台完整的车上”。虽然自己造出来的车子没有大公司造的跑得快,但是你还是可以获得满满的参与感和成就感。不过在小公司工作,就没有在大公司工作那样的“安全感”了。毕竟公司的发展和个人的发展是密不可分的。如果一开始就选好队伍,遇到像瑞幸咖啡这样的潜力股,当然前途无限光明啦但是也有很多创业公司在初期摸爬滚打,好不容上市了却股票暴跌,很多员工不得不跳槽的也有。 招聘标准比较大公司对大公司来说,他们有比较深厚的技术和财务基础,更有能力培养新人。因此,在面试的时候,大公司对应聘者的实战经验不一定要求很高,通常会主要考察应聘者的技术知识体系,学习能力和基本素养。虽然对经验要求比较低,但是在学历方面,也会趋向学习能力更强的高学历。 小公司而小公司,比如说像大疆、瓜子、小红书这些独角兽公司的面试要求反而更高,有时候甚至比BAT还严格。除了基本功之外,他们也非常看重应聘者是否有和公司现有业务的项目的实际工作经验,是否能快速的融入技术团队。 这主要是因为小公司发展还不成熟,产品需要快速迭代,而团队规模比较小,他们需要的是能入职后快速做事的老手。 最后其实大公司和小公司各有优劣,没有好不好,只有合不合适。但是在大家选择的时候,可以将以下几点作为重点考虑: 自己的职业目标 + 方向公司未来的成长形势不过,无论你去到哪个公司,扎实的基本功和丰富的项目经历,才是打开你求职道路的不二法门。

May 24, 2019 · 1 min · jiezi

Gatsby极速入门添加博客文章列表3

1.查数据 { allMarkdownRemark(sort: {order: DESC, fields: [frontmatter___date]}) { edges { node { frontmatter { title path date excerpt } } } }}如图所示, 2.套页面 打开index.js import React from "react"import Header from '../components/header'import { Link,graphql } from 'gatsby'const Layout = ({ data }) => { const { edges } = data.allMarkdownRemark; return ( <div> <Header /> <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}> { edges.map(edge => { const { frontmatter } = edge.node; return ( <div key={frontmatter.path}> <Link to={frontmatter.path}> {frontmatter.title} </Link> </div> ) }) } </div> </div> )}export const query = graphql`query{ allMarkdownRemark (sort:{ order:DESC, fields:[frontmatter___date] }){ edges { node { frontmatter { title path date excerpt } } } }}`;export default Layout;打开首页,看到文章列表就大功告成了。 ...

May 24, 2019 · 1 min · jiezi

ES6项目小练习TodoList15

ES6技术本身非常容易,相信大家也体会到了。各种新特性都不难,但是为什么很多人学习起来困难呢? 其实ES6难的不是技术,而是实际工作环境的搭建。比如我们想写一个简单的ES6版本的TodoList. 很多同学学生是这么放弃的: 通过搜索引擎,发现很多教程都是直接引入Traceur.js 然后讲解ES6各种功能和语法的,但是好像实际并不是直接引入Traceur.js ,而是用babel。 使用babel的话好像需要安装node.js,还有会用npm 安装好npm 以后我是该使用gulp还是webpack呢? 嗯,应该webpack吧,这个好像目前更主流。好,就选它了。 webpack怎么安装?是不是需要webpack-cli?另外webpack 4.0好像更好用,但是好像安装又有兼容性问题? 纠结ing…… 考虑了半天后,终于下定决心,就用最新版本,学老的没用。 安装好webpack 4.0,对了是不是要配置工作流? 对配置吧,这才是“工作的方式”,js需要压缩,装个压缩插件,webpack怎么用啊?有各种rule,plugins,还有entry. 开始头疼,耐着性子把网上的教程配置完,这个插件怎么报错了啊? 继续查,原来是webpack 4.0和它不兼容,换webpack 3.0还是换插件? 纠结半天后,终于鼓起勇气,换插件! 又配置出错了…… 开始进入暴走模式,又气又恼。 好吧,折腾半天,请教大牛总算通过了。 那么问题来了,学了这么久css和js,我居然不知道往哪里写css…… 好吧,听说得用sass…… 配置完了,sass语法不会…… 我就想写一个ES6的todoList,太费劲了,咋得学这么东西啊…… 还是放弃吧,我感觉我不适合做前端。 虽然夸张了些,但是大部分前端都有类似的经历。 今天我就让大家彻底的学会如何工作中写ES6,我们依然用todoList举例,对了我们并不需要学习webpack,sass,插件等等。我们只学习ES6,对其它的统统不用学,你会用就可以,也不过几个命令而已。 ok,我们开始。 首先,拿到我配置好的工作流,直接在根目录下进入命令行,然后 npm install安装完成后,使用 npm run dev然后就可以用了, 就这几个文件,对应写html,js和css。 首先我们先写 html文件 。 <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width,initial-scale=1.0, maximum-scale=1.0,minimum-scale=1.0,user-scalable=no"> <title>TODOList</title></head><body> <div class="container"> <div class="item-area"> <h1 class="title">TODO-LIST</h1> <ul class="item"> <li class="item-li-default">输入任务...</li> </ul> </div> <div class="input-area"> <form class="add-item"> <input maxlength="15" type="text" name="item" placeholder="待办事件..." class="add-text" required> <input type="submit" value="+ 添加" class="add-button"> </form> </div> </div></body></html>接着,写css美化一下 * { padding: 0; margin: 0; } li { list-style: none; } html { display: flex; justify-content: center; align-items: center; text-align: center; min-height: 100vh; box-sizing: border-box; font-family: Futura, "Trebuchet MS", Arial, sans-serif; background: #ffc600; } svg { fill: #fff; background: rgba(0,0,0,0.1); padding: 20px; border-radius: 50%; width: 100px; margin-bottom: 50px; } .container { padding: 20px; max-width: 350px; box-shadow: 0 0 0 10px rgba(0,0,0,0.1); background: #fff; } .container .item-area .title { text-align: center; margin: 10px 0; color: #aaa; font-weight: 200; } .container .item-area .item { background: #fff; border-radius: 4px; } .container .item-area .item .item-li-default { color: #46b7b9; } .container .item-area .item .item-li { width: 100%; border-bottom: 1px solid #eee; height: 30px; line-height: 30px; text-align: left; color: #f95959; } .container .item-area .item .item-li .delete-item { float: right; outline: none; width: 44px; height: 26px; border-radius: 4px; color: #f05941; background: #fafafa; } .container .item-area .item .item-li input:checked + .item-text { color: #196b69; text-decoration: line-through; } .container .input-area { padding-top: 10px; } .container .input-area .add-text { outline: 0; border: 1px solid rgba(0,0,0,0.1); height: 40px; border-radius: 4px; text-indent: 10px; } .container .input-area .add-button { outline: 0; height: 40px; border-radius: 4px; width: 60px; padding: 4px; background-color: #fff; border: 1px solid rgba(0,0,0,0.1); }ok,使用 ...

May 21, 2019 · 3 min · jiezi

Vue开发总结-及-一些最佳实践-已更新

基本开发环境vue-cli3 创建的项目,vscode 作为代码编写工具vscode插件推荐:vscode 插件配置 文章目录项目目录结构介绍UI 框架选择main,js 处理axios 请求二次封装1. 项目目录结构简介├── public // index.html├── src // 业务相关│ ├── assets // 静态文件(css, images)│ ├── components // 全局组件│ ├── layouts // 基础样式布局│ ├── plugin // 样式及工具引入│ ├── request // 请求配置│ ├── router // 路由│ ├── store // 全局状态管理│ ├── utils // 工具文件│ ├── app.vue // 入口文件│ ├── main.js // 主要配置文件│ └── views // 页面├── .eslintrc.js // eslint 检查配置├── .env.release // 测试环境变量├── .env.pre-build // 预发环境变量└── vue.config.js // webpack 打包配置2. UI 框架选择经框架选择验证对比 element,iview,ant-design-vue最终选择 ant-design-vue,传送门 https://vue.ant.design/docs/vue/introduce-cn/ ...

May 15, 2019 · 3 min · jiezi

Gatsby极速入门支持Markdown1

1.安装插件 我用Gatsby就是因为它支持Markdown.所以不墨迹,直接整Md支持。 yarn add gatsby-source-filesystemyarn add gatsby-transformer-remark2.添加格式化文章 在src>pages下面,添加三篇文章,头部格式如下: path : "/blog/my-first-post" date : "2019-05-01" title : "Hello world" tags : ['教程','Gatsby'] excerpt : "Gatsby hello word post" 完整代码如图所示:

May 10, 2019 · 1 min · jiezi

基于Taro开发的小程序多端UI组件库-tarocustomui

taro-custom-uiCustomUI 是一套基于Taro开发的小程序多端UI组件库,目的在于给开发者提供更灵活的布局组件及样式,以满足更多个性化的场景 Featured Components<CustomPage /> <CustomTransition /> //类似vue的transition 支持transition动画Commmon Components<CustomList /><CustomSwitch />Developing , Welcome To Experience // 开发中,欢迎体验

May 10, 2019 · 1 min · jiezi

推荐几个精致的前端web-UI框架

1.AliceuiAliceui是支付宝的样式解决方案,是一套精选的基于 spm 生态圈的样式模块集合,是 Arale 的子集,也是一套模块化的样式命名和组织规范,是写 CSS 的更好方式。 gitHub地址:https://github.com/aliceui/al... 2.AmazeuiAmaze UI 是一个轻量级、 Mobile first 的前端框架, 基于开源社区流行前端框架编写的。 官网地址:http://amazeui.org/ 3.suiSUI是一套基于bootstrap开发的前端组件库,同时她也是一套设计规范。通过SUI,可以非常方便的设计和实现精美的页面。 官网地址:http://sui.taobao.org/ 同时sui还有移动端版本msui,msui是阿里巴巴共享业务事业部UED团队的作品。目的是为了手机H5页面提供一个常用的组件库,减少重复工作。 地址:http://m.sui.taobao.org/ 4.FrozeUIFrozen UI是一个开源的简单易用,轻量快捷的移动端UI框架。基于手Q样式规范,选取最常用的组件,做成手Q公用离线包减少请求,升级方式友好,文档完善,目前全面应用在腾讯手Q增值业务中。 官网地址:http://frozenui.github.io/ 5.uiKituiKit是一款轻量级、模块化的前端框架,可快速构建强大的web前端界面。 官网地址:http://www.getuikit.net/ 6.H-uiH-ui是轻量级前端框架,简单免费,兼容性好,适用于中国网站。 官网地址:http://www.h-ui.net/ 7.WeuiweUI 是一套同微信原生视觉体验一致的基础样式库,由微信官方设计团队为微信 Web 开发量身设计,可以令用户的使用感知更加统一。包含button、cell、dialog、 progress、 toast、article、actionsheet、icon等各式元素。 官网地址:https://github.com/weui/weui 8.layuiLayui 诞生于2016年金秋,是一款带着浓烈情怀的国产前端UI框架,她追求极简,又不失丰盈的内在,说她是史上最轻量的结晶,似乎并不为过。一切都源自于她对原生态的执着,对前端社区的那些噪杂声音的过滤,以及她本身的精心雕琢。 官网地址:http://www.layui.com/ 9.YDUI TouchYDUI Touch 专为移动端打造,在技术实现、交互设计上兼容主流移动设备,保证代码轻、性能高;使用 Flex 技术,灵活自如地对齐、收缩、扩展元素,轻松搞定移动页面布局;实现强大的屏幕适配布局,等比例适配所有屏幕。什么?用得不开心?轻松切换 px;自定义Javascript组件、Less文件、Less变量,定制一份属于自己的YDUI; 官网地址:http://www.ydui.org/ 10.组件库1.基于vuejs基于vuejs的精致组件库很多,专门写了篇文章介绍:vuejs组件库 2.基于React推荐antd,阿里的React开源库,地址:https://ant.design/ 本文最早由我发布到个人博客,现发布到segmentfault,转载请注明来自【喻小右】www.iyuxy.com或者保留本文本文链接

May 10, 2019 · 1 min · jiezi

Gatsby安装和博客搭建

1.安装 yarn global add gatsby-cli2.创建gatsby项目 gatsby new blog3.开发 gatsby develop注意报错: error UNHANDLED EXCEPTIONError: no parsers registered for: "\"文件空格问题,最好不要用空格和中文。直接放在硬盘根目录下就不会有问题。 打开http://localhost:8000/,看到如下效果就成功了: 4.构建 gatsby build5.查看效果 gatsby serve打开浏览器:http://localhost:9000/看到如下效果: 大功告成!

May 8, 2019 · 1 min · jiezi

Vue基础20X笔记

- vue- 前端- vue-routerVue基础2.0X 详细解毒淘宝镜像安装 $ npm install cnpm -g --registry = https://registry.npm.taobao.org//npm有的它都有加速安装,只要在原有命令加上c就可以了安装CDN学习版本: <script src="https://cdn.jsdelivr.net/npm/...;></script> 使用包管理器全局安装 $ npm install vue$ cnpm install vue安装vue-cli npm install -g @vue/clicnpm install -g @vue/cli创建项目(创建并且进入) vue create <project-name> && cd <project-name>刚开始上手之间选择第一项default(默认)就????了然后静静地等待生产目录 创建好后使用????启动项目 vue serve目录结构 下一步删除掉多余我们不需要的东西保持干净的结构<template> <div class="home"> <h1>This is an homne page</h1> </div></template><script>export default { name:"home", data() { return { } },}</script>页面显示如下 我们点击Home 或者About的时候This is an homne page 会进行一个改变这里涉及到一个知识点router 打开我们的主文件App.vue可以看见 <div id="nav"> <router-link to="/">Home</router-link> | <router-link to="/about">About</router-link></div><router-view/><router-link> 组件支持用户在具有路由功能的应用中 (点击) 导航。 通过 to属性指定目标地址,默认渲染成带有正确链接的 <a> 标签,可以通过配置 tag属性生成别的标签.。另外,当目标路由成功激活时,链接元素自动设置一个表示激活的 CSS 类名 ...

April 30, 2019 · 1 min · jiezi

2019-前端框架对比及评测

Jacek Schae 原作,授权 LeanCloud 翻译。 我们将基于 RealWorld 示例应用对比前端框架。RealWorld 示例应用的特点: RealWorld 应用比待办事项类应用更复杂。通常待办事项类应用不足以传达足够多的知识见解构建实际应用。 标准化项目遵循特定规则。提供后端 API、静态标记语言、风格、API 规范。 专业人士编写、审阅理想情况下,会是高一致性、高真实度的项目,由使用该技术的专业人士编写或审阅。 比较的库和框架撰写本文时,RealWorld 示例应用仓库共包括 18 个 Conduit (Medium.com 克隆应用)实现。 本文不考虑框架的流行程度,RealWorld 仓库中列出的前端框架皆纳入对比范围。 测度性能应用显示内容、可以使用需要花多久? 尺寸应用有多大?我们只比较编译后的 JavaScript 文件大小。所有应用使用同样的 CSS 样式文件,CSS 文件加载自 CDN。所有应用使用的 HTML 也是一样的。这些框架都支持编译或转换为 JavaScript,所以我们仅仅测量 JavaScript 文件大小。 代码行数根据规范创建 RealWorld 应用需要多少行代码?公平地说,某些实现的功能要略微多一点,但这应该没有什么显著的影响。我们仅仅测量每个应用的 src/ 目录。 性能我们将使用 Chrome 的 [Lighthouse Audit] 测试性能。Lighthouse 返回 0 至 100 间的评分。0 为最低分。 配置所有测试均使用如下配置: 性能评分基于以下测度得出: First Contentful Paint (页面中内容元素首次渲染时间)First Meaningful Paint (页面中有意义的内容元素首次渲染时间)Speed Index (页面加载过程视觉上的变化速度)First CPU Idle (到 CPU 首次空闲的时间)Time to Interactive (到页面可交互的时间)Estimated Input Latency (预计输入延迟)详见 Lighthouse 评分指南。 ...

April 24, 2019 · 1 min · jiezi

Vue实战如何细化Vue项目目录设计2

通过上一篇文章我们了解了Vue项目核心文件(src)以及在内的各个文件的职能。 接下来我们进一步细化Vue项目的目录设计: 在开发项目的时候前端避免不了请求后端接口。为了同时开发,我们知道的通常会用到mock.js。data文件夹内新建一些json格式的数据,这样更省时间,更方便;项目中我们会对静态资源统一管理;项目中需要有引入的字体,自定义,等项目插件,工具;组件的设计考虑到以后可能会引用图片,拓展的因素,所以我们最后将组件用文件夹包裹一层。综合以上,我们将目录设计为: 如图所示, data:用来模拟我们请求的数据内部主要存放json数据格式的数据,比如评价,商品等 。 resource:统一管理图片、文档、图标这三种资源。 src:项目的核心文件 assets:assets目录中的文件会被webpack处理解析为模块依赖,更换频率不高的菜单背景图片,按钮icon等可放置在内。common:公共工具 fonts:用来存放字体。 js:用来存放我们使用js插件。 style:存放样式,less,sass等。components:Vue组件。 header:注意这里是一个文件夹。 Hearder.Vue: 头部组件。router:路由配置文件夹。app.Vue 根组件。总结 项目目录设计可以根据实际具体需求变动,但是我们结构化去思考。那么我们设计的目录肯定会方便易用,利于维护拓展的。下一篇我们将把组件按需拆分,还请持续关注。

April 23, 2019 · 1 min · jiezi

在同一基准下对前端框架进行比较(2019年更新)

翻译:疯狂的技术宅https://medium.freecodecamp.o...本文首发微信公众号:前端先锋欢迎关注,每天都给你推送新鲜的前端技术文章 这是我们第三次用 Real World example apps 对前端框架进行比较。 RealWorld example apps 为我们提供: RealWorld App 这不仅仅是“todo”。通常“todo”并没有传达出在构建真正的程序时所需的足够的知识和视角,。 标准化 一个符合某些规则的项目。提供后端API、静态标记、样式和规范。 由专家撰写或审核 一个一致的、真正的项目,在理想情况下,由相关技术的专家建立或审查。 我们比较哪些库和框架?截至撰稿时为止,在 RealWorld example app 的 repo 已经中有18个 库或框架的实现。 是否有大量的拥趸者并不重要。唯一要求是它是否出现在了 RealWorld 的代码仓库页面上。 我们关注那些指标?表现此程序需要多长时间才能显示内容并变得可用? 大小应用有多大?我们只会比较已编译的 JavaScript 文件的大小。 CSS 对所有变体都是通用的,并从 CDN(内容分发网络)下载。 HTML 也适用于所有变体。所有技术都编译或转换为 JavaScript,因此我们只比较这种文件的大小。 代码行数作者根据规范创建 RealWorld app 需要多少行代码?公平地说,一些程序过于花里胡哨,但它不应该产生重大影响。我们量化的唯一文件夹是每个程序中的 src/。 度量标准 #1:性能我们将通过检查 Chrome 附带的 Lighthouse Audit 的效果得分。 Lighthouse 返回的性能分数在 0 到 100 之间,0 是低分。 审核设置 测试用的所有 Lighthouse Audit 设置 效果是基于以下指标的综合得分 ...

April 22, 2019 · 2 min · jiezi

10 个最受欢迎的 JavaScript 框架

翻译:疯狂的技术宅原文:https://www.edureka.co/blog/t...本文首发微信公众号:前端先锋欢迎关注,每天都给你推送新鲜的前端技术文章 多年来,业界已经发布了大量 JavaScript 框架,怎样进行选择可能是一个挑战。如果你感到困惑,不知道应该选哪个或者究竟哪个适合你,那么我已经帮你解决了问题。在本文中,我将列出用来构建 Web 应用程序的前10个 JavaScript 框架。 10. AureliaAurelia 是一个开源的现代 JavaScript 模块工具箱,其有助于 Web 和移动应用程序的发展。它也被称为“下一代框架”。该框架自推出以来一直受到广泛认可。 Fileee、Freska、Ordami 和 BTEK Software 等公司以及800多个网站都使用了 Aurelia。 不要忘记,Aurelia 是唯一允许开发人员使用原生 TypeScript 或 JavaScript 构建组件的框架。 下面我列出了 Aurelia 的一些主要功能: 多语言支持:Aurelia 的 API 经过精心设计,可以用于当今和未来最实用的 Web 编程语言。 Aurelia 支持 ES5、ES2015、ES2016 和 Typescript,它非常有用,并能够为你提供高度的灵活性。模块框架:Aurelia 不是采用单一框架的方式,而是由较小的、专注的模块组成。把它们放在一起可以组成功能齐全的框架,也可以通过自定义构建可选择的方案。整洁的文档:Aurelia 提供了一个非常详细并有用的文档集,可以帮助所有的开发人员。它以良好的文档维护而闻名。可扩展的 HTML:Aurelia 的可扩展 HTML 编译器允许你创建自定义 HTML 元素,可以向现有元素中添加自定义属性并控制模板生成,所有这些都完全支持动态加载、数据绑定和高性能批量渲染。9. PolymerPolymer 是一个由 Google 维护的开源 JavaScript 库,用于使用 Web 组件构建 Web 应用。目前,有超过3000个网站正在使用聚合物,比如virustotal.com、rogers.com、zeplin.io等。 与其他 JavaScript 框架不同,Polymer 让开发人员构建组件时去利用 Web 中存在的功能。它是第一个利用 Web 组件来对应用进行交互式构建的库。 ...

April 22, 2019 · 3 min · jiezi

Vue实战—从目录结构谈可扩展项目架构设计

很多人都会用项目脚手架,也会跑hello world,然后再写写简单的todolist。但是再往下深入就难了。比如很多教程和老师都会说,大家要多问一个为什么。其实我想说多问你妹啊。我都不知道问为什么怎么多问?!比如如果我不说,很少有人会去思考和研究为什么vue的项目目录要如此设计,这么做好处。先不说说别的,我们先看看vue的目录,一图抵万言,不墨迹。好的项目代码结构会大大提升项目的维护性和可扩展性。同时我们可以提供友好的说明,以便其他成员理解项目和快速定位。其实有一点比较重要,就是公共组件、工具等同类的文件,放置一起维护会比较好。而且还有个小 技巧,我们可以在搭建项目的时候,在 README.md 里面描述下该项目下的代码和文件结构。多说无益,我这里直接给大家一个示意图,大家可以按照我给的这个项目结构组织项目。这里我强调两点,1.第一点注意每一个组件的大小写。2.注意每个组件所用到的图片的位置。很多人写组件的时候被命名或者大小写或者分隔符弄的晕头转向,这里我就说说代码规范。代码规范其实是团队合作中最重要的地方,使用相同的代码规范,会大大减少我们接手别人代码时候卧槽的次数。好的写码习惯很重要,命名习惯、适当的注释,会对代码的可读性有很大的提升。但是习惯是每个人都不一样,所以在此之上,我们需要有这样统一的代码规范。一些工具可以很好地协助我们,像 Eslint、Tslint等,加上代码的打包工具协助,可以把一些规范强行标准化,来获取代码的统一性。还有像 prettier 这样的工具,能自动在打包的时候帮我们进行代码规范化。除了这些简单的什么驼峰啊、全等啊、单引双引等基础的规范,其实更重要的是流程规范。最基础的是改动公共库或是公共组件的时候,需要进行 code review。通常我们使用 Git 维护代码,这样在合并或是版本控制上有更好的体验。但其实最重要的还是沟通,沟通是一个团队里必不可少同时很容易出问题的地方,要学会沟通方式、表达方式。很多人觉得命名了或者项目目录了这些不重要,非得把复杂的功能实现出来才牛逼,这才是技术大牛或者脑袋上闪耀着光环的架构师的范儿。其实,项目的维护所有程序员都需要,而且要想成为一个架构师,你写的代码别人是否能看得,用着舒服,架构是否健壮可扩展,这些是基本功。你连文件目录都设计不好,我拿什么相信你能设计出来可扩展的程序?

April 13, 2019 · 1 min · jiezi

React项目指导

原文链接:React项目详解GITHUB:react-webpack-config持续更新…React项目指导使用webpack需要安装的依赖webpack,webpack-cli,react,react-dombabel-loader,@babel/core,@babel/preset-env,@babel/preset-react设置.babelrc,{“presets”: ["@babel/preset-env","@babel/preset-react"]}设置scripts: “dev”: “webpack –mode development”, “build”: “webpack –mode production"设置webpack-dev-server:devServer: { compress: true, port: 9000, hot: true},“start”: “webpack-dev-server –config webpack.config.js” 设置performance:performance: { hints: false}Component基本组件let title = <h1>Hello, world!</h1>ReactDOM.render(title,document.getElementById(‘root’))动态组件import React from ‘react’;import ReactDOM from ‘react-dom’;let displayTime = () => { let nowTime = ( <div> <span>现在时间:{new Date().toLocaleTimeString()}</span> </div> ); ReactDOM.render(t nowTime, document.getElementById(‘root’) );};setInterval(displayTime, 1000);class组件构建器import React, {Component} from ‘react’;import ReactDOM from ‘react-dom’;class HelloTitle extends Component { render() { return <h1>Hello,World!</h1> }}ReactDOM.render( <HelloTitle/>, document.getElementById(‘root’));props属性import React, {Component} from ‘react’;import ReactDOM from ‘react-dom’;class HelloTitle extends Component { render() { return <h1>Hello,{this.props.name}!</h1> }}let titleDiv = ( <div> <HelloTitle name=“React”/> <HelloTitle name=“World”/> </div>);ReactDOM.render( titleDiv, document.getElementById(‘root’));props多层使用import React, {Component} from ‘react’;import ReactDOM from ‘react-dom’;class HelloTitle extends Component { render() { return <h1>Hello,{this.props.name}!</h1> }}class HelloDiv extends Component { render() { return <div><HelloTitle name={this.props.name}/></div> }}ReactDOM.render( <HelloDiv name=“React”/>, document.getElementById(‘root’));组件复用import React, {Component} from ‘react’;import ReactDOM from ‘react-dom’;class HelloTitle extends Component { render() { return <h1 style={this.props.style}>{this.props.content}</h1> }}class HelloDiv extends Component { render() { return <div> <HelloTitle content=“比较大的字” style={{‘fontSize’: 18}}/> <HelloTitle content=“比较小的字” style={{‘fontSize’: 12}}/> </div> }}ReactDOM.render( <HelloDiv/>, document.getElementById(‘root’));Component的状态state和生命周期state属性constructor(props) { super(props); this.state = { time: new Date().toLocaleTimeString() }}render() { return <h1>现在时间是{this.state.time}</h1>}组件构建完成后先执行的动作,componentDidMount():componentDidMount() { let upTime = () => { this.setState({time: new Date().toLocaleTimeString()}) }; setInterval(upTime, 1000)}setState()修改状态值this.setState({time: new Date().toLocaleTimeString()}) ...

April 10, 2019 · 1 min · jiezi

懂编译真的可以为所欲为|不同前端框架下的代码转换

背景整个前端领域在这几年迅速发展,前端框架也在不断变化,各团队选择的解决方案都不太一致,此外像小程序这种跨端场景和以往的研发方式也不太一样。在日常开发中往往会因为投放平台的不一样需要进行重新编码。前段时间我们需要在淘宝页面上投放闲鱼组件,淘宝前端研发DSL主要是React(Rax),而闲鱼前端之前研发DSL主要是Vue(Weex),一般这种情况我们都是重新用React开发,有没有办法一键将已有的Vue组件转化为React组件呢,闲鱼技术团队从代码编译的角度提出了一种解决方案。编译器是如何工作的日常工作中我们接触最多的编译器就是Babel,Babel可以将最新的Javascript语法编译成当前浏览器兼容的JavaScript代码,Babel工作流程分为三个步骤,由下图所示:抽象语法树AST是什么在计算机科学中,抽象语法树(Abstract Syntax Tree,AST),或简称语法树(Syntax tree),是源代码语法结构的一种抽象表示。它以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构,详见维基百科。这里以const a = 1转成var a = 1操作为例看下Babel是如何工作的。将代码解析(parse)成抽象语法树ASTBabel提供了@babel/parser将代码解析成AST。const parse = require(’@babel/parser’).parse;const ast = parse(‘const a = 1’);经过遍历和分析转换(transform)对AST进行处理Babel提供了@babel/traverse对解析后的AST进行处理。@babel/traverse能够接收AST以及visitor两个参数,AST是上一步parse得到的抽象语法树,visitor提供访问不同节点的能力,当遍历到一个匹配的节点时,能够调用具体方法对于节点进行处理。@babel/types用于定义AST节点,在visitor里做节点处理的时候用于替换等操作。在这个例子中,我们遍历上一步得到的AST,在匹配到变量声明(VariableDeclaration)的时候判断是否const操作时进行替换成var。t.variableDeclaration(kind, declarations)接收两个参数kind和declarations,这里kind设为var,将const a = 1解析得到的AST里的declarations直接设置给declarations。const traverse = require(’@babel/traverse’).default;const t = require(’@babel/types’);traverse(ast, { VariableDeclaration: function(path) { //识别在变量声明的时候 if (path.node.kind === ‘const’) { //只有const的时候才处理 path.replaceWith( t.variableDeclaration(‘var’, path.node.declarations) //替换成var ); } path.skip(); }});将最终转换的AST重新生成(generate)代码Babel提供了@babel/generator将AST再还原成代码。const generate = require(’@babel/generator’).default;let code = generate(ast).code;Vue和React的异同我们来看下Vue和React的异同,如果需要做转化需要有哪些处理,Vue的结构分为style、script、template三部分style样式这部分不用去做特别的转化,Web下都是通用的scriptVue某些属性的名称和React不太一致,但是功能上是相似的。例如data需要转化为state,props需要转化为defaultProps和propTypes,components的引用需要提取到组件声明以外,methods里的方法需要提取到组件的属性上。还有一些属性比较特殊,比如computed,React里是没有这个概念的,我们可以考虑将computed里的值转化成函数方法,上面示例中的length,可以转化为length()这样的函数调用,在React的render()方法以及其他方法中调用。Vue的生命周期和React的生命周期有些差别,但是基本都能映射上,下面列举了部分生命周期的映射created -> componentWillMountmounted -> componentDidMountupdated -> componentDidUpdatebeforeDestroy -> componentWillUnmount在Vue内函数的属性取值是通过this.xxx的方式,而在Rax内需要判断是否state、props还是具体的方法,会转化成this.state、this.props或者this.xxx的方式。因此在对Vue特殊属性的处理中,我们对于data、props、methods需要额外做标记。template针对文本节点和元素节点处理不一致,文本节点需要对内容{{title}}进行处理,变为{title}。Vue里有大量的增强指令,转化成React需要额外做处理,下面列举了部分指令的处理方式事件绑定的处理,@click -> onClick逻辑判断的处理,v-if=“item.show” -> {item.show && ……}动态参数的处理,:title=“title” -> title={title}还有一些是正常的html属性,但是React下是不一样的,例如style -> className。指令里和model里的属性值需要特殊处理,这部分的逻辑其实和script里一样,例如需要{{title}}转变成{this.props.title}Vue代码的解析以下面的Vue代码为例<template> <div> <p class=“title” @click=“handleClick”>{{title}}</p> <p class=“name” v-if=“show”>{{name}}</p> </div></template><style>.title {font-size: 28px;color: #333;}.name {font-size: 32px;color: #999;}</style><script>export default { props: { title: { type: String, default: “title” } }, data() { return { show: true, name: “name” }; }, mounted() { console.log(this.name); }, methods: { handleClick() {} }};</script>我们需要先解析Vue代码变成AST值。这里使用了Vue官方的vue-template-compiler来分别提取Vue组件代码里的template、style、script,考虑其他DSL的通用性后续可以迁移到更加适用的html解析模块,例如parse5等。通过require(‘vue-template-compiler’).parseComponent得到了分离的template、style、script。style不用额外解析成AST了,可以直接用于React代码。template可以通过require(‘vue-template-compiler’).compile转化为AST值。script用@babel/parser来处理,对于script的解析不仅仅需要获得整个script的AST值,还需要分别将data、props、computed、components、methods等参数提取出来,以便后面在转化的时候区分具体属于哪个属性。以data的处理为例:const traverse = require(’@babel/traverse’).default;const t = require(’@babel/types’);const analysis = (body, data, isObject) => { data._statements = [].concat(body); // 整个表达式的AST值 let propNodes = []; if (isObject) { propNodes = body; } else { body.forEach(child => { if (t.isReturnStatement(child)) { // return表达式的时候 propNodes = child.argument.properties; data._statements = [].concat(child.argument.properties); // 整个表达式的AST值 } }); } propNodes.forEach(propNode => { data[propNode.key.name] = propNode; // 对data里的值进行提取,用于后续的属性取值 });};const parse = (ast) => { let data = { }; traverse(ast, { ObjectMethod(path) { /* 对象方法 data() {return {}} / const parent = path.parentPath.parent; const name = path.node.key.name; if (parent && t.isExportDefaultDeclaration(parent)) { if (name === ‘data’) { const body = path.node.body.body; analysis(body, data); path.stop(); } } }, ObjectProperty(path) { / 对象属性,箭头函数 data: () => {return {}} data: () => ({}) / const parent = path.parentPath.parent; const name = path.node.key.name; if (parent && t.isExportDefaultDeclaration(parent)) { if (name === ‘data’) { const node = path.node.value; if (t.isArrowFunctionExpression(node)) { / 箭头函数 () => {return {}} () => {} / if (node.body.body) { analysis(node.body.body, data); } else if (node.body.properties) { analysis(node.body.properties, data, true); } } path.stop(); } } } }); / 最终得到的结果 { _statements, //data解析AST值 list //data.list解析AST值 } */ return data;};module.exports = parse;最终处理之后得到这样一个结构:app: { script: { ast, components, computed, data: { _statements, //data解析AST值 list //data.list解析AST值 }, props, methods }, style, // style字符串值 template: { ast // template解析AST值 }}React代码的转化最终转化的React代码会包含两个文件(css和js文件)。用style字符串直接生成index.css文件,index.js文件结构如下图,transform指将Vue AST值转化成React代码的伪函数。import { createElement, Component, PropTypes } from ‘React’;import ‘./index.css’;export default class Mod extends Component { ${transform(Vue.script)} render() { ${transform(Vue.template)} }}script AST值的转化不一一说明,思路基本都一致,这里主要针对Vue data继续说明如何转化成React state,最终解析Vue data得到的是{_statements: AST}这样的一个结构,转化的时候只需要执行如下代码const t = require(’@babel/types’);module.exports = (app) => { if (app.script.data && app.script.data._statements) { // classProperty 类属性 identifier 标识符 objectExpression 对象表达式 return t.classProperty(t.identifier(‘state’), t.objectExpression(app.script.data._statements)); } else { return null; }};针对template AST值的转化,我们先看下Vue template AST的结构:{ tag: ‘div’, children: [{ tag: ’text’ },{ tag: ‘div’, children: [……] }]}转化的过程就是遍历上面的结构针对每一个节点生成渲染代码,这里以v-if的处理为例说明下节点属性的处理,实际代码中会有两种情况:不包含v-else的情况,<div v-if=“xxx”/>转化为{ xxx && <div /> }包含v-else的情况,<div v-if=“xxx”/><text v-else/>转化为{ xxx ? <div />: <text /> }经过vue-template-compiler解析后的template AST值里会包含ifConditions属性值,如果ifConditions的长度大于1,表明存在v-else,具体处理的逻辑如下:if (ast.ifConditions && ast.ifConditions.length > 1) { // 包含v-else的情况 let leftBlock = ast.ifConditions[0].block; let rightBlock = ast.ifConditions[1].block; let left = generatorJSXElement(leftBlock); //转化成JSX元素 let right = generatorJSXElement(rightBlock); //转化成JSX元素 child = t.jSXExpressionContainer( //JSX表达式容器 // 转化成条件表达式 t.conditionalExpression( parseExpression(value), left, right ) );} else { // 不包含v-else的情况 child = t.jSXExpressionContainer( //JSX表达式容器 // 转化成逻辑表达式 t.logicalExpression(’&&’, parseExpression(value), t.jsxElement( t.jSXOpeningElement( t.jSXIdentifier(tag), attrs), t.jSXClosingElement(t.jSXIdentifier(tag)), children )) );}template里引用的属性/方法提取,在AST值表现上都是标识符(Identifier),可以在traverse的时候将Identifier提取出来。这里用了一个比较取巧的方法,在template AST值转化的时候我们不对这些标识符做判断,而在最终转化的时候在render return之前插入一段引用。以下面的代码为例<text class=“title” @click=“handleClick”>{{title}}</text><text class=“list-length”>list length:{{length}}</text><div v-for="(item, index) in list" class=“list-item” :key="item-${index}"> <text class=“item-text” @click=“handleClick” v-if=“item.show”>{{item.text}}</text></div>我们能解析出template里的属性/方法以下面这样一个结构表示:{ title, handleClick, length, list, item, index}在转化代码的时候将它与app.script.data、app.script.props、app.script.computed和app.script.computed分别对比判断,能得到title是props、list是state、handleClick是methods,length是computed,最终我们在return前面插入的代码如下:let {title} = this.props;let {state} = this.state;let {handleClick} = this;let length = this.length();最终示例代码的转化结果import { createElement, Component, PropTypes } from ‘React’;export default class Mod extends Component { static defaultProps = { title: ’title’ } static propTypes = { title: PropTypes.string } state = { show: true, name: ’name’ } componentDidMount() { let {name} = this.state; console.log(name); } handleClick() {} render() { let {title} = this.props; let {show, name} = this.state; let {handleClick} = this; return ( <div> <p className=“title” onClick={handleClick}>{title}</p> {show && ( <p className=“name”>{name}</p> )} </div> ); }}总结与展望本文从Vue组件转化为React组件的具体案例讲述了一种通过代码编译的方式进行不同前端框架代码的转化的思路。我们在生产环境中已经将十多个之前的Vue组件直接转成React组件,但是实际使用过程中研发同学的编码习惯差别也比较大,需要处理很多特殊情况。这套思路也可以用于小程序互转等场景,减少编码的重复劳动,但是在这类跨端的非保准Web场景需要考虑更多,例如小程序环境特有的组件以及API等,闲鱼技术团队也会持续在这块做尝试。本文作者:闲鱼技术-玉缜阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

April 3, 2019 · 3 min · jiezi

Vuex源码学习(八)模块的context如何被创建以及它的作用

你不知道action与mutation怎么被调用的?赶紧回去看啊Vuex源码学习(七)action和mutation如何被调用的(调用篇))上两个小节已经讲述了commit与dispatch如何调用mutation与action的,但是action中有几个参数感觉涉及到了一些我们遗漏(故意不讲)的点。模块的context在installModule的时候 给每个模块绑定了一个属性context。通过makeLocalContext函数创建的,在注册action、mutation和getters都有使用。这个context是什么呢?makeLocalContext函数创建了一个什么东西返回值local对象 由两个方法、两个属性构成的。这个目的是什么?创建局部模块的dispatch、commit、getters、state也就是这个东西我们按照类型分析dispatch与commit// 查看全名,如果没有全名 可能是根模块或者没有设置命名空间const noNamespace = namespace === ‘’;// 如果没有全名 就使用全局(store)上的disptach// 有全名的话 构建一个新的dispatch // 这个新的dispatch仍然接收三个参数(与store上的dispatch一样)// dispatch: noNamespace ? store.dispatch : (_type, _payload, _options) => { //unifyObjectStyle 对额外传入的_options没有任何处理 只是确定一下位置 const args = unifyObjectStyle(_type, _payload, _options) // options 值没有发生变化 const { payload, options } = args let { type } = args // 在disptach的时候是否指定选择root(根) // 如果options设置为{root : true} 那么就会跳过下面 if (!options || !options.root) { // 拼接真正的名字 type = namespace + type if (process.env.NODE_ENV !== ‘production’ && !store._actions[type]) { console.error([vuex] unknown local action type: ${args.type}, global type: ${type}) return } } // 调用(补全名字后)的action return store.dispatch(type, payload) },这段代码我们可以看出来,local对象(也就是模块的context属性)中的dispacth会在未指定使用根模块名字时,会把dispatch调用的名字强行加上这个模块的全名,用这个dispatch调用的action都会变成你这个模块下的action所以local中的dispatch与store中的disptach有什么不同通俗的讲我们想要调用A模块(有命名空间的某个action B)需要做的是this.$store.dispatch(‘A模块的全名/B的名字’); 在A模块的action中想要使用dispatch来做一些事情。actions.jsexport const ajaxGetUserName = ({dispatch})=>{ // 这个时候用dispatch调用自己模块内的其余的action不需要加上全名 dispatch(‘ajaxGetUserAge’); // 想要变成和根模块一样的dispatch 需要加上一个options注明{root : true} // 这个时候dispatch就会变成全局的 不会主动帮你拼接全名了}export const ajaxGetUserAge = () => { // do something}同理local对象下的commit也是做了同样的事情,这里就不多加解释了,相信聪明的你早就可以举一反三了。两个方法说完了,下面该讲两个属性了getters与state这两个属性就是我们的getters与state,但是这是我们local对象中,也就是局部模块下的getters与state。getters与state如何创建的getters首先判断全名是不是为空,为空就返回store对象的getters,有的话就创建局部getters。与其说是创建不如说是代理如何创建局部的getters? 代理的方式makeLocalGetters源码function makeLocalGetters (store, namespace) { // 设计思想 //其实我们并不需要创建一套getters, // 只要我们在local中通过getters来获取一些局部模块的值的时候, // 可以被代理到真正存放这些getters的地方。 // 创建代理对象 const gettersProxy = {} // 找到切割点 const splitPos = namespace.length Object.keys(store.getters).forEach(type => { // skip if the target getter is not match this namespace // 得去getters里面找一下有没有这个namespace为前缀的getter。 // 没有就找不到了 if (type.slice(0, splitPos) !== namespace) return // extract local getter type // 拿到模块内注册的那个局部的getter名字 // 全名是set/getName // localType就是getName const localType = type.slice(splitPos) // Add a port to the getters proxy. // Define as getter property because // we do not want to evaluate the getters in this time. // 完成代理任务, // 在查询局部名字是被代理到对应的store.getters中的(全名)getter Object.defineProperty(gettersProxy, localType, { get: () => store.getters[type], enumerable: true }) }) //返回代理对象 return gettersProxy}创建局部的getters就是一个代理的过程,在使用模块内使用(没有加上命名空间的)getters的名字,会被代理到,store实例上那个真正的(全名的)getters。state这个相对来说就简单很多了与代理类似,只是state只需要代理到state中对应那个模块的state,这个就比较简单了。创建完毕context是如何被创建的大家已经比较了解了。context的作用是什么?(local就是contenxt)之前说过注册mutation、action、getters都用到了local。用他们干什么?一一介绍1. 注册mutation我们注册的mutation在被commit调用时,使用的state是局部的state,当前模块内的state,所以不用特殊方式mutation无法更新父(祖先)模块和兄弟模块的内容。2. 注册dispatchdispatch是可以调用到模块内的mutation、disptach,也就是说它有更新模块内数据的能力,但是只给了dispatch传入了store的getters与state(虽然有了这俩你想要什么放在vuex的数据都能得到),并没有给store的dispatch与mutation。这就说名dispatch可以查看store中的所有数据,你放在vuex里面的数据我都可以看,但是你想改不使用特殊手段,不好意思只能改自己模块的。3. 注册gettersgetters并没有改变数据的能力,你愿意怎么操作数据都可以,模块内的数据,全模块的数据都可以给你,你愿意怎么计算都可以。在注册中我们可以看到,vuex对这个改变数据的权限控制的很严格,但是查看数据控制的很松,改只能改自己模块的,查你愿意怎么看都OK。总结context(也是local)是经过一个makeLocalContext的函数创建的,里面有局部的dispatch、commit方法和getters、state属性。局部的方法属性都是只能访问局部模块内的,除非在使用时额外传入options({root:true})来解开局部模块的限制。局部的getters是通过makeLocalGetters来实现的,主要思想是依靠代理的方式,把局部的名字的getter代理到store的getters中那个全名的getter。context 的作用可以帮助dispatch与commit控制更新数据的权限,帮助模块内getters拿到局部与全模块的数据。这个章节结束,我们所有和模块有关的内容就已经完结了。对于模块做一个小的总结。模块的意义模块与模块链接把Vuex初始化传入的内容,整理成一个方便处理的模块树(方便)模块让action、mutation、getters、state都有了自己的全名(设置namespaced为true),起名字不再被约束,减少了命名冲突。模块还给action、mutation、getters提供了局部上下文(context)让模块内的这些方法和属性,可以方便的修改模块内的数据以及获取全模块与模块内的数据。dispatch与commit也对模块进行了全力的支持(不支持不白做了吗),所以模块为Vuex提供了很多方便,方便的去获取数据、修改数据。那么Vuex真正的数据仓库在哪里?数据都存储在哪里?我们下一章见我是一个应届生,最近和朋友们维护了一个公众号,内容是我们在从应届生过渡到开发这一路所踩过的坑,已经我们一步步学习的记录,如果感兴趣的朋友可以关注一下,一同加油~ ...

April 2, 2019 · 2 min · jiezi

Vuex源码学习(六)action和mutation如何被调用的(前置准备篇)

module与moduleCollection你一定要会啊!Vuex源码学习(五)加工后的module在组件中使用vuex的dispatch和commit的时候,我们只要把action、mutation注册好,通过dispatch、commit调用一下方法名就可以做到。使用方式vue组件内//in vue componentthis.$store.commit(‘setName’,{name : ‘xLemon’});this.$store.commit(’list/setName’,{name : ‘xLemon’});vuex的mutation中// in mutation.jsexport const setName = function(state,payload){ state.name = payload.name;}// in list mutation.js 在名为list的模块下的mutation(有自己的命名空间)export const setName = function(state,payload){ state.name = payload.name;}我们传递的只是一个字符串,commit是如何找到注册mutation时的同名方法的呢?有命名空间的这种mutation是如何被找到并且执行的呢?上源码属性的意义_actions用于存放所有注册的action_mutations用于存放所有注册的mutation被注册的action和mutation如何被放到对应的属性中的呢?轮到installModule函数要出马了。installModule的意义是初始化根模块然后递归的初始化所有模块,并且收集模块树的所有getters、actions、mutations、以及state。看一下installModule的代码,installModule并不是在类的原型上,并不暴露出来,属于一个私有的方法,接收五个参数。store(Vuex.store的实例对象。rootState (根结点的state数据)。path 被初始化模块的path(前两张讲过path的意义)。module 被初始化的模块。hot 热更新(并不关键)function installModule (store, rootState, path, module, hot) { const isRoot = !path.length // 获取全名 const namespace = store._modules.getNamespace(path) // register in namespace map if (module.namespaced) { // 设置命名空间对照map store._modulesNamespaceMap[namespace] = module //console.log(store._modulesNamespaceMap); } // set state if (!isRoot && !hot) { const parentState = getNestedState(rootState, path.slice(0, -1)) const moduleName = path[path.length - 1] // 把子模块的state(数据)绑定到父模块上(按照层级) store._withCommit(() => { Vue.set(parentState, moduleName, module.state) }) } const local = module.context = makeLocalContext(store, namespace, path) // 使用模块暴露出来的方法来注册mutation、action、getter module.forEachMutation((mutation, key) => { const namespacedType = namespace + key registerMutation(store, namespacedType, mutation, local) }) module.forEachAction((action, key) => { const type = action.root ? key : namespace + key const handler = action.handler || action registerAction(store, type, handler, local) }) module.forEachGetter((getter, key) => { const namespacedType = namespace + key registerGetter(store, namespacedType, getter, local) }) module.forEachChild((child, key) => { installModule(store, rootState, path.concat(key), child, hot) })}这个函数虽然只有40多行,但处于一个承上启下的关键点,这一章只会分析如何收集mutation与action其余的内容会再下一章讲述。installModule首先获取一下这个模块的命名(我称之为全名)依赖_modules(ModuleCollection实例对象)的getNamespace方法。根据模块的path,path有从根结点到当前节点这条路径上按顺序排序的所有模块名(根结点没有模块名,并没有设置在path,所以根模块注册时是个空数组,他的子模块的path就是[子模块的名字])。那么Vuex如何整理模块名的呢?效果:如果这个模块有自己的命名空间(namespaced为true)这个模块的全名就是父模块的全名+自己的模块名,如果这个模块没有自己的命名空间(namespaced为false)这个模块的全名就是父模块的全名为什么会是这样?分析一下代码getNamespace (path) { let module = this.root //根模块 return path.reduce((namespace, key) => { //根模块的path是个空数组不执行 // path的第一项是根模块的儿子模块。 // 获取儿子模块 并且将替换module (path的下一项就是儿子模块中的子模块了) // 下次累加 就是这个module(轮到子模块了)去找它(子模块)的子模块 module = module.getChild(key) // 查看儿子模块是不是设置了命名空间 //如果设置了这个模块的全名就增加自己的模块名和一个’/‘分割后面的模块名, //没有的话返回一个’’, // reduce累加可以把这个名称进行累加 return namespace + (module.namespaced ? key + ‘/’ : ‘’) }, ‘’)}获取完模块的全名了,之后我们看一下这两个函数module.forEachActionmodule.forEachMutation在上一章节module提供了遍历自己内部的action、mutation的方法。 module.forEachMutation((mutation, key) => { const namespacedType = namespace + key registerMutation(store, namespacedType, mutation, local)})module.forEachAction((action, key) => { const type = action.root ? key : namespace + key const handler = action.handler || action registerAction(store, type, handler, local) })const namespacedType = namespace + key 这句话 就是拼接出真正的mutation、action的名字模块全名+mutation/action的名字。也就是一开始我举例的list/setName是这个mutation的全名(被调用的时候用)this.$store.commit(’list/setName’,{name : ‘xLemon’});名称已经获取到了,下一步怎么办?把这些函数按照对应名字放到之前说的_actions、_mutations属性中啊看一下这个名字的mutation有没有被注册过,没有就声明一下,然后push进去。如果这个名字的mutation被注册过,就push进去。action同理小彩蛋 设置两个不同模块的同名mutation(全名一样哦)这两个mutation都会执行,action也是一样的。总结action和mutation在被dispatch和commit调用前,首先遍历模块树获取每个模块的全名。把模块内的action和mutation加上模块全名,整理成一个全新的名字放入_actions 、 _mutations属性中。dispacth和commit如何调用aciton和mutation 的将在下章讲述下一章:我们讨论action和mutation如何被调用的(调用篇)。我是一个应届生,最近和朋友们维护了一个公众号,内容是我们在从应届生过渡到开发这一路所踩过的坑,已经我们一步步学习的记录,如果感兴趣的朋友可以关注一下,一同加油~ ...

March 31, 2019 · 2 min · jiezi

React Native 升级指南 从0.55升级到0.59

前言本月早些时候facebook发布了React Native 0.59,个人感觉算是RN的里程碑,主要有:增加了对hooks的支持更新了Android端JSC(JavaScript Core)inline requires精简代码:一些组件交给社区来维护CLI的提升,同时也从rn库中独立了出来详细内容升级到0.59核心就是参考RN diff PURGE来手动升级,这也是官方推荐的方式。此外,rn0.59中移除了react-native-git-upgrade,取而代之的是react-native upgrade,而react-native upgrade就是基于RN diff PURGE。如何查看指定版本之间的diff?手动编辑URL:https://github.com/react-nati…[当前版本号]…version/[目标版本号]。比如我是从0.55.4到0.59.2 那么就是https://github.com/react-nati…然后查看变更(点击Files changed)来手动更改。如果你习惯在本地查看变更,比如在vs code中,那么可以在releases中下载对应的版本,在本地创建一个git仓库,再用目标版本的文件替换之。后来发现了一个网站。。(捂脸 和方法1其实是一样的遇到的一些坑iOS的project.pbxproj变更非常多(0.55.4 -> 0.59.2),其实根本不用管他。(有待验证,我改吐了,后来不管他项目也跑起来了)cocoaPods: pod install时报错could not find compatible versions for pod “Folly”,解决方法:Podfile中添加 # Third party deps podspec link pod ‘DoubleConversion’, :podspec => ‘../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec’ pod ‘glog’, :podspec => ‘../node_modules/react-native/third-party-podspecs/glog.podspec’ pod ‘Folly’, :podspec => ‘../node_modules/react-native/third-party-podspecs/Folly.podspec’官方说明iOS打包完毕后启动app,报错:can’t find variable requireNativeComponent。 Google无果。一番排查后发现是rn源码(Modal.js)“出错了”,本以为可以pr走一波,后来才发现GitHub上的源码没问题。OK,npm接锅。删除node_modules下的rn文件夹,npm i react-native, 解决。报错2: decorator相关。因为用了babel7,transform-decorators-legacy并不适用,使用@babel/plugin-proposal-decorators,同时带上@babel/plugin-proposal-class-properties。继续报错: TypeError: undefined is not an object (evaluating ‘props.getItem’): 这个是@babel/plugin-proposal-class-properties的锅相关issue。如果你不需要这个,直接移除即可。又或者,添加新的babel plugin@babel/plugin-transform-flow-strip-types并且保证顺序在@babel/plugin-proposal-class-properties之上。附:本人项目中.babelrcplugins部分"plugins": [ ["@babel/plugin-transform-flow-strip-types"], ["@babel/plugin-proposal-decorators", { “legacy”: true }], ["@babel/plugin-proposal-class-properties", { “loose”: true }]]Enjoy new version React Native至此,你的rn项目应该可以跑起来了。一进去你(可能)会发现非常多的Yellow Box Warning按照提示慢慢去改进吧~也希望这篇文章对那些和我一样没有原生经验的小伙伴有所帮助。Thanks for reading ...

March 30, 2019 · 1 min · jiezi

如何优雅地定位外网问题?

本文由云+社区发表作者:elson现状分析在定位外网问题时,最怕的是遇到无法复现或者是偶现的问题,我们无法在用户的设备上通过抓包、打断点或日志来分析问题,只能靠仅有的页面截图和用户的片面描述作为线索。此时,也只能结合“猜想法”和“排除法”进行分析定位,排查了半天也很有可能没有结果,最后只能回复“可能是缓存或者app的原因,请清下缓存或者重新安装app试试”。导致我们定位外网问题时效率低下,主要还是因为缺乏定位线索;其次由于用户并不了解技术层面的前因后果,他们可能会忽略掉一些关键信息,或者提供了带有误导性的线索。常见的外网问题成因从笔者实际上所遇到的外网问题进行归类,主要有以下成因:后台数据返回异常,或部分数据为空;针对边界情况,页面未做相对应的容错措施,导致页面报错;用户的网络环境、APP版本问题;通过上一级入口进入页面时,漏传部分参数;与用户特定的操作步骤相关所引发。针对页面JS报错,我们已有脚本异常上报监控机制,业界也不乏相关的优秀开源产品,如sentry。但往往很多情况下的用户反馈以及外网异常并不是脚本异常引起的,此时无法触发异常上报。因此针对这部分场景,我们需要有另一套机制进行上报监控,辅助我们定位分析。用户的行为轨迹的重要性从上面的问题成因可以得出,如果我们能采集到并结合以下几方面数据,那外网异常的定位自然会事半功倍:页面的运行环境页面所加载的数据页面JS报错信息用户的操作日志(时间线)我们可以通过时间戳将以上数据串联起来,形成时间线。这样一来,页面的运行环境、页面中每个动作相关的数据、动作之间先后关系就会一目了然,就像一部案发现场的录像。因此这里强调“轨迹”的重要性,能够把散乱的数据串联起来,这对我们分析定位问题非常有帮助。基于上面的分析结论,我们搭建了一套用户行为轨迹追踪系统,大致工作流程为:在页面中加载JS SDK用于数据记录和上报,服务器接收并处理数据,再以接口的方式提供数据给内部查询系统,支持通过用户UIN以及页面地址进行查询。下面我们从报什么、怎么报、服务器如何处理数据、数据怎样展示四方面具体谈一下整体的设计思路。设计思路报什么:确定上报内容及协议根据上面的分析,我们已经初步得出了需要上报的数据内容。上报的内容最终需要落地到查询系统中,因此首先需要确定怎样查询。我们将用户在某页面的单次访问作为基本查询单位,假设某用户访问了3次A页面,那么在查询平台中就可以查出3条记录,每条记录可以包含多条不同类型的子记录,它们共用“基础信息”。大致的数据结构如下:const log = { baseInfo: {}, childLogs: [{…}, {…}, …]};基础信息baseInfo中记录的是页面的运行环境,可以称为“基础信息”,具体包括以下字段:字段名描述可选参数FtraceId某次页面访问的唯一标识(自动生成) Fuanavigator.userAgent FclientType客户端类型0:未知 1:qqmusic 2:weixin 3:mqqFos系统0:未知 1:ios 2:androidFurl页面地址 navigator.userAgent Frefer页面上级入口 document.referrer FloginType帐号类型0:wx 1:qqFuin用户帐号 childLogs中保存所有子记录,以下是子记录的公用字段以及三种不同类型。子记录公共字段每条子记录需要记录时间戳、标识上报类型,因此需要定义以下的公共字段:字段名描述可选参数/格式备注Flogtype上报类型0: ajax通信 1:用户操作 2:报错异常 FtimeStamp时间戳 串联不同类型的上报记录,形成轨迹Forder数字顺序Number当前记录在整条轨迹中的自增序号Forder的作用在于当两条记录的 FtimeStamp 值相同时,作为辅助的排序依据。子记录类型1:ajax通信记录页面中所有ajax通信的数据,方便排查异常是否与后台数据有关。字段名描述可选参数FajaxSendTimeajax请求发起时间点 FajaxReceiveTimeajax数据接收到时间点 FajaxMethodajax请求类型0:get 1:postFajaxParamajax请求参数 FajaxUrlajax请求链接 FajaxReceiveDataajax请求到的数据 FajaxHttpCodehttp返回码(200, 404) FajaxStateCode后台返回的业务相关code码 子记录类型2:用户操作行为记录打点数据以及用户点击操作的DOM上的数据字段名描述可选参数/格式FtraceContent自定义上报内容StringFdomPath操作目标DOM的xpath Fattr目标DOM的所有data-attr属性及其值{att1: ‘123’, att2: ‘234’}子记录类型3:报错异常记录JS报错信息以及我们手动抛出的异常信息字段名描述可选参数/格式备注FerrorType错误类型0:原生错误 1:手动抛出的异常 FerrorStack错误堆栈 仅原生错误报FerrorFilename出错文件 FerrorLineNo出错行 FerrorColNo出错列位置 FerrorMessage错误描述原生错误的errmsg或者开发自定义 怎么报:SDK的数据采集及上报策略上述的数据需要通过页面加载SDK进行采集,那么怎样采集,如何上报?数据采集方式从业务场景以及常见的外网问题考虑,我们只关注带有登录态的场景。对于未登录或获取不到登录态的场景,SDK不做任何数据采集和上报。( 1 ) 基础信息FtraceId可以直接搜 uuid 的生成算法,用户每进入页面时自动生成一个,后续采集的子记录共用此 ID。其他字段则可以从 cookie 或者原生 API 中获取,这里不再赘述。( 2 ) ajax 通信数据这里用到了一个开源组件 Ajax-hook ,源码很简练,GZIP 后只有 639 字节。主要原理是通过代理 XMLHttpRequest 以及相关实例属性和方法,提供各个阶段的钩子函数。hookAjax({ open: this.handleOpen, onreadystatechange: this.handleStage});一次 ajax 通信包含 open,send,readyStateChange 等阶段,因此需要在不同阶段的钩子函数中采集从请求发起到接收到请求响应的各方面数据。具体来说在 open 中可以采集:请求发起时间点、请求方法、请求参数等。需要注意过滤掉无用的请求,如数据采集后的上报请求。send 中主要用于采集 POST 请求的请求参数。handleOpen(arg, xhr) { const urlPath = arg[1] && arg[1].split(’?’); xhr.urlPath = urlPath[0]; // 过滤掉上报请求 if (/stat.y.qq.com/.test(urlPath[0])) { return; } curAjaxFields = $.extend({}, ajaxFields, { FtimeStamp: getNowDate(), FajaxSendTime: getNowDate(), FajaxMethod: arg[0] ? methodMap[arg[0].toUpperCase()] : ‘’, FajaxUrl: urlPath[0], FajaxParam: urlPath[1], Forder: logger.order++ }); xhr.curAjaxFields = curAjaxFields; const _oriSend = xhr.send.bind(xhr); xhr.send = function(body) { // POST请求 获取请求体中的参数 if (body) { curAjaxFields.FajaxParam = body; } _oriSend && _oriSend(body); }; }在 readyStateChange 中,当 xhr.readyState 为 2(HEADERS_RECEIVED) 或 4(DONE) 时,分别采集 FajaxReceiveTime 和 响应数据相关数据。这里需要注意的,为了把前期从 open 和 send 中采集到的数据传递下来,我们将数据对象挂载在当前 xhr 对象上: xhr.curAjaxFields = curAjaxFields; 。handleStage({ xhr }) { // 过滤掉上报请求 if (/stat.y.qq.com/.test(xhr.urlPath)) { return; } switch (+xhr.readyState) { case 2: // HEADERS_RECEIVED $.extend(xhr.curAjaxFields, { FajaxReceiveTime: getNowDate(), FajaxHttpCode: xhr.status }); break; case 4: // DONE const xhrResponse = xhr.response || xhr.responseText; let jsonRes; try { // 如果回包不是json格式的话会报错 jsonRes = xhrResponse ? JSON.parse(xhrResponse) : ‘’; … } catch (e) { console.error(e); } $.extend(xhr.curAjaxFields, { FajaxReceiveData: xhrResponse, FajaxStateCode: jsonRes ? getStateCode(jsonRes).join(’,’) : ’’ }); break; } }( 3 ) 用户操作行为通过事件代理,在 document 上监听指定类 .js_qm_tracer 的事件回调。在回调中通过event.path 取到当前 dom 的路径;通过 event.currentTarget.attributes 取到当前 dom 上的所有属性。同时还提供 API 实现自行上报 action.report(data)。$(document).on(‘click’, ‘.js_qm_trace’, e => { const target = e.currentTarget; // 时间戳 let FtimeStamp = getNowDate(); // Dom的xpath let FdomPath = getDomPath(e.path); // dom的所有data-attr属性以及值 let Fattr, FtraceContent = null; if (target.hasAttributes()) { let processedData = processAttrMap(target.attributes); Fattr = processedData.Fattr; FtraceContent = processedData.FtraceContent; } ……});上报策略上面的数据,如果我们记录一条就上报一条,这无疑是给自己制造DDOS攻击。此外,我们的初衷在于帮助排查外网问题,因此在我们需要用的时候再报上来就行了。所以需要引入本地缓存和用户白名单机制,采集完先在本地缓存起来,需要的时候再根据用户白名单“捞取”。本地缓存机制我们选用的是 IndexedDB,它容量大( 500M ),异步读写的特性保证其不会对页面渲染产生阻塞,此外还支持建立自定义索引,易于检索,更适合管理采集到的数据。用户白名单机制则是通过一个后台服务,SDK初始化后都会先查询当前用户和页面URL是否均在白名单中,是的话则将之前缓存的数据进行上报,而之后的用户行为操作也会直接上报,不再先缓存。但如果遇到JS错误报错,属于紧急情况,这时则不再遵循“缓存优先”,而是直接上报错误信息以及当前采集到的其他数据。上报策略流程图:白名单机制流程图:获取到白名单用户的数据需要用户再次访问页面,一方面从性能和开发成本考虑,另一方面反馈外网问题的用户很大概率是会再次访问当前页面的。只需要再次进入页面,无需额外操作,这样对用户来说也没有沉重的操作成本和沟通成本,简单易操作。数据处理:服务器对数据的处理策略( 1 ) 首先,数据上报请求经过 nginx 服务器后,会生成 access.log。http { log_format trace ‘$request_body’; server { location /trace/ { client_body_buffer_size 1000m; client_max_body_size 1000m; proxy_pass http://127.0.0.1:6699/env; access_log /data/qmtrace/log/access.log trace; } } server { listen 6699; location /env/ { client_max_body_size 1000m; alias /data/qmtrace/; } }}使用 nginx 日志进行记录,主要是因为 nginx 优异的性能,能抗住高并发;此外其接入和维护成本也较低。这里在处理 POST 请求的日志时,遇到一个坑。如果不经过 proxy_pass 转发一次的话,nginx 无法对 POST 请求产生日志记录。此外需要注意的是缓冲区的大小, client_body_buffer_size 默认只有 8K 或 16K,如果实际请求体大小超过了它,那就会被忽略,无法产生日志记录。( 2 ) 通过 crontab 每五分钟定期处理一次 access.log将 access.log 移动到相应的以年月日小时命名的目录下,生成 access${minute}.log。移走 access.log 之后,此时需要执行以下命令,发送通知给 nginx,收到通知后会重新生成新的 access.log。kill -USR1 cat ${nginx_pid}最后用node脚本,对 access${minute}.log 进行解析处理后入库。数据展示:搭建查询平台查询平台采集到的数据,在内部查询平台通过用户 UIN 进行检索,同时支持输入特定的页面 URL,进一步聚焦检索结果。在之前我们提到,将用户在某页面的单次访问作为基本查询单位,假设某用户访问了3次A页面,那么在左侧就会检索出3条记录(每条记录都有唯一标识 FtraceId )。为了查询平台的性能考虑,每次查询只会返回左侧的记录列表以及第一条记录的详细信息。点击其他记录再根据 FtraceId 进行异步查询。右侧展示的是某条记录的详细信息,通过时间线的形式将用户在某次页面访问期间的行为轨迹直观地展示出来。通过客观且直观的用户轨迹数据,我们就可以更高效更有针对性地分析定位外网问题。总结我们通过报什么(上报内容及协议)、怎么报(SDK采集及上报策略)、数据如何处理、数据怎样展示,四个方面介绍了如何搭建用户行为轨迹追踪系统。目前只是个初级版本,有很多地方需要继续完善和改进。有了追踪用户轨迹数据,能够从很大程度上有效灵活地应对用户反馈和外网异常,从而也很好地提升了我们的工作效率。参考前端异常监控解决方案研究监控平台前端SDK开发实践浏览器数据库 IndexedDB 入门教程Ajax-hook 原理解析此文已由腾讯云+社区在各渠道发布获取更多新鲜技术干货,可以关注我们腾讯云技术社区-云加社区官方号及知乎机构号 ...

March 19, 2019 · 2 min · jiezi

Vue.js-Vue实例

推荐阅读原文 学习笔记:Vue实例Vue实例组件间通信<p data-height=“265” data-theme-id=“0” data-slug-hash=“wjXLMa” data-default-tab=“js,result” data-user=“whjin” data-embed-version=“2” data-pen-title=“组件间通信” class=“codepen”>See the Pen 组件间通信 by whjin (@whjin) on CodePen.</p><script async src=“https://static.codepen.io/ass...;></script>用户信息表<p data-height=“365” data-theme-id=“0” data-slug-hash=“zjagOy” data-default-tab=“js,result” data-user=“whjin” data-embed-version=“2” data-pen-title=“zjagOy” class=“codepen”>See the Pen zjagOy by whjin (@whjin) on CodePen.</p><script async src=“https://static.codepen.io/ass...;></script>图书电商数据<p data-height=“565” data-theme-id=“0” data-slug-hash=“zjaVWL” data-default-tab=“js,result” data-user=“whjin” data-embed-version=“2” data-pen-title=“图书电商数据” class=“codepen”>See the Pen 图书电商数据 by whjin (@whjin) on CodePen.</p><script async src=“https://static.codepen.io/ass...;></script>渲染微信精选数据<p data-height=“565” data-theme-id=“0” data-slug-hash=“WJyqmO” data-default-tab=“js,result” data-user=“whjin” data-embed-version=“2” data-pen-title=“渲染微信精选数据” class=“codepen”>See the Pen 渲染微信精选数据 by whjin (@whjin) on CodePen.</p><script async src=“https://static.codepen.io/ass...;></script>图书管理系统<p data-height=“565” data-theme-id=“0” data-slug-hash=“NMzQjy” data-default-tab=“html,result” data-user=“whjin” data-embed-version=“2” data-pen-title=“图书管理系统” class=“codepen”>See the Pen 图书管理系统 by whjin (@whjin) on CodePen.</p><script async src=“https://static.codepen.io/ass...;></script>模态窗口-课程表<p data-height=“365” data-theme-id=“0” data-slug-hash=“KReORr” data-default-tab=“html,result” data-user=“whjin” data-embed-version=“2” data-pen-title=“模态窗口-课程表” class=“codepen”>See the Pen 模态窗口-课程表 by whjin (@whjin) on CodePen.</p><script async src=“https://static.codepen.io/ass...;></script>filterBy过滤器<p data-height=“265” data-theme-id=“0” data-slug-hash=“MGXNdM” data-default-tab=“js,result” data-user=“whjin” data-embed-version=“2” data-pen-title=“filterBy过滤器” class=“codepen”>See the Pen filterBy过滤器 by whjin (@whjin) on CodePen.</p><script async src=“https://static.codepen.io/ass...;></script>更新信息表<p data-height=“565” data-theme-id=“0” data-slug-hash=“odMvNZ” data-default-tab=“html,result” data-user=“whjin” data-embed-version=“2” data-pen-title=“更新信息表” class=“codepen”>See the Pen 更新信息表 by whjin (@whjin) on CodePen.</p><script async src=“https://static.codepen.io/ass...;></script>猜数字游戏<p data-height=“265” data-theme-id=“0” data-slug-hash=“dejyNw” data-default-tab=“js,result” data-user=“whjin” data-embed-version=“2” data-pen-title=“猜数字游戏” class=“codepen”>See the Pen 猜数字游戏 by whjin (@whjin) on CodePen.</p><script async src=“https://static.codepen.io/ass...;></script>弹窗组件<p data-height=“465” data-theme-id=“0” data-slug-hash=“MGBWEE” data-default-tab=“js,result” data-user=“whjin” data-embed-version=“2” data-pen-title=“弹窗组件” class=“codepen”>See the Pen 弹窗组件 by whjin (@whjin) on CodePen.</p><script async src=“https://static.codepen.io/ass...;></script>菜单组件<p data-height=“365” data-theme-id=“0” data-slug-hash=“mLjdLd” data-default-tab=“html,result” data-user=“whjin” data-embed-version=“2” data-pen-title=“菜单组件” class=“codepen”>See the Pen 菜单组件 by whjin (@whjin) on CodePen.</p><script async src=“https://static.codepen.io/ass...;></script>网格组件<p data-height=“325” data-theme-id=“0” data-slug-hash=“QrBwba” data-default-tab=“js,result” data-user=“whjin” data-embed-version=“2” data-pen-title=“网格组件” class=“codepen”>See the Pen 网格组件 by whjin (@whjin) on CodePen.</p><script async src=“https://static.codepen.io/ass...;></script>树形视图<p data-height=“265” data-theme-id=“0” data-slug-hash=“aGjzdr” data-default-tab=“js,result” data-user=“whjin” data-embed-version=“2” data-pen-title=“树形视图” class=“codepen”>See the Pen 树形视图 by whjin (@whjin) on CodePen.</p><script async src=“https://static.codepen.io/ass...;></script>可伸缩性的 Header<p data-height=“565” data-theme-id=“0” data-slug-hash=“qYyErB” data-default-tab=“html,result” data-user=“whjin” data-embed-version=“2” data-pen-title=“可伸缩性的 Header” class=“codepen”>See the Pen 可伸缩性的 Header by whjin (@whjin) on CodePen.</p><script async src=“https://static.codepen.io/ass...;></script>内嵌组件<p data-height=“265” data-theme-id=“0” data-slug-hash=“QrBwgr” data-default-tab=“js,result” data-user=“whjin” data-embed-version=“2” data-pen-title=“内嵌组件” class=“codepen”>See the Pen 内嵌组件 by whjin (@whjin) on CodePen.</p><script async src=“https://static.codepen.io/ass...;></script>SVG 图表<p data-height=“565” data-theme-id=“0” data-slug-hash=“JvBoOo” data-default-tab=“js,result” data-user=“whjin” data-embed-version=“2” data-pen-title=“SVG 图表” class=“codepen”>See the Pen SVG 图表 by whjin (@whjin) on CodePen.</p><script async src=“https://static.codepen.io/ass...;></script>实时 deepstreamHub<p data-height=“565” data-theme-id=“0” data-slug-hash=“dejPJM” data-default-tab=“js,result” data-user=“whjin” data-embed-version=“2” data-pen-title=“实时 deepstreamHub” class=“codepen”>See the Pen 实时 deepstreamHub by whjin (@whjin) on CodePen.</p><script async src=“https://static.codepen.io/ass...;></script> ...

March 18, 2019 · 2 min · jiezi

Vue.js-状态管理与Vuex

学习笔记:状态管理与Vuex状态管理与Vuex非父子组件(跨级组件和兄弟组件)通信时,使用了bus(中央事件总线)的一个方法,用来触发和接收事件,进一步起到通信的作用。一个组件可以分为数据(model)和视图(view),数据更新时,视图也会自动更新。在视图中又可以绑定一个事件,它们触发methods里指定的方法,从而可以改变数据、更新视图,这是一个组件基本的运行模式。const store = new Vuex.Store({});仓库store包含了应用的数据(状态)和操作过程。Vuex里的数据都是响应式的,任何组件使用同一store的数据时,只要store的数据变化,对应的组件也会立即更新。数据保存在Vuex选项的state字段内。const store = new Vuex.Store({ state: { count: 0 }});在任何组件内,可以直接通过$store.state.count读取。<template> <div> <h1>首页</h1> {{$store.state.count}} </div></template>直接卸载template里显得有点乱,可以用一个计算属性来显示:<div> <h1>首页</h1> {{count}}</div>export default { computed: { count() { return $store.state.count; } }}在组件内来自store的数据只能读取,不能手动修改,修改store中数据的唯一途径是显式地提交mutations。mutations是Vuex的第二个选项,用来直接修改state里的数据。在组件内,通过this.$store.commit方法来执行mutations。mutations还可以接受第二个参数,可以是数字、字符串或对象等类型。ES6语法函数的参数可以设定默认值,当没有传入该参数时,使用设置的值。increment(state,n=1)等同于: increment(state,n){ n=n||1; }提交mutations的另一种方式是直接使用包含type属性的对象。mutations里尽量不要异步操作数据,否则组件在commit后数据不能立即改变,而且不知道什么时候会改变。高级用法Vuex还有其他3个选项可以使用:getter、actions、modules。getter能将computed的方法提取出来,也可以依赖其他的getter,把getter作为第二个参数。action与mutation很像,不同的是action里面提交的是mutation,并且可以一步操作业务逻辑。action在组件内通过$store.dispatch触发。modules用来将store分割到不同模块,当项目足够大时,store里的state、getters、mutations、actions会非常多,使用modules可以把它们写到不同的文件中。module的mutation和getter接收的第一个参数state是当前模块的状态。在actions和getters中还可以接收一个参数rootState,来访问根节点的状态。实战:中央事件总线插件vue-bus中央事件总线bus作为一个简单的组件传递事件,用于解决跨级和兄弟组件通信的问题。vue-bus插件给Vue添加一个属性$bus,并代理$emit、$on、$off三个方法。ES6语法emit(event,..args)中的…是函数参数的解构。因为不知道组件会传递多少个参数进来,使用…args可以把从当前参数到最后的参数都获取到。使用vue-bus有两点需要注意:第一是$bus.on应该在created钩子内使用,如果在mounted使用,它可能接收不到其他组件来自created钩子内发出的事件;第二点是使用了$bus.on在beforeDestroy钩子里应该再使用$bus.off解除,因为组件销毁后,就没有必要把监听的句柄存储在vue-bus中。Vue插件注册插件需要一个公开的方法install,它的第一个参数时Vue构造器,第二个参数是一个可选的选项对象。<p data-height=“350” data-theme-id=“0” data-slug-hash=“RJVOXd” data-default-tab=“js” data-user=“whjin” data-embed-version=“2” data-pen-title=“Vue插件” class=“codepen”>See the Pen Vue插件 by whjin (@whjin) on CodePen.</p><script async src=“https://static.codepen.io/ass...;></script>前端路由与vue-routerSPA的核心就是前端路由,对于一个网址,每次GET或POST等请求在服务端有一个专门的正则配置列表,然后匹配到具体的一条路径后,分发到不同的Controller,进行各种操作,最终将html或数据返回给前端,这样就完成了一次IO。前端路由,即由前端来维护一个路由规则。实现方式有两种;一种是利用url的hash,就是常说的锚点(#),JavaScript通过hashChange事件来监听url的改变;另一种就是HTML5的history模式,它使url看起来像普通网站那样,以/分割,没有#,但也没并没有跳转,不过使用这种模式需要服务端支持,服务端在接收到所有的请求后,都指向同一个html文件,不然会出现404。因此,SPA只有一个html,整个网站所有的内容都在这个html里,通过JavaScript来处理。如果要独立开发一个前端路由,需要考虑到页面的可插拔、生命周期、内存管理等问题。vue-routervue-router的实现原理与通过is特性实现动态组件的方法类似,路由不同的页面事实上就是动态加载不同的组件。创建一个数组来指定路由匹配列表,每一个路由映射一个组件:const Routers = [ { path: ‘/index’, component: (resolve) => require([’./views/index.vue’], resolve) }, { path: ‘/about’, component: (resolve) => require([’./views/about.vue’], resolve) }];Routers里每一项的path属性就是指定当前匹配的路径,component是映射的组件。webpack会把每一个路由都打包为一个js文件,在请求道该页面时,再去加载这个页面的js,也就是异步实现的懒加载(按需加载)。这样做的好处是不需要在打开首页的时候就把所有的页面内容全部加载进来,只在访问时才加载。使用了异步路由后,变移除的每个页面的js都叫做chunk(块),它们命名默认是0.main.js、1.main.js…可以在webpack配置的出口output里通过设置chunkFilename字段修改chunk命名。output: { publicPath: “/dist/”, filename: “[name].js”, chunkFilename: “[name].chunk.js”}有了chunk后,在每个页面(.vue文件)里写的样式也需要配置后才会打包进main.css,否则仍然会通过JavaScript动态创建<style>标签的形式写入。const RouterConfig = { //使用HTML5的History路由模式 mode: ‘history’, routes: Routers};const router = new VueRouter(RouterConfig);new Vue({ el: “#app”, router: router, render: h => { return h(App) }});在RouterConfig里设置mode为history会开启HTML5的History路由模式,通过/设置路径。如果不配置mode,就会使用#来设置路径。开启History路由,在生产环境时必须进行配置,将所有路由都指向同一个html,或设置404页面,否则刷新时页面就会出现404。在路由列表里,可以在最后新加一项,当访问的路径不存在时,重定向到首页:{ path: ‘*’, redirect: ‘/index’}路由列表的path可以带参数,比如/user/123,其中用户ID123是动态的,但它们路由到同一个页面,在这个页面里,期望获取这个ID,然互殴请求相关数据。跳转vue-router有两种跳转页面的方法,第一种是使用内置的<router-link>组件,它会被渲染为一个<a>标签。<template> <div> <h1>首页</h1> <router-link to="/about”>跳转到about</router-link> </div></template>它的用法与一般的组件一样,to是一个prop,指定需要跳转的路径,也可以用v-bind动态设置。使用<router-link>,在HTML5的History模式下会拦截点击,避免浏览器重新加载页面。<router-view>还有其他一些prop,常用的有:tag 可以指定渲染成什么标签,比如<router-link to="/about" tag=“li”>渲染的结果就是<li>,而不是<a>replace 使用replace不会留下History记录,所以导航后不能用后退键返回上一个页面,如<router-link to="/about" replace>active-class 当<router-link>对应的路由匹配成功时,会自定给当前元素设置一个名为router-link-active的class,设置prop:active-class可以修改默认的名称。在做类似导航栏时,可以使用该功能高亮显示当前页面对应的导航栏单项,但是一般不会修改active-class,直接使用默认值router-link-active。有时候,跳转页面可能需要在JavaScript中进行,类似于window.location.href。这时可以使用第二种跳转方法,使用router实例的方法。<template> <div> <h1>介绍页</h1> <button @click=“handleRouter”>跳转到user</button> </div></template><script> export default { methods: { handleRouter() { this.$router.push(’/user/123’); } } }</script>$router还有其他一些方法:replace 类似于<router-link>的replace功能,它不会向history添加新纪录,而是替换掉当前的history记录,如this.$router.replace(’/user/123’)go 类似于window.history.go(),在history记录中向前或后退多少步,参数是整数高级用法在SPA项目中,如何修改网页的标题?在页面发生路由变化时,统一设置。vue-router提供了导航钩子beforeEach和afterEach,它们会在路由即将改变前和改变后触发,所以设置标题可以在beforeEach钩子完成。<p data-height=“365” data-theme-id=“0” data-slug-hash=“gKRaLm” data-default-tab=“js” data-user=“whjin” data-embed-version=“2” data-pen-title=“vue-router导航钩子” class=“codepen”>See the Pen vue-router导航钩子 by whjin (@whjin) on CodePen.</p><script async src=“https://static.codepen.io/ass...;></script>导航钩子有3个参数:to 即将要进入的目标的路由对象from 当前导航即将要离开的路由对象next 调用该方法后,才能进入下一个钩子路由列表的meta字段可以自定义一些信息,将每个页面的title写入meta来统一维护,beforeEach钩子可以从路由对象to里获取meta信息,从而改变标题。某些页面需要校验是否登录,如果登录就可以访问,否则跳转到登录页。通过localStorage来简单判断是否登录。router.beforeEach((to, from, next) => { if (window.localStorage.getItem(’token’)) { next() } else { next(’/login’) }});next()的参数设置为false,可以取消导航,设置为具体的路径可以导航到指定的页面。使用webpack构建webpack的主要适用场景时单页面富应用(SPA)。SPA通过是由一个html文件和一堆按需加载的js文件组成。export和import是用来导出和导入模块的。一个模块就是一个js文件,它拥有独立的作用域,里面定义的变量外部是无法获取的。在module对象的rules属性中可以指定一系列的loaders,每一个loader都必须包含test和use两个选项。当webpack编译过程中遇到require()或import语句导入一个后缀名为.css的文件时,先将它通过css-loader转换,再通过style-loader转换,然后继续打包。use选项的值可以是数组或字符串,如果是数组,它的编译顺序就是从后往前。webpack的主要核心部分包括入口(Entry)、出口(Output)、加载器(Loaders)、插件(Plugin)。单文件组件与vue-loader<style>标签使用scoped属性,表示当前的CSS只在这个组件有效,如果不加,namediv的样式会应用到整个项目。使用.vue文件需要先安装vue-loader、vue-style-loader等加载器并做配置。如果要使用ES6语法,还需要安装babel和babel-loader等加载器。<p data-height=“465” data-theme-id=“0” data-slug-hash=“NzjNgp” data-default-tab=“js” data-user=“whjin” data-embed-version=“2” data-pen-title=“Vue-webpack.config.js” class=“codepen”>See the Pen Vue-webpack.config.js by whjin (@whjin) on CodePen.</p><script async src=“https://static.codepen.io/ass...;></script>新建.babelrc文件,并写入babel的配置,webpack会依赖此配置文件来使用babel编译ES6代码。{ “presets”: [“es2015”], “plugins”: [“transform-runtime”], “comments”: false}每个.vue文件代表一个组件,组件之间可以相互依赖。ES语法:=>是箭头函数render: h=>h(App)等同于 render: function(h) { return h(App) } 也等同于: render: h=>{ return h(App) } 箭头函数里的this指向与普通函数不一样,箭头函数体内的this对象就是定义时所在的对象,而不是使用时所在的对象。 ...

March 18, 2019 · 1 min · jiezi

Vue.js-函数化组件

学习笔记:函数化组件函数化组件Vue提供了一个functional的布尔值选项,设置为true可以使组件无状态和无实例,也就是没有data和this上下文。这样用render函数返回虚拟节点可以更容易渲染,因为函数化组件只是一个函数,渲染开销要小很多。使用函数化组件时,Render函数提供了第二个参数context来提供临时上下文。组件需要的data、prop、slots、children、parent都是通过这个上下文来传递。比如this.level要改写为context.props.level,this.$slots.default改变为context.children。用函数化组件展示一个根据数据智能选择不同组件的场景:<p data-height=“365” data-theme-id=“0” data-slug-hash=“mKRKGm” data-default-tab=“html,result” data-user=“whjin” data-embed-version=“2” data-pen-title=“Vue-函数化组件-根据数据选择组件” class=“codepen”>See the Pen Vue-函数化组件-根据数据选择组件 by whjin (@whjin) on CodePen.</p><script async src=“https://static.codepen.io/ass...;></script>函数化组件主要适用于以下两个场景:程序化地在多个组件中选择一个。在将children、props、data传递给子组件之前操作它们。JSX为了让Render函数更好地书写和阅读,Vue提供了插件babel-plugin-transform-vue-jsx来支持JSX语法。使用createElement时,常用配置:<p data-height=“365” data-theme-id=“0” data-slug-hash=“Vdpwpz” data-default-tab=“js” data-user=“whjin” data-embed-version=“2” data-pen-title=“Vue-createElement” class=“codepen”>See the Pen Vue-createElement by whjin (@whjin) on CodePen.</p><script async src=“https://static.codepen.io/ass...;></script>JSX写法:<p data-height=“300” data-theme-id=“0” data-slug-hash=“eKvYvm” data-default-tab=“js” data-user=“whjin” data-embed-version=“2” data-pen-title=“Vue-JSX” class=“codepen”>See the Pen Vue-JSX by whjin (@whjin) on CodePen.</p><script async src=“https://static.codepen.io/ass...;></script>实战:使用Render函数开发可排序的表格组件表格组件的所有的内容(表头和行数据)由两个prop构成:columns和data。两者都是数组,columns用来描述每列的信息,并渲染在表头<head>内,可以指定某一列是否需要排序;data时每一行的数据,由columns决定每一行里各列的顺序。为了让排序后的columns和data不影响原始数据,给v-table组件的data选项添加两个对应的数据,组件所有的操作将在这两个数据上完成,不对原始数据做任何处理。columns的每一项是一个对象,其中title和key字段是必填的,用来标识这列的表头标题,key的对应data中列内容的字段名。sortable是选填字段,如果值为true,说明该列需要排序。v-talbe组件的prop:columns和data的数据已经从父级传递过来,v-table不直接使用它们,而是使用data选项的currentColumns和currentData。所以在v-table初始化时,需要把columns和data赋值给currentColumns和currentData。在v-table的methods选项里定义两个方法用来复制,并在mounted钩子内调用。map()是JavaScript数组的一个方法,根据传入的函数重新构造一个新数组。排序分升序(asc)和降序(desc)两种,而且同时只能对一列数据进行排序,与其他列互斥,为了标识当前列的排序状态,在map列添加数据时,默认给每列都添加一个_sortType的字段,并且赋值为normal,表示默认排序(也就是不排序)。在排序后,currentData每项的顺序可能都会发生变化,所以给currentColumns和currentData的每个数据都添加_index字段,代表当前数据在原始数据中的索引。render(h) { var ths = [], trs = []; return h(’table’, [ h(’thead’, [ h(’tr’, ths) ]), h(’tbody’, trs) ])}这里的h就是createElement,只是换了个名称。表格主题trs是一个二维数组,数据由currentColumns和currentData组成。先遍历所有的行,然后再每一行内再遍历各列,最终组合出主题内容节点trs。如果col.sortable没有定义,或值为false,就直接把col.title渲染出来,否则除了渲染title,还加了两个<a>元素来实现升序和降序的操作。排序使用了JavaScript数组的sort()方法,这里之所以返回1或-1,而不直接返回a[key]<b[key],也就是true或false,是因为在部分浏览器对sort()的处理不同,而1和-1可以做到兼容。排序前,先将所有列的排序状态都重置为normal,然后设置当前列的排序状态(asc或desc),对用到render的<a>元素的class名称on,后面通过CSS来高亮显示当前列的排序状态。当渲染完表格后,父级修改了data数据,比如增加或删除,v-table的currentData也应该更新,如果某列已经存在排序状态,更新后应该直接处理一次排序。通过遍历currentColumns来找出是否按某一列进行过排序,如果有,就按照当前排序状态对更新后的数据做一次排序操作。<p data-height=“365” data-theme-id=“0” data-slug-hash=“XYMmJr” data-default-tab=“html,result” data-user=“whjin” data-embed-version=“2” data-pen-title=“Vue-可排序表格组件” class=“codepen”>See the Pen Vue-可排序表格组件 by whjin (@whjin) on CodePen.</p><script async src=“https://static.codepen.io/ass...;></script>实战:留言列表发布一条留言,需要的数据有昵称和留言内容,发布操作应该在根实例app内完成。留言列表的数据也是从app获取。数组list存储了所有的留言内容,通过函数handleSend给list添加一项留言数据,添加成后把texrarea文本框置空。Render函数内的节点使用v-model:动态绑定value,并且监听input事件,把输入的内容通过$emit(‘input’)派发给父组件。列表数据list为空时,渲染一个“列表为空”的信息提示节点;不为空时,每个list-item赢包含昵称、留言内容和回复按钮3个子节点。this.list.forEach相当于template里的v-for指令,遍历出每条留言。句柄handleReply直接向父组件派发一个事件reply,父组件(app)接收后,将当前list-item的昵称提取,并设置到v-textarea内。<p data-height=“365” data-theme-id=“0” data-slug-hash=“ZRKGrR” data-default-tab=“html,result” data-user=“whjin” data-embed-version=“2” data-pen-title=“Vue-留言列表” class=“codepen”>See the Pen Vue-留言列表 by whjin (@whjin) on CodePen.</p><script async src=“https://static.codepen.io/ass...;></script> ...

March 18, 2019 · 1 min · jiezi

Vue.js-Render函数

学习笔记:Render函数Render函数Vue2与Vue1最大的区别就在于Vue2使用了虚拟DOM来更新DOM节点,提升渲染性能。Vue2与Vue1最大的区别就在于Vue2使用了虚拟DOM来更新DOM节点,提升渲染性能。Vue2与Vue1最大的区别就在于Vue2使用了虚拟DOM来更新DOM节点,提升渲染性能。Vue2与Vue1最大的区别就在于Vue2使用了虚拟DOM来更新DOM节点,提升渲染性能。虚拟DOMReact和Vue2都使用了虚拟DOM技术,虚拟DOM并不是真正意义上的DOM,而是一个轻量级的JavaScript对象,在状态发生变化时,虚拟DOM会进行Different运算,来更新只需要被替换的DOM,而不是全部重绘。与DOM操作相比,虚拟DOM是基于JavaScript计算的,所以开销会小很多。在Vue2中,虚拟DOM就是通过一种VNode类表达,每个DOM元素或组件对对应一个VNode对象。VNodeData节点解析:children 子节点,数组,也是VNode类型。text 当前节点的文本,一般文本节点或注释节点会有该属性。elm 当前虚拟节点对应的真实的DOM节点。ns 节点的namespacecontent 编译作用域functionalContext 函数化组件的作用域key 节点的key属性,用于作为节点的标识,有利于patch的优化componentOptions 创建组件实例时会用到的选项信息。child 当前节点对应的组件实例。parent 组件的占位节点。raw 原始htmlisStatic 静态节点的标识isRootInset 是否作为根节点插入,被<transition>包裹的节点,该属性的值为false。isConment 当前节点是否是注释节点。isCloned 当前节点是否为克隆节点。isOnce 当前节点是否有v-once指令。VNode主要可以分为以下几类:TextVNode 文本节点。ElementVNode 普通元素节点。ComponentVNode 组件节点。EmptyVNode 没有内容的注释节点。CloneVNode 克隆节点,可以是以上任意类型的节点,唯一的区别在于isCloned属性为true。Render函数通过createElement参数来创建虚拟DOM,结构精简。其中,访问slot的用法,使用场景集中在Render函数。<p data-height=“265” data-theme-id=“0” data-slug-hash=“wXozpg” data-default-tab=“html,result” data-user=“whjin” data-embed-version=“2” data-pen-title=“Vue-render函数” class=“codepen”>See the Pen Vue-render函数 by whjin (@whjin) on CodePen.</p><script async src=“https://static.codepen.io/ass...;></script>createElement用法基本参数createElement构成了Vue虚拟DOM的模板,它有3个参数:createElement( //{String | Object | Function} //一个HTML标签,组件选项,或一个函数 //必须return上面其中一个 ‘div’, //{Object} //一个对应属性的数据对象,可选 //可以在template中使用 //{String | Array} //子节点(VNode),可选 [ createElement(‘h1’,‘hello world’), createElement(MyComponent,{ props:{ someProp:‘foo’ } }), ‘bar’ ]);第一个参数必选,可以是一个HTML标签,也可以是一个组件或函数;第二个是可选参数,数据对象,在template中使用。第三个是子节点,也是可选参数,用法一直。之前在template中都是在组件的标签上使用v-bind:class、v-bind:style、v-on:click这样的指令,在Render函数都将其写在了数据对象中。约束所有的组件树中,如果VNode是组件或含有组件的slot,nameVNode必须唯一。在Render函数里创建了一个cloneVNode的工厂函数,通过递归将slot所有子节点都克隆了一份,并对VNode的关键属性也进行复制。使用JavaScript代替模板功能在Render函数中,不再需要Vue内置的指令,比如v-if、v-for。无论要实现什么功能,都可以使用原生JavaScript。<p data-height=“265” data-theme-id=“0” data-slug-hash=“eKgVgQ” data-default-tab=“html,result” data-user=“whjin” data-embed-version=“2” data-pen-title=“render–v-for” class=“codepen”>See the Pen render–v-for by whjin (@whjin) on CodePen.</p><script async src=“https://static.codepen.io/ass...;></script>map()方法时快速改变数组结构,返回一个新数组。map常和filter、sort等方法一起使用,它们返回的都是新数组。Render函数里没有与v-model对应的API,需要自己来实现逻辑。<p data-height=“265” data-theme-id=“0” data-slug-hash=“BVpYdd” data-default-tab=“html,result” data-user=“whjin” data-embed-version=“2” data-pen-title=“Vue-render-API” class=“codepen”>See the Pen Vue-render-API by whjin (@whjin) on CodePen.</p><script async src=“https://static.codepen.io/ass...;></script>v-model就是prop:value和event:input组合使用的一个语法糖,虽然在Render里写起来比较复杂,但是可以自由控制,深入到更底层。对于事件修饰符和按键修饰符,基本需要自己实现:修饰符对应的句柄.stopevent.stopPropagation().preventevent.preventDefault().selfif(event.target!==event.currentTarget) return.ente、.13if(event.keyCode!==13) return替换13位需要的keyCode.ctrl、.alt、.shift、.metaif(!event.ctrlKey) return根据需要替换ctrlKey位altKey、shiftKey或metaKey对于事件修饰符.capture和.once,Vue提供了特殊的前缀,可以直接写在on的配置里。修饰符前缀.capture!.once~.capture.once或.once.capture~!写法如下:on: { ‘!click’: this.doThisInCapturingMode, ‘keyup’: this.doThisOnce, ‘!mouseover’: this.doThisOnceInCapturingMode}简单模拟聊天发送内容的场景:<p data-height=“265” data-theme-id=“0” data-slug-hash=“ZRLrPN” data-default-tab=“html,result” data-user=“whjin” data-embed-version=“2” data-pen-title=“Vue-模拟聊天发送内容” class=“codepen”>See the Pen Vue-模拟聊天发送内容 by whjin (@whjin) on CodePen.</p><script async src=“https://static.codepen.io/ass...;></script>在Render函数中会大量使用slot,在没有使用slot时会显示一个默认的内容,这部分需要自己实现。this.$slots.default等于undefined,就说明父组件中没有定义slot,这是可以自定义显示的内容。 ...

March 18, 2019 · 1 min · jiezi

Vue.js-内置指令

学习笔记:内置指令内置指令基本指令v-cloakv-cloak不需要表达式,它会在Vue实例结束编译时从绑定的HTML元素上移除,经常和CSS的display: none;配合使用:<div id=“app” v-cloak> {{message}}</div>当网速较慢、Vue.js文件还没加载完时,在页面上会显示{{message}}的字样,直到Vue创建实例、编译模板时,DOM才会被替换,所以这个过程屏幕有闪。只要加一句CSS就可以解决这个问题:[v-cloak] { display: none;}v-cloak是一个解决初始化慢导致页面闪动的最佳实践,对于简单的项目很实用。在工程化的项目中,项目的HTML结构只有一个空的div元素,剩下的内容都由路由挂载不同组件完成,这时不再需要v-cloak。v-oncev-once是一个不需要表达式的指令,作用是定义它的元素或者组件只渲染一次,包括元素或组件的所有子节点。首次渲染后,不再随数据的变化重新渲染,将被视为静态内容。v-once在业务中很少使用,如果需要进一步优化性能时,可能会用到。条件渲染指令v-if、v-else-if、v-elseVue.js的条件指令可以根据表达式的值在DOM中渲染或销毁元素/组件。v-else-if要紧跟v-if,v-else要紧跟v-else-if或v-if,表达式的值为真时,当前元素/组件及所有子节点将被渲染,为假时被移除。如果一次判断的是多个元素,可以在Vue.js内置的<template>元素上使用条件指令,最终渲染的结果不会包含该元素。Vue在渲染元素时,处于效率考虑,会尽可能地复用已有的元素,而非重新渲染。<p data-height=“265” data-theme-id=“0” data-slug-hash=“KewYmd” data-default-tab=“html,result” data-user=“whjin” data-embed-version=“2” data-pen-title=“条件渲染指令” class=“codepen”>See the Pen 条件渲染指令 by whjin (@whjin) on CodePen.</p><script async src=“https://static.codepen.io/ass...;></script>示例中键入内容后,点击切换按钮,虽然DOM改变了,但是之前在输入框键入的内容并没有改变,只是替换了placeholder的内容,说明<input>元素被复用了。使用Vue.js提供的key属性,可以让你自己决定是否要复用元素,key的值必须是唯一的。<input type=“text” placeholder=“输入用户名” key=“name-input”>给两个<input>元素都增加了key后,就不会复用了。切换类型时键入的内容也会被删除,不过<label>元素仍然会被复用,因为没有添加key属性。v-showv-show的用法与v-if基本一致,只不过v-show是改变元素的CSS属性display。当v-show表达式的值为false时元素会隐藏,DOM结构元素上加载了内联样式display:none;。v-show不能在<template>上使用。v-if与v-show的选择v-if和v-show具有类似的功能,不过v-if才是真正的条件渲染,它会根据表达式适当地销毁或重建元素及绑定的事件或子组件。若表达式初始值为false,则一开始元素/组件并不会渲染,只有当条件第一次变为真时才开始编译。而v-show只是简单的CSS属性切换,无论条件真与否,都会被编译。相比之下,v-if更适合条件不经常改变的场景,因为它的切换开销相对较大,而v-show适用于频繁切换条件。列表渲染指令v-for基本用法当需要将一个数组遍历或枚举一个对象循环显示时,就会用到列表渲染指令v-for。它的表达式需结合in来使用,类似item in items的形式。列表渲染也支持用of代替in作为分隔符,它更接近JavaScript迭代器的语法:<li v-for=“book of books”>{{book.name}}</li>v-for的表达式支持一个可选参数作为当前项的索引。<li v-for="(book,index) of books”>{{index}} - {{book.name}}</li>分隔符in前的语句使用括号,第二项就是books当前项的索引。与v-if一样,v-for也可以用在内置标签<template>上,将多个元素进行渲染。除了数组外,对象的属性也是可以遍历的。遍历对象属性时,有两个可选参数,分别是键名和索引:<div id=“app”> <ul> <li v-for="(value,key,index) of users"> {{index}} - {{key}} - {{value}} </li> </ul></div>v-for还可以迭代整数:<div id=“app”> <span v-for=“n in 10”>{{n}}</span></div>数组更新Vue的核心是数据与视图的双向绑定,包含了一组观察数组变化的方法,使用它们改变数组也会触发视图更新:push()pop()shift()unshift()splice()sort()reverse()使用以上方法会改变被这些方法调用的原始数组。以下方法不会改变原数组:filter()concat()slice()它们返回的是一个新数组,在使用这些非变异方法时,可以用新数组来替换元素组。Vue在检测到数组变化时,并不是直接重新渲染整个列表,而是最大化地复用DOM元素。替换的数组中,含有相同元素的项不会被重新渲染,因此可以大胆地用新数组来替换旧数组,不用担心性能问题。需要注意的是,以下变动的数组中,Vue时不能检测到的,也不会触发视图更新:通过索引直接设置项,app.books[3]={}修改数组长度,app.books.length=1解决第一个问题可以用两种方法实现同样的效果,第一种是使用Vue内置的set方法:Vue.set(app.books, 3, { name: ‘《CSS秘密花园》’, author: ‘无名氏’});如果是在webpack中使用组件化的方式,默认是没有导入Vue的,这时可以使用this.$set。另一种方法:app.books.splice(3,1,{})过滤与排序如果不希望改变原数组,想通过一个数组的副本来做过滤或排序的显示时,可以使用计算属性返回过滤或排序后的数组。

March 18, 2019 · 1 min · jiezi

Vue.js-组件详解

学习笔记:组件详解组件详解组件与复用Vue组件需要注册后才可以使用。注册有全局注册和局部注册两种方式。全局注册Vue.component(‘my-component’, {});要在父实例中使用这个组件,必须要在实例创建前注册,之后就可以用<my-component></my-component>的形式来使用组件。Vue.component(‘my-component’, { template: &lt;div&gt;这是一个组件&lt;/div&gt;});template的DOM结构必须被一个元素包含,缺少<div></div>会无法渲染并报错。在Vue实例中,使用components选项可以局部注册组件,注册后的组件只在该实例作用域下有效。组件中也可以使用components选项来注册组件,使组件可以嵌套。var Child = { template: &lt;div&gt;局部注册组件的内容&lt;/div&gt;};new Vue({ el: ‘#app’, components: { ‘my-component’: Child },});Vue组件的模板在某些情况下会受到HTML的限制,比如<table>内规定只允许是<tr>、<td>、<th>等这些表格元素,所以在<table>内直接使用组件时无效的。这种情况下,可以使用特殊的is属性来挂载组件。<div id=“app”> <table> <tbody is=“my-component”></tbody> </table></div>Vue.component(‘my-component’, { template: &lt;div&gt;这里是组件内容&lt;/div&gt;});常见的限制元素还有<ul>、<ol>、<select>。除了template选项外,组件中还可以像Vue实例那样使用其他的选项,比如data、computed、methods等。但是在使用data时,data必须是函数,然后将数据return出去。JavaScript对象是引用关系,如果return的对象引用了外部的一个对象,那这个对象就是共享的,任何一方修改都会同步。使用props传递数据组件不仅要把模板的内容进行复用,更重要的是组件间进行通信。通常父组件的模板中包含子组件,父组件要正向地向子组件传递数据或参数,子组件接收后根据参数的不同渲染不同的内容或者执行操作。这个正向传递数据的过程通过props来实现。在组件中,使用选项props声明需要从父级接收的数据,props的值可以是两种,一种是字符串数组,一种是对象。<my-component message=“来自父组件的数据”></my-component>props: [‘message’],template: &lt;div&gt;{{message}}&lt;/div&gt;,props中声明的数据与组件data函数中return的数据主要区别就是props的数据来自父级,而data中的是组件自己的数据,作用域是组件本身,这两种数据都可以在模板template及计算属性computed和方法methods中使用。由于HTML特性不区分大小写,当使用DOM模板时,驼峰命名的props名称要转为短横线分割命名。<my-component warning-text=“提示信息”></my-component>有时候,传递的数据并不是直接写死,而是来自父级的动态数据,这时可以使用指令v-bind动态绑定props的值,当父组件的数据变化时,也会传递子组件。<div id=“app”> <input type=“text” v-model=“parentMessage”> <my-component :message=“parentMessage”></my-component></div>props: [‘message’],template: &lt;div&gt;{{message}}&lt;/div&gt;,data: { parentMessage: ‘’}这里用v-model绑定了父级的数据parentMessage,当通过输入框任意输入时,子组件接收到的props[“message”]也会实时响应,并更新组件模板。单向数据流业务中会经常遇到两种需要改变prop的情况,一种是父组件传递初始值进来,子组件将它作为初始值保存起来,在自己的作用域下可以随意使用和修改。这种情况可以在组件data内再声明一个数据,引用父组件的prop。<div id=“app”> <my-component :init-count=“1”></my-component></div>Vue.component(‘my-component’, { props: [‘initCount’], template: &lt;div&gt;{{count}}&lt;/div&gt;, data() { return { count:this.initCount } }});组件中声明了数据count,它在组件初始化时会获取来自父组件的initCount,之后就与之无关了,只用维护count,这样就可以避免直接操作initCount。另一种情况就是prop作为需要被转变的原始值传入,这种情况用计算属性就可以。<div id=“app”> <my-component :width=“100”></my-component></div>Vue.component(‘my-component’, { props: [‘width’], template: &lt;div :style="style"&gt;组件内容&lt;/div&gt;, computed: { style: function () { return { width: this.width + ‘px’ } } }});因为用CSS传递宽度要带单位(px),数值计算一般不带单位,所以统一在组件内使用计算属性。在JavaScript中对象和数组时引用类型,指向同一个内存空间,所以props是对象和数组时,在子组件内改变是会影响父组件。数组验证当prop需要验证时,需要对象写法。一般当组件需要提供给别人使用时,推荐都进行数据验证。比如某个数据必须是数字类型,如果传入字符串,就会在控制台弹出警告。<p data-height=“565” data-theme-id=“0” data-slug-hash=“WywyjV” data-default-tab=“js” data-user=“whjin” data-embed-version=“2” data-pen-title=“prop” class=“codepen”>See the Pen prop by whjin (@whjin) on CodePen.</p><script async src=“https://static.codepen.io/ass...;></script>验证的type类型可以是:StringNumberBooleanObjectArrayFunctiontype也可以是一个自定义构造器,使用instanceof检测。组件通信组件关系可分为父组件通信、兄弟组件通信、跨级组件通信。自定义事件当子组件需要向父组件传递数据时,就要用到自定义事件。v-on除了监听DOM事件外,还可以用于组件之间的自定义事件。JavaScript的设计模式——观察者模式方法:dispatchEventaddEventListenerVue组件的子组件用$emit()来触发事件,父组件用$on()来监听子组件的事件。父组件也可以直接在子组件的自定义标签上使用v-on来监听子组件触发的自定义事件。<p data-height=“365” data-theme-id=“0” data-slug-hash=“ZRWjKv” data-default-tab=“js,result” data-user=“whjin” data-embed-version=“2” data-pen-title=“自定义事件” class=“codepen”>See the Pen 自定义事件 by whjin (@whjin) on CodePen.</p><script async src=“https://static.codepen.io/ass...;></script>在改变组件的data “counter"后,通过$emit()再把它传递给父组件,父组件用@increase和@reduce。$emit()方法的第一个参数是自定义事件的名称。除了用v-on在组件上监听自定义事件外,也可以监听DOM事件,这时可以用.native修饰符表示监听时一个原生事件,监听的是该组件的根元素。<my-component @click:native=“handleClick”></my-component>使用v-modelVue可以在自定义组件上使用v-model指令。<my-component v-model=“total”></my-component>组件$emit()的事件名时特殊的input,在使用组件的父级,并没有在<my-component>上使用@input=“handler”,而是直接用了v-model绑定的一个数据total。<my-component @input=“handleGetTotal”></my-component>v-model还可以用来创建自定义的表单输入组件,进行数据双向绑定。<p data-height=“365” data-theme-id=“0” data-slug-hash=“zaqJBQ” data-default-tab=“html,result” data-user=“whjin” data-embed-version=“2” data-pen-title=“v-model双向绑定” class=“codepen”>See the Pen v-model双向绑定 by whjin (@whjin) on CodePen.</p><script async src=“https://static.codepen.io/ass...;></script>实现这样一个具有双向绑定的v-model组件要满足下面两个要求:接收一个value属性在有新的value时触发input事件非父子组件通信在实际业务中,除了父子组件通信外,还有很多非父子组件通信的场景,非父子组件一般有两种,兄弟组件和跨多级组件。在Vue 1.x版本中,除了$emit()方法外,还提供了¥dispatch()和$broadcast()。$dispatch()用于向上级派发事件,只要是它的父级(一级或多级以上),都可以在Vue实例的events选项内接收。此实例只在Vue 1.x版本中有效:<p data-height=“365” data-theme-id=“0” data-slug-hash=“pKyOOY” data-default-tab=“html,result” data-user=“whjin” data-embed-version=“2” data-pen-title=“dispatch派发事件” class=“codepen”>See the Pen dispatch派发事件 by whjin (@whjin) on CodePen.</p><script async src=“https://static.codepen.io/ass...;></script>$broadcast()是由上级向下级广播事件,用法完全一致,方向相反。这两种方法一旦发出事件后,任何组件都可以接收到,就近原则,而且会在第一次接收到后停止冒泡,除非返回true。这些方法在Vue 2.x版本中已废弃。在Vue 2.x中,推荐任何一个空的Vue实例作为中央事件总线(bus),也就是一个中介。<p data-height=“365” data-theme-id=“0” data-slug-hash=“dKMgvJ” data-default-tab=“html,result” data-user=“whjin” data-embed-version=“2” data-pen-title=“Vue-bus事件总线” class=“codepen”>See the Pen Vue-bus事件总线 by whjin (@whjin) on CodePen.</p><script async src=“https://static.codepen.io/ass...;></script>首先创建了一个名为bus的空的Vue实例;然后全局定义了组件component-a;最后创建了Vue实例app。在app初始化时,也就是在生命周期mounted钩子函数里监听了来自bus的事件on-message,而在组件component-a中,点击按钮后会通过bus把事件on-message发出去。此时app就会接收到来自bus的事件,进而在回调里完成自己的业务逻辑。这种方法巧妙而轻量地实现了任何组件间的通信,包括父子、兄弟、跨级。如果深入使用,可以扩展bus实例,给它添加data、methods、computed等选项,这些都是可以公用的。在业务中,尤其是协同开发时非常有用,因为经常需要共享一些通用的信息,比如用户登录的昵称、性别、邮箱等,还有用户的授权token等。只需在初始化时让bus获取一次,任何时间、任何组件就可以从中直接使用,在单页面富应用(SPA)中会很实用。除了中央事件总线bus外,还有两种方法可以实现组件间通信:父链和子组件索引。父链在子组件中,使用this.$parent可以直接访问该组件的父实例或组件,父组件也可以通过this.$children访问它所有的子组件,而且可以递归向上或向下无限访问,直到根实例或最内层的组件。<div id=“app”> <p>{{message}}</p> <component-a></component-a></div>Vue.component(‘component-a’, { template: &lt;button @click="handleEvent"&gt;通过父链直接修改数据&lt;/button&gt;, methods: { handleEvent: function () { this.$parent.message = ‘来自组件component-a的内容’ } }});var app = new Vue({ el: ‘#app’, data: { message: ’’ }});尽管Vue允许这样操作,但在业务中,子组件应该尽可能地避免依赖父组件的数据,更不应该去主动修改它的数据,因为这样使得父子组件紧耦合,只看父组件,很难理解父组件的状态,因为它可能被任意组件修改,理想状态下,只有组件自己能修改它的状态。父子组件最好还是通过props和$emit()来通信。子组件索引当子组件较多时,通过this.$children来遍历出需要的一个组件实例是比较困难的,尤其是组件动态渲染时,它们的序列是不固定的。Vue提供了子组件索引的方法,用特殊的属性ref来为子组件指定一个索引名称。<p data-height=“365” data-theme-id=“0” data-slug-hash=“dKMLXY” data-default-tab=“html,result” data-user=“whjin” data-embed-version=“2” data-pen-title=“Vue-$refs” class=“codepen”>See the Pen <a href=“https://codepen.io/whjin/pen/dKMLXY/">Vue-$refs</a> by whjin (@whjin) on CodePen.</p><script async src=“https://static.codepen.io/ass...;></script>在父组件模板中,子组件标签上使用ref指定一个名称,并在父组件内通过this.$refs来访问指定名称的子组件。$refs只在组件渲染完成后才填充,并且它是非响应式的。它仅仅作为一个直接访问子组件的应急方案,应当避免在模板或计算属性中使用$refs。Vue 2.x将v-el和v-ref合并成ref,Vue会自动去判断是普通标签还是组件。使用slot分发内容当需要让组件组合使用,混合父组件的内容与子组件的模板时,就会用到slot,这个过程叫做内容分发。<app>组件不知道它的挂载点会有什么内容。挂载点的内容是由<app>的父组件决定的。<app>组件很可能有它自己的模板。props传递数据、events触发事件和slot内容分发就构成了Vue组件的3个API来源,再复杂的组件也是由这3部分构成。作用域父组件中的模板:<child-component> {{message}}</child-component>这里的message就是一个slot,但是它绑定的是父组件的数据,而不是组件<child-component>的数据。父组件模板的内容是在父组件作用域内编译,子组件模板的内容是在子组件作用域内编译。<div id=“app”> <child-component v-modle=“showChild”></child-component></div>Vue.component(‘child-component’, { template: &lt;div&gt;子组件1&lt;/div&gt;,});var app = new Vue({ el: ‘#app’, data: { showChild: true }});这里的状态showChild绑定的是父组件的数据。在子组件上绑定数据:<div id=“app”> <child-component></child-component></div>Vue.component(‘child-component’, { template: &lt;div v-model="showChild"&gt;子组件&lt;/div&gt;, data() { return { showChild: true } }});var app = new Vue({ el: ‘#app’,});因此,slot分发的内容,作用域是在父组件上。单个slot在子组件内使用特殊的<slot>元素就可以为这个组件开启一个slot(插槽),在父组件模板里,插入在子组件标签内的所有内容将替代子组件的<slot>标签及它的内容。<div id=“app”> <child-component> <p>分发的内容</p> <p>更多分发的内容</p> </child-component></div>Vue.component(‘child-component’, { template: &lt;div&gt; &lt;slot&gt; &lt;p&gt;如果没有父组件插入内容,我将作为默认出现。&lt;/p&gt; &lt;/slot&gt; &lt;/div&gt; ,});var app = new Vue({ el: ‘#app’,});子组件child-component的模板内定义了一个<slot>元素,并且用一个<p>作为默认的内容,在父组件没有使用slot时,会渲染这段默认的文本;如果写入了slot,就会替换整个<slot>。子组件<slot>内的备用内容,它的作用域是子组件本身。具名Slot给<slot>元素指定一个name后可以分发多个内容,具名slot可以与单个slot共存。<p data-height=“265” data-theme-id=“0” data-slug-hash=“RJRVQJ” data-default-tab=“html,result” data-user=“whjin” data-embed-version=“2” data-pen-title=“Vue-slot” class=“codepen”>See the Pen Vue-slot by whjin (@whjin) on CodePen.</p><script async src=“https://static.codepen.io/ass...;></script>子组件内声明了3个<slot>元素,其中在<div class=“main”>内的<slot>没有使用name特性,它将作为默认slot出现,父组件没有使用slot特性的元素与内容都将出现在这里。如果没有指定默认的匿名slot,父组件内多余的内容都将被抛弃。在组合使用组件时,内容分发API至关重要。作用域插槽作用域插槽是一种特殊的slot,使用一个可以复用的模板替换已渲染元素。<div id=“app”> <child-component> <template scope=“props”> <p>来自父组件的内容</p> <p>{{props.msg}}</p> </template> </child-component></div>Vue.component(‘child-component’, { template: &lt;div class="container"&gt; &lt;slot msg="来自子组件的内容"&gt;&lt;/slot&gt;&lt;/div&gt; ,});var app = new Vue({ el: ‘#app’,});子组件的模板,在<slot>元素上有一个类似props传递数据给组件的写法msg=“xxx”,将数据传递到插槽。父组件中使用了<template>元素,而且拥有一个scope=“props"的特性,这里的props是一个临时变量。template内可以通过临时变量props访问来自子组件插槽的数据msg。作用域插槽更具代表性的用例是列表组件,允许组件自定义应该如何渲染列表每一项。<div id=“app”> <my-list :book=“books”> <!–作用域插槽也可以是具名的Slot–> <template slot=“book” scope=“props”> <li>{{props.bookName}}</li> </template> </my-list></div>Vue.component(‘my-list’, { props: { books: { type: Array, default: function () { return []; } } }, template: &lt;ul&gt; &lt;slot name="book" v-for="book in books" :book-name="book.name"&gt;&lt;/slot&gt;&lt;/ul&gt; ,});子组件my-list接收一个来自父级的prop数组books,并且将它在name为book的slot上使用v-for指令循环,同时暴露一个变量bookName。作用域插槽的使用场景是既可以复用子组件的slot,又可以使slot内容不一致。访问slotVue 2.x提供了用来访问被slot分发的内容的方法$slots。<p data-height=“365” data-theme-id=“0” data-slug-hash=“vrKZew” data-default-tab=“html,result” data-user=“whjin” data-embed-version=“2” data-pen-title=“Vue-$slots” class=“codepen”>See the Pen <a href=“https://codepen.io/whjin/pen/vrKZew/">Vue-$slots</a> by whjin (@whjin) on CodePen.</p><script async src=“https://static.codepen.io/ass...;></script>通过$slots可以访问某个具名slot,this.$slots.default包括了所有没有被包含在具名slot中的节点。组件高级用法递归组件给组件设置name选项,组件在它的模板内可以递归地调用自己。<div id=“app”> <child-component :count=“1”></child-component></div>Vue.component(‘child-component’, { name: ‘child-component’, props: { count: { type: Number, default: 1 } }, template: &lt;div class="child"&gt; &lt;child-component :count="count+1" v-if="count&lt;3"&gt;&lt;/child-component&gt;&lt;/div&gt; ,});组件递归使用可以用来开发一些具有未知层级关机的独立组件,比如级联选择器和树形控件等。内联模板组件的模板一般都是在template选项内定义的,Vue提供了一个内联模板的功能,在使用组件时,给组件标签使用inline-template特性,组件就会把它的内容当做模板,而不是把它当内容分发,这让模板更灵活。<p data-height=“265” data-theme-id=“0” data-slug-hash=“OEXjLb” data-default-tab=“html,result” data-user=“whjin” data-embed-version=“2” data-pen-title=“Vue-inline-template” class=“codepen”>See the Pen Vue-inline-template by whjin (@whjin) on CodePen.</p><script async src=“https://static.codepen.io/ass...;></script>在父组件中声明的数据message和子组件中声明的数据msg,两个都可以渲染(如果同名,优先使用子组件的数据)。这是内联模板的缺点,就是作用域比较难理解,如果不是非常特殊的场景,建议不要轻易使用内联模板。动态组件Vue.js提供了一个特殊的元素<component>用来动态地挂载不同的组件,使用is特性来选择要挂载的组件。<p data-height=“365” data-theme-id=“0” data-slug-hash=“zaBdyY” data-default-tab=“html,result” data-user=“whjin” data-embed-version=“2” data-pen-title=“Vue-component” class=“codepen”>See the Pen Vue-component by whjin (@whjin) on CodePen.</p><script async src=“https://static.codepen.io/ass...;></script>可以直接绑定在组件对象上:<div id=“app”> <component :is=“currentView”></component></div>var Home = { template: &lt;p&gt;Welcome home!&lt;/p&gt;};var app = new Vue({ el: ‘#app’, data: { currentView: Home }});异步组件Vue.js允许将组件定义为一个工厂函数,动态地解析组件。Vue.js只在组件需要渲染时触发工厂函数,并且把结果缓存起来,用于后面的再次渲染。<div id=“app”> <child-component></child-component></div>Vue.component(‘child-component’, function (resolve, reject) { window.setTimeout(function () { resolve({ template: &lt;div&gt;我是异步渲染的!&lt;/div&gt; }) }, 1000)});var app = new Vue({ el: ‘#app’,});工厂函数接收一个resolve回调,在收到从服务器下载的组件定义时调用。也可以调用reject(reason)指示加载失败。其他$nextTick异步更新队列Vue在观察到数据变化时并不是直接更新DOM,而是开启一个队列,并缓冲在同一个事件循环中发生的所有数据变化。在缓冲时会去除重复数据,从而避免不必要的计算和DOM操作。然后,在一下个事件循环tick中,Vue刷新队列并执行实际(已去重的)工作。Vue会根据当前浏览器环境优先使用原生的Promise.then和MutationObserver,如果都不支持,就会采用setTimeout代替。$nextTick就是用来确定什么时候DOM更新已经完成。<p data-height=“365” data-theme-id=“0” data-slug-hash=“RJRjgm” data-default-tab=“html,result” data-user=“whjin” data-embed-version=“2” data-pen-title=“Vue-$nextTick” class=“codepen”>See the Pen <a href=“https://codepen.io/whjin/pen/RJRjgm/">Vue-$nextTick</a> by whjin (@whjin) on CodePen.</p><script async src=“https://static.codepen.io/ass...;></script>X-TemplatesVue提供了另一种定义模板的方式,在<script>标签中使用text/x-template类型,并且指定一个id,将这个id赋给template。<div id=“app”> <my-component></my-component> <script type=“text/x-template” id=“my-component”> <div>这是组件的内容</div> </script></div>Vue.component(‘my-component’, { template: #my-component,});var app = new Vue({ el: ‘#app’,});手动挂载实例在一些非常特殊的情况下,需要动态地创建Vue实例,Vue提供了Vue.extend和$mount两个方法来手动挂载一个实例。Vue.extend是基础Vue构造器,创建一个“子类”,参数是一个包含组件选项的对象。如果Vue实例在实例化时没有收到el选项,它就处于“未挂载”状态,没有关联的DOM元素。可以使用$mount手动地挂载一个未挂载的实例。这个方法返回实例自身,因而可以链式调用其他实例方法。<p data-height=“265” data-theme-id=“0” data-slug-hash=“BVzmbL” data-default-tab=“html,result” data-user=“whjin” data-embed-version=“2” data-pen-title=“Vue-$mount” class=“codepen”>See the Pen <a href=“https://codepen.io/whjin/pen/BVzmbL/">Vue-$mount</a> by whjin (@whjin) on CodePen.</p><script async src=“https://static.codepen.io/ass...;></script>除了以上写法外,还有两种写法:new MyComponent().$mount("#app”);new MyComponent({ el: ‘#app’})手动挂载实例(组件)是一种比较极端的高级用法,在业务中几乎用不到,只在开发一些复杂的独立组件时可能会使用。 ...

March 18, 2019 · 3 min · jiezi