如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里
附Java/C/C++/机器学习/算法与数据结构/前端/安卓/Python/程序员必读书籍书单大全:
书单导航页(点击右侧 极客侠栈 即可打开个人博客):极客侠栈
①【Java】学习之路吐血整理技术书从入门到进阶最全50+本(珍藏版)
②【算法数据结构+acm】从入门到进阶吐血整理书单50+本(珍藏版)
③【数据库】从入门到进阶必读18本技术书籍网盘吐血整理网盘(珍藏版)
④【Web前端】从HTML到JS到AJAX到HTTP从框架到全栈帮你走更少弯路(珍藏版)
⑤【python】书最全已整理好(从入门到进阶)(珍藏版)
⑥【机器学习】+python整理技术书(从入门到进阶已经整理好)(珍藏版)
⑦【C语言】推荐书籍从入门到进阶带你走上大牛之路(珍藏版)
⑧【安卓】入门到进阶推荐书籍整理pdf书单整理(珍藏版)
⑨【架构师】之路史诗级必读书单吐血整理四个维度系列80+本书(珍藏版)
⑩【C++】吐血整理推荐书单从入门到进阶成神之路100+本(珍藏)
⑪【ios】IOS书单从入门到进阶吐血整理(珍藏版)
--------------------------------------------------------------------------------------------------------------------
如果您已经入门reactjs,请绕道~ 这篇博客只适合初学者,初学reactjs的时候,如果你不会webpack,相信很多人都会被官方的例子绕的晕头转向。 ES6的例子也会搞死一批入门者。之前一直用的gulp,突然换了webpack,我也非常不习惯。在这块也卡住了,对于想学reactjs的朋友,我的学习建议是这样的:
nodejs => webpack => ES6 => reactjs
官方的很多例子都是ES6语法+webpack打包的,所以在学习reactjs之前,最好是会ES6和webpack,这样能事半功倍!
1、首先来说说nodejs
先安装版本的nodejs,npm一般都是自带的。安装成全局的比较方便构建项目。
npm install -g grunt-cli # 全局安装
npm可安装的插件可以在这里去找找 www.npmjs.com/ 如果不能安装,可以使用淘宝的镜象资源
2、webpack
webpack是一款打包工具,可以做一些js压缩,sass,less转换,图片处理,js代码合成,ES6转ES5语法等很多功能,如果用过grunt,或者gulp的朋友,webpack也就不陌生了。都是需要写配置文件。
3、ES6
github上很多案例都是用到了ES6的语法,所以,这里我们可以通过webpack的工具 babel ,把ES6的语法转化为ES5的语法,这样我们就可以使用github上面的demo了。
比如:
import '../css/common.scss';
import React from 'react'
import { render } from 'react-dom'
import { Router, Route, Link, IndexRoute } from 'react-router'
import { browserHistory } from 'react-router'
这里的import 就是ES6的语法,在webpack里面使用babel工具将其转化为 ES5的语法。我这里用了JSX(reactjs 提供的一种简洁的语法)如果对JSX不了解的,可以去百度下。
4、快速开发
每次我们在修改JSX文件,或者SASS文件后,都要执行webpack命令进行打包,这样的开发效率很慢,官方提供了一个很牛X的工具,react-hot-loader + webpack-dev-server 可以帮助你快速开发,自动刷新页面。
5、DEMO小试牛刀
这里我把自己做的一个DEMO分享给大家,如果你已经安装了nodejs,并且npm也是全局的。下载后解压,打开 start.bat,输入 npm install 安装所需的插件,安装成功后执行 npm start ,等项目跑起来后,在浏览器输入 http://127.0.0.1:3000 就可以访问项目了。
这里是一个 react-router 的一个例子。
github 地址:https://github.com/mtsee/react-router-demo
蓝蓝设计( www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 、平面设计服务。
如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里
按照这个来配置就好了,这里相当于是帮你把webpack需要的繁琐的配置给你都弄好了,然后你直接从那里全部下下来就可以
记得先下好nodejs,安装的代码记得全都c v,不要自己敲
安装完nodejs之后再安装淘宝npm镜像,安装完了之后后面的安装速度会快一点
npm install -g cnpm --registry=https://registry.npm.taobao.org
1
然后按照下面的五步流程一个一个来
1、全局安装vuecli
npm install -g vue-cli
1
2、进入目录–初始化项目
vue init webpack my-project //这个要进入项目目录再用,会创建一个my-project的文件夹
1
3、进入项目
cd my-project
1
4、安装依赖
npm install
1
5、启动项目
npm run dev
1
以上执行完后会出现一个 my-project 项目文件夹,用vscode打开后会看到以下目录
下面解释一下这些文件及目录分别是干什么的
目录结构的分析
1、bind
├── build // 项目构建(webpack)相关代码 记忆:(够贱) 9个
│ ├── build.js // 生产环境构建代码
│ ├── checkversions.js // 检查node&npm等版本
│ ├── devclient.js // 热加载相关
│ ├── devserver.js // 构建本地服务器
│ ├── utils.js // 构建配置公用工具
│ ├── vueloader.conf.js // vue加载器
│ ├── webpack.base.conf.js // webpack基础环境配置
│ ├── webpack.dev.conf.js // webpack开发环境配置
│ └── webpack.prod.conf.js // webpack生产环境配置
2、config
├── config// 项目开发环境配置相关代码 记忆: (环配) 3个
│ ├── dev.env.js // 开发环境变量(看词明意)
│ ├── index.js //项目一些配置变量
│ └── prod.env.js // 生产环境变量
3、node_modules
├──node_modules// 项目依赖的模块 记忆: (依赖) *个
4、src
├── src// 源码目录 5
1
│ ├── assets// 资源目录
│ │ └── logo.png
2
│ ├── components// vue公共组件
│ │ └── Hello.vue
3
│ ├──router// 前端路由
│ │ └── index.js// 路由配置文件
4
│ ├── App.vue// 页面入口文件(根组件)
5
│ └── main.js// 程序入口文件(入口js文件)
5、static
└── static// 静态文件,比如一些图片,json数据等
│ ├── .gitkeep
6、剩余的文件
├── .babelrc// ES6语法编译配置
├── .editorconfig// 定义代码格式
├── .gitignore// git上传需要忽略的文件格式
├── index.html// 入口页面
├── package.json// 项目基本信息
├── README.md// 项目说明
---------------------
蓝蓝设计( www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 、平面设计服务。
如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里
项目概述QQ 20周年,是互联网产品长青的一个传说,就在 20 周年的时间节点,QQ 向用户提供一份关于他自己的、真诚热切的 QQ 数据总结。通过数字,汇集出每个人自己的 QQ 时光历程,从而牵引出每个人对于成长、青春、沟通、时代流动的感知与回忆,引发 2019 年一场集体的情感共鸣。
△ QQ 20年来的一路变迁
项目历时两个月,从项目初期,视觉和产品密集沟通,了解需求,进行头脑风暴,输出多个视觉方案纵向对比,最后确定视觉风格,分配视觉工作(插画,动画,3D)。项目后期,包括开发还原,数据调配等众多环节,环环相扣,缺一不可,一路过关斩将,整个 H5 需要不同岗位的高度配合和各专业的高度默契。
△ H5的项目流程图
1. 异地合作
本次 H5 联动了动效,3D,插画设计师的共同合作,由于支持 3D 的设计师在韩办公,所以大部分都是在线上全英沟通,包括前期的项目进度同步,还有后期的模型调整等环节都能及时反馈,快速反应,最后如期打磨出五款 spaceQQ。算是一次顺利的异地合作。
△ 五款3D spaceQQ最终效果
2. 3D spaceQQ视觉还原
3D 鹅从设计软件转化为 H5 展示过程中,存在模型文件过大、材质缺失等问题,直接影响用户体验。在开发的过程中同学通过模型减面、重新选择材质,还有模型拆分等解决方法,在视觉观感和性能中建立了平衡的杠杆,最终实现了 15 个 SPACE QQ 的视觉还原。
△ 五款3D spaceQQ最终视觉还原效果
3. 视觉工作的同步进行
由于项目时间比较紧迫,而且 H5 加入了 3D 模型,还有需要大量的动效,所以需要在同一时间线上,同步进行,视觉稿输出,动画制作和 3D 模型打磨。考验设计师的沟通和执行能力。与此同时,需要随时和开发同步动画 demo 以确保动画可实现。和产品密集沟通,及时根据文案调整画面。
因为 QQ 是陪伴了大多数用户的一个产品,见证了整个互联网社交的演变过程,容易引起用户的情感共鸣。设计的初期,围绕「个人轨迹」的主题发散了不同方向的视觉概念,在引起客户共鸣感的复古元素和传递不断「探索」未来的概念间寻找平衡点。最后沿用了 20 周年的太空概念贴合「探索」的主题,结合有年代特征的代表性视觉符号来引起「个人轨迹」的这一概念的用户情感共鸣。另外,H5 运用了 3D 打造了 15 只太空鹅,打造「个人轨迹」的专属感,既联动用户温暖的回忆之余,也增添了一些小惊喜。
1. H5整体视觉风格
QQ,是陪伴了大多数人成长的一个互联网产品,其本身带有很多高辨识度的视觉元素,例如对话框,提醒上线的音效等。因为 H5 本身是一个大数据总结,专属感非常强的一个产物,所以希望是唤醒用户一些封尘已久的记忆,就像打开时间胶囊般的期待和感动。同时也象征着 QQ 一直陪伴在身边,从而引起用户的共鸣。
设计方面,除了通过一些标志性的视觉元素唤醒用户的回忆之外,也加入 QQ 20周年的太空「探索」主题的元素,响应 20 周年的主题之余,也寓意 QQ 不断地对外探索,从多个维度来看世界,寻找有趣的内容。
对话框,是承载数据的视觉符号,也是贯穿整个 H5 的重要视觉符号之一。寓意着 QQ 一直致力于「沟通」,而且不同时代的 QQ 对话框都各有特点,也是见证 QQ 时代变迁的重要元素,所以提取了三个阶段代表性的对话框样式来承载数据,并且加入有时代特征的视觉元素(如bb机,像素化的小箭头等)作为辅助,增加 H5 的氛围感,也算是视觉上的「小彩蛋」。
△ QQ原始对话框
H5 加入一些趣味感的元素,如笑脸,爱心等元素,背景辅以流动的彩色不规则图案,来增加对话框的玩味。也寓意 QQ 20年来给用户带来源源不断的乐趣,留下了不可取代的时代印记。
△ 重绘对话框
H5 中也加入了很多好玩的元素,不同时代所用的移动设备,融合贴近太空「探索」主题的背景;利用带手套的手和不同的元素进行互动;增加重绘经典头像的互动动画,加入 QQ 空间的植物等怀旧元素,增加 H5 的可玩性和惊喜感。
△ 经典头像的重绘
△ 经典头像穿插在H5中的小彩蛋
△ 带手套的手和有时代标签的元素互动
1. 视觉动态化方案
在动画制作前期会出一份详细的动态化分页策划,以及一份尽可能表达完整的动画 demo,能便于设计与开发能准确的估算出制作周期。在开发完成预研测试后,基于动画 demo 共同制定一个大致的动态化方案──对话框等大面积使用代码实现,其他装饰性小元素全部使用序列帧。在观众每滑一页即可触发当前页的动画,且能保证大体动画的流畅度。
2. 动画制作
为了尽可能减少 H5 的运算体积,又保证画面动态的流畅程度,导出的序列必须满足以下所有条件:
△ 设计了4个不同的色块流动loop,方便开发安插在每页合适的位置
3. 导出与同步
在配合开发导出的阶段,为了能明确序列的标记,所有序列文件命名为 xx-in/xx-loop/xx-out。使用同步工具以实现 AE 动画序列导出和开发提取素材能同步进行,并保持实时更新和迭代。
△ 第6页动画导出序列
创建了四款全新的 spaceQQ,根据用户的 QQ 年龄而设计。根据用户的 QQ 年龄,分了四个款式的鹅:奢华,智能,闪亮和神秘四个概念,一个递进的尊贵程度,刺激用户分享欲。包括基础款的 QQ 在内,共创造了 5 个类型的 spaceQQ。每个设计都基于基本 spaceQQ 的形式,但是套装的颜色和细节根据各自的概念各有特色。
△ spaceQQ总览图
用户可以根据自己的喜好更改这 5 款 spaceQQ 的装扮颜色。
1. 专属感
整个 H5 始终紧扣 QQ 20周年的太空「探索」主题,整个 H5,除了用数据唤醒用户和 QQ 多年的点点滴滴之外,5 个 spaceQQ 概念设定,既能增加用户的新鲜感,又能刺激用户的分享欲,C4D 建立的 3D 形象丰富了整个 H5 的视觉层次,深化了 QQ 品牌的影响力,寓意鹅厂一直与时俱进,紧贴潮流。
2. 情绪调动
对于当下的运营活动来说,趣味性是引爆转发量的重要元素,H5 结合 QQ 用户创作的背景音乐和能够调动用户情感的设计语言来释放用户的情绪,比如惊喜感(用户结合 QQ 的标志性音效创作的背景音乐),荣誉感(不同 Q 龄获取相应的 spaceQQ)等,让用户产生持续的惊喜感,在怀旧和新鲜感中得到满足,提升用户活跃性。
QQ 20周年 H5 设计,尝试结合了 3D,动画,插画等设计形态,为每一位用户打造专属的「个人轨迹」,通过数字,拼凑出和 QQ 的过往朝夕,引起用户共鸣。鹅厂不断创新探索的同时,也为用户温存属于用户自己的专属回忆,深化 QQ 的品牌价值。
福利
考虑到部分同学想更仔细地查看、保存或收藏高清大图的需求,我们设置了关键词,微信公众号后台以「spaceQQ+序号」的方式回复,例如「spaceQQ1」,即可逐一获取对应的高清头像和壁纸。
扫码领取专属「个人轨迹」:
蓝蓝设计( www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 、平面设计服务。
如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里
加粗样式
***@TOC
欢迎使用Markdown编辑器
你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。
新的改变
我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,帮助你用它写博客:
全新的界面设计 ,将会带来全新的写作体验;
在创作中心设置你喜爱的代码高亮样式,Markdown 将代码片显示选择的高亮样式 进行展示;
增加了 图片拖拽 功能,你可以将本地的图片直接拖拽到编辑区域直接展示;
全新的 KaTeX数学公式 语法;
增加了支持甘特图的mermaid语法1 功能;
增加了 多屏幕编辑 Markdown文章功能;
增加了 焦点写作模式、预览模式、简洁写作模式、左右区域同步滚轮设置 等功能,功能按钮位于编辑区域与预览区域中间;
增加了 检查列表 功能。
功能快捷键
撤销:Ctrl/Command + Z
重做:Ctrl/Command + Y
加粗:Ctrl/Command + B
斜体:Ctrl/Command + I
标题:Ctrl/Command + Shift + H
无序列表:Ctrl/Command + Shift + U
有序列表:Ctrl/Command + Shift + O
检查列表:Ctrl/Command + Shift + C
插入代码:Ctrl/Command + Shift + K
插入链接:Ctrl/Command + Shift + L
插入图片:Ctrl/Command + Shift + G
合理的创建标题,有助于目录的生成
直接输入1次#,并按下space后,将生成1级标题。
输入2次#,并按下space后,将生成2级标题。
以此类推,我们支持6级标题。有助于使用TOC语法后生成一个完美的目录。
如何改变文本的样式
强调文本 强调文本
加粗文本 加粗文本
标记文本
删除文本
引用文本
H2O is是液体。
210 运算结果是 1024.
插入链接与图片
链接: link.
图片:
带尺寸的图片:
居中的图片:
居中并且带尺寸的图片:
当然,我们为了让用户更加便捷,我们增加了图片拖拽功能。
如何插入一段漂亮的代码片
去博客设置页面,选择一款你喜欢的代码片高亮样式,下面展示同样高亮的 代码片.
// An highlighted block
var foo = 'bar';
1
2
生成一个适合你的列表
项目
项目
项目
项目1
项目2
项目3
计划任务
完成任务
创建一个表格
一个简单的表格是这么创建的:
项目 Value
电脑 $1600
手机 $12
导管 $1
设定内容居中、居左、居右
使用:---------:居中
使用:----------居左
使用----------:居右
第一列 第二列 第三列
第一列文本居中 第二列文本居右 第三列文本居左
SmartyPants
SmartyPants将ASCII标点字符转换为“智能”印刷标点HTML实体。例如:
TYPE ASCII HTML
Single backticks 'Isn't this fun?' ‘Isn’t this fun?’
Quotes "Isn't this fun?" “Isn’t this fun?”
Dashes -- is en-dash, --- is em-dash – is en-dash, — is em-dash
创建一个自定义列表
Markdown
Text-to-HTML conversion tool
Authors
John
Luke
如何创建一个注脚
一个具有注脚的文本。2
注释也是必不可少的
Markdown将文本转换为 HTML。
KaTeX数学公式
您可以使用渲染LaTeX数学表达式 KaTeX:
Gamma公式展示 Γ(n)=(n−1)!∀n∈N \Gamma(n) = (n-1)!\quad\foralln\in\mathbb NΓ(n)=(n−1)!∀n∈N 是通过欧拉积分
Unexpected text node: ' 'Unexpected text node: ' '
Γ(z)=∫
0
∞
t
z−1
e
−t
dt.
你可以找到更多关于的信息 LaTeX 数学表达式here.
新的甘特图功能,丰富你的文章
Mon 06
Mon 13
Mon 20
已完成
进行中
计划一
计划二
现有任务
Adding GANTT diagram functionality to mermaid
关于 甘特图 语法,参考 这儿,
UML 图表
可以使用UML图表进行渲染。 Mermaid. 例如下面产生的一个序列图::
张三
李四
王五
你好!李四, 最近怎么样?
你最近怎么样,王五?
我很好,谢谢!
我很好,谢谢!
李四想了很长时间,文字太长了不适合放在一行.
打量着王五...
很好... 王五, 你怎么样?
张三
李四
王五
这将产生一个流程图。:
链接
长方形
圆
圆角长方形
菱形
关于 Mermaid 语法,参考 这儿,
FLowchart流程图
我们依旧会支持flowchart的流程图:
开始
我的操作
确认?
结束
yes
no
关于 Flowchart流程图 语法,参考 这儿.
导出与导入
导出
如果你想尝试使用此编辑器, 你可以在此篇文章任意编辑。当你完成了一篇文章的写作, 在上方工具栏找到 文章导出 ,生成一个.md文件或者.html文件进行本地保存。
导入
如果你想加载一篇你写过的.md文件或者.html文件,在上方工具栏可以选择导入功能进行对应扩展名的文件导入,
继续你的创作。
mermaid语法说明 ↩︎
注脚的解释 ↩︎
蓝蓝设计( www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 、平面设计服务。
如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里
1. 如何实现一个响应式对象
最近在看 Vue 的源码,其中最核心基础的一块就是 Observer/Watcher/Dep, 简而言之就是,Vue 是如何拦截数据的读写, 如果实现对应的监听,并且特定的监听执行特定的回调或者渲染逻辑的。总的可以拆成三大块来说。这一块,主要说的是 Vue 是如何将一个 plain object 给处理成 reactive object 的,也就是,Vue 是如何拦截拦截对象的 get/set 的
我们知道,用 Object.defineProperty 拦截数据的 get/set 是 vue 的核心逻辑之一。这里我们先考虑一个最简单的情况 一个 plain obj 的数据,经过你的程序之后,使得这个 obj 变成 Reactive Obj (不考虑数组等因素,只考虑最简单的基础数据类型,和对象):
如果这个 obj 的某个 key 被 get, 则打印出 get ${key} - ${val} 的信息
如果这个 obj 的某个 key 被 set, 如果监测到这个 key 对应的 value 发生了变化,则打印出 set ${key} - ${val} - ${newVal} 的信息。
对应的简要代码如下:
Observer.js
export class Observer {
constructor(obj) {
this.obj = obj;
this.transform(obj);
}
// 将 obj 里的所有层级的 key 都用 defineProperty 重新定义一遍, 使之 reactive
transform(obj) {
const _this = this;
for (let key in obj) {
const value = obj[key];
makeItReactive(obj, key, value);
}
}
}
function makeItReactive(obj, key, val) {
// 如果某个 key 对应的 val 是 object, 则重新迭代该 val, 使之 reactive
if (isObject(val)) {
const childObj = val;
new Observer(childObj);
}
// 如果某个 key 对应的 val 不是 Object, 而是基础类型,我们则对这个 key 进行 defineProperty 定义
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: () => {
console.info(`get ${key}-${val}`)
return val;
},
set: (newVal) => {
// 如果 newVal 和 val 相等,则不做任何操作(不执行渲染逻辑)
if (newVal === val) {
return;
}
// 如果 newVal 和 val 不相等,且因为 newVal 为 Object, 所以先用 Observer迭代 newVal, 使之 reactive, 再用 newVal 替换掉 val, 再执行对应操作(渲染逻辑)
else if (isObject(newVal)) {
console.info(`set ${key} - ${val} - ${newVal} - newVal is Object`);
new Observer(newVal);
val = newVal;
}
// 如果 newVal 和 val 不相等,且因为 newVal 为基础类型, 所以用 newVal 替换掉 val, 再执行对应操作(渲染逻辑)
else if (!isObject(newVal)) {
console.info(`set ${key} - ${val} - ${newVal} - newVal is Basic Value`);
val = newVal;
}
}
})
}
function isObject(data) {
if (typeof data === 'object' && data != 'null') {
return true;
}
return false;
}
index.js
import { Observer } from './source/Observer.js';
// 声明一个 obj,为 plain Object
const obj = {
a: {
aa: 1
},
b: 2,
}
// 将 obj 整体 reactive 化
new Observer(obj);
// 无输出
obj.b = 2;
// set b - 2 - 3 - newVal is Basic Value
obj.b = 3;
// set b - 3 - [object Object] - newVal is Object
obj.b = {
bb: 4
}
// get b-[object Object]
obj.b;
// get a-[object Object]
obj.a;
// get aa-1
obj.a.aa
// set aa - 1 - 3 - newVal is Basic Value
obj.a.aa = 3
这样,我们就完成了 Vue 的第一个核心逻辑, 成功把一个任意层级的 plain object 转化成 reactive object
2. 如何实现一个 watcher
前面讲的是如何将 plain object 转换成 reactive object. 接下来讲一下,如何实现一个watcher.
实现的伪代码应如下:
伪代码
// 传入 data 参数新建新建一个 vue 对象
const v = new Vue({
data: {
a:1,
b:2,
}
});
// watch data 里面某个 a 节点的变动了,如果变动,则执行 cb
v.$watch('a',function(){
console.info('the value of a has been changed !');
});
// watch data 里面某个 b 节点的变动了,如果变动,则执行 cb
v.$watch('b',function(){
console.info('the value of b has been changed !');
})
Vue.js
// 引入将上面中实现的 Observer
import { Observer } from './Observer.js';
import { Watcher } from './Watcher.js';
export default class Vue {
constructor(options) {
// 在 this 上挂载一个公有变量 $options ,用来暂存所有参数
this.$options = options
// 声明一个私有变量 _data ,用来暂存 data
let data = this._data = this.$options.data
// 在 this 上挂载所有 data 里的 key 值, 这些 key 值对应的 get/set 都被代理到 this._data 上对应的同名 key 值
Object.keys(data).forEach(key => this._proxy(key));
// 将 this._data 进行 reactive 化
new Observer(data, this)
}
// 对外暴露 $watch 的公有方法,可以对某个 this._data 里的 key 值创建一个 watcher 实例
$watch(expOrFn, cb) {
// 注意,每一个 watcher 的实例化都依赖于 Vue 的实例化对象, 即 this
new Watcher(this, expOrFn, cb)
}
// 将 this.keyName 的某个 key 值的 get/set 代理到 this._data.keyName 的具体实现
_proxy(key) {
var self = this
Object.defineProperty(self, key, {
configurable: true,
enumerable: true,
get: function proxyGetter() {
return self._data[key]
},
set: function proxySetter(val) {
self._data[key] = val
}
})
}
}
Watch.js
// 引入Dep.js, 是什么我们待会再说
import { Dep } from './Dep.js';
export class Watcher {
constructor(vm, expOrFn, cb) {
this.cb = cb;
this.vm = vm;
this.expOrFn = expOrFn;
// 初始化 watcher 时, vm._data[this.expOrFn] 对应的 val
this.value = this.get();
}
// 用于获取当前 vm._data 对应的 key = expOrFn 对应的 val 值
get() {
Dep.target = this;
const value = this.vm._data[this.expOrFn];
Dep.target = null;
return value;
}
// 每次 vm._data 里对应的 expOrFn, 即 key 的 setter 被触发,都会调用 watcher 里对应的 update方法
update() {
this.run();
}
run() {
// 这个 value 是 key 被 setter 调用之后的 newVal, 然后比较 this.value 和 newVal, 如果不相等,则替换 this.value 为 newVal, 并执行传入的cb.
const value = this.get();
if (value !== this.value) {
this.value = value;
this.cb.call(this.vm);
}
}
}
对于什么是 Dep, 和 Watcher 里的 update() 方法到底是在哪个时候被谁调用的,后面会说
3. 如何收集 watcher 的依赖
前面我们讲了 watcher 的大致实现,以及 Vue 代理 data 到 this 上的原理。现在我们就来梳理一下,Observer/Watcher 之间的关系,来说明它们是如何调用的.
首先, 我们要来理解一下 watcher 实例的概念。实际上 Vue 的 v-model, v-bind , {{ mustache }}, computed, watcher 等等本质上是分别对 data 里的某个 key 节点声明了一个 watcher 实例.
<input v-model="abc">
<span>{{ abc }}</span>
<p :data-key="abc"></p>
...
const v = new Vue({
data:{
abc: 111,
}
computed:{
cbd:function(){
return `${this.abc} after computed`;
}
watch:{
abc:function(val){
console.info(`${val} after watch`)
}
}
}
})
这里,Vue 一共声明了 4 个 watcher 实例来监听abc, 1个 watcher 实例来监听 cbd. 如果 abc 的值被更改,那么 4 个 abc - watcher 的实例会执行自身对应的特定回调(比如重新渲染dom,或者是打印信息等等)
不过,Vue 是如何知道,某个 key 对应了多少个 watcher, 而 key 对应的 value 发生变化后,又是如何通知到这些 watcher 来执行对应的不同的回调的呢?
实际上更深层次的逻辑是:
在 Observer阶段,会为每个 key 都创建一个 dep 实例。并且,如果该 key 被某个 watcher 实例 get, 把该 watcher 实例加入 dep 实例的队列里。如果该 key 被 set, 则通知该 key 对应的 dep 实例, 然后 dep 实例会将依次通知队列里的 watcher 实例, 让它们去执行自身的回调方法
dep 实例是收集该 key 所有 watcher 实例的地方.
watcher 实例用来监听某个 key ,如果该 key 产生变化,便会执行 watcher 实例自身的回调
相关代码如下:
Dep.js
export class Dep {
constructor() {
this.subs = [];
}
// 将 watcher 实例置入队列
addSub(sub) {
this.subs.push(sub);
}
// 通知队列里的所有 watcher 实例,告知该 key 的 对应的 val 被改变
notify() {
this.subs.forEach((sub, index, arr) => sub.update());
}
}
// Dep 类的的某个静态属性,用于指向某个特定的 watcher 实例.
Dep.target = null
observer.js
import {Dep} from './dep'
function makeItReactive(obj, key, val) {
var dep = new Dep()
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: () => {
// 收集依赖! 如果该 key 被某个 watcher 实例依赖,则将该 watcher 实例置入该 key 对应的 dep 实例里
if(Dep.target){
dep.addSub(Dep.target)
}
return val
},
set: (newVal) => {
if (newVal === val) {
return;
}
else if (isObject(newVal)) {
new Observer(newVal);
val = newVal;
// 通知 dep 实例, 该 key 被 set,让 dep 实例向所有收集到的该 key 的 watcher 实例发送通知
dep.notify()
}
else if (!isObject(newVal)) {
val = newVal;
// 通知 dep 实例, 该 key 被 set,让 dep 实例向所有收集到的该 key 的 watcher 发送通知
dep.notify()
}
}
})
}
watcher.js
import { Dep } from './Dep.js';
export class Watcher {
constructor(vm, expOrFn, cb) {
this.cb = cb;
this.vm = vm;
this.expOrFn = expOrFn;
this.value = this.get();
}
get() {
// 在实例化某个 watcher 的时候,会将Dep类的静态属性 Dep.target 指向这个 watcher 实例
Dep.target = this;
// 在这一步 this.vm._data[this.expOrFn] 调用了 data 里某个 key 的 getter, 然后 getter 判断类的静态属性 Dep.target 不为null, 而为 watcher 的实例, 从而把这个 watcher 实例添加到 这个 key 对应的 dep 实例里。 巧妙!
const value = this.vm._data[this.expOrFn];
// 重置类属性 Dep.target
Dep.target = null;
return value;
}
// 如果 data 里的某个 key 的 setter 被调用,则 key 会通知到 该 key 对应的 dep 实例, 该Dep实例, 该 dep 实例会调用所有 依赖于该 key 的 watcher 实例的 update 方法。
update() {
this.run();
}
run() {
const value = this.get();
if (value !== this.value) {
this.value = value;
// 执行 cb 回调
this.cb.call(this.vm);
}
}
}
总结:
至此, Watcher, Observer , Dep 的关系全都梳理完成。而这些也是 Vue 实现的核心逻辑之一。再来简单总结一下三者的关系,其实是一个简单的 观察-订阅 的设计模式, 简单来说就是, 观察者观察数据状态变化, 一旦数据发生变化,则会通知对应的订阅者,让订阅者执行对应的业务逻辑 。我们熟知的事件机制,就是一种典型的观察-订阅的模式
Observer, 观察者,用来观察数据源变化.
Dep, 观察者和订阅者是典型的 一对多 的关系,所以这里设计了一个依赖中心,来管理某个观察者和所有这个观察者对应的订阅者的关系, 消息调度和依赖管理都靠它。
Watcher, 订阅者,当某个观察者观察到数据发生变化的时候,这个变化经过消息调度中心,最终会传递到所有该观察者对应的订阅者身上,然后这些订阅者分别执行自身的业务回调即可
参考
Vue源码解读-滴滴FED
代码参考
蓝蓝设计( www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 、平面设计服务。
如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里
视觉设计师作为展示产品最终形态的执行层,产品上线前走查视觉与交互还原是必经环节,而留给设计师走查修改的时间其实非常少,有时候为了配合产品上线时间,通常只能牺牲一些细节,在下一次迭代进行优化。为了每一次上线的产品都能够得到更好地还原,这就需要设计师去了解开发到底是根据哪些规则还原我们的设计稿,以及在每一次制作和交付设计稿的时候,我们应如何设定好每一个细节的规则。开发:这里已经完全对齐了。
视觉:看起来还没完全对齐,我的图也没有切错吧?
开发:字体大小和间距都是按照视觉稿来的。
视觉:这里间距偏差这么大,为什么不按照视觉稿?
开发:视觉样式好多,每个设计师的间距好像都不一样。
视觉:……
我们经常会听到身边的设计师与开发小哥的一些对话,关于对齐、大小、间距等设计还原问题经常会讨论很久,有时甚至会觉得,几个像素的间距是不是没有必要这么纠结。以我较常接触的云产品官网为例,云产品官网体量庞大,单个页面或信息模块的样式复用可高达上百个子产品页面,此时第一个模块设计的规范性、扩展性、复用性则变得尤为重要,所以为了让设计方案更加合理,为了让合作更加,这里总结一些设计经验,与大家一起探讨。
本文将从以下三个方面,思考作为视觉设计师,应当如何让设计更加合理有效:
1. 字体结构
网页设计中,我们总避免不了与字体打交道,字体也是我们在设计中经常容易忽视的部分,而经常出错的原因往往是因为我们对文字的理解不够清晰。相比西文字体,中文字体结构复杂,字库庞大,网页的渲染效果会比西文字体艰难很多。
但无论是中文还是西文,我们经常需要用到的无非是字体大小、字重、字色,还有就是经常被我们忽视的行高和行宽,我们从西文字体提取三个最主要的因素,即字高、行高、行宽。基于西文字体的结构转换为中文,我们可以理解为,字高指的就是我们肉眼所能看到的字体的实际高度,而行高指的是字高+上边距+下边距,反过来说,行高减去字高除以 2 就能得到我们的上下边距,行宽指的就是整个文本的宽度。
举一个图文模块的例子,图(1)中我们肉眼所看到间距,在我们做标注时,看到的其实是图(2)中的三个色块,我们实际给到开发的标注,是色块的尺寸和色块之间的间距,以及详细的文本属性。
2. 文字行宽
关于行宽,以设计 banner 的标题及描述文字为例,定义行宽是为了让文本在极限宽度的时候进行换行,再固定好配图的尺寸,从而得到文本与配图之间的间距,定义行宽、行数、字数,能够更好地为运营人员规范输出的文案,避免因字数过多或过少所造成的视觉不平衡。
当我们处理无序列表时,四个短句文本,长短不一,同样我们需要限制一行文本宽度,定义一行能承载的最多字数,以及跟产品确认可能出现的最多字数的情况,确认模块设计的可行性,保证后续运营人员在替换文案的时候不会出错。
以上两个例子都是我们设计文字经常出错的地方,正确的定义规范,无论是交付开发或者其他下游,都能保证模块设计的可扩展性及规范化,保证最终上线质量。
3. 图标视错觉
关于图标,这里提到一个几何学错觉的概念,视觉上的大小、长度、面积、方向、角度等几何构成,和实际上测得的数字有明显差别的错觉,称为几何学错觉。人眼所接受的视觉平衡,往往不是设计软件上精准的对齐,我们总是会通过调整间距、大小或角度来补齐一些负空间,让画面保持视觉平衡。
以客户案例的卡片样式为例,客户案例在 to B 产品中是必不可少的模块,我们的客户 logo 有的圆形,有的长方形,有的纯文字,尺寸差距比较悬殊,这种情况下我们需要给他限制一个高度,在这个高度以内,再根据 logo 本身的体量来调整图形的大小,处理好 logo 的视觉平衡,最后红色区域是 logo 的实际尺寸,蓝色区域则是我们实际给到开发的尺寸,从开发的角度来看其实就是占位符,而规范的作图则是把占位符的透明度调整为 0,以占位符为实际有效作图区。
UI 设计中通常以「向右箭头」来表示当前链接可跳转,使用箭头作图时,当我们把箭头和文字右对齐,箭头其实会更加的往外突出,这时候我们会人为的往里边推 1 至 2 像素,最后实际给到开发的也应该是红框的尺寸,也就是 16×16 的占位图尺寸。
「按钮」也是 UI 设计中常用的组件,当我们在按钮里使用图标加文字时,由于文字的体量更大,整体重心会往右偏,所以我们通常会认为让图标和文字整体往左偏移,使整体的视觉更加平衡,实际给到开发的,也是两个不同等的边距。
1. 理性的设计
在 iOS 和 Android 的设计规范中,都有提到过使用「8点栅格」的概念,即建议使用 8×8 的网格系统进行设计,我们都知道 0.5px 的渲染在屏幕上会变模糊,之所以使用 8 的倍数是因为市场上主流的屏幕都能被 8 整除,使用 8 点栅格能够的让我们所设计的内容样式在屏幕上保持高清显示,而在日常的网页设计中,我其实更加倾向使用 4 点栅格系统。
我们以下图 4 组数列为例,大家可能都曾使用过上面三组蓝色数列中的数值应用到设计中,或以 5 为倍数,或以 10 为倍数、或以偶数为设计逻辑,而实际上以 5 为倍数则会包含奇数,奇数会导致控件文字对不齐,当 5 的倍数和偶数同时使用时,则会出现类似 14、15、16 这种相差为 1 的相邻数,这样会导致我们的尺寸规范不好定义规则,难以形成逻辑,而使用 4 的倍数,他们的公差为 4,不会出现奇数,也不会出现相邻数。
我们再看看一些通用的尺寸定义,例如常见的 icon 设计尺寸都是以 4 为倍数。
常见的网页栅格及其所均分的卡片和间距,也都是 4 的倍数,如果我们的控件尺寸,图标尺寸和间距都使用 4 的倍数来定义,那所有的信息模块自然都能更好的相互适应,层层递进的逻辑关系也会更加明显。
我们把 4 点栅格的设计逻辑套用到卡片设计上,第一眼我们可能比较难去评判两者的好坏,但仔细看,我们就会发现第一个卡片的按钮没有水平对齐,相互之间的间距尺寸也是没什么逻辑性。假如今天调整一个 8px 的间距,明天调一个 10px 的间距,设计师走查起来也很难发现有问题,对接的开发也难以有一个可以参考的规范标准。而相对的,以 4 为倍数,我们会发现所有的信息都会完美对齐,而且倍数为 4 的每个数值之间公差为 4,即使设计稿微调了 1px 我们都能很快发现,开发在还原设计稿时也会有一个衡量标准。
网格设计在报纸、杂志、海报等平面设计领域中也是十分常见的设计手法,通过建立网格,考究每一个信息模块在页面中的摆放位置,大小占比,颜色占比,从而使得页面信息保持秩序、均衡。
使用 4 点栅格系统,通过理性、秩序、逻辑的设计方式赋予画面秩序感以及阅读体验,而以 4 为倍数,每个数字之间都相差为 4,不会太大,也不会太小,同时保持着秩序,让设计更加理性。对于团队合作,设计师与开发也将更有默契,不必再为不清不楚的间距浪费时间。
1. 有效切图
关于切图,切图之前应跟开发确定好输出的格式和尺寸,确定应该用 SVG,一倍图或是两倍图。SVG 体量小渲染质量好,单色使用时还能替换颜色,PNG 则通常用在实景图,一倍图和二倍图则根据实际需要进行输出。
如果要做分层动画,那我们就需要分层切图,如果桌面端和手机端样式差别较大,那我们需要和开发沟通好如何实现,是否需要特殊切图,所有的特殊切图和特殊样式,我们都应该提前跟开发沟通好。
2. 交互细节
如果某个控件或信息模块交互样式较多,那我们可以有一个空白画板来清晰地标注这些状态和样式,很多开发在还原过程中都是一手视觉稿一手交互稿,但视觉设计师作为展示产品最终形态的执行层,很多情况下,视觉阶段依然会有很多需要跟交互和产品沟通修改的地方,所以为了避免遗漏修改点,视觉稿应该呈现最完整的设计细节,这样也会很大程度上节省开发的时间,减少出错的几率。
当页面内容有一定的更新频率,我们则需要标明视觉样式规范,以及后续的运营规则,完整的收尾,可以避免产品经常过来寻求上线素材和规范。有些需要隔三个月或半年才上线的需求,清晰的标注也能帮助我们快速回忆起需求背景,让我们在日常工作中保持头脑清晰,有条不紊,这其实也是在给我们自己节省时间。
3. 重构稿走查
走查还原的时候,在 Chrome 浏览器的空白处右键点击检查,找到里面的 Computed 窗口,我们可以找到具体的文字、间距、投影等属性,有时候一些比较细微的调整,我们可以双击具体的数值进行调整,调整到自己满意之后再把具体的数值给到开发,这样就不用在我们摇摆不定的情况下,造成双方的困扰。
最后,在预发布的时候,我们可以利用 SwitchHosts 的客户端来配置开发环境进行体验,保证最终上线的效果。
蓝蓝设计( www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 、平面设计服务。
蓝蓝设计的小编 http://www.lanlanwork.com